769 Commits

Author SHA1 Message Date
4ba927623b feat(gui-v2): drawer registry + port RightDrawer to TypeScript
Adds composables/drawerRegistry.ts (boundary-safe register-by-call map:
register/resolve, zero static component imports — composables zone may
not import components, RFC-WS-GUI-REDESIGN AD-G5). Extends useRightDrawer
with resolveDrawerComponent (thin facade, prior API/tests preserved).
RightDrawer.vue: PrimeVue <Drawer position=right>, v-model:visible via a
writable computed ↔ useRightDrawer isOpen/close; title/flush read from the
open() props object (A4); dynamic <component :is> via resolveDrawerComponent
with a graceful empty state on null; #actions header slot retained. 18
unit/component tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:22:42 +02:00
615a114f33 fix(gui-v2): breadcrumb navigation via router.push + button type + void logout
- FIX A (IMPORTANT): PrimeVue Breadcrumb ignores `route` key; map non-last
  items with `command: () => router.push(item.to)` for real client-side nav
- FIX B: add type="button" to all 6 native <button> chrome elements
- FIX C: authStore.logout() bare call matches project no-void pattern
- FIX D: document param-route edge case in toBreadcrumbItems
- FIX E: regression test asserts command+push on non-last, no command on last,
  no `route` key on any item

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:07:57 +02:00
4f1fb7385b chore(types): sync auto-imports.d.ts for useBreadcrumb
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:51:53 +02:00
7489301195 feat(gui-v2): port AppTopbar + useBreadcrumb to TypeScript
- useBreadcrumb composable: pure toBreadcrumbItems() helper + thin
  useRoute() wrapper; route-driven, no prop coupling
- AppTopbar: hamburger→setMobileOpen, theme/density toggles→shell store,
  PrimeVue Breadcrumb/OverlayBadge/Popover/Avatar/Menu; replaces all
  manual document.mousedown listeners with PrimeVue built-in dismissal;
  notifications stubbed (useNotificationStore is a toast queue, not a
  feed — TODO TECH-WS-GUI-REDESIGN); sign-out→authStore.logout()
- Unit tests: 10 breadcrumb + 6 AppTopbar assertions (16 total, all pass)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:51:48 +02:00
23e1262f9c fix(gui-v2): mount Drawer only on mobile (v-if) + shared Tailwind breakpoint
CRITICAL: replace `lg:hidden` on PrimeVue Drawer with `v-if="isMobile"` so the
teleported portal/overlay is never created on desktop viewports regardless of
mobileOpen state. Replace useMediaQuery raw string in SidebarHeader with
useBreakpoints(breakpointsTailwind).smaller('lg') shared by both components.
Add desktop/mobile comments; adapt tests to useBreakpoints mock; add
Drawer-absent-on-desktop and aside w-16/w-64 width-class assertions (21 tests).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:41:50 +02:00
f0f9cb7e36 feat(gui-v2): decompose AppSidebar into SidebarHeader + AppSidebar
Ports crewli-starter's monolithic AppSidebar.vue into two typed production
components: SidebarHeader (the .brand block) and AppSidebar (composing
SidebarHeader + SidebarNav + WorkspaceSwitcher). AppSidebar renders a
permanent <aside> on desktop (lg+) and a PrimeVue Drawer on mobile, both
wired to useShellUiStore for collapse/mobile state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 20:29:18 +02:00
d479d35881 fix(gui-v2): WorkspaceSwitcher review nits (Tailwind grid, scoped-CSS trim, a11y button, initials guard)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:58:30 +02:00
3720e8c3d3 feat(gui-v2): port WorkspaceSwitcher to TypeScript
Ports crewli-starter WorkspaceSwitcher into the Crewli SPA as production
TypeScript: PrimeVue Popover replaces the manual click-outside listener,
data is derived from useAuthStore/useOrganisationStore (no new store), gradient
pairs are deterministic via a new pure util with full Vitest coverage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:48:50 +02:00
8444ea7443 fix(gui-v2): SidebarNav uses RouterLink (a11y) + review-nit cleanup
- FIX 1: Replace <button @click="router.push"> with <RouterLink custom>
  + <a> for real link semantics (middle-click, ⌘-click, screen-reader);
  custom isNavItemActive prefix-match stays the active source of truth;
  adds :aria-current="page" on active items; drops useRouter/router.push.
  RouterLink to prop cast via itemTo() helper (RouteLocationRaw from
  unplugin-vue-router) to satisfy typed RouterLinkTyped<RouteNamedMap>.
