Commit Graph

338 Commits

Author SHA1 Message Date
59007e60e0 feat(layout): Plan 2.5 P1 foundation — APP_NAVIGATION + walkNavTree + AppBreadcrumb
Per RFC-WS-PRIMEVUE-PLAN-2-5 §8 step 1. Foundation scaffolding only —
no shell fixes, no Public Sans removal, no useShellUiStore changes
(P2–P6 scope).

Implements:
- theme darkModeSelector verified at '.dark' (already correct in
  plugins/primevue/index.ts — config site is here, not theme.ts).
- src/config/navigation.ts: APP_NAVIGATION registry per AD-2.5-B1
  (Dashboard entry only — v2-dashboard is the only v2 route today).
- src/composables/useBreadcrumb.ts: walkNavTree pure helper +
  useNavBreadcrumb composable per AD-2.5-B1. The legacy meta-based
  useBreadcrumb is preserved (consumed by AppTopbar, P1 may not
  touch AppTopbar); P4 retires it and renames useNavBreadcrumb.
- src/components-v2/layout/AppBreadcrumb.vue: layout primitive
  wrapping PrimeVue Breadcrumb, consuming useNavBreadcrumb.
- Tests: walkNavTree (4 specs, co-located), AppBreadcrumb mount
  (2 specs, tests/component/layouts/).

Suite 564 → 570 (+6, all new specs green). vue-tsc clean. Scoped
ESLint clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 07:23:16 +02:00
637d77b327 docs(plan-3): close out Plan 3 — BACKLOG entries, RFC status, primitives registry, tooling conventions
- BACKLOG: add 3 spawned follow-ups (EnergyDots NaN, DraggableBlock pointercancel, AD-3 Menubar a11y)
- RFC-WS-GUI-REDESIGN-CREWLI-STARTER: mark Plan 3 complete with commit refs + DoD ledger
- PRIMEVUE_COMPONENTS: v2 primitives registry (8 components), statusSeverity SoT, Menubar-wrap pattern
- ARCH-TESTING: mount-helper type convention (Plan 3 codified, Plan 4 carry-over)
- FRONTEND-TOOLING: scoped lint invocation note (DoD #13 root cause)
- AppDialog.stories.ts: rename title to 'Shared/AppDialog' for sibling consistency
2026-05-19 01:41:19 +02:00
0b19e7856b style(gui-v2): resolve 7 ESLint errors in Plan 3 components/specs (behavior-neutral, DoD #13)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:23:30 +02:00
1a66ac6e64 refactor(gui-v2): delete X.vue stub, repoint 2 boundary refs to StatusTag, add shared/* regression locks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:48:21 +02:00
237afc89e6 fix(gui-v2): cleanup(b) — keep mobile workspace btn a free #end sibling (Plan-2 flex parity) + lock data-tb=search 2026-05-18 13:36:12 +02:00
f03a3f16c6 refactor(gui-v2): cleanup(b) — AppTopbar wraps PrimeVue Menubar per RFC AD-3 2026-05-18 12:55:18 +02:00
183218effa refactor(gui-v2): cleanup(a) — co-locate Plan 2's 6 stories per amended §6 (_helpers stays)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 12:00:58 +02:00
814d11c8db feat(gui-v2): DraggableBlock §7.1 abstraction (PointerEvent drag, A2-reconciled) + CT + stories 2026-05-18 11:48:59 +02:00
91d20d0dd2 feat(gui-v2): EnergyPicker interactive 5-step (crewli-starter port) + story 2026-05-18 11:25:27 +02:00
79650d0b72 feat(gui-v2): EnergyDots 5-dot meter (scoped CSS justified per §8) + story 2026-05-18 11:16:57 +02:00
b64b024166 feat(gui-v2): TagsInput re-impl on PrimeVue AutoComplete (5 behavioural rules) + story
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:08:45 +02:00
284fdcc437 feat(gui-v2): StateBlock 3-state wrapper (exhaustive Vitest, no @visual per constraint #5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:00:20 +02:00
b0d5e9611f feat(gui-v2): PageHead (Tailwind flex title/sub/#actions) + story 2026-05-18 10:52:09 +02:00
12cff8c03a feat(gui-v2): StatCard (PrimeVue Card KPI tile, replaces AppKpiCard) + story
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:41:34 +02:00
9d1fd16f0f feat(gui-v2): StatusTag (PrimeVue Tag + statusSeverity map) + story
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:34:21 +02:00
20af2ebd32 feat(gui-v2): statusSeverity SoT map + bidirectional §8.X consistency test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:23:44 +02:00
aa4b651870 refactor(gui-v2): imports-first in shell specs, drop eslint-disable
Vitest hoists vi.mock()/vi.hoisted() above all imports, so the
component import can sit with the other imports (import/first satisfied)
without the eslint-disable-next-line directives — the mock factories
only deref their refs at mount time. Honors the no-eslint-disable rule.
28/28 affected specs green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 13:29:58 +02:00
7d326720ab fix(gui-v2): rename unused _bp mock arg to _ (no-unused-vars gate)
The project's no-unused-vars only ignores all-underscore names (/^_+$/u);
`_bp` in the @vueuse/core useBreakpoints mock failed it. Latent since
Task 3 — masked because the whole-codebase `pnpm lint` stylish formatter
OOMs (RangeError on the legacy-code message volume) and emitted no
results. Scoped errors-only lint surfaced it. 21/21 specs still green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 03:24:19 +02:00
92d8051903 fix(gui-v2): scope AppTopbar dark story + DRY shell story renders
Code-review follow-up. AppTopbar DarkTheme mutated <html>.dark which
leaked into Default/CompactDensity stacked on the same autodocs page;
scope dark to the story subtree via a `.dark` wrapper (Aura
darkModeSelector is the `.dark` class) — verified isolated on the docs
page. Also factor the duplicated render scaffolds in AppDialog (shared
dialogStory factory) and WorkspaceSwitcher (meta-level render).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 02:29:03 +02:00
e356bc8a95 feat(gui-v2): Storybook stories for the v2 shell components 2026-05-17 02:18:37 +02:00
41b4017bd1 feat(gui-v2): wire OrganizerLayoutV2 to compose the real shell components
Replaces the Plan-1 skeleton stubs: OrganizerLayoutV2 now fills
AppShellV2's #sidebar/#topbar/#drawer slots with the ported AppSidebar /
AppTopbar / RightDrawer and sources orgNavItems via useV2Nav() (legal
now that OrganizerLayoutV2.vue is the layouts-v2 zone). AppShellV2 is
unchanged; its contract test stays green. New component test locks the
composition (right component per slot, :groups forwarded, no skeleton).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 02:04:07 +02:00
a341a60412 feat(boundaries): add layouts-v2 zone so v2 shell layout can use components-v2
AD-G2 ("OrganizerLayoutV2 wraps AppShellV2") was in tension with AD-G5:
src/layouts/OrganizerLayoutV2.vue classified as the v1 `layouts` zone,
which is deliberately barred from components-v2. New `layouts-v2` zone
(src/layouts/*V2*.vue, mode:file) gets pages-v2-equivalent v2 capability;
the v1 `layouts` zone is unchanged so v2 isolation is preserved. RFC
AD-G5 amended; locked by 3 boundaries-v2.spec.ts regression tests (7/7).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 02:03:00 +02:00
6e5c5bbec3 fix(gui-v2): rename AppDialog test stub off reserved HTML name "Dialog"
vue/no-reserved-component-names is error-level; <dialog> is native HTML.
Matching is via the stubs key + findComponent reference, not the stub's
own name, so the rename is behaviour-neutral (14/14 tests still pass).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:55:14 +02:00
3685797e18 fix(gui-v2): wire AppDialog accessible name + cover close/width in tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:53:04 +02:00
c26b281fa7 feat(gui-v2): port AppModal -> AppDialog (PrimeVue Dialog) to TypeScript
Replaces crewli-starter's hand-rolled Teleport/scrim/keydown Escape
pattern with a typed PrimeVue Dialog wrapper. v-model:open via writable
computed; slots #tabs/#footer gated; 12 unit tests all passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:37:46 +02:00
ca0332d17a fix(gui-v2): :key on RightDrawer dynamic body to remount on switch
Adds :key="component" to <component :is="Body"> so opening drawer B
while A is open fully remounts (A's instance/state can't leak into B) —
a real defect for a primary shell overlay. Same-name reopen still
relies on the body reacting to prop changes (documented inline).
Companion test asserts the cross-component switch swaps the body
cleanly (A unmounts, B mounts). Addresses the Task 5 code-review
Important finding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:30:43 +02:00
2d7d4b49d8 chore(types): sync auto-imports.d.ts for drawerRegistry
unplugin-auto-import scans src/composables/; the new drawerRegistry
exports added global + vue-module declarations. auto-imports.d.ts is
tracked — keep it in sync (same precedent as prior composable syncs).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:22:55 +02:00
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
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