Commit Graph

35 Commits

Author SHA1 Message Date
6a8d21a5b6 feat: registration field polish, multi-category tags, file uploads, Partner icon
- Restructure field editor dialog: move Options section to bottom with
  divider and subheader, fix delete button with flex layout
- Change tag_category (single string) to tag_categories (JSON array)
  supporting multiple category selection in tag picker fields
- Portal tag picker now groups tags by category with subheaders
- Add generic file upload endpoint (FileUploadService + UploadController)
- Replace email branding logo URL text field with ImageUploadField
- Update Partner crowd type default icon to tabler-affiliate
- Apply changes consistently to both field and template dialogs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:03:49 +02:00
d57dcdb616 feat: HEADING field type for registration forms — replace section property with structural field
Replace the per-field `section` text property with a dedicated HEADING field type that
organizers add as a separate block for visual grouping. Also fixes duplicate heading bug
on portal radio fields, replaces cramped VBtnToggle with VSelect for field width, and
adds grouped field type dropdown with structure/input categories.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:40:41 +02:00
9718e27029 feat: registration form field display_width and option descriptions
Add configurable column widths (full/half) and optional descriptions
for radio/select/checkbox options on registration form fields.

- Migration adds display_width column to both tables
- FieldDisplayWidth enum with smart defaults per field type
- normalized_options accessor for backwards-compatible option format
- Portal form renderer uses display_width for VRow/VCol grid layout
- Radio/select/checkbox options render with descriptions
- Admin field editor supports display_width toggle and description input
- System templates updated with appropriate widths and descriptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 07:46:36 +02:00
c4a23b6763 feat: passwordless registration — defer account creation to approval
Removes password from the volunteer registration form. Account
creation is now deferred to the approval step:

Backend:
- Registration creates Person without User (user_id=null)
- On approval, system finds or creates User by person.email
- New accounts get a "set password" email with activation link
- Existing accounts get a portal link email
- Added registration_source column to persons (self/organizer)
- Fuzzy name matching skipped for self-registered persons
- person.email is always source of truth for account linking

Frontend:
- Registration form no longer collects password
- Email check shows info alert with login suggestion
- New wachtwoord-instellen.vue page for account activation
- PasswordRequirements.vue component (reused on reset page)
- Success page updated with activation messaging

Tests: 837 passed (all updated for new flow)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 03:27:47 +02:00
79b7fe0b42 feat: account settings with Vuexy tab pattern and MFA banner fix
Restructures account/profile pages to match Vuexy's account-settings
tab pattern (Account, Security, Notifications) and fixes the MFA
enforcement banner that stayed visible after successful setup.

Backend:
- Add phone column to users table with migration
- Add PUT /me/profile endpoint for profile updates
- Create UpdateProfileRequest form request
- Update MeResource to include phone field

Organizer app:
- Rewrite account-settings as tabbed page (VTabs pill style + VWindow)
- Create AccountTab: avatar, profile form, email change, danger zone
- Create SecurityTab: password change, MFA method cards, backup codes,
  trusted devices, disable MFA danger zone
- Create NotificationsTab: placeholder with disabled toggles
- Fix MFA banner: set authStore.mfaSetupRequired = false on setup complete
- Update router guard to redirect to ?tab=security for MFA enforcement
- Update UserProfile menu links to use tab query params

Portal:
- Restructure profiel.vue with VTabs (Mijn profiel + Beveiliging)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:18:16 +02:00
948687f27e feat: enterprise MFA with TOTP, email codes, backup codes, and trusted devices
Three verification methods (TOTP authenticator, email code, backup codes),
trusted device management with 30-day expiry, role-based enforcement for
super_admin and org_admin, admin reset capability, and full test coverage
(46 tests). Modifies login flow to support MFA challenge/response with
temporary session tokens stored in cache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:45:55 +02:00
65978104d8 feat: complete email infrastructure with queue, templates, logging, and API
Adds the full transactional email system:
- Redis queue (QUEUE_CONNECTION=redis), SES config in .env.example
- 3 migrations: organisation_email_settings, organisation_email_templates, email_logs
- EmailTemplateType and EmailLogStatus enums with Dutch defaults
- EmailService as central entry point for all email sending
- SendTransactionalEmail queued job with retries and idempotency
- TransactionalMail mailable with responsive HTML + plain text templates
- Organisation-level branding (colors, logo, footer, reply-to)
- Per-type template overrides with {variable} substitution
- Email log with filtering by status, type, date range, recipient
- Preview and send-test endpoints for template management
- API endpoints: email-settings, email-templates (CRUD), email-logs (read-only)
- Integrated into existing flows: invitations, password reset, email
  verification, registration approval/rejection
