feat(SPA): schema list and the sidebar Content group

This commit is contained in:
Florian Sylvain committed 2026-10-19 19:34:45 +02:00
1 parent 73a75d5cb9
commit 3c67cd2314
34 files changed
+2181 -18

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { computed, onBeforeUnmount, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useMediaQuery } from '@vueuse/core'
import DashboardShell from '@/components/DashboardShell.vue'
import DirectoryAddButton from '@/components/DirectoryAddButton.vue'
import DirectoryLoadError from '@/components/DirectoryLoadError.vue'
import SchemaEmpty from '@/components/SchemaEmpty.vue'
import SchemaForbidden from '@/components/SchemaForbidden.vue'
import SchemaTable from '@/components/SchemaTable.vue'
import { useDelayedSkeleton } from '@/composables/useDelayedSkeleton'
import { builtinRow, customRow, schemaSummary } from '@/lib/schemaRows'
import { useContentTypesStore } from '@/stores/contentTypes'
import { useSessionStore } from '@/stores/session'
const DESKTOP_QUERY = '(min-width: 48rem)'
const NEW_TYPE_PATH = '/schema/new'
const router = useRouter()
const session = useSessionStore()
const store = useContentTypesStore()
const isDesktop = useMediaQuery(DESKTOP_QUERY)
const skeleton = useDelayedSkeleton()
const sessionReady = computed(() => session.status === 'ready')
const forbidden = computed(
() => (sessionReady.value && !session.canManageContent) || store.forbidden,
)
const loading = computed(
() =>
!forbidden.value &&
(store.status === 'idle' ||
store.status === 'loading' ||
store.builtinStatus === 'idle' ||
store.builtinStatus === 'loading'),
)
const failed = computed(
() => !forbidden.value && !loading.value && (store.error || store.builtinStatus === 'failed'),
)
const empty = computed(() => !loading.value && !failed.value && store.items.length === 0)
const populated = computed(() => !loading.value && !failed.value)
const showSkeleton = computed(() => loading.value && skeleton.visible.value)
const customRows = computed(() => store.items.map(customRow))
const builtinRows = computed(() => store.builtinItems.map(builtinRow))
const summary = computed(() => schemaSummary(store.items.length, store.builtinItems.length))
function retry() {
store.retry()
}
watch(
loading,
(isLoading) => {
if (isLoading) {
skeleton.start()
} else {
skeleton.stop()
}
},
{ immediate: true },
)
onBeforeUnmount(skeleton.stop)
function createType() {
router.push(NEW_TYPE_PATH)
}
function goHome() {
router.push('/home')
}
</script>
<template>
<DashboardShell compact-padding :full-bleed="forbidden" :mobile-title="t('Schema')">
<SchemaForbidden v-if="forbidden" @back="goHome" />
<div v-else-if="sessionReady" class="flex flex-col gap-4">
<div class="flex flex-wrap items-end gap-4">
<div class="flex min-w-50 flex-1 flex-col gap-0.5">
<h1 class="text-xl font-semibold text-highlighted max-lg:sr-only">
{{ t('Schema') }}
</h1>
<p class="text-muted">
{{ t('Content types and the fields they are made of.') }}
</p>
</div>
<UButton
icon="i-lucide-plus"
class="h-8 px-3.5 max-md:h-11"
:disabled="loading"
@click="createType"
>
{{ t('New content type') }}
</UButton>
</div>
<DirectoryLoadError
v-if="failed"
:title="t('Couldn\'t load content types')"
:description="
t('GET /v1/content-type failed. Check your connection and try again.')
"
@retry="retry"
/>
<template v-else>
<SchemaEmpty v-if="empty" @create="createType" />
<SchemaTable
v-if="showSkeleton || populated"
:custom="customRows"
:builtin="builtinRows"
:loading="showSkeleton"
/>
<p v-if="populated && !empty" class="text-muted">{{ summary }}</p>
</template>
</div>
<DirectoryAddButton
v-if="!isDesktop && !forbidden && populated && !empty"
pinned
@click="createType"
>
{{ t('New content type') }}
</DirectoryAddButton>
</DashboardShell>
</template>