feat(SPA): schema list and the sidebar Content group
This commit is contained in:
1 parent
73a75d5cb9
commit
3c67cd2314
34 files changed
+2181
-18
No files matched your search
@@ -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>
|
||||
Reference in new issue
Block a user