Commit Graph

55 Commits

Author SHA1 Message Date
13ea2304b3 refactor: show event slug after name in header
Display the event slug in small muted text directly after the event
name in the EventTabsNav header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 01:06:05 +02:00
9e7f28420c feat: platform admin frontend — pages, composables, navigation, impersonation
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>
2026-04-14 23:49:36 +02:00
ec31646a93 fix: shift dialog info alert layout and full-width toggle
Made-with: Cursor
2026-04-14 22:58:33 +02:00
103d57c979 refactor: polish shift dialog with Vuexy-style alert and sticky footer
Made-with: Cursor
2026-04-14 22:54:47 +02:00
8afee801f8 feat: make shift dialog time-slot help collapsible
Made-with: Cursor
2026-04-14 22:47:20 +02:00
1c3ce547fa refactor: polish shift create dialog layout and hierarchy
Made-with: Cursor
2026-04-14 22:45:12 +02:00
1c6aed71fe fix: replace InfoTooltip v-tooltip with v-menu popover card
v-tooltip renders a forced-dark background unsuitable for multi-line
help content. Switch to v-menu + v-card which follows the app theme.
Use surface-variant background for the "Tip:" block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:41:01 +02:00
cc7cbbf29d fix: use inline style for time slot dimming in teleported dropdown
VAutocomplete renders its dropdown list in a teleported overlay outside
the component DOM tree. Scoped :deep() CSS cannot reach teleported
content. Switch from class-based opacity to inline style on VListItem.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:37:48 +02:00
948965e664 fix: time slot dropdown group headers and dimming via boundary detection
VAutocomplete ignores interleaved fake header items — they were filtered
out before reaching the template. Replace with Approach A: keep only
real selectable items sorted by group, detect group boundaries in the
#item template by comparing adjacent groupName values, and render
VListSubheader before each new group.

- Remove _isGroupHeader from TimeSlotDropdownItem interface
- Rename groupTimeSlots → sortedItems (returns only selectable items)
- Add hasGroups computed for conditional header rendering
- Add isNewGroup(index) boundary detection in CreateShiftDialog
- Add scoped .time-slot-dimmed CSS class (opacity: 0.65)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:31:32 +02:00
5bd028f408 refactor(app): event header status menu and volunteer share dialog
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
2026-04-14 22:19:09 +02:00
7bc0f1a0c7 feat: fix time slot hierarchy — seeder, API include_children, frontend dropdown, navigation
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>
2026-04-14 22:07:37 +02:00
c4712cea77 feat: edit event type label in dialog, drop non-functional status field
Made-with: Cursor
2026-04-14 21:48:07 +02:00
eec222d423 feat: toon leeftijd naast geboortedatum in persoon-detailpanel
Made-with: Cursor
2026-04-14 21:41:08 +02:00
cf02500453 fix: shift edit time slot dropdown loading state and test coverage
The time slot dropdown in the shift edit dialog could flash the
"create a time slot first" alert during loading, and show raw ULIDs
when time slot data hadn't loaded yet. Fixed by:
- Adding loading state indicator to the time slot dropdown
- Using the shift's existing time_slot object as a fallback item
  while the full list is fetching
- Showing the dropdown (with loading spinner) instead of the
  misleading "no time slots" alert during fetch

Added test coverage for time_slot_id validation on shift updates:
- Update with valid same-event time slot (200)
- Update with cross-org time slot (422)
- Update on sub-event with parent festival time slot (200)
- Store/update responses include nested time_slot object

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:46:36 +02:00
185637fa50 feat: add EmailBrandingTab component for organisation email branding
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:04:13 +02:00
a29fa32ac6 feat: add "Lid toevoegen als deelnemer" shortcut for org members
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>
2026-04-14 18:38:53 +02:00
ed1eddd486 fix: allow organiser to approve shift assignments when shift is full
The approve() and bulkApprove() methods in ShiftAssignmentService
hard-blocked with a 422 when all slots were filled. This was incorrect
for organiser actions — only volunteer claims (portal self-service)
should enforce capacity limits. Organiser assign() already allowed
overbooking, making the approve block inconsistent.

Changes:
- Remove capacity hard-block from approve() and bulkApprove(), replace
  with audit log entry (shift.overbooked_approval)
- Add overbook confirmation dialog in ShiftDetailPanel before approving
  a full shift (single + bulk approve)
- Add onError handlers to all mutations in ShiftDetailPanel (approve,
  reject, cancel, bulk-approve) so errors display in the snackbar
- Add global 422 validation error display in axios interceptor via
  useNotificationStore as safety net for all components
- Add PHPUnit test for approve-when-full scenario

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 17:42:04 +02:00
eb1a0ac666 feat: complete person identity matching system with fuzzy detection, revert, and manual link
Implements the full identity matching engine: email matching (HIGH confidence),
fuzzy name matching with Levenshtein distance (MEDIUM confidence, upgradable to
HIGH with DOB tiebreaker), manual link/unlink, revert confirmed matches, and
automatic detection via PersonObserver. Includes 33 comprehensive tests, frontend
integration with confirm/dismiss/unlink UI, and match indicators in the persons list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:44:24 +02:00
7932e53daf security: A01-13 — nest all event routes under organisation prefix
Move all authenticated organiser-facing event sub-resource routes from
/events/{event}/... to /organisations/{organisation}/events/{event}/...
to enforce multi-tenancy at the routing layer.

Changes:
- Routes: restructured api.php to nest all event sub-resources under
  the existing organisation prefix group
- Controllers: added Organisation parameter and VerifiesOrganisationEvent
  trait to all 12 affected controllers (sections, time-slots, shifts,
  persons, crowd-lists, locations, shift-assignments, registration-fields,
  availabilities, field-values, section-preferences, stats)
- Tests: updated all 20 feature test files with new route paths
- Frontend: updated 8 API composables and 20 Vue components/pages
- API.md: updated documentation to reflect new route structure

Portal routes, public routes (volunteer-register), and invitation routes
remain unchanged as they operate without organisation context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:16:36 +02:00
ef195a6777 feat(mail): center-align action button in email template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 00:49:41 +02:00
a9dcee0fc7 feat(app): registration fields management page in event settings
Adds a new settings sub-page for managing dynamic registration form fields
per event. Includes sortable field list, create/edit dialog, template picker,
and import-from-event functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:44:14 +02:00
c43a922641 fix(app): don't send tag_category when field type is not tag_picker
The backend validates tag_category as 'prohibited' for non-tag_picker
field types. Sending null triggered a 422 validation error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:19:15 +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
1c0ac488b0 feat(app): organisation settings page with tags & registration field templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:02:07 +02:00
1172c41d33 feat(app): event status transitions on detail header
Add transition buttons from allowed_transitions with Dutch labels,
confirmation dialog, TanStack mutation + cache invalidation, and
422/generic error handling via notification store.

Made-with: Cursor
2026-04-12 22:20:36 +02:00
b2737ba5c8 fix(app): toon API-fout bij opnieuw toewijzen shift in snackbar
Made-with: Cursor
2026-04-12 15:38:46 +02:00
c6912c0d54 fix: maak crowd list detail drawer scrollbaar
Made-with: Cursor
2026-04-12 13:59:00 +02:00
6dccf87234 feat: add date_of_birth field to persons across all layers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:06:29 +02:00
d2f282eb4c feat: split name into first_name + last_name across users, persons, and companies
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>
2026-04-10 23:04:55 +02:00
bd4297f891 fix: use white text in recommendation tooltip for dark background
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:15:47 +02:00
27d64e409f feat: default Aanbevolen filter on, larger search field, recommendation tooltip per person
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:14:31 +02:00
04ceecc51d feat: enrich assignable-persons with tags, preferences, availability and cascading filters
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:05:02 +02:00
3e292567c3 feat: smart re-assignment with cancellation source tracking
Add cancelled_by, cancellation_source (organiser|volunteer|system), and
cancelled_at columns to shift_assignments. Cancel flow now records who
cancelled and why. Assign flow reactivates existing cancelled/rejected
records instead of creating duplicates, preventing UNIQUE constraint
violations. Assignable-persons endpoint returns previous_assignment data
for contextual UI indicators. Frontend shows cancellation source labels,
previous assignment history in assign dialog, and "Opnieuw toewijzen"
buttons with volunteer-cancelled confirmation dialogs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:50:24 +02:00
78cc19373e feat: allow organizer overbooking with confirmation dialog
Remove capacity and status validation from organizer assign flow so
organizers can intentionally overbook shifts. Log overbooked assignments
for audit trail. Volunteer claims still enforce hard limits. Frontend
shows a warning banner when a shift is full and requires confirmation
before overbooking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:09:11 +02:00
d1ad0e1f89 fix: refresh assignable persons list after assignment and keep dialog open
Invalidate assignable-persons query cache in useAssignPersonToShift
onSuccess so the list reflects the new assignment immediately. Keep the
dialog open after assigning a person to allow sequential assignments,
showing a brief success snackbar instead of closing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:37:38 +02:00
968e17c6d6 feat: smart assign person dialog with conflict details and assignable-persons endpoint
Add GET /events/{event}/shifts/{shift}/assignable-persons endpoint that
returns approved persons with availability status, conflict details, and
already-assigned flags. Improve ShiftAssignmentService conflict errors to
include section name, time slot, and time range. Replace both assign
dialogs with a new AssignPersonDialog featuring search, crowd type
filtering, availability toggle, and inline conflict warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:32:31 +02:00
c21bc085e9 feat: registration section preferences with show_in_registration filtering and deduplication
Add show_in_registration and registration_description columns to festival_sections.
Registration form now shows deduplicated sections by name (across sub-events),
filtered by show_in_registration=true, grouped by category with card-based UI.
Section preferences use section_name instead of section_id.
Add GET/PUT registration-settings endpoints for festival-level bulk management.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:03:54 +02:00
874eeee770 feat: event dashboard metric cards with stats endpoint (UX-02)
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>
2026-04-10 16:19:31 +02:00
e70904741d feat(app): dedicated Tijdsloten tab with grouped view and fill rates
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>
2026-04-10 15:47:36 +02:00
ee1ee6f41d feat(app): enhanced crowd list detail panel with person management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:04:36 +02:00
e14cfe8ae2 fix: remove non-existent GET persons endpoint from crowd list detail panel
The backend only has POST (add) and DELETE (remove) for crowd list
persons — no GET to list them. Reworked the detail panel to show
person count from the crowd list data instead of fetching individual
persons.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:19:40 +02:00
331f662c67 feat: crowd lists frontend with list view, create/edit dialog and person management
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:14:17 +02:00
9b7aa92e84 fix: remove resetForm from onSuccess to prevent validation flash in dialogs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:03:02 +02:00
03ca1a50a7 feat: local sections in sub-events can use festival-level time slots
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:16:32 +02:00
37fecf7181 fix: auth race condition on refresh, section edit dialog, time slot duplicate, autocomplete disable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:16:22 +02:00
4388811be9 feat: companies CRUD with person dialog integration and navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:16:01 +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
10bd55b8ae feat: festival/series model with sub-events, cross-event sections, tab navigation, SectionsShiftsPanel extraction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:15:19 +02:00
11b9f1d399 feat(app): polish event type selector and branding mark
Refine the event type segmented control styling and avoid duplicate "Crewli" text in the sidebar header.

Made-with: Cursor
2026-04-08 10:13:54 +02:00
c776331cf8 feat: festival/event model frontend + topbar activeren
- Events lijst: card grid met festival/serie chips
- Festival detail: programmaonderdelen grid
- CreateSubEventDialog voor sub-events binnen festival
- EventTabsNav: breadcrumb terug naar festival
- Sessie A: festival-bewuste EventResource + children endpoint
- Topbar: zoekbalk, theme switcher, shortcuts, notificaties
- Schema v1.7 + BACKLOG.md toegevoegd
- 121 tests groen
2026-04-08 10:06:47 +02:00