WS-3 session 1b-i Task 3.
The Tier 1 + Tier 2 autofix passes (curly-brace stripping in
particular) left trailing whitespace on the affected lines. \`git diff
--check\` flagged 1 file with 7 trailing-whitespace lines:
- apps/app/src/layouts/components/DefaultLayoutWithVerticalNav.vue
Used full-file sed strip per the prompt's <30-files decision rule.
Once the trailing whitespace was gone, a follow-up
\`eslint --fix\` on the same file resolved 8 additional cascading
items that the original Tier 1 pass couldn't reach because of
ESLint's default 10-pass cap (curly-strip → exposed-indent →
multi-blank-line cascade). The re-indented body is now consistent
(4/8/6 spaces), no logic touched. This second-pass cleanup is folded
into this commit because it was triggered by — and is only a
mechanical follow-up to — the whitespace strip.
Other Tier 1 / Tier 2 files may have similar pass-cap residue
(161 fixable items remain in the post-Tier-2 baseline). Those are
deferred to session 1b-ii's planned second-pass autofix and are
flagged in the audit report.
Tests + typecheck still green.
Lint baseline progression:
- Pre-Task-3 (post-Tier-2): 246 problems
- Post-Task-3: 231 problems
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
WS-3 session 1a Task 3.
Vitest covers each layout with: (1) it mounts without throwing,
(2) it renders the expected DOM structure (top-bar/main/footer for
PortalLayout, none for PublicLayout, slot-passthrough for
OrganizerLayout), (3) it places <RouterView /> in the right region.
Vuetify components (VApp/VAppBar/VMain/VFooter) are stubbed to their
semantic HTML equivalents so the structural assertions still hold
without pulling vuetify/components into the trimmed-down vitest
config (which lacks the CSS plugin needed to transform Vuetify's
.css side effects). OrganizerLayout uses vi.mock to short-circuit
the DefaultLayoutWithVerticalNav import for the same reason.
Vitest count: 41 -> 49 in apps/app.
WS-3 session 1a Task 2.
Three layout skeletons added to apps/app/src/layouts/. They are NOT
yet referenced by the router — that wiring is a later session.
- OrganizerLayout: thin wrapper around DefaultLayoutWithVerticalNav,
visually identical to default.vue. Provides a semantically named
target for future router meta:layout='OrganizerLayout'.
- PortalLayout: scaffold for volunteer/crew portal experience.
Top bar + main + footer regions, no content yet.
- PublicLayout: minimal centered viewport for unauthenticated pages
(login, password-reset, public form viewer).
default.vue and blank.vue are unchanged and remain the active layouts
referenced by the router. Their replacement happens in the router
consolidation session.
Refs: ARCH-CONSOLIDATION-2026-04.md §4 + §6.8.
Replace position:fixed VSystemBar + fragile :deep() CSS overrides
with a normal-flow div inside the Vuexy content area. The banner
renders in VerticalNavLayout's default slot (layout-page-content)
so it sits naturally below the navbar without fighting the layout
system. Sidebar and navbar are no longer affected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous :deep() overrides had equal specificity to Vuexy's
unscoped styles in VerticalNavLayout.vue. Since child component
styles are injected after parent styles, Vuexy's inset-block-start: 0
won by source order. Add !important and simplify the navbar selector
to target .layout-navbar directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous paddingTop on a wrapper div didn't affect the Vuexy
layout's fixed-position sidebar or sticky navbar. Replace with
scoped :deep() CSS overrides that shift both elements down 48px
when impersonation is active.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Banner: white elevated button for contrast, fixed 48px height,
layout top padding offset so content isn't obscured
- Middleware: allow GET me/profile (viewing), block mutations only;
add auth/refresh to blocked routes
- Navigation: hide Platform section during impersonation; hide
org-dependent items when impersonated user has no organisation
- Test: add read-only routes allowed test, auth/refresh blocked test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Build the frontend for platform admin in apps/app/:
- TypeScript types (admin.ts) and API composable (useAdmin.ts) with
TanStack Query for all admin endpoints
- ImpersonationStore (Pinia) + ImpersonationBanner component integrated
in the main layout, with token-based session management
- Platform navigation section (conditionally shown for super_admin users)
- Route guard blocking /platform/* for non-super_admin users
- 6 pages: dashboard with stats cards, organisations list/detail,
users list/detail with impersonation, activity log with expandable rows
- All pages implement loading/error/empty states per conventions
- Vite build passes cleanly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Password reset: multi-app support with custom notification linking to correct
frontend (app/portal/admin). Email change: self-service with password
confirmation and admin-initiated, both sending verification to new address
with 24h expiry. Confirmation sent to old email on completion. Password
change: authenticated endpoint revoking other sessions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-cutting migration affecting the entire stack:
- Database: 3 migrations splitting name columns with data migration
- Models: first_name/last_name on User, Person; contact_first_name/contact_last_name on Company; backward-compatible name accessors
- API: all resources return first_name, last_name, full_name; assignablePersons endpoint updated
- Requests: validation rules updated for all person/user/company forms
- Services: VolunteerRegistrationService, ShiftAssignmentService, InvitationService updated
- Frontend: TypeScript types, Zod schemas, all forms split into Voornaam/Achternaam fields
- Display: all person/user name references use full_name; initials use first_name[0]+last_name[0]
- Tests: all 371 tests passing
- Docs: SCHEMA.md and API.md updated
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>