From 3bd53e56b0bf0cfa1d9cbe087d919f23ea147ab3 Mon Sep 17 00:00:00 2001 From: fxai Date: Tue, 27 Oct 2026 13:53:14 +0100 Subject: [PATCH 1/7] fix(frontend): use a trash icon for removing assets from an activity --- .../src/components/assessment/ActivityAssetsManager.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/assessment/ActivityAssetsManager.vue b/frontend/src/components/assessment/ActivityAssetsManager.vue index e56cfc4..10ac066 100644 --- a/frontend/src/components/assessment/ActivityAssetsManager.vue +++ b/frontend/src/components/assessment/ActivityAssetsManager.vue @@ -18,9 +18,9 @@ import { SearchCode, Shield, ShieldCheck, + Trash, User, Users, - X, } from '@lucide/vue'; import { computed, onMounted, ref, watch } from 'vue'; import ManageAssetsModal from '@/components/assessment/ManageAssetsModal.vue'; @@ -91,6 +91,7 @@ const iconMap: Record = { Database, Shield, ShieldCheck, + Trash, Users, Network, SearchCode, @@ -359,9 +360,10 @@ onMounted(() => { variant="ghost" size="icon" class="absolute top-1 right-1 h-5 w-5 rounded-full text-muted-foreground hover:text-destructive z-10" + title="Remove from activity" @click.stop="removeAsset(asset.id, section.key)" > - + @@ -395,9 +397,10 @@ onMounted(() => { variant="ghost" size="icon" :class="['h-5 w-5 rounded-full text-muted-foreground hover:text-destructive shrink-0', asset.deleted ? 'ml-auto' : '']" + title="Remove from activity" @click.stop="removeAsset(asset.id, section.key)" > - + From 285b2b4659d485bcb6342c6dcbb57bab9b62201e Mon Sep 17 00:00:00 2001 From: fxai Date: Tue, 27 Oct 2026 13:58:18 +0100 Subject: [PATCH 2/7] feat(frontend): keep the activity form tab bar visible while scrolling --- .../assessment/ActivityAssetsManager.vue | 3 +- .../components/assessment/ActivityForm.vue | 39 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/assessment/ActivityAssetsManager.vue b/frontend/src/components/assessment/ActivityAssetsManager.vue index 10ac066..aaa6272 100644 --- a/frontend/src/components/assessment/ActivityAssetsManager.vue +++ b/frontend/src/components/assessment/ActivityAssetsManager.vue @@ -331,11 +331,12 @@ onMounted(() => {
+
import { BookOpen, Copy, History } from '@lucide/vue'; +import { useElementSize } from '@vueuse/core'; import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'; import { toast } from 'vue-sonner'; import ActivityAssetsManager from '@/components/assessment/ActivityAssetsManager.vue'; @@ -130,6 +131,27 @@ watch( }, ); +// The tab bar sticks right below the sticky header, whose height varies +const stickyHeader = ref(null); +const sectionTabs = ref<{ $el: HTMLElement } | null>(null); +const { height: stickyHeaderHeight } = useElementSize(stickyHeader, undefined, { + box: 'border-box', +}); + +// Clicking a tab while the bar is stuck jumps to the start of that section +function onSectionTabClicked() { + const tabsEl = sectionTabs.value?.$el; + const headerEl = stickyHeader.value; + if (!tabsEl || !headerEl) return; + if ( + tabsEl.getBoundingClientRect().top < + headerEl.getBoundingClientRect().bottom + ) { + // scroll-margin-top on the tabs keeps them below the header + tabsEl.scrollIntoView({ block: 'start' }); + } +} + // Browser-style section tabs: the active tab joins the page below and shows its section colour on top const SECTION_TABS = [ { @@ -579,7 +601,7 @@ defineExpose({ hasUnsavedChanges });