From 864cc558e20a7796c449afbbda9f4040f6a98563 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Wed, 20 May 2026 18:14:31 +0200 Subject: [PATCH] =?UTF-8?q?feat(layout):=20Plan=202.5=20P4=20=E2=80=94=20W?= =?UTF-8?q?orkspaceSwitcher=20no-sub=20+=20SidebarNav=20APP=5FNAVIGATION?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per RFC-WS-PRIMEVUE-PLAN-2-5 §4 AD-2.5-W1 and AD-2.5-B1, §5.4 Fix 4. Changes: - WorkspaceSwitcher: sub field removed from template, WorkspaceDisplay type, and buildDisplay derivation. Stories did not carry sub args (auto-derived from seeded org.role); no WithSub story existed. New regression spec (WorkspaceSwitcher.spec.ts) locks the no-sub render. - SidebarNav: now consumes APP_NAVIGATION from src/config/navigation.ts as the single source of truth (shared with breadcrumb derivation in useNavBreadcrumb). The groups: V2NavGroup[] prop is removed; render walks top-level NavItems (branch nodes render label-heading + children; leaf nodes render as rows; items without routeName render as non-clickable dormant placeholders). Previous nav data source: groups prop fed by useV2Nav(orgNavItems) in OrganizerLayoutV2. - APP_NAVIGATION expanded with 7 entries to preserve visual sidebar continuity (Evenementen at top-level + Beheer branch with 5 children). All new entries use routeName: undefined until the corresponding v2 page lands (TODOs noted per entry); only Dashboard maps to v2-dashboard. - AppSidebar: groups prop removed; passes only :collapsed to SidebarNav. - OrganizerLayoutV2: useV2Nav(orgNavItems) plumbing retired; the layout now renders with no nav-data wiring. - Tests: AppSidebar.spec drops the "passes groups prop to SidebarNav" assertion; OrganizerLayoutV2.spec drops the "forwards orgNavItems" assertion. New WorkspaceSwitcher no-sub regression spec (+2 tests). - Storybook: SidebarNav.stories and AppSidebar.stories updated to no longer thread navFixture/groups; WithActiveItem pushes v2-dashboard. Position of WorkspaceSwitcher (Fix 3), workspace dropdown panel (Fix 5), and AppBreadcrumb wiring (Fix 2) remain unchanged in P4 — both lands in P5. The legacy useBreadcrumb composable also remains untouched until P5 (atomic with AppTopbar refactor). Orphans flagged for follow-up cleanup (intentionally not deleted in P4): useV2Nav composable + spec, V2NavGroup/V2NavItem types, sidebarNavActive helper + spec, navFixture in stories/v2/_helpers.ts. Suite delta: 575 → 575 (+2 WorkspaceSwitcher no-sub spec, -1 AppSidebar groups-prop assertion, -1 OrganizerLayoutV2 groups-forward assertion). vue-tsc clean. Scoped ESLint clean (0 errors). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../layout/AppSidebar.stories.ts | 21 +- .../src/components-v2/layout/AppSidebar.vue | 26 +-- .../layout/SidebarNav.stories.ts | 24 +- .../src/components-v2/layout/SidebarNav.vue | 212 ++++++++++++------ .../layout/WorkspaceSwitcher.vue | 15 +- .../layout/__tests__/AppSidebar.spec.ts | 42 +--- .../__tests__/WorkspaceSwitcher.spec.ts | 86 +++++++ apps/app/src/config/navigation.ts | 56 ++++- apps/app/src/layouts/OrganizerLayoutV2.vue | 14 +- .../layouts/OrganizerLayoutV2.spec.ts | 21 +- 10 files changed, 339 insertions(+), 178 deletions(-) create mode 100644 apps/app/src/components-v2/layout/__tests__/WorkspaceSwitcher.spec.ts diff --git a/apps/app/src/components-v2/layout/AppSidebar.stories.ts b/apps/app/src/components-v2/layout/AppSidebar.stories.ts index c5ffe438..1800e2e1 100644 --- a/apps/app/src/components-v2/layout/AppSidebar.stories.ts +++ b/apps/app/src/components-v2/layout/AppSidebar.stories.ts @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { navFixture, orgA, userFixture, withPinia } from '@/stories/v2/_helpers' +import { orgA, userFixture, withPinia } from '@/stories/v2/_helpers' import AppSidebar from '@/components-v2/layout/AppSidebar.vue' import { useAuthStore } from '@/stores/useAuthStore' import { useShellUiStore } from '@/stores/useShellUiStore' @@ -10,6 +10,9 @@ import { useShellUiStore } from '@/stores/useShellUiStore' * via WorkspaceSwitcher, useAuthStore. Each story seeds both stores on a * fresh Pinia. The desktop @@ -119,10 +110,7 @@ const mobileVisible = computed({ }" > - + diff --git a/apps/app/src/components-v2/layout/SidebarNav.stories.ts b/apps/app/src/components-v2/layout/SidebarNav.stories.ts index c44d619f..9b00074c 100644 --- a/apps/app/src/components-v2/layout/SidebarNav.stories.ts +++ b/apps/app/src/components-v2/layout/SidebarNav.stories.ts @@ -1,13 +1,13 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' import { useRouter } from 'vue-router' -import { navFixture } from '@/stories/v2/_helpers' import SidebarNav from '@/components-v2/layout/SidebarNav.vue' /** - * SidebarNav needs no Pinia, but vue-router is installed app-wide in - * preview.ts (the component uses useRoute + RouterLink). The - * WithActiveItem story pushes the `events` route in setup so the - * Evenementen item renders in its active state. + * SidebarNav now reads APP_NAVIGATION from `@/config/navigation` directly + * (AD-2.5-B1 / Plan 2.5 P4). The previous `groups` prop is gone — stories + * only vary the `collapsed` axis and the active route. vue-router is + * installed app-wide in preview.ts; the WithActiveItem story pushes the + * `v2-dashboard` route so the Dashboard item renders in its active state. */ const meta: Meta = { title: 'v2 Shell/SidebarNav', @@ -22,7 +22,7 @@ export default meta type Story = StoryObj export const Expanded: Story = { - args: { groups: navFixture, collapsed: false }, + args: { collapsed: false }, render: args => ({ components: { SidebarNav }, setup() { @@ -30,14 +30,14 @@ export const Expanded: Story = { }, template: `
- +
`, }), } export const Collapsed: Story = { - args: { groups: navFixture, collapsed: true }, + args: { collapsed: true }, render: args => ({ components: { SidebarNav }, setup() { @@ -45,26 +45,26 @@ export const Collapsed: Story = { }, template: `
- +
`, }), } export const WithActiveItem: Story = { - args: { groups: navFixture, collapsed: false }, + args: { collapsed: false }, render: args => ({ components: { SidebarNav }, setup() { const router = useRouter() - router.push({ name: 'events' }) + router.push({ name: 'v2-dashboard' }) return { args } }, template: `
- +
`, }), diff --git a/apps/app/src/components-v2/layout/SidebarNav.vue b/apps/app/src/components-v2/layout/SidebarNav.vue index 7fac0a39..36365f3c 100644 --- a/apps/app/src/components-v2/layout/SidebarNav.vue +++ b/apps/app/src/components-v2/layout/SidebarNav.vue @@ -1,71 +1,145 @@ diff --git a/apps/app/src/components-v2/layout/WorkspaceSwitcher.vue b/apps/app/src/components-v2/layout/WorkspaceSwitcher.vue index c69cefd3..ea4bdd57 100644 --- a/apps/app/src/components-v2/layout/WorkspaceSwitcher.vue +++ b/apps/app/src/components-v2/layout/WorkspaceSwitcher.vue @@ -23,7 +23,7 @@ import type { Organisation } from '@/types/auth' defineProps<{ /** - * When true (collapsed sidebar), hide the name/sub meta text and + * When true (collapsed sidebar), hide the name meta text and * show only the logo square — mirrors crewli-starter's collapsed prop. */ collapsed?: boolean @@ -43,8 +43,6 @@ interface WorkspaceDisplay { id: string initials: string name: string - /** The role string is the relevant context identifier in Crewli. */ - sub: string gradient: [string, string] } @@ -60,7 +58,6 @@ function buildDisplay(org: Organisation): WorkspaceDisplay { id: org.id, initials, name: org.name, - sub: org.role, gradient: computeOrgGradient(org.id), } } @@ -129,7 +126,7 @@ function selectOrg(ws: WorkspaceDisplay): void { {{ current.initials }} - + {{ current.name }} - - - {{ current.sub }} - @@ -185,12 +178,10 @@ function selectOrg(ws: WorkspaceDisplay): void { :style="{ background: `linear-gradient(135deg, ${ws.gradient[0]}, ${ws.gradient[1]})` }" >{{ ws.initials }} - +
{{ ws.name }}
- -
{{ ws.sub }}
diff --git a/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts b/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts index 32cce9b2..399c68c8 100644 --- a/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts +++ b/apps/app/src/components-v2/layout/__tests__/AppSidebar.spec.ts @@ -4,11 +4,14 @@ * Strategy: mount with @vue/test-utils stubs for all heavy children (SidebarHeader, * SidebarNav, WorkspaceSwitcher, Drawer) so we test only: * 1. Renders the 3 child components (SidebarHeader, SidebarNav, WorkspaceSwitcher). - * 2. Passes `groups` prop to SidebarNav. - * 3. Mobile Drawer v-model:visible wires to shell.mobileOpen (get path). - * 4. Drawer close (v-model:visible = false) calls shell.setMobileOpen(false). - * 5. Drawer is NOT rendered when isMobile=false (desktop); IS rendered when isMobile=true. - * 6. Desktop