Drift from Session 4 step 11 — unplugin-vue-components and unplugin-vue-router
regenerated their .d.ts files for the new timetable surface. Was missed in the
original commit because the test runner doesn't trigger regen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routing wiring (Phase D of WS-3 PR-B1):
- apps/app/src/plugins/1.router/guards.ts: add a single early-return
carve-out before the org-selection redirect — `if (to.meta.context
=== 'portal') return`. Per ARCH-CONSOLIDATION-2026-04 §4.3,
meta.context is the canonical contract; PR-B2 evolves the guards
from this key to full context-aware logic (post-login landing,
context-switcher, role checks).
- apps/app/env.d.ts: extend RouteMeta with the new layout names
('OrganizerLayout' | 'PortalLayout' | 'PublicLayout'), context,
requiresAuth, requiresToken, navMode, navTitle.
- apps/app/typed-router.d.ts: regenerated by unplugin-vue-router to
pick up portal/* and register/* route names.
- Page meta finalisation: portal pages have layout: 'PortalLayout',
context: 'portal', preserving original requiresAuth + nav fields;
register pages have layout: 'PublicLayout' + public: true (the
apps/app guard convention for public routes, since meta.public is
what the existing guard recognises).
Form-types restructure (boundaries cleanup):
- apps/app/src/composables/forms/types/formBuilder.ts → src/types/forms/
- apps/app/src/composables/forms/utils/{formValidation,validators}.ts
→ src/utils/forms/
- All `@/composables/forms/{types,utils}/*` imports rewritten across
pages, components, composables, tests.
- This avoids a `types → composables` boundaries violation at
src/types/formSchema.ts which re-exports primitives from the
inlined form-schema. types/formSchema.ts now imports from
@/types/forms/formBuilder which is in the same boundaries zone.
Lint cleanup for moved portal sources (apps/portal had no
.eslintrc.cjs; the migrated code now has to pass apps/app's stricter
config):
- axios.isAxiosError → named import { isAxiosError }
(ClaimenTab, RoosterTab, profiel.vue)
- void schemaQuery.refetch() → schemaQuery.refetch()
(register/[public_token].vue)
- if-then-else collapsed to single boolean return (formatFieldValue)
- :delay-on-touch-only="true" → delay-on-touch-only shorthand
(FieldSectionPriority)
- ml-2 class → ms-2 (FieldAvailabilityPicker)
- multi-statement-per-line splits in profiel.vue + spec files
- unused emailConfigured ref removed (profiel.vue)
- one-component-per-file disabled with TODO TECH-WS3-PORTAL-LINT-CLEANUP
ref (FieldOptionsLocale.spec.ts — multi-Wrapper test pattern)
- restored `import Draggable from 'vuedraggable'` after lint:fix
removed it (template-only usage; the import IS needed)
- camelcase param renamed in FieldOptionsLocale harness factory
- typecheck nudge: spec state.data typed via PublicFormSectionOption[] /
PublicFormTimeSlot[] aliases instead of Record<string, unknown>
- PortalLayout.vue: explicit `import { useRoute, useRouter }` so the
vitest mock can intercept (the trimmed AutoImport set doesn't pull
vue-router's auto-imports)
Vitest: 23 / 162 passing. Lint: 0 errors / 0 new warnings (only the
pre-existing boundaries v5→v6 deprecation warnings remain). Typecheck:
clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Drop the Algemeen tab together with the Organisatie subheader — organisatie-
gegevens verhuizen naar /organisation. Voeg een GEVAARLIJK-subheader toe met
een Gevaarlijke acties tab, die de bestaande platform-beheerder-notitie bevat
(self-delete blijft buiten scope). Legacy ?tab=algemeen/general redirects
door naar /organisation; default tab valt terug op Crowd Types.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- useMembers.ts gains a scope param ('organisation' | 'platform') on list,
invite, update-role, and remove; endpoints branch accordingly.
- Platform Admin's [id].vue now consumes useMembers via scope='platform';
deleted the duplicated useInviteOrganisationMember / useRemoveOrganisationMember
/ useUpdateOrganisationMemberRole helpers from useAdmin.ts.
- Deduplicated InviteMemberPayload / UpdateMemberRolePayload / AdminOrganisationMember
from types/admin.ts; Member is now the canonical type.
- SettingsMembers.vue and EditMemberRoleDialog.vue removed (no remaining imports).
- InviteMemberDialog accepts an optional scope prop and is restricted to the
two organisation-level roles matching the /members UX.
Replace horizontal tabs with VList-based vertical sidebar following the
Vuexy ecommerce settings pattern. Consolidate Tags, Crowd Types, Members,
and Registration Fields pages into the settings page as sidebar tabs.
Add SettingsGeneral panel with org details form and danger zone.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds three new tabs to the organisation settings page:
- E-mail opmaak: replaces old EmailBrandingTab to use the new
organisation_email_settings API (logo, colors, footer, reply-to)
- E-mail templates: list/edit/preview/test/reset all 6 template types
with variable hints, defaults comparison, and iframe preview
- E-mail log: server-side paginated table with filters (search, status,
type, date range), status chips, and expandable row details
Supporting files:
- types/email.ts: TypeScript interfaces for settings, templates, logs
- composables/api/useEmail.ts: TanStack Query hooks for all email endpoints
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>
Replace separate status chips/buttons with one status dropdown next to
edit, move dates under the title, add share dialog for registration URL,
and remove RegistrationLinkCard.
Made-with: Cursor
Restructure the festival hierarchy end-to-end:
Seeder: Remove duplicate festival-level VOLUNTEER time slots, keep only CREW
operational slots. Rename sub-events to "Dag 1/2/3 — ..." pattern. Change
Nachtsecurity to Security (cross_event). EHBO/Security shifts now use sub-event
time slots via cross_event exception. Add flat event "Braderie Dorpstown 2026".
API: Add ?include_children=true to TimeSlotController for festivals, returning
all sub-event time slots with source and event_name fields. Update
StoreShiftRequest and UpdateShiftRequest to accept child time slots for
cross_event sections.
Frontend: Create useTimeSlotDropdown composable with 4-scenario dropdown logic.
Replace AppSelect with VAutocomplete in CreateShiftDialog with grouped items,
dimmed festival slots, and info tooltips. Add InfoTooltip reusable component.
Show festival context labels on cross_event sections in sub-event section lists.
Add read-only festival time slots on sub-event time-slots page. Add cross_event
context banner with "Bekijk alle diensten" link.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds two new API endpoints to quickly add organisation members as event
persons with user_id pre-linked and status approved:
- GET /organisations/{org}/members/available-for-event/{event}
- POST /organisations/{org}/events/{event}/persons/from-member
Includes frontend dialog with member search, crowd type selection, and
click-to-add behavior in the Personen tab.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add GET /events/{event}/stats endpoint returning aggregate counts for
persons (by status, approved without shift), pending identity matches,
and shift fill rates. Frontend metric cards component shows four
actionable KPIs on the event overview tab.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract time slots from Secties & Shifts into a dedicated Tijdsloten tab.
New tab groups time slots by date with Dutch date headers, person type
filter pills, fill rate progress bars, and sections count. Includes
duplicate, edit, and delete actions with confirmation dialog.
- Create types/timeSlot.ts with enriched TimeSlot interface
- Add Tijdsloten tab to EventTabsNav between Publiekslijsten and Secties
- Create time-slots page with loading, error, and empty states
- Remove time slots panel from SectionsShiftsPanel
- Update CreateShiftDialog to navigate to time slots tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>