From 5ad08ff0e320f511c072358113497d0e12550953 Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Tue, 3 Mar 2026 20:32:21 +0100 Subject: [PATCH] routing and UX improvements - Always route naked domain and unknown slugs to /home - Redirect hidden pages to home for non-admin visitors - Add two-step delete confirmation for pages in sidebar Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/layout/AppShell.vue | 4 ++-- frontend/src/components/layout/Sidebar.vue | 19 +++++++++++++++++-- frontend/src/views/PageView.vue | 4 ++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/layout/AppShell.vue b/frontend/src/components/layout/AppShell.vue index ffe5520..9e40e54 100644 --- a/frontend/src/components/layout/AppShell.vue +++ b/frontend/src/components/layout/AppShell.vue @@ -61,7 +61,7 @@ watchEffect(() => { // Redirect "/" to first page once pages are loaded watch(pages, (loaded) => { if (loaded.length > 0 && (route.path === "/" || route.path === "")) { - void router.replace({ name: "page", params: { slug: loaded[0].slug } }); + void router.replace({ name: "page", params: { slug: "home" } }); } }); @@ -76,7 +76,7 @@ function scrollToTop() { onMounted(async () => { await Promise.all([loadSiteTitle(), fetchPages()]); if (pages.value.length > 0 && (route.path === "/" || route.path === "")) { - void router.replace({ name: "page", params: { slug: pages.value[0].slug } }); + void router.replace({ name: "page", params: { slug: "home" } }); } }); diff --git a/frontend/src/components/layout/Sidebar.vue b/frontend/src/components/layout/Sidebar.vue index 6b3f83f..08306d1 100644 --- a/frontend/src/components/layout/Sidebar.vue +++ b/frontend/src/components/layout/Sidebar.vue @@ -48,14 +48,21 @@ async function saveRename(page: Page) { } // --- Delete page --- +const confirmDeleteId = ref(null); + async function handleDelete(page: Page) { + if (confirmDeleteId.value !== page.id) { + confirmDeleteId.value = page.id; + return; + } + confirmDeleteId.value = null; const result = await deletePage(page.id); if (result.ok && route.params.slug === page.slug) { const remaining = pages.value.filter((p) => p.id !== page.id); if (remaining.length > 0) { await router.push({ name: "page", params: { slug: remaining[0].slug } }); } else { - await router.push("/"); + await router.push({ name: "page", params: { slug: "home" } }); } } } @@ -157,6 +164,7 @@ async function handleLogin() { v-else :to="{ name: 'page', params: { slug: page.slug } }" class="flex items-center gap-1.5 rounded-md px-2 py-2 text-sm transition-colors" + @blur.capture="confirmDeleteId = null" :class="[ $route.params.slug === page.slug ? 'border-l-2 border-primary-400 bg-primary-50 font-medium text-gray-900 dark:border-primary-400 dark:bg-gray-600 dark:text-white' @@ -200,8 +208,9 @@ async function handleLogin() { - + + diff --git a/frontend/src/views/PageView.vue b/frontend/src/views/PageView.vue index 83be991..657e241 100644 --- a/frontend/src/views/PageView.vue +++ b/frontend/src/views/PageView.vue @@ -37,11 +37,11 @@ async function fetchSegments(pageId: string) { function resolvePage(slug: string) { if (!pages.value.length) return; const found = pages.value.find((p) => p.slug === slug); - if (found) { + if (found && (!found.is_hidden || isAdmin.value)) { currentPage.value = found; void fetchSegments(found.id); } else { - void router.replace({ name: "page", params: { slug: pages.value[0].slug } }); + void router.replace({ name: "page", params: { slug: "home" } }); } }