new CMS pages append to the bottom of their category

This commit is contained in:
corsacca 2026-05-21 10:55:15 +01:00
parent 6da69b57c7
commit 56104fa554
4 changed files with 19 additions and 15 deletions

View file

@ -94,7 +94,6 @@ const categoryItems = computed(() => [
// Metadata editable in the top bar
const slug = ref('')
const categoryId = ref<string | null>(null)
const menuOrder = ref(0)
const theme = ref<PageTheme>('default')
const customCss = ref('')
// Original category id at load time — used to detect moves that also
@ -105,7 +104,6 @@ watchEffect(() => {
slug.value = data.value.page.slug
categoryId.value = data.value.page.category_id
originalCategoryId.value = data.value.page.category_id
menuOrder.value = data.value.page.menu_order
theme.value = data.value.page.theme ?? 'default'
customCss.value = data.value.page.custom_css ?? ''
}
@ -217,7 +215,6 @@ async function saveAll(statusOverride?: 'draft' | 'published', localeOverride?:
body: {
slug: slug.value,
category_id: categoryId.value,
menu_order: menuOrder.value,
theme: theme.value,
custom_css: customCss.value.trim() ? customCss.value : null
}
@ -664,9 +661,6 @@ function formatVersionTime(iso: string): string {
>
<UInput v-model="slug" />
</UFormField>
<UFormField label="Menu order" description="Position within the category sidebar.">
<UInput v-model.number="menuOrder" type="number" />
</UFormField>
<UFormField label="Page theme" description="Applied to <body>.">
<USelect v-model="theme" :items="THEME_OPTIONS" class="w-full" />
</UFormField>

View file

@ -53,7 +53,6 @@ function categoryLabel(cat: CategoryRow): string {
const createModalOpen = ref(false)
const newSlugLeaf = ref('')
const newCategoryId = ref<string | null>(null)
const newMenuOrder = ref(0)
const creating = ref(false)
const newCategoryPrefix = computed(() => {
@ -75,7 +74,6 @@ const categoryItems = computed(() => [
function resetCreateForm() {
newSlugLeaf.value = ''
newCategoryId.value = null
newMenuOrder.value = 0
}
async function createPage() {
@ -91,8 +89,7 @@ async function createPage() {
method: 'POST',
body: {
slug,
category_id: newCategoryId.value,
menu_order: newMenuOrder.value || 0
category_id: newCategoryId.value
}
})
toast.add({ title: 'Page created', color: 'success' })
@ -274,9 +271,6 @@ const filteredRows = computed<PageRow[]>(() => {
</template>
</UInput>
</UFormField>
<UFormField label="Menu order" description="Sort order within the category sidebar.">
<UInput v-model.number="newMenuOrder" type="number" />
</UFormField>
</div>
</template>
<template #footer>

View file

@ -23,7 +23,9 @@ export default defineEventHandler(async (event) => {
}
const category_id = body?.category_id ? String(body.category_id) : null
const menu_order = Number.isFinite(body?.menu_order) ? Number(body.menu_order) : 0
// Leave unset when not explicitly provided so the service appends the
// page to the bottom of its category instead of forcing order 0 (top).
const menu_order = Number.isFinite(body?.menu_order) ? Number(body.menu_order) : undefined
try {
const result = await createCmsPage(

View file

@ -334,12 +334,26 @@ export async function createCmsPage(
// change the prefix between validation and write.
await validatePageSlug(slug, { categoryId: input.category_id ?? null }, tx)
// New pages append to the bottom of their category. When the caller
// does not pin a menu_order, take MAX(menu_order)+1 within the same
// scope (category_id, or NULL for uncategorized) so the page sorts
// last. coalesce(..., -1) makes the first page in an empty scope 0.
let menuOrder = input.menu_order
if (menuOrder === undefined) {
const maxRow = await (input.category_id
? tx.selectFrom('pages').where('category_id', '=', input.category_id)
: tx.selectFrom('pages').where('category_id', 'is', null))
.select(sql<number>`coalesce(max(menu_order), -1)`.as('max'))
.executeTakeFirstOrThrow()
menuOrder = Number(maxRow.max) + 1
}
const page = await tx
.insertInto('pages')
.values({
slug: input.slug,
category_id: input.category_id ?? null,
menu_order: input.menu_order ?? 0,
menu_order: menuOrder,
...(input.theme ? { theme: input.theme } : {}),
...(input.custom_css !== undefined ? { custom_css: input.custom_css } : {})
})