From d544413cd0ccb588441d2fb92fe0af8b9d98afd8 Mon Sep 17 00:00:00 2001 From: Justin Visser Date: Tue, 3 Mar 2026 17:07:32 +0100 Subject: [PATCH] some UI improvements --- .../src/components/admin/SegmentEditor.vue | 28 ++++++++++---- frontend/src/components/layout/AppShell.vue | 38 ++++++++++++++++++- frontend/src/views/PageView.vue | 2 +- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/admin/SegmentEditor.vue b/frontend/src/components/admin/SegmentEditor.vue index 1857fb7..698c032 100644 --- a/frontend/src/components/admin/SegmentEditor.vue +++ b/frontend/src/components/admin/SegmentEditor.vue @@ -26,10 +26,18 @@ const teamMembers = ref<{ name: string; student_number: string }[]>( const newMemberName = ref(""); const newMemberNumber = ref(""); +function normaliseUrl(url: string): string { + const trimmed = url.trim(); + if (trimmed && !/^https?:\/\//i.test(trimmed)) return `https://${trimmed}`; + return trimmed; +} + function handleSave() { const updates: { title?: string; content?: string; metadata?: Record } = {}; if (title.value !== props.segment.title) updates.title = title.value; - if (content.value !== props.segment.content) updates.content = content.value; + const savedContent = + props.segment.type === "iframe" ? normaliseUrl(content.value) : content.value; + if (savedContent !== props.segment.content) updates.content = savedContent; if (props.segment.type === "team") { metadata.value = { ...metadata.value, members: teamMembers.value }; } @@ -86,13 +94,17 @@ const assetAcceptMap: Record = { - +
+ +

+ Most major sites (Google, GitHub, etc.) block embedding. Use dedicated embed URLs where available, e.g. YouTube's youtube.com/embed/…. +

+
diff --git a/frontend/src/components/layout/AppShell.vue b/frontend/src/components/layout/AppShell.vue index 79ed674..d928ed8 100644 --- a/frontend/src/components/layout/AppShell.vue +++ b/frontend/src/components/layout/AppShell.vue @@ -13,6 +13,8 @@ const { pages, fetchPages } = usePages(); const { isAdmin, authHeaders } = useAdmin(); const mobileNavOpen = ref(false); +const scrolledDown = ref(false); +const mainEl = ref(null); const siteTitle = ref("Untitled Site"); const editingSiteTitle = ref(false); const siteTitleInput = ref(""); @@ -63,6 +65,14 @@ watch(pages, (loaded) => { } }); +function handleScroll(e: Event) { + scrolledDown.value = (e.target as HTMLElement).scrollTop > 200; +} + +function scrollToTop() { + mainEl.value?.scrollTo({ top: 0, behavior: "smooth" }); +} + onMounted(async () => { await Promise.all([loadSiteTitle(), fetchPages()]); if (pages.value.length > 0 && (route.path === "/" || route.path === "")) { @@ -145,13 +155,30 @@ onMounted(async () => {
-
+
+ + + + + + + diff --git a/frontend/src/views/PageView.vue b/frontend/src/views/PageView.vue index c02d61f..1a657d5 100644 --- a/frontend/src/views/PageView.vue +++ b/frontend/src/views/PageView.vue @@ -161,7 +161,7 @@ async function handleReorder(ids: string[]) {