refac: stale test entries, failing web tests, Postgre backup bug, Hardcoded MySQL table count, Stale Postgre connection in test, own-scope visibility filter, GetAll ignoring its error, JSON tags in use-case types, 31 copies of DESKTOP_QUERY, ProfileView endpoint paths
This commit is contained in:
1 parent
efecdee200
commit
5d6f378fce
87 files changed
+696
-373
No files matched your search
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import ApiSearchPanel from '@/components/ApiSearchPanel.vue'
|
||||
import type { SearchEntry, SearchIndex } from '@/lib/apiSearch'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
defineProps<{
|
||||
index: SearchIndex
|
||||
@@ -12,10 +12,8 @@ defineProps<{
|
||||
|
||||
const emit = defineEmits<{ select: [entry: SearchEntry] }>()
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 64rem)'
|
||||
|
||||
const open = defineModel<boolean>('open', { required: true })
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('64rem')
|
||||
|
||||
const contentClass = computed(() =>
|
||||
isDesktop.value
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { pageItems, rangeFor } from '@/lib/pagination'
|
||||
import PageSizeSelect from '@/components/PageSizeSelect.vue'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const props = defineProps<{
|
||||
page: number
|
||||
@@ -23,7 +21,7 @@ const emit = defineEmits<{
|
||||
'update:size': [size: number]
|
||||
}>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
|
||||
const busy = computed(() => props.loadingPage !== null)
|
||||
const atStart = computed(() => busy.value || props.page <= 1)
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref, useId } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import AuthorAvatar from '@/components/AuthorAvatar.vue'
|
||||
import type { Byline } from '@/composables/useArticles'
|
||||
import { moveItem } from '@/lib/arrayMove'
|
||||
import { insertionTarget } from '@/lib/articleMeta'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 64rem)'
|
||||
const DESKTOP_AVATAR = 22
|
||||
const MOBILE_AVATAR = 28
|
||||
|
||||
@@ -19,7 +18,7 @@ const props = withDefaults(defineProps<{ authors: Byline[]; hintHidden?: boolean
|
||||
const emit = defineEmits<{ 'update:authors': [authors: Byline[]] }>()
|
||||
|
||||
const labelId = useId()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('64rem')
|
||||
const list = ref<HTMLElement | null>(null)
|
||||
const working = ref<Byline[] | null>(null)
|
||||
const grabbed = ref<number | null>(null)
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ColumnPickerList from '@/components/ColumnPickerList.vue'
|
||||
import type { BuiltinField } from '@/lib/contentTypes'
|
||||
import { COLUMN_CAP } from '@/lib/entryColumns'
|
||||
import { BOTTOM_SHEET_UI } from '@/lib/sheetUi'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const FOCUS_RING =
|
||||
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
|
||||
const TRIGGER = `h-11 w-11 justify-center rounded-none border border-accented bg-default p-0 text-highlighted ring-0 md:h-8 md:w-auto md:px-3 ${FOCUS_RING}`
|
||||
@@ -17,7 +16,7 @@ defineProps<{ fields: BuiltinField[]; keys: string[]; atCap: boolean; disabled?:
|
||||
|
||||
const emit = defineEmits<{ toggle: [key: string, on: boolean]; reset: [] }>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const open = ref(false)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ColumnPicker from '@/components/ColumnPicker.vue'
|
||||
import SortSelect from '@/components/SortSelect.vue'
|
||||
import type { BuiltinField } from '@/lib/contentTypes'
|
||||
import type { EntrySortField } from '@/lib/entryListQuery'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
defineProps<{
|
||||
sort: string
|
||||
@@ -25,7 +23,7 @@ const emit = defineEmits<{
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import DirectoryRowMenu from '@/components/DirectoryRowMenu.vue'
|
||||
import type { DirectoryMenuItem } from '@/lib/directoryMenu'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const DEFAULT_SKELETON_ROWS = 3
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -31,7 +30,7 @@ const props = withDefaults(
|
||||
|
||||
const emit = defineEmits<{ open: [item: T] }>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const openMenuId = ref<number | null>(null)
|
||||
|
||||
function setMenuOpen(item: T, isOpen: boolean) {
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref, useId } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import type { DirectoryNotice } from '@/lib/directoryTypes'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const AUTOFOCUS_SELECTOR = '[data-autofocus]'
|
||||
const INVALID_SELECTOR = '[aria-invalid="true"]'
|
||||
|
||||
@@ -38,7 +37,7 @@ const emit = defineEmits<{ close: []; submit: [] }>()
|
||||
const open = defineModel<boolean>('open', { default: true })
|
||||
|
||||
const overlay = useLocalizedOverlay()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const formId = useId()
|
||||
const form = ref<{ $el: HTMLElement } | null>(null)
|
||||
const side = computed(() => (isDesktop.value ? 'right' : 'bottom'))
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import { useElementBounding, useMediaQuery } from '@vueuse/core'
|
||||
import { useElementBounding } from '@vueuse/core'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const CONTENT_SELECTOR = '#dashboard-content'
|
||||
|
||||
defineProps<{
|
||||
@@ -18,7 +18,7 @@ const emit = defineEmits<{
|
||||
clear: []
|
||||
}>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const content = useElementBounding(() => document.querySelector<HTMLElement>(CONTENT_SELECTOR))
|
||||
const centre = computed(() => `${content.left.value + content.width.value / 2}px`)
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import MediaArticleStatusBadge from '@/components/MediaArticleStatusBadge.vue'
|
||||
import MediaThumb from '@/components/MediaThumb.vue'
|
||||
import MediaVariantsTab from '@/components/variants/MediaVariantsTab.vue'
|
||||
@@ -19,8 +18,8 @@ import { usageSplitOf } from '@/lib/mediaUsage'
|
||||
import { PANEL_TAB_TRIGGER } from '@/lib/tabStyles'
|
||||
import { useCanManageVariants } from '@/composables/useCanManageVariants'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const ACTION_BUTTON =
|
||||
'h-11 justify-center border border-accented bg-default px-3 text-sm ring-0 max-md:flex-1 md:h-8'
|
||||
|
||||
@@ -41,7 +40,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false })
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const canManageVariants = useCanManageVariants()
|
||||
const permissions = usePermissionsStore()
|
||||
const canRename = computed(() => permissions.can('media.upload'))
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref, useId } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import DirectoryField from '@/components/DirectoryField.vue'
|
||||
import ImageUploader from '@/components/ImageUploader.vue'
|
||||
import type { MediaItem } from '@/composables/useMedia'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
import {
|
||||
imageButtonName,
|
||||
imageMeta,
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
type MediaSummary,
|
||||
} from '@/lib/mediaField'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const BUTTON_CLASS = 'h-11 gap-1.5 px-3 text-sm text-highlighted disabled:opacity-40 md:h-8'
|
||||
const OUTLINE_CLASS = `${BUTTON_CLASS} border border-accented bg-default ring-0`
|
||||
const ROW_BUTTON_CLASS = `${BUTTON_CLASS} max-md:flex-1`
|
||||
@@ -46,7 +45,7 @@ const labelId = `${id}-label`
|
||||
const helpId = `${id}-help`
|
||||
const errorId = `${id}-error`
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const pickerOpen = ref(false)
|
||||
const hovered = ref(false)
|
||||
const announcement = ref('')
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import MediaThumb from '@/components/MediaThumb.vue'
|
||||
import VariantPickerField from '@/components/variants/VariantPickerField.vue'
|
||||
import type { LibraryStatus } from '@/composables/useLibraryList'
|
||||
@@ -10,9 +9,9 @@ import type { ImageVariant } from '@/composables/useImageVariants'
|
||||
import type { MediaItem } from '@/composables/useMedia'
|
||||
import type { FieldPick } from '@/lib/imageChoice'
|
||||
import { accessibleName, formatDimensions, formatSize } from '@/lib/mediaFormat'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 120
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const FIRST_LOAD_SKELETONS_DESKTOP = 8
|
||||
const FIRST_LOAD_SKELETONS_MOBILE = 9
|
||||
const COLUMNS_DESKTOP = 4
|
||||
@@ -62,7 +61,7 @@ const tileAspect = computed(() =>
|
||||
const alt = defineModel<string>('alt', { default: '' })
|
||||
const variant = defineModel<string>('variant', { default: '' })
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const grid = ref<HTMLElement | null>(null)
|
||||
const searchText = ref(props.query)
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import MediaVariantsFilter from '@/components/MediaVariantsFilter.vue'
|
||||
import type { MediaCounts } from '@/composables/useMedia'
|
||||
import type {
|
||||
@@ -13,9 +12,9 @@ import type {
|
||||
MediaView,
|
||||
} from '@/lib/mediaQuery'
|
||||
import { UNDERLINE_TAB_TRIGGER } from '@/lib/tabStyles'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 120
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
|
||||
const SORT_LABELS: Record<MediaSort, string> = {
|
||||
get newest() {
|
||||
@@ -71,7 +70,7 @@ const emit = defineEmits<{
|
||||
purge: []
|
||||
}>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const searchText = ref(props.controls.q)
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, ref, watch } from 'vue'
|
||||
import { useEventListener, useMediaQuery } from '@vueuse/core'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import { MEDIA_USAGE_KEY } from '@/lib/mediaUsageContext'
|
||||
import { tooltipLines } from '@/lib/mediaUsage'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const TOOLTIP_UI = {
|
||||
content:
|
||||
'h-auto flex-col items-start gap-0 bg-inverted px-2 py-1.5 text-xs/4.5 text-inverted shadow-(--shadow-popover) ring-0',
|
||||
@@ -17,7 +17,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const usage = inject(MEDIA_USAGE_KEY, null)
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const hovered = ref(false)
|
||||
const dismissed = ref(false)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ApiPreview from '@/components/ApiPreview.vue'
|
||||
import ApiPreviewSheet from '@/components/ApiPreviewSheet.vue'
|
||||
import BuilderActions from '@/components/BuilderActions.vue'
|
||||
@@ -22,12 +21,11 @@ import { useUnsavedChangesGuard } from '@/composables/useUnsavedChangesGuard'
|
||||
import { sampleJson } from '@/lib/schemaBuilderSample'
|
||||
import { entriesNotice } from '@/lib/schemaEntries'
|
||||
import { formatClock } from '@/lib/updateFormat'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const props = defineProps<{ edit?: EditSource }>()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const builder = useSchemaBuilder(props.edit)
|
||||
const { form } = builder
|
||||
const remove = props.edit ? useSchemaDelete(() => props.edit!) : undefined
|
||||
|
||||
@@ -3,7 +3,7 @@ import AppSwitch from '@/components/AppSwitch.vue'
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { createReusableTemplate, useMediaQuery } from '@vueuse/core'
|
||||
import { createReusableTemplate } from '@vueuse/core'
|
||||
import DirectoryField from '@/components/DirectoryField.vue'
|
||||
import DirectoryPanel from '@/components/DirectoryPanel.vue'
|
||||
import FallbackChip from '@/components/FallbackChip.vue'
|
||||
@@ -34,8 +34,8 @@ import {
|
||||
type SeoPreviewInput,
|
||||
} from '@/lib/seoPreview'
|
||||
import type { SeoFocus, SeoSuggestion, SuggestionCode, SuggestionField } from '@/lib/seoTypes'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const PANEL_FIELDS: SuggestionField[] = [
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
@@ -79,7 +79,7 @@ const [DefineFields, Fields] = createReusableTemplate()
|
||||
const [DefinePreviews, Previews] = createReusableTemplate()
|
||||
const [DefinePreviewButton, PreviewButton] = createReusableTemplate()
|
||||
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const layout = ref<HTMLElement | null>(null)
|
||||
const focusTarget = ref<SeoFocus | null>(props.focus)
|
||||
const device = ref<PreviewDevice>('desktop')
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import { locale, t } from '@/i18n'
|
||||
|
||||
import { computed, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { colorModeOptions, useAccountMenu } from '@/composables/useAccountMenu'
|
||||
import { chooseLanguage, languageOptions } from '@/composables/useLanguage'
|
||||
import { useAccountMenuRadios } from '@/composables/useRadioMenuRoles'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 64rem)'
|
||||
const ITEM_CLASS = 'h-8 items-center gap-2.5 px-2 py-0 text-sm font-normal text-highlighted'
|
||||
const CHOICE_CLASS = 'h-8 items-center gap-2.5 px-2 py-0 text-base font-normal text-highlighted'
|
||||
const SECTION_LABEL_CLASS =
|
||||
@@ -27,7 +26,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const { colorMode, name, initials, onProfile, goToProfile } = useAccountMenu(() => props)
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('64rem')
|
||||
const open = ref(false)
|
||||
useAccountMenuRadios(open)
|
||||
const chip = ref<{ $el: HTMLElement } | null>(null)
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { t } from '@/i18n'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ProfileCard from '@/components/ProfileCard.vue'
|
||||
import RoleBadge from '@/components/RoleBadge.vue'
|
||||
import RoleSummaryDialog from '@/components/RoleSummaryDialog.vue'
|
||||
import { buildHeldRolePreview } from '@/lib/rolePreview'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const permissions = usePermissionsStore()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const summaryOpen = ref(false)
|
||||
|
||||
const model = computed(() => buildHeldRolePreview(permissions.grants))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import VariantFormFields from '@/components/variants/VariantFormFields.vue'
|
||||
@@ -19,8 +18,8 @@ import {
|
||||
type VariantFormValues,
|
||||
} from '@/lib/variants/variantForm'
|
||||
import { useImageVariantsStore } from '@/stores/imageVariants'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 40rem)'
|
||||
const CONFLICT = 409
|
||||
const UNPROCESSABLE = 422
|
||||
const TOAST_STATUSES = [400, 403, 404]
|
||||
@@ -39,7 +38,7 @@ const emit = defineEmits<{
|
||||
const store = useImageVariantsStore()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const { announceFailure } = useVariantToasts()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('40rem')
|
||||
const form = useVariantForm(props.mode, props.initial)
|
||||
const side = computed(() => (isDesktop.value ? 'right' : 'bottom'))
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ describe('getMe maps the account fields', () => {
|
||||
await expect(useApi().getMe()).resolves.toEqual({
|
||||
id: 1,
|
||||
verified: true,
|
||||
access: ownerAccess(),
|
||||
access: { ...ownerAccess(), roleAssignedBy: null },
|
||||
username: 'marta',
|
||||
email: 'marta@renew.example',
|
||||
displayName: 'Marta Alves',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useContentTypeDetail } from '@/composables/useContentTypeDetail'
|
||||
import { useEntryColumns } from '@/composables/useEntryColumns'
|
||||
import { useEntryList } from '@/composables/useEntryList'
|
||||
@@ -8,8 +7,8 @@ import { resolveContentListState } from '@/lib/contentListState'
|
||||
import { entryCaption } from '@/lib/entryListQuery'
|
||||
import { fieldCountLabel } from '@/lib/schemaRows'
|
||||
import { HOME_PATH, entryPath, schemaPath } from '@/lib/routePaths'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const DESKTOP_SKELETON_ROWS = 8
|
||||
const MOBILE_SKELETON_ROWS = 6
|
||||
|
||||
@@ -19,7 +18,7 @@ export function useContentListPage(
|
||||
canEdit: () => boolean,
|
||||
) {
|
||||
const router = useRouter()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const list = useEntryList(typeKey)
|
||||
const columns = useEntryColumns(typeKey, () => detail.type.value?.fields ?? [])
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { t } from '@/i18n'
|
||||
import { useLocalizedToast } from '@/i18n/presentation'
|
||||
import { useEntryDraft } from '@/composables/useEntryDraft'
|
||||
@@ -12,8 +11,8 @@ import { entryTitle } from '@/lib/entryForm'
|
||||
import type { EntryTarget } from '@/lib/entryEditorState'
|
||||
import { formatEntryStamp, formatSavedClock } from '@/lib/entryStamp'
|
||||
import { contentPath, entryPath } from '@/lib/routePaths'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const SAVED_TOAST_ID = 'entry-saved'
|
||||
const MISSING_NAME = '–'
|
||||
|
||||
@@ -26,7 +25,7 @@ export function useEntryEditorPage(
|
||||
) {
|
||||
const router = useRouter()
|
||||
const toast = useLocalizedToast()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const editor = useEntryDraft(typeKey, target)
|
||||
const { detail, load, form } = editor
|
||||
const names = useEntryNames(() => load.entry.value)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
|
||||
export type DesktopBreakpoint = '40rem' | '48rem' | '64rem'
|
||||
|
||||
export function useIsDesktop(minWidth: DesktopBreakpoint = '48rem') {
|
||||
return useMediaQuery(`(min-width: ${minWidth})`)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useApi } from '@/composables/useApi'
|
||||
|
||||
const JSON_HEADERS = { 'Content-Type': 'application/json' }
|
||||
|
||||
export function useProfile() {
|
||||
const { request } = useApi()
|
||||
|
||||
function updateDisplayName(displayName: string) {
|
||||
return request<{ display_name?: string | null }>('/v1/user/me', {
|
||||
method: 'PATCH',
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ display_name: displayName }),
|
||||
})
|
||||
}
|
||||
|
||||
function sendPasswordReset(email: string) {
|
||||
return request('/v1/auth/password-reset', {
|
||||
method: 'POST',
|
||||
headers: JSON_HEADERS,
|
||||
body: JSON.stringify({ email }),
|
||||
})
|
||||
}
|
||||
|
||||
return { updateDisplayName, sendPasswordReset }
|
||||
}
|
||||
@@ -42,7 +42,11 @@ beforeEach(() => {
|
||||
setViewportWidth(DESKTOP_WIDTH)
|
||||
setActivePinia(createPinia())
|
||||
useSessionStore().status = 'ready'
|
||||
request.mockReset().mockResolvedValue([])
|
||||
request
|
||||
.mockReset()
|
||||
.mockImplementation(async (path: string) =>
|
||||
path === '/v1/image-variants' ? { items: [] } : [],
|
||||
)
|
||||
vi.mocked(useApi).mockReturnValue({
|
||||
request,
|
||||
getMe: vi.fn().mockResolvedValue(null),
|
||||
@@ -50,10 +54,11 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
vi.unstubAllGlobals()
|
||||
vi.restoreAllMocks()
|
||||
wrapper?.unmount()
|
||||
wrapper = undefined
|
||||
await flushPromises()
|
||||
vi.unstubAllGlobals()
|
||||
vi.restoreAllMocks()
|
||||
document.body.innerHTML = ''
|
||||
localStorage.clear()
|
||||
initializeLocale()
|
||||
|
||||
@@ -3,7 +3,6 @@ import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ActivityCardList from '@/components/ActivityCardList.vue'
|
||||
import ActivityChips from '@/components/ActivityChips.vue'
|
||||
import ActivityDetailsPanel from '@/components/ActivityDetailsPanel.vue'
|
||||
@@ -21,12 +20,11 @@ import { useActivityPage } from '@/composables/useActivityPage'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import type { ActivityFilters } from '@/lib/activity'
|
||||
import type { StripKey } from '@/lib/activitySummary'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const router = useRouter()
|
||||
const session = useSessionStore()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const activity = useActivityPage()
|
||||
const {
|
||||
filters,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay, useLocalizedToast } from '@/i18n/presentation'
|
||||
import ApiKeyCardList from '@/components/ApiKeyCardList.vue'
|
||||
import ApiKeyReveal from '@/components/ApiKeyReveal.vue'
|
||||
@@ -16,8 +15,8 @@ import { toastsAboveBulkBar } from '@/composables/useToastOffset'
|
||||
import { displayPrefix } from '@/lib/apiKeys'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const AUTH_HANDLED_STATUSES = [401, 403]
|
||||
const NOT_FOUND_STATUS = 404
|
||||
const REVOKE_FAILED_COPY = "Couldn't revoke the key. Try again in a moment."
|
||||
@@ -30,7 +29,7 @@ const overlay = useLocalizedOverlay()
|
||||
const session = useSessionStore()
|
||||
const permissions = usePermissionsStore()
|
||||
const { listApiKeys, revokeApiKey } = useApiKeys()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
|
||||
const loadState = ref<LoadState>('loading')
|
||||
const keys = ref<ApiKey[]>([])
|
||||
|
||||
@@ -3,7 +3,6 @@ import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import AuthorAvatar from '@/components/AuthorAvatar.vue'
|
||||
import AuthorFormPanel from '@/components/AuthorFormPanel.vue'
|
||||
@@ -31,8 +30,8 @@ import { classifyFailure } from '@/lib/directoryErrors'
|
||||
import type { DirectoryMenuItem } from '@/lib/directoryMenu'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const LIST_COLUMNS = 'minmax(0,1.4fr) minmax(0,1fr) 110px 100px 44px'
|
||||
const INITIAL_SKELETON_ROWS = 6
|
||||
const NOBODY = 'none'
|
||||
@@ -49,7 +48,7 @@ const permissions = usePermissionsStore()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const toasts = useDirectoryToasts()
|
||||
const { rememberOpener, restoreFocus, leavePanel, followEditLink } = useDirectoryView()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const { listAuthors, listAuthorPage, listAllAuthors, deleteAuthor } = useAuthors()
|
||||
const removal = useDirectoryDelete<Author>()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { formatNumber, t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay, useLocalizedToast } from '@/i18n/presentation'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
@@ -27,8 +26,8 @@ import { formatVariantSize } from '@/lib/variants/variantFormat'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useVariantCacheStore } from '@/stores/variantCache'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 40rem)'
|
||||
const SKELETON_DELAY_MS = 150
|
||||
const NOT_FOUND = 404
|
||||
const CONFIRM_WORD = 'purge'
|
||||
@@ -39,7 +38,7 @@ const store = useVariantCacheStore()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const toast = useLocalizedToast()
|
||||
const { announceFailure, announceSuccess } = useVariantToasts()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('40rem')
|
||||
|
||||
const showSkeleton = ref(false)
|
||||
const purgeUnusedButton = ref<{ $el: HTMLElement } | null>(null)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, provide, ref, watch } from 'vue'
|
||||
import { useRoute, type LocationQuery } from 'vue-router'
|
||||
import { useEventListener, useMediaQuery, useOnline } from '@vueuse/core'
|
||||
import { useEventListener, useOnline } from '@vueuse/core'
|
||||
import { useLocalizedToast } from '@/i18n/presentation'
|
||||
import AppPaginator from '@/components/AppPaginator.vue'
|
||||
import BulkDeleteMediaDialog from '@/components/BulkDeleteMediaDialog.vue'
|
||||
@@ -51,8 +51,8 @@ import { pageCountFor } from '@/lib/pagination'
|
||||
import { purgedToast } from '@/lib/mediaPurge'
|
||||
import { RATE_LIMITED_TEXT, RATE_LIMITED_TITLE, UPLOAD_ACCEPT } from '@/lib/mediaUploadRows'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const MEDIA_PATH = '/media'
|
||||
const TOAST_DURATION_MS = 5000
|
||||
const PERSISTENT_TOAST = 0
|
||||
@@ -84,7 +84,7 @@ const permissions = usePermissionsStore()
|
||||
const media = useMedia()
|
||||
const selection = useMediaSelection(media.items)
|
||||
const upload = useMediaUpload({ onAdded: refreshInBackground })
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const online = useOnline()
|
||||
|
||||
function differsOnlyByPage(query: MediaControls, current: MediaControls) {
|
||||
|
||||
@@ -10,7 +10,8 @@ import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import ProfileCard from '@/components/ProfileCard.vue'
|
||||
import YourRoleCard from '@/components/YourRoleCard.vue'
|
||||
import { ApiError, authRedirectPending, useApi } from '@/composables/useApi'
|
||||
import { ApiError, authRedirectPending } from '@/composables/useApi'
|
||||
import { useProfile } from '@/composables/useProfile'
|
||||
import { useRetryAfter } from '@/composables/useRetryAfter'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useSessionStore, type Session } from '@/stores/session'
|
||||
@@ -36,7 +37,7 @@ const session = useSessionStore()
|
||||
const permissions = usePermissionsStore()
|
||||
const toast = useLocalizedToast()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const { request } = useApi()
|
||||
const { updateDisplayName, sendPasswordReset } = useProfile()
|
||||
const retryAfter = useRetryAfter()
|
||||
|
||||
const account = ref<Session | null>(session.session)
|
||||
@@ -189,11 +190,7 @@ async function onSave() {
|
||||
saveFailed.value = false
|
||||
serverLimitError.value = false
|
||||
try {
|
||||
const updated = await request<{ display_name?: string | null }>('/v1/user/me', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ display_name: draft.value }),
|
||||
})
|
||||
const updated = await updateDisplayName(draft.value)
|
||||
const displayName = updated.display_name ?? ''
|
||||
session.setDisplayName(displayName)
|
||||
field.value = displayName
|
||||
@@ -223,11 +220,7 @@ async function onSendReset() {
|
||||
}
|
||||
resetState.value = 'sending'
|
||||
try {
|
||||
await request('/v1/auth/password-reset', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email: email.value }),
|
||||
})
|
||||
await sendPasswordReset(email.value)
|
||||
resetState.value = 'sent'
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError && error.status === 429) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { t } from '@/i18n'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import PermissionGrid from '@/components/PermissionGrid.vue'
|
||||
import PermissionGroup from '@/components/PermissionGroup.vue'
|
||||
@@ -14,12 +13,12 @@ import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import { useRoleEditor } from '@/composables/useRoleEditor'
|
||||
import type { CallerScope } from '@/lib/grantLocks'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 64rem)'
|
||||
const CONTENT_GROUP = 'content'
|
||||
|
||||
const permissions = usePermissionsStore()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('64rem')
|
||||
const editor = useRoleEditor()
|
||||
const overlay = useLocalizedOverlay()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedToast } from '@/i18n/presentation'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import DeleteRoleDialog from '@/components/DeleteRoleDialog.vue'
|
||||
@@ -14,14 +13,13 @@ import { useRoles } from '@/composables/useRoles'
|
||||
import { canCover } from '@/lib/grantLocks'
|
||||
import type { RoleItem } from '@/lib/roles'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const router = useRouter()
|
||||
const toast = useLocalizedToast()
|
||||
const permissions = usePermissionsStore()
|
||||
const { listRoles } = useRoles()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const skeleton = useDelayedSkeleton()
|
||||
|
||||
const loadState = ref<LoadState>('loading')
|
||||
|
||||
@@ -4,7 +4,6 @@ import { t } from '@/i18n'
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import DirectoryAddButton from '@/components/DirectoryAddButton.vue'
|
||||
import DirectoryLoadError from '@/components/DirectoryLoadError.vue'
|
||||
@@ -13,13 +12,13 @@ import NoAccessState from '@/components/NoAccessState.vue'
|
||||
import SchemaTable from '@/components/SchemaTable.vue'
|
||||
import { useSchemaGate } from '@/composables/useSchemaGate'
|
||||
import { builtinRow, customRow, schemaSummary } from '@/lib/schemaRows'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const NEW_TYPE_PATH = '/schema/new'
|
||||
|
||||
const router = useRouter()
|
||||
const permissions = usePermissionsStore()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
|
||||
const {
|
||||
sessionReady,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { t, formatNumber } from '@/i18n'
|
||||
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
@@ -29,8 +28,8 @@ import { classifyFailure } from '@/lib/directoryErrors'
|
||||
import type { DirectoryMenuItem } from '@/lib/directoryMenu'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
const LIST_COLUMNS = 'minmax(0,1.1fr) minmax(0,1.5fr) 110px 44px'
|
||||
const INITIAL_SKELETON_ROWS = 3
|
||||
|
||||
@@ -42,7 +41,7 @@ const permissions = usePermissionsStore()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const toasts = useDirectoryToasts()
|
||||
const { rememberOpener, restoreFocus, leavePanel, followEditLink } = useDirectoryView()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
const { listAllSites, listSitePage, setDefaultSite, deleteSite } = useSites()
|
||||
const removal = useDirectoryDelete<Site>()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import ChangeRoleDialog from '@/components/ChangeRoleDialog.vue'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
import InviteUserModal from '@/components/InviteUserModal.vue'
|
||||
@@ -15,16 +14,16 @@ import type { AcceptedUser, UserListItem } from '@/lib/users'
|
||||
import type { UserMenuContext } from '@/lib/userMenu'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const SKELETON_DELAY_MS = 150
|
||||
const DESKTOP_QUERY = '(min-width: 48rem)'
|
||||
|
||||
type LoadState = 'loading' | 'ready' | 'error'
|
||||
|
||||
const session = useSessionStore()
|
||||
const permissions = usePermissionsStore()
|
||||
const { listUsers } = useUsers()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop()
|
||||
|
||||
const loadState = ref<LoadState>('loading')
|
||||
const items = ref<UserListItem[]>([])
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { formatNumber, t } from '@/i18n'
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { useLocalizedOverlay } from '@/i18n/presentation'
|
||||
import ConfirmDialog from '@/components/ConfirmDialog.vue'
|
||||
import DashboardShell from '@/components/DashboardShell.vue'
|
||||
@@ -30,8 +29,8 @@ import { formatVariantSize } from '@/lib/variants/variantFormat'
|
||||
import { useImageVariantsStore } from '@/stores/imageVariants'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { usePermissionsStore } from '@/stores/permissions'
|
||||
import { useIsDesktop } from '@/composables/useIsDesktop'
|
||||
|
||||
const DESKTOP_QUERY = '(min-width: 40rem)'
|
||||
const SKELETON_DELAY_MS = 150
|
||||
const NOT_FOUND = 404
|
||||
|
||||
@@ -46,7 +45,7 @@ const permissions = usePermissionsStore()
|
||||
const store = useImageVariantsStore()
|
||||
const overlay = useLocalizedOverlay()
|
||||
const { announceFailure, announceSuccess } = useVariantToasts()
|
||||
const isDesktop = useMediaQuery(DESKTOP_QUERY)
|
||||
const isDesktop = useIsDesktop('40rem')
|
||||
|
||||
const showSkeleton = ref(false)
|
||||
const formTarget = ref<FormTarget | null>(null)
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('Single relation, empty, focused search', () => {
|
||||
await typeSearch('venue', 'rue ')
|
||||
await settleSearch(vi)
|
||||
|
||||
expect(searches()).toEqual(['/v1/content-type/venues/entry?q=rue&page=1&size=10'])
|
||||
expect(searches()).toEqual(['/v1/content-type/venues/entry?q=rue&page=1&size=25'])
|
||||
expect(optionLabels()).toEqual([
|
||||
'Rue Verte hall Lyon · 120 seats',
|
||||
'Rue Verte yard Lyon · outdoor',
|
||||
@@ -103,7 +103,7 @@ describe('Single relation, empty, focused search', () => {
|
||||
|
||||
await openList('venue', vi)
|
||||
|
||||
expect(searches()).toEqual(['/v1/content-type/venues/entry?page=1&size=10'])
|
||||
expect(searches()).toEqual(['/v1/content-type/venues/entry?page=1&size=25'])
|
||||
expect(optionLabels()).toHaveLength(4)
|
||||
})
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('No match', () => {
|
||||
await flushPromises()
|
||||
await settleSearch(vi)
|
||||
expect(relationInput('venue').value).toBe('')
|
||||
expect(searches().at(-1)).toBe('/v1/content-type/venues/entry?page=1&size=10')
|
||||
expect(searches().at(-1)).toBe('/v1/content-type/venues/entry?page=1&size=25')
|
||||
})
|
||||
|
||||
it('says the type has no entries at all', async () => {
|
||||
@@ -123,7 +123,7 @@ describe('Search failures', () => {
|
||||
await flushPromises()
|
||||
|
||||
expect(calls.slice(before)).toEqual([
|
||||
'/v1/content-type/venues/entry?q=rue&page=1&size=10',
|
||||
'/v1/content-type/venues/entry?q=rue&page=1&size=25',
|
||||
])
|
||||
expect(optionLabels()).toHaveLength(3)
|
||||
},
|
||||
@@ -175,7 +175,7 @@ describe('Load more', () => {
|
||||
button('Load more')!.click()
|
||||
await flushPromises()
|
||||
|
||||
expect(searches().at(-1)).toBe('/v1/content-type/venues/entry?page=2&size=10')
|
||||
expect(searches().at(-1)).toBe('/v1/content-type/venues/entry?page=2&size=25')
|
||||
expect(optionLabels()).toHaveLength(12)
|
||||
expect(button('Load more')).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -652,8 +652,7 @@ describe('Errors', () => {
|
||||
'The expiry must be a date in the future.',
|
||||
'You must be logged in to access this resource.',
|
||||
'This action needs a real session.',
|
||||
'Only owners and admins can do this.',
|
||||
'Only the owner can do this.',
|
||||
'missing_permission',
|
||||
'You must verify your account to access this resource.',
|
||||
'Article not found',
|
||||
'Image not found',
|
||||
@@ -697,8 +696,7 @@ describe('Errors table rows of the custom content types round', () => {
|
||||
|
||||
const order = [
|
||||
'This action needs a real session.',
|
||||
'Only owners and admins can do this.',
|
||||
'Only the owner can do this.',
|
||||
'missing_permission',
|
||||
'You must verify your account to access this resource.',
|
||||
].map((message) => text.indexOf(message))
|
||||
expect(order.every((position) => position > -1)).toBe(true)
|
||||
@@ -909,7 +907,7 @@ describe('Expanded cards of the custom content types round', () => {
|
||||
[
|
||||
'POST /v1/content-type/',
|
||||
['key', 'name', 'icon', 'fields'],
|
||||
['400', '401', 'Only owners and admins can do this.', 'content_type_key_taken'],
|
||||
['400', '401', '403', 'content_type_key_taken'],
|
||||
],
|
||||
[
|
||||
'PUT /v1/content-type/{key}',
|
||||
@@ -1134,7 +1132,7 @@ describe('Authentication copy of the custom content types round', () => {
|
||||
"Requests act as the user who created the key, with that user's role (owner or admin).",
|
||||
)
|
||||
expect(auth).toContain(
|
||||
'Owner and admin routes answer 403 to a key whose creator is neither.',
|
||||
'Routes that need a permission answer 403 "missing_permission" to a key when the role of its creator does not hold it.',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -187,7 +187,7 @@ describe('Search and sharing group', () => {
|
||||
const wrapper = await mountIntegration()
|
||||
|
||||
const forbidden = ERROR_ROWS.find((row) => row.status === 403)!
|
||||
expect(forbidden.messages).toHaveLength(4)
|
||||
expect(forbidden.messages).toHaveLength(3)
|
||||
expect(forbidden.note).toBe(NOTE)
|
||||
const notes = wrapper.findAll('[data-testid="error-note"]').map((item) => item.text())
|
||||
expect(notes.filter((item) => item === NOTE)).toHaveLength(2)
|
||||
|
||||
Reference in new issue
Block a user