- FIX 2: Align .nav-item comment to actual template values (py-[9px]
  rounded-md, not CSS vars); replace inaccurate Tailwind v3/v4 before:
  composability justification in <style scoped> with the real reason
  (accent bar at left:-10px is clipped by the overflow-y-auto nav).
- FIX 3: text-left → text-start (logical property, RTL-safe).
- FIX 4: Document id=route-name assumption in useV2Nav.ts with a
  one-line comment at the id: assignment.
- FIX 5: Reword misleading "dotted names" spec description to state
  the real invariant (id = v1 route name, already kebab-case).
- FIX 6: Add 2 tests — useV2Nav wrapper .value equality, and
  consecutive-headings edge case (empty-items group produced).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:39:56 +02:00
80551eeb98 chore(types): sync auto-imports.d.ts for useV2Nav composable
unplugin-auto-import scans src/composables/, so the new useV2Nav added
a global + vue-module declaration. auto-imports.d.ts is tracked; keep
it in sync (same precedent as Plan 1's useRightDrawer sync).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:23:18 +02:00
8a8e419ed1 feat(gui-v2): port SidebarNav to TypeScript
Ports crewli-starter's sidebar nav into the SPA as production TS:
V2NavGroup/V2NavItem types, a pure toV2NavGroups adapter wrapped by
useV2Nav(items) (composables zone can't import @/navigation, so the
v1 nav array is passed in — the layout supplies orgNavItems in Task 7),
a pure isNavItemActive helper, and SidebarNav.vue (props-only,
router-driven nav, route-based active state, collapsed mode, main.css
translated to Tailwind inline). 16 unit tests. Icon import is
allowed via the components-foundation bridge (no eslint-disable).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:23:10 +02:00
4e9eeb99c4 fix(lint): mode:'file' for the components-foundation Icon.vue bridge
Plan-1 Task-4 added { type:'components-foundation', pattern:
'src/components/Icon.vue' } without mode:'file'. eslint-plugin-boundaries
defaults to folder mode, so the single-file pattern never matched and
Icon.vue fell through to the generic `components` catch-all — breaking
the sanctioned components-v2 -> Icon bridge (RFC AD-G5) for every v2
shell component. Plan-1's boundary test only exercised the forms/**
folder-glob edge so the gap was latent. Adds mode:'file' + a regression
test locking the components-v2 -> Icon.vue edge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 19:23:00 +02:00
3976c0cf0c feat(gui-v2): add mobileOpen to useShellUiStore
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 13:21:22 +02:00
547a281644 fix(storybook): use named installPrimeVue import in preview.ts
Storybook failed to boot ("module does not provide an export named
'default'"): .storybook/preview.ts did a default import while the
PrimeVue installer is a named-only export (deliberate — f218ac6e
switched to named to stop Vuexy registerPlugins() double-registration).
That commit missed updating preview.ts, so Storybook has been broken on
main since. Align the consumer with the producer (named import, same as
main.ts) rather than re-adding a default export. Pre-existing bug,
unrelated to the GUI-redesign merge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 12:33:58 +02:00
2cd6b02a1f docs(primevue): add anti-pattern note for responsive utility wrapping 2026-05-16 12:33:07 +02:00
a8c86d9dbc Merge branch 'feat/gui-redesign-foundation' (GUI redesign Plan 1)
Plan 1 of the crewli-starter GUI redesign: bootable parallel /v2/*
foundation with zero v1 disturbance.

- New RFC RFC-WS-GUI-REDESIGN-CREWLI-STARTER (supersedes F4a-F4d)
- v2RouteName helper + vite routesFolder mounting src/pages-v2 at /v2/
  with a v2- route-name collision guard
- eslint-plugin-boundaries components-v2/pages-v2 zones + narrow
  components-foundation bridge (no v1->v2 back-port)
- local-rules/require-v2-layout-meta ESLint rule on pages-v2
- useShellUiStore (sidebar/theme/density/drawer) + useRightDrawer facade
- OrganizerLayoutV2 + AppShellV2 Tailwind-grid skeleton (no PrimeVue yet)
- env.d.ts layout union extended; /v2/dashboard boot page + CT smoke

15 commits, per-task spec+quality reviews + final review (READY TO
MERGE). Gate green: 361 unit tests, v2 component + Playwright-CT pass,
typecheck/eslint/build clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 12:25:41 +02:00
53335dd308 chore(types): sync auto-imports.d.ts for useRightDrawer composable
unplugin-auto-import scans src/composables/, so the new useRightDrawer
(Task 7) added a global + vue-module declaration. auto-imports.d.ts is
tracked (committed for editor/CI type resolution without a build), so
keep it in sync — same precedent as committing typed-router.d.ts. No
new symbol from useShellUiStore: src/stores is not in the auto-import
dirs (stores are imported explicitly), which is correct.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 12:00:55 +02:00
6a45d86b6f feat(v2): boot /v2/dashboard through OrganizerLayoutV2 + AppShellV2
Replaces the Task 3 stub in pages-v2/dashboard.vue with the boot-proof
page (data-testid v2-dashboard, correct definePage meta). Adds the
Playwright CT smoke (appshell-boot.spec.ts) that mounts AppShellV2 in
Chromium and asserts both the shell root and slot content are visible;
uses page scope for the root-element assertion (CT component locator
only matches descendants, not the root itself). Full Plan-1 gate green:
typecheck 0 new errors, eslint clean, 5 vitest files / 21 tests + 2
component tests, vite build succeeded, typed-router has v2-dashboard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:57:41 +02:00
31f5a7c4f0 fix(types): register OrganizerLayoutV2/PortalLayoutV2 in route-meta layout union
The vite-plugin-vue-meta-layouts layout name is a hand-maintained union
in env.d.ts (not auto-generated). Task 8 created OrganizerLayoutV2 and
Task 3's boot-proof page sets meta.layout: 'OrganizerLayoutV2', but the
union lacked the v2 entries so a typed definePage failed vue-tsc.
Registers both v2 layouts (PortalLayoutV2 added now to pre-empt the same
gap when portal v2 lands). Completes Task 8 (RFC-WS-GUI-REDESIGN AD-G2).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:47:47 +02:00
73b2dea363 feat(layouts): add OrganizerLayoutV2 + AppShellV2 skeleton
Tailwind-grid shell skeleton with named slot regions (sidebar, topbar,
default, drawer). OrganizerLayoutV2 wires the skeleton with RouterView,
selectable via definePage meta. Vitest component mount test: 2 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:44:42 +02:00
b160f53f13 feat(composables): add useRightDrawer facade over useShellUiStore
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:40:02 +02:00
fc9c6ef164 feat(stores): add useShellUiStore for v2 shell UI state
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:37:13 +02:00
9754d26e07 test(lint): cover the PortalLayoutV2 branch of require-v2-layout-meta
Adds a valid case (PortalLayoutV2 under pages-v2/portal) and an invalid
case (OrganizerLayoutV2 under pages-v2/portal -> wrongLayout) so the
rule's portal-path branch has positive + negative coverage. Closes the
Task 5 code-review Important finding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:35:25 +02:00
93e4fe398b feat(lint): enforce definePage layout meta on pages-v2
Adds a custom ESLint rule (local-rules/require-v2-layout-meta) that
fails any src/pages-v2/**.vue page missing
definePage({ meta: { layout: 'OrganizerLayoutV2' } }) (or PortalLayoutV2
under pages-v2/portal), preventing a silent wrong-shell fallback to the
default layout (RFC-WS-GUI-REDESIGN AD-G2). Wires eslint-plugin-local-rules
+ a pages-v2 override. The RuleTester spec is called at top level (ESLint
RuleTester self-manages describe/it under Vitest) and vitest.config.ts
gains the eslint-rules test glob so the spec is discovered.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 11:30:03 +02:00
2465290614 docs(test): note boundaries/element-types deprecated-alias coupling
Documents that the test filter and the .eslintrc rule key both use the
v5-era 'boundaries/element-types' alias; a future eslint-plugin-boundaries
bump that drops the alias must update both together or the filter silently
matches nothing. Addresses the Task 4 code-review Minor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:55:02 +02:00
b1d3b9f53b feat(lint): add components-v2/pages-v2 boundary zones (no back-port)
Adds three new eslint-plugin-boundaries element zones and their matrix
rows so the GUI-redesign v2 surface is structurally isolated: v1 code
cannot import from v2 (back-porting forbidden), v2 can reach the
narrow FormField/Icon bridge via the components-foundation zone, and
pages-v2 can import from components-v2. Backed by a Vitest spec
running via the ESLint Node API (node environment; happy-dom's
document object breaks the case-police resolver). Adds a placeholder
src/components-v2/shared/X.vue so the resolver can classify the
import target during the test (unresolvable imports are not boundary-
checked by the plugin).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:46:31 +02:00
9d5398e0a2 refactor(router): make v2RouteName the single authority for the v2 name rule
Moves the `v2-` de-dup (needed because getPascalCaseRouteName folds the
v2/ URL segment into the base) into the unit-tested v2RouteName helper
and simplifies the vite.config.ts call site to v2RouteName(raw, nodePath).
Removes the duplicated isV2 detection. No behavioural change: /v2/dashboard
still resolves to route name v2-dashboard; v1 names unchanged. Addresses
the Task 3 code-review Important finding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:37:07 +02:00
714abd7178 feat(router): mount pages-v2 at /v2/* with v2- name prefix
Adds a second routesFolder (src/pages-v2 -> /v2/) and extends
getRouteName so v2 routes get a v2- NAME prefix, preventing collisions
with same-named v1 pages. getPascalCaseRouteName already folds the v2/
URL segment into the base name, so the leading v2- is stripped before
v2RouteName re-adds the canonical prefix (avoids v2-v2-dashboard).
Includes the regenerated typed-router.d.ts and a boot-proof
pages-v2/dashboard.vue placeholder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:30:33 +02:00
be245080e1 feat(router): add v2RouteName collision-guard helper
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 01:17:05 +02:00
d2c91f4e80 docs: fix blockquote spacing in PRIMEVUE_COMPONENTS GUI-redesign pointer
Add blank line between the new pointer blockquote and the **Aligned to:**
paragraph so the blockquote closes cleanly across markdown renderers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 01:15:29 +02:00
5bd7478614 docs: add GUI-redesign RFC superseding F4a-F4d
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 01:10:02 +02:00
01b0930679 docs: add GUI-redesign foundation implementation plan (Plan 1 of 5)
RFC + bootable /v2/ vertical slice (spec §9 deliverable 1). TDD task
breakdown: v2RouteName guard, routesFolder wiring, boundary zones,
definePage ESLint rule, useShellUiStore, useRightDrawer, OrganizerLayoutV2
+ AppShellV2 skeleton, /v2/dashboard boot proof. Plans 2-5 outlined.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 00:48:58 +02:00
4302ed389d docs: apply spec review round 2 corrections (GUI redesign design)
All corrections audited against the codebase:
- §7.4 useWorkspaceStore ghost removed (computed over auth/org stores)
- §12 portal /portal/* verified in repo; observability is meta-based,
  /api/v1/p/* is separate backend layer — no cross-doc conflict
- §3 getRouteName v2- name-prefix convention (route-name collision)
- §4 theme parallel-mode AD + useRightDrawer in useShellUiStore
- §8/§9 DraggableBlock is foundation, not Tier-4
- §3 single ESLint enforcement for definePage meta-key
- §8 StatusTag severity map; §14 brace-glob fallback; §13 CT/Storybook

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 00:33:51 +02:00
5068ee5db9 docs: apply spec review round 1 corrections (GUI redesign design)
All corrections audited against the codebase:
- TEST-INFRA-001 verified Resolved; add §13 testing strategy
- §3 specify exact routesFolder + definePage layout meta convention
- §5 boundaries claim corrected; add §14 zone/matrix extension
- §4 drop useWorkspaceStore (dup) → reuse auth/org stores + useShellUiStore
- §12 explicit portal scope (/portal/*); §10 SmartFilter own sub-sprint

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 23:17:38 +02:00
890bcc88cb docs: add GUI redesign design spec (crewli-starter as design source)
Brainstorming outcome: pivot the PrimeVue redesign to use crewli-starter
as the design source of truth, parallel /v2/ routes, PrimeVue-first
fidelity, page-by-page cutover. Supersedes F4a-F4d of
RFC-WS-FRONTEND-PRIMEVUE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 22:54:32 +02:00
524d0ee586 Merge pull request 'chore(f3.5): AppShell mockup parity — sidebar, topbar, plugin fixes' (#26) from chore/f3.5-appshell-mockup-parity into main
Reviewed-on: #26
2026-05-14 13:38:49 +02:00
71585e1bbc fix(appshell): wrap PrimeVue responsive elements to bypass specificity conflict
Tailwind's lg:hidden loses to PrimeVue's .p-button { display: inline-flex }
due to equal specificity but later cascade order. Resulted in the mobile
hamburger remaining visible on desktop, allowing the Drawer to open over
the already-visible permanent sidebar.

Fix: wrap mobile-only cluster (hamburger + title) in a plain <div lg:hidden>
so the wrapper owns the visibility toggle. The wrapper is not a PrimeVue
component, so no specificity competition.

The Drawer itself had the same anti-pattern (class="lg:hidden") and is
worse, because PrimeVue Drawer teleports to body — a wrapping div on the
parent does not isolate the teleported overlay, and a class on the Drawer
root loses to .p-drawer { display: flex } when visible. Converted to
v-if="!isLg" driven by useMediaQuery('(min-width: 1024px)'). Vue simply
does not render the component on lg+, so no display rule competes.

Audited all 5 layouts for the same anti-pattern:
- AppShell.vue — fixed (Button + Drawer described above)
- default.vue / OrganizerLayout.vue / PortalLayout.vue — delegate to
  AppShell; no PrimeVue elements with responsive classes
- blank.vue — plain <div>, no PrimeVue
- PublicLayout.vue — plain <main>, no PrimeVue

useMediaQuery is auto-imported via unplugin-auto-import's @vueuse/core
entry in vite.config.ts; explicit imports get stripped by the post-edit
ESLint --fix hook as redundant.

F3-introduced bug (commit 43915501); surfaced during F3.5 testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:36:00 +02:00
f218ac6e69 fix(primevue): switch installer to named export to stop double-registration
main.ts explicitly calls installPrimeVue(app) AFTER registerPlugins(app)
per the comment in main.ts ("so PrimeVue lives outside the Vuexy @core
machine"). The intent was a single registration site outside the
auto-discovery loop.

Bug: registerPlugins (src/@core/utils/plugins.ts) globs
plugins/*/index.{ts,js} eagerly and invokes the `default` export of
each match. plugins/primevue/index.ts was exporting installPrimeVue
as the default, so registerPlugins also picked it up and called it.
End result: PrimeVue and its three services (Toast, Confirmation,
Dialog) were each registered twice on every app boot. Visible
symptoms: duplicate Toast emissions on a single Toast.add() call,
and ConfirmationService callbacks firing twice for one user
confirmation.

Fix: convert `export default function installPrimeVue` to a NAMED
export, and update main.ts's import to `{ installPrimeVue }`. The
registerPlugins glob still picks up the module path but the
`pluginImportModule.default?.(app)` invocation becomes a no-op via
optional chaining (no default export to call). main.ts remains the
single registration site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:59 +02:00
b1443be414 fix(iconify): bootstrap Tabler icon set at runtime for @iconify/vue
The PrimeVue side of the parallel-mode app renders icons via
@iconify/vue's <Icon> component (src/components/Icon.vue). At runtime
@iconify/vue resolves an icon name like "tabler-eye" by looking up
its data in the in-memory icon registry; on a miss it falls back to
fetching https://api.iconify.design/tabler/eye.json. The CSP blocks
that origin, so every Tabler icon used in AppShell, SidebarHeader,
SidebarUserCard, and the migrated login form rendered as an empty
<svg viewBox="0 0 16 16"></svg>.

New plugins/iconify.ts loads the full Tabler set
(@iconify-json/tabler/icons.json, already in package.json as 1.2.23)
and registers it via addCollection() at module-load time. main.ts
side-effect-imports it before any other import so the registry is
warm before the first Icon mounts.

This is a NEW concern, separate from the existing plugins/iconify/
(index.ts + icons.css) which generates Vuexy-style i-tabler-* CSS
classes for Vuetify's VIcon adapter. The two systems must coexist
during F3–F6 parallel mode; the legacy directory can be deleted
alongside Vuetify when F6 lands.

Bundle cost: ~1.9 MB uncompressed JSON, ~400 KB gzipped in the main
chunk. Per-icon imports are a future optimisation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:59 +02:00
29f3fdf2a3 fix(appshell): explicitly import SidebarHeader and SidebarUserCard
unplugin-vue-components' Components({ dirs }) in vite.config.ts only
scans src/components, src/@core/components, and src/views/demos. The
sub-components introduced in B1/B3 live under src/layouts/components/,
which is NOT in the auto-import scan path. Without an explicit script
import, Vue renders <SidebarHeader> and <SidebarUserCard> as unknown
HTML elements (no DOM output, no errors), which is why the topbar and
sidebar-bottom cards looked empty in browser inspection.

Adding the two imports inline with the existing Icon import keeps the
component graph explicit. The post-edit eslint --fix hook preserves
the imports because the template usages (already present from B1 and
B3) make vue-eslint-parser see them as used.

The original B1/B3 commits had the imports stripped by the hook
because the imports were added in a separate Edit *before* the
template usages — eslint --fix correctly removed them as unused at
that moment, and the next Edit added the template usage but not the
import.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:58 +02:00
3df55b4d1c feat(appshell): topbar breadcrumb, notification bell, and help icon
Left side gains a desktop-only breadcrumb "Organisation / Page title"
using the current organisation from useAuthStore and a page title
resolved by:

  1. route.meta.title (if a page sets it explicitly), then
  2. matching the active route name against the navItems prop, then
  3. humanizing the route name as a last-resort fallback.

The chevron separator is suppressed when either side is empty, so
portal and pre-org users see just the page title. Mobile preserves
the existing hamburger + title text (the breadcrumb is hidden on
<lg to keep the topbar single-row).

Right side gains a notification bell and a help icon. The bell is a
visual placeholder (no badge) — clicking shows a PrimeVue Toast
"Notificaties komen binnenkort beschikbaar" until the notification
framework lands as a separate sprint.

The help icon would normally open https://docs.crewli.app in a new
tab, but the host currently serves with a TLS cert that does not
cover the name (ERR_TLS_CERT_ALTNAME_INVALID), so the click handler
falls back to a Toast. A TODO comment in the source records the
target URL and the one-line switch to make once the cert is fixed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:57 +02:00
f8fddc0e14 feat(appshell): add user-info card to sidebar bottom; remove topbar avatar
Consolidates the user menu into a single sidebar-bottom location.
SidebarUserCard.vue shows avatar (initial), full name, role (Dutch
label mapped from org pivot role or 'Super Admin' fallback) and a
chevron-up that opens a PrimeVue Menu with "Mijn Profiel" and
"Uitloggen". The Menu uses popup mode; PrimeVue v4's absolutePosition
logic auto-flips above the trigger when the panel would overflow the
viewport bottom — verify in Phase C.

AppShell loses the topbar avatar Button + Menu and the associated
state (userMenuRef, userInitial, userMenuItems, toggleUserMenu) plus
its imports (Avatar, Menu, useAuthStore, computed). The component is
now a pure layout shell with no auth-store coupling. The topbar's
right side is intentionally empty in this commit; B4 fills it with
breadcrumb / notification bell / help icon.

Layout: nav uses min-h-0 flex-1 overflow-y-auto so it shrinks under
viewport pressure and lets the user card stay pinned at the bottom
of the sidebar. Mobile Drawer's content pt-override sets the same
flex-column behaviour so the user card sits flush at the bottom of
the drawer overlay.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:57 +02:00
4089a14bb8 feat(appshell): refine section label styling for sidebar nav
Section headings ("Beheer" / organisation name, "Platform") were
already uppercase + muted but read as bold paragraph dividers more
than as quiet group markers. Tighten letter-spacing, drop weight
from semibold to medium, lighten the color one step (surface-500 →
surface-400), and shrink text to 11px so the headings recede and
let the nav items themselves carry the visual weight.

Spacing nudged from mt-4/mb-2/px-2 → mt-6/mb-1/px-3: more breathing
room above each group, less below (the items already have py-2 on
top), and the heading left-edge now lines up with the icons of the
nav items beneath it (both at px-3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:56 +02:00
8f3a404a42 feat(appshell): add org-switcher card and bump sidebar width to w-72
Introduces SidebarHeader.vue — a PrimeVue-only org-switcher that
replaces the centered Crewli wordmark at the top of the sidebar. The
component mirrors the legacy Vuetify OrganisationSwitcher (avatar with
org initials, organisation name, plan-tier placeholder, dropdown
chevron, PrimeVue Menu of available orgs) but cannot reuse it
directly per the R-10 layout-shell-isolation invariant.

Plan-tier shows a hardcoded "Pro" placeholder until the backend
Organisation resource exposes a plan field — tracked separately, not
in F3.5 scope. When the user has no active organisation (portal
users, fresh super_admin), the component degrades to the original
title block so PortalLayout continues to read "Crewli Portal".

Desktop sidebar width bumped w-64 → w-72 (256 → 288 px) to give the
org-switcher card breathing room and accommodate the user-info card
arriving in B3. Mobile Drawer width bumped 16rem → 18rem to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:35:56 +02:00
a17dbb7dfd Merge pull request 'chore: add Storybook 10 setup with PrimeVue + Tailwind integration' (#25) from claude/reverent-driscoll-a37dce into main
Reviewed-on: #25
2026-05-14 13:32:09 +02:00
3c6bd05289 docs: fix stale Vitest note in FRONTEND-TOOLING + update RFC-WS-FRONTEND-PRIMEVUE §13 Storybook entry 2026-05-14 11:53:01 +02:00
999e30f0fc docs: add Storybook section to FRONTEND-TOOLING.md 2026-05-14 11:50:44 +02:00
ebb8e3bcf6 chore: add Storybook 10 setup with PrimeVue + Tailwind integration
Installs Storybook 10.4 in apps/app/ as a component-development and
autodoc tool. Configures viteFinal with all seven SPA aliases so
stories resolve imports identically to the dev/build pipeline.
preview.ts reuses @/plugins/primevue's installPrimeVue() so Storybook
stays in lock-step with main.ts whenever the PrimeVue config changes.

Only the addons we need are wired: addon-docs (autodocs) and
addon-a11y (axe-core checks). addon-interactions is intentionally
omitted — interaction testing stays in Playwright CT per the testing
architecture.

Seed stories: PrimeVue Button (Primary/Secondary/Danger), Tailwind
utility box, and FormField (Default/WithError/Disabled) wrapped in
@primevue/forms Form + Zod resolver.

Adds make storybook target alongside make app / make docs.
2026-05-14 11:50:21 +02:00
e36f57b8e1 Merge pull request 'chore(primevue): F3 — PrimeVue foundation with parallel-mode operation' (#24) from chore/f3-primevue-foundation into main
Reviewed-on: #24
2026-05-11 20:07:56 +02:00
d5c9cf1927 docs(rfc): correct AD-2/AD-5 and Appendix B to reflect ecosystem state
Three RFC drift corrections discovered during F3 implementation:

1. AD-5 icon rendering: corrected from "<i :class='i-tabler-X'>"
   utility-class pattern (which would require UnoCSS, not installed)
   to "@iconify/vue's <Icon> component with name='tabler-X' prop"
   (existing Crewli pattern producing real SVG output). The thin wrapper
   shipped in F3 B6 as apps/app/src/components/Icon.vue accordingly.

2. AD-2 theme architecture: corrected package reference from
   @primevue/themes@^4.5 (deprecated by PrimeFaces) to
   @primeuix/themes@^2 (the path now prescribed by PrimeVue 4's
   official install docs at primevue.org/vite/). Same maintainers,
   same API surface (definePreset, Aura preset, semantic tokens).
   F3 commit B1 already uses the corrected package.

3. Appendix B Aura theme token plan: updated import-path examples to
   @primeuix/themes and @primeuix/themes/aura accordingly.

Also updated:
- §6 F3 deliverables list: dependency line now reads @primeuix/themes@^2
  with a footnote linking to the B1 rationale.
- Appendix C Version Pinning Policy: separated @primeuix/themes from
  the primevue/^primevue/forms lockstep pin (independent release cadence).
- dev-docs/PRIMEVUE_COMPONENTS.md §3 (Data display): VIcon row updated
  to <Icon name="tabler-..." />; surrounding migration-spirit paragraph
  rewritten; §10 external-resources link relabeled to @primeuix/themes.

These are RFC drift corrections — the implementation in F3 (commits
B1, B2, B6 of this sprint) already uses the corrected packages and
import paths. This commit aligns the spec with reality so future
contributors don't reach for the deprecated/inaccurate documentation.

.claude-sync/ regenerates automatically post-commit via lefthook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 01:16:13 +02:00