Commit Graph

16 Commits

Author SHA1 Message Date
473b22ac9e feat(router): context-aware guards with meta-driven role/context resolution
Rewrites plugins/1.router/guards.ts per ARCH-CONSOLIDATION §4.3. The
B1 portal-context carve-out is removed; portal/organizer routing is
now declarative via meta.context, role gates via meta.requiresRole.

Guard pipeline:
1. Initialize auth store on first navigation
2. Public routes pass through (authenticated user on guest-only path
   is bounced to resolveLandingRoute)
3. Auth required → /login?to=<path>
4. MFA setup gate → /account-settings?tab=security
5. requiresRole declarative check (replaces hardcoded /platform path
   prefix + isSuperAdmin)
6. Context routing — portal returns early, organizer falls through
   and sets lastContext
7. Org-selection check (organizer routes only)

Page meta updates (mechanical, idempotent):
- 4 portal pages: removed `requiresAuth: true` (auth is implicit)
- 4 pages: replaced `requiresAuth: false` with `meta.public: true`
  (registreren, wachtwoord-instellen, advance/[token],
  invitations/[token])
- 22 organizer pages: added `context: 'organizer'`
  (account-settings, events/**, organisation/form-failures/**,
  select-organisation, dashboard, events/index, members,
  organisation/{index,companies,settings})
- 8 platform pages: added `context: 'organizer'` +
  `requiresRole: 'super_admin'`
- 6 organizer pages had no definePage block — one was added with
  `context: 'organizer'`

Adds plugins/1.router/__tests__/guards.spec.ts (11 tests) covering
public passthrough, unauthenticated redirect, portal/organizer
context branching, declarative requiresRole, org-selection
redirect, MFA gate.

Test count 178 → 189 (11 new). Lint + typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 21:32:54 +02:00
1289b217d0 fix(app): resolve Bucket E.2-E.5 lint findings
WS-3 session 1b-ii Task 5b+c (audit Bucket E.2-E.5 — 6 items resolved,
2 promise/no-promise-in-callback warnings remain on dynamic-import
sites — see deviations).

This commit is split out from the originally-planned grouped Task 5
because the API stream timed out mid-session. E.1 (isAxiosError) is in
the preceding commit 0f155d9.

E.2 — vitest spec to Composition API (1× vue/component-api-style):
- useFormFailures.spec.ts: rewrote the test wrapper from
  \`{ setup() { return { result } }, render: () => h('div') }\`
  to \`setup(_, { expose }) { expose({ result }); return () => h('div') }\`.
  Pure Composition API: setup returns the render function; expose()
  declares the instance-visible \`result\` that the 7 \`vm.result.*\`
  assertions consume. Tests still pass green (49 tests).

E.3 — REAL BUG: missing return in computed (1× vue/return-in-computed-property):
- useTimeSlotDropdown.ts:80: the \`fetchParams\` computed had a switch
  over the \`DropdownScenario\` type (4 string-literal cases) without
  a \`default\` branch. If \`scenario.value\` ever returned a value
  outside the four narrowed cases (e.g. via a future type-assertion
  drift), the computed silently returned \`undefined\`, and the
  consumer code (\`fetchParams.value.includeParent\`) would throw
  \`Cannot read property 'includeParent' of undefined\`. Added a
  \`default\` branch returning \`{ includeParent: false, includeChildren: false }\`
  — same as the 'flat' case (the safest baseline: include only own
  slots, no hierarchy).

E.4 — SECURITY (1× vue/no-template-target-blank):
- pages/organisation/index.vue:343: the external website anchor had
  \`target='_blank'\` with \`rel='noopener'\` (only one). The rule
  requires the full \`rel='noopener noreferrer'\` pair. Updated.
  Mitigates reverse-tabnabbing (window.opener) AND referrer-leakage
  to the linked third-party site.

E.5 — axios fire-and-forget (3× promise/no-promise-in-callback,
1 fully resolved + 2 warnings remain):
- lib/axios.ts:42: changed \`error => Promise.reject(error)\` to
  \`async error => { throw error }\`. Semantically identical (axios
  interceptor onRejected returns a rejected promise either way) and
  satisfies the lint rule.
- lib/axios.ts:61, 73: prefixed the dynamic-import chains with \`void\`
  per Q4's option-a decision (\`void import('@/stores/...').then(...)\`).
  This makes the discard intent explicit, but empirically does NOT
  satisfy promise/no-promise-in-callback — the rule fires on any
  promise creation inside a callback, regardless of the discard
  pattern. The 2 warnings remain in the post-Task-5 baseline.
  Resolution path is Bert's call: either keep \`void\` and accept
  the warnings as documentation, or rewrite to \`async error => {
  const { useStore } = await import(...); ... }\` which sequentializes
  the dynamic-import resolution with the rejection. Out of scope for
  this session per the literal Q4 recipe.

Tests + typecheck verified green.

Lint baseline: 34 → 32.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 15:15:29 +02:00
47bd533179 style(app): apply eslint --fix to Tier 1 (Vue templates)
WS-3 session 1b-i Tier 1.

Scope: src/components/**, src/pages/**, src/layouts/**, src/views/**
restricted to *.vue files. Mechanical formatting only — predominantly
vue/html-indent (506 fixes in CrowdListDetailPanel.vue alone),
padding-line-between-statements, antfu/if-newline.

Excludes (per session prompt):
- apps/app/vite.config.ts (Tier 3)
- apps/app/themeConfig.ts (Tier 3)
- apps/app/vitest.config.ts (Tier 3)
- All TypeScript-only files in src/composables, src/lib, src/stores,
  src/plugins, src/types (Tier 2 — separate commit)

Includes session 1a layouts (PortalLayout.vue, PublicLayout.vue) where
2 'lines-around-comment' errors were flagged in the previous 1b-i
pre-flight inspection.

Tests + typecheck verified green post-fix:
- apps/app vitest: 49 passed (unchanged)
- apps/app vue-tsc: clean (unchanged)
- apps/portal vitest: 113 passed (unchanged — not touched)
- backend pest: 1486 passed (unchanged — not touched)

Lint baseline progression:
- Pre-Tier-1: 1451 problems
- Post-Tier-1: 422 problems

Visual smoke status:
- NOT YET SMOKED — Bert to verify before merge. This Claude Code
  session has no UI access; cannot run pnpm dev and click through
  affected routes. The high-traffic candidates are
  CrowdListDetailPanel (506 fixes), AssignPersonDialog (44),
  ShiftDetailPanel (36), and the events / form-failures pages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 11:04:46 +02:00
2ae90ed57f feat(app): unify KPI tiles with AppKpiCard
Introduce AppKpiCard for consistent metric layout (icon + value, title,
subtitle row) and default VCard chrome without mixed border-shadow accents.
Use on organisation overview (all primary icons, equal stretch row) and
home dashboard. Regenerate component type declarations.

Made-with: Cursor
2026-04-29 00:46:48 +02:00
027c5dac4e feat(organisation): expand /organisation page to full dashboard
Replace the minimal placeholder with a dashboard: header + edit action,
drie stat-tegels (Leden / Evenementen / Personen — de eerste twee
clickable), organisatiegegevens + leden-top-5 infokaarten en een recente-
activiteit lijst. Nieuwe TypeScript-types en useOrganisationDashboardStats
composable sluiten aan op de nieuwe backend-endpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 10:27:51 +02:00
9f19c9ed37 feat: move organisation members to sidebar, drop tabs on org page
Made-with: Cursor
2026-04-15 22:31:21 +02:00
66e4167c03 refactor: identical VDataTable for members on both organisation pages
Both org pages now use the same VDataTable with:
- Search field (name/email filter)
- Sortable columns (Naam, E-mail, Rol) with default sort on name
- Pagination (10 per page)
- Avatar with initials, role chips with color mapping
- Consistent empty state with icon

Platform page: replaced VTable with VDataTable, added role chips
(replacing inline AppSelect), role editing via menu on edit button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:22:01 +02:00
ca275723db fix: use consistent text-body-1 text-disabled for timestamps
Replace custom text-caption span with the standard
<p class="text-body-1 text-disabled mb-0"> pattern used across
all other pages in the codebase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:16:29 +02:00
c7dd6aa59c fix: slug in parentheses, capitalize status, lighter timestamps, rename button
Both organisation pages: slug wrapped in parentheses, billing status
label capitalized, timestamps use text-disabled for lighter appearance,
edit button labeled "Bewerken" consistently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:15:16 +02:00
1629b514e2 fix: unify date formatting and add missing updated_at timestamp
Both organisation pages now use the same date format:
"14 april 2026 om 01:11 uur". Added missing "Gewijzigd op" timestamp
to the organizer organisation page header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:13:22 +02:00
1e5aa3c06b fix: align organisation page header layout with platform design
Match the header structure of /organisation to /platform/organisations/[id]:
wrap name+chip in a flex row with gap-x-2, place timestamp as span
below it inside the same container div.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:10:25 +02:00
c1bacb5ee9 refactor: show organisation slug after name in header
Display the organisation slug in small muted text directly after the
organisation name on both the organizer page (/organisation) and the
platform admin detail page (/platform/organisations/[id]).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:08:31 +02:00
a8a2bc92d1 feat: refactor organisation pages with tabs, members tab, and danger zone
Organizer org page (/organisation):
- Timestamps moved below title as muted caption
- VTabs with Algemeen (details) and Leden (members) tabs
- Members content embedded from separate page with full functionality:
  invite, edit role, change email, remove, pending invitations

Platform org detail (/platform/organisations/[id]):
- Timestamps moved below title alongside slug
- VTabs with Algemeen and Leden tabs
- Danger zone redesigned: type-to-confirm delete dialog, disabled
  Transfer Ownership button with "Nog niet beschikbaar" tooltip

Navigation:
- Removed standalone "Leden" menu item (now a tab on org page)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:59:45 +02:00
63bc351c59 refactor(app): unify settings tab design for tags, templates & crowd types
Move crowd types management to organisation settings as a new tab and
align all three settings tabs (Tags, Registration Field Templates, Crowd
Types) to the same layout pattern: header with title/subtitle, VDataTable
for active items, and a separate inactive section with VList. Also fix
the API to return inactive records for person tags and registration field
templates so the frontend can display them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:18:10 +02:00
169a078a92 feat: crowd types management UI with create/edit/deactivate
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:15:51 +02:00
c417a6647a feat(app): auth, orgs/events UI, router guards, and dev tooling
- Add Sanctum auth flow (store, composables, login, axios interceptors)
- Add dashboard, organisation list/detail, events CRUD dialogs
- Wire router guards, navigation, organisation switcher in layout
- Replace Vuexy @db types in NavSearchBar; add @iconify/types; themeConfig title typing
- Vuetify settings.scss + resolve configFile via fileURLToPath; drop dead path aliases
- Root index redirects to dashboard; fix events table route name
- API: DevSeeder + DatabaseSeeder updates; docs TEST_SCENARIO; corporate identity assets

Made-with: Cursor
2026-04-07 21:51:10 +02:00