- 37 new tests across 4 test files, all existing tests updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:12:21 +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
ae7ba63822 feat: add PersonStatus enum and PortalMeRequest form request
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:04:10 +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
52f6380ac0 security: round 3 — token security (crypto random, hashed storage, portal middleware)
Token generation:
- Replace Str::ulid() with bin2hex(random_bytes(32)) for 256-bit entropy
- Store SHA-256 hash in database, never plaintext tokens
- Hash input before lookup on all token endpoints

Invitation tokens:
- InvitationService: generate crypto random, store hash, pass plain
  token transiently for email URL via UserInvitation::$plainToken
- InvitationController show/accept: hash input before DB lookup
- AcceptInvitationRequest: hash token before invitation lookup
- Migration: widen user_invitations.token and artists.portal_token
  from char(26) to char(64) for SHA-256 hex digests

Portal token auth:
- PortalTokenController: remove Schema::hasTable() runtime checks,
  hash token before lookup, return shaped response via PortalEventResource
  instead of raw model data
- Create PortalEventResource (name, dates, status only — no internals)
- Handle missing production_requests table gracefully via try/catch

Portal token middleware:
- Implement full token validation: extract from Bearer header or ?token=
  query param, hash, look up in artists/production_requests, verify
  event exists and is not draft/closed, set portal context on request
- Return generic 401 on any failure (no information leakage)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 06:52:54 +02:00
090d2b7d89 security: round 2 — multi-tenancy isolation (OrganisationScope, scoped validation, boundary checks)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 06:38:19 +02:00
1028498705 security: round 1 — quick wins (rate limiting, headers, mass assignment, logging)
- Add throttle middleware to login (5/min), portal/token-auth (10/min),
  volunteer-register (5/min), and invitation routes (10/min)
- Set Sanctum token expiration to 7 days
- Remove billing_status from UpdateOrganisationRequest (super_admin only)
- Revoke all Sanctum tokens on password reset
- Strengthen password rules: min 8 chars, mixed case, numbers
- Create SecurityHeaders middleware (X-Content-Type-Options, X-Frame-Options,
  HSTS, Referrer-Policy, Permissions-Policy)
- Fix open redirect on all 3 login pages (validate ?to= starts with /)
- Set APP_DEBUG=false in .env.example
- Log failed login attempts with email, IP, user-agent
- Log authorization failures (403) with user, IP, path, method
- Harden mass assignment: remove user_id from Person, audit fields from
  ShiftAssignment, system fields from UserInvitation $fillable
- Replace real DB records with factory make() in mail preview routes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 01:34:51 +02:00
ec4ba8733d feat(api): organisation email branding and shared mail layout
- Add email branding columns to organisations table (logo, color, reply-to, sender name, footer)
- Create MailBrandingService for resolving per-org branding with defaults
- Create CrewliMailable abstract base class with branded from/reply-to
- Create shared Blade layout (mail.layouts.crewli) with inline CSS
- Refactor Registration*Mail and InvitationMail to extend CrewliMailable
- Add config/crewli.php for platform-wide defaults (portal_url, app_url, logo)
- Add dev-only /mail-preview/{type} route for browser email previewing
- Update Organisation model, resource, and form requests with branding fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:44:34 +02:00
8435e74fd3 feat(api): registration auth, account creation, check-email & email notifications
- Add POST /public/check-email endpoint with rate limiting (10/min)
- Create user accounts during volunteer registration (new or returning)
- Returning volunteers authenticate with existing password
- Add password validation to VolunteerRegistrationRequest
- Normalize emails to lowercase throughout registration flow
- Handle race condition on duplicate accounts gracefully
- Create RegistrationConfirmationMail, RegistrationApprovedMail, RegistrationRejectedMail
- Wire approval/rejection emails into PersonController
- Add POST persons/{person}/reject endpoint
- Trigger TagSyncService on registration and approval
- Add CheckEmailTest, PersonApprovalEmailTest, extend VolunteerRegistrationTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:37:04 +02:00
73c8e6c466 feat(api): extend registration endpoints with dynamic fields and section preferences
- PublicRegistrationData now returns registration_fields (portal-visible only),
  form toggles, and available_tags for tag_picker fields
- Volunteer registration accepts field_values and section_preferences with
  festival_section_id, processed via existing services
- PortalMe eager-loads fieldValues and sectionPreferences
- Section preferences now use the proper relational table instead of custom_fields JSON

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:44:26 +02:00
f6e3568011 feat: registration form fields, section preferences, tag sync & schema updates
Implement EAV system for dynamic event-specific registration fields
with organisation-level templates, person section preferences with
priority ranking, and TagSyncService for deferred tag_picker sync.

New tables: registration_field_templates, registration_form_fields,
person_field_values, person_section_preferences.
New columns: persons.remarks, events.registration_show_section_preferences,
events.registration_show_availability.

58 tests, 126 assertions — all 432 tests pass (zero regressions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:10:16 +02:00
d06cb01726 refactor: remove access_requirements (toegangsbehoeften) from application
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:53:28 +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
0d741550a8 feat: event registration branding with vertical wizard layout
- Add registration_banner_url, registration_welcome_text, registration_logo_url
  columns to events table with migration
- Add uploadImage endpoint (POST .../upload-image) with form request validation
  for banner and logo images (jpg/png/webp, max 5MB)
- Include branding fields in EventResource and PublicRegistrationDataController
- Build registration settings UI in organizer event settings page with
  banner/logo upload and welcome text editor
- Redesign portal registration page: hero banner with gradient overlay,
  welcome text card, vertical step navigation (desktop) / horizontal chips
  (mobile), two-column form fields with density="comfortable"
- Update success page with event banner and consistent branding
- Seed welcome text for Echt Feesten 2026
- Add 9 PHPUnit tests covering image upload, branding fields in API responses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 21:09:49 +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
0cdc192239 feat: shift assignment workflow with claim, approve, reject, cancel, and bulk approve
Implements the complete ShiftAssignment lifecycle:
- ShiftAssignmentStatus enum with allowed transitions
- ShiftAssignmentService with claim/assign/approve/reject/cancel/bulkApprove
- ShiftAssignmentController with event-scoped endpoints
- ShiftAssignmentPolicy (organizer + volunteer self-cancel)
- VolunteerAvailability model, controller, and sync endpoint
- Refactored ShiftController to delegate to service layer
- 31 workflow tests covering all paths and multi-tenancy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 17:00:56 +02:00
cae2242502 feat: crowd lists audit, enum, factory, service and tests
Audit and complete the Crowd Lists module:
- Add CrowdListType enum (internal/external) with proper casts
- Create CrowdListService for business logic (add/remove person,
  max_persons enforcement, auto_approve, activity logging)
- Create CrowdListFactory with Dutch names and states
- Create AddPersonToCrowdListRequest form request
- Fix FormRequests to use Rule::enum instead of hardcoded strings
- Fix CrowdListResource to use enum->value and add is_full field
- Refactor controller to be thin (delegates to service)
- Add eager loading for crowdType and recipientCompany
- Write 18 comprehensive tests (CRUD, auth, edge cases)
- Update API.md with request/response documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:53:57 +02:00
4b182b449a feat: person identity matching with detection, confirmation and audit trail
Implements enterprise-grade identity resolution (detect → suggest → confirm)
for Person ↔ User linking. Matches are detected automatically on person
creation and user account creation, then surfaced to organisers for explicit
confirmation or dismissal. No silent auto-linking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:50:25 +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
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
d37a45b028 feat: person tags system - org-level skills with self-reported and organiser-assigned sources
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 11:15:43 +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
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
9acb27af3a feat: fase 2 backend — crowd types, persons, sections, shifts, invite flow
- Crowd Types + Persons CRUD (73 tests)
- Festival Sections + Time Slots + Shifts CRUD met assign/claim flow (84 tests)
- Invite Flow + Member Management met InvitationService (109 tests)
- Schema v1.6 migraties volledig uitgevoerd
- DevSeeder bijgewerkt met crowd types voor testorganisatie
2026-04-08 01:34:46 +02:00
0d24506c89 feat: consolidate frontend API layer, add query-client, and harden backend Fase 1
Frontend:
- Consolidate duplicate API layers into single src/lib/axios.ts per app
- Remove src/lib/api-client.ts and src/utils/api.ts (admin)
- Add src/lib/query-client.ts with TanStack Query config per app
- Update all imports and auto-import config

Backend:
- Fix organisations.billing_status default to 'trial'
- Fix user_invitations.invited_by_user_id to nullOnDelete
- Add MeResource with separated app_roles and pivot-based org roles
- Add cross-org check to EventPolicy view() and update()
- Restrict EventPolicy create/update to org_admin/event_manager (not org_member)
- Attach creator as org_admin on organisation store
- Add query scopes to Event and UserInvitation models
- Improve factories with Dutch test data
- Expand test suite from 29 to 41 tests (90 assertions)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:35:34 +02:00
1cb7674d52 refactor: align codebase with EventCrew domain and trim legacy band stack
- Update API: events, users, policies, routes, resources, migrations
- Remove deprecated models/resources (customers, setlists, invitations, etc.)
- Refresh admin app and docs; remove apps/band

Made-with: Cursor
2026-03-29 23:19:06 +02:00
34e12e00b3 feat: initial commit - Band Management application 2026-01-06 03:11:46 +01:00