Files
crewli/resources/design/design-document.md

77 KiB
Raw Permalink Blame History

Crewli

Product Design & Technical Specification

Full Stack SaaS — Event & Festival Management Platform

Version: 1.3 | Datum: Maart 2026 | Status: Concept | Tech Stack: Laravel 12 + Vue 3

1. Product Vision & Scope

Wat is Crewli en voor wie is het gebouwd?

1.1 Samenvatting

Crewli is een multi-tenant SaaS platform voor de professionele organisatie van evenementen en festivals. Het platform ondersteunt de volledige operationele cyclus: van artiestenbooking en advancing, via personeelsplanning en vrijwilligersbeheer, tot accreditatie, briefings, en real-time show-day operaties (Mission Control).

Het platform wordt gebouwd als een API-first Laravel 12 backend met een Vue 3 + Vuexy frontend, en is ontworpen om meerdere organisaties (klanten) te bedienen vanuit één installatie, met volledige data-isolatie per organisatie.

KERNPRINCIPE Crewli combineert de sterkste onderdelen van drie marktleiders: de accreditatie-engine en Mission Control van In2Event, het sectie-gebaseerde advance-portal en vrijwilligersbeheer van Crescat, en de mission/task-planning, SMS-communicatie en formulierbouwer van WeezCrew — aangevuld met differentiërende functies die geen van deze platformen biedt.

1.2 Doelgroepen

Rol Beschrijving Primaire module(s)
Super Admin Platform-eigenaar (jij/Anthropic). Beheert organisaties, billing, globale instellingen. Admin panel
Organisation Admin Klant-hoofdbeheerder. Beheert evenementen, teamleden, globale instellingen per organisatie. Org. management, events
Event Manager Operationele verantwoordelijke per evenement. Beheert alle modules binnen dat event. Alle event-modules
Staff Coordinator Beheert bemanning, diensten, accreditatie voor specifieke secties. Staff, Shifts, Accreditation
Artist Manager Beheert artiesten, advancing, timetable. Artists, Advancing, Timetable
Volunteer Coordinator Beheert vrijwilligersregistraties, toewijzingen, communicatie. Volunteers, Time Slots, Shifts
Crew Member (staff) Externe medewerker. Ontvangt briefing, ziet eigen rooster, checkt in. Briefing portal (read-only)
Volunteer Zelfregistrerende vrijwilliger. Registreert, selecteert beschikbaarheid, claimt shifts. Volunteer self-service portal
Artist / Tour Manager Externe artiest-vertegenwoordiger. Vult eigen advance-portal in. Artist advance portal
Supplier / Partner Externe leverancier. Vult productieverzoek in, beheert eigen lijsten. Supplier portal

1.3 Kernbegrippen & Terminologie

De volgende termen worden door het hele document en de codebase consistent gehanteerd:

Term Definitie
Organisation Een klant/organisatie die Crewli gebruikt. Heeft eigen evenementen, teamleden en instellingen. Equivalent aan 'Launchpad' in In2Event.
Event Een specifiek evenement of festival binnen een organisatie. Alle operationele data is event-scoped.
Festival Section Een operationele eenheid binnen een event: Bar, Hospitality, Technical, Security, etc. Heeft eigen Crew, Volunteers en Shifts.
Crowd Type Classificatie van deelnemers: Crew, Volunteer, Artist, Guest, Press, Partner, Supplier. Configureerbaar per organisatie.
Person Individuele deelnemer gekoppeld aan een event via een Crowd Type.
Time Slot Event-niveau tijdvenster met persoonscategorie (CREW / VOLUNTEER / PRESS). Aangemaakt door de organisator als temporeel raamwerk.
Shift Sectie-specifieke toewijzing binnen een Time Slot. Koppelt een Festival Section aan een tijdvenster met capaciteit.
Slot (capacity) Het aantal personen dat een Shift kan bevatten. Opgesplitst in Admin-assigned en Open for Claiming.
Open for Claiming Aantal shift-slots dat zichtbaar en claimbaar is in het vrijwilligersportal. (Crescat: 'For Sale')
Accreditation Een recht of artikel toegekend aan een persoon: toegangszones, items te ontvangen (polsbandje, maaltijd, portofoon).
Advance / Advancing Het proces waarbij artiestenvereisten worden verzameld vóór het evenement via een sectie-gebaseerd portal.
Mission Control Real-time operationele hub op show-dag: inchecken, accreditatie uitreiken, artiestenstatus.
Briefing Gepersonaliseerde communicatie (e-mail + PDF) naar deelnemers met e-tickets, informatie en taakbeschrijving.
Infosheet / Advance Section Configureerbaar formulier-sectie dat naar artiesten of leveranciers gestuurd wordt voor het ophalen van informatie.

2. Tech Stack & Infrastructuur

Bestaande en te installeren technologieen

2.1 Backend

Component Keuze / Versie
Language PHP 8.2+
Framework Laravel 12 (laravel/framework ^12.0)
Authentication Laravel Sanctum ^4.0 (SPA token auth)
Database MySQL 8.x (primair), Redis (cache, queues, sessions)
Queue Driver Redis via Laravel Horizon (monitoring + priority queues)
Real-time Laravel Echo + Pusher / Soketi (zelf-gehoste WebSocket server)
File Storage Laravel Filesystem: S3 (productie) / local (dev). Signed URLs voor beveiligde toegang.
Email Laravel Mailable + SendGrid of Postmark. Queue-based batch sending.
PDF / Ticket Laravel Browsershot (Puppeteer) of DomPDF voor server-side PDF generatie.
Barcode picqer/php-barcode-generator. Alphanumeriek + numeriek. QR-code via endroid/qr-code.
Testing PHPUnit ^11.5 + Pest. Feature tests per API route.
Tooling Laravel Pint (CS), Laravel Sail (Docker dev), Laravel Pail (log viewer), Tinker.
Roles & Perms Spatie laravel-permission (te installeren). Drie niveaus: App, Organisation, Event.
Multi-tenancy Custom: organisation_id scoping op alle queries. Geen package-tenancy.
SMS + WhatsApp Zender (zelf-gehoste instantie) via HTTP API. Zender gebruikt Android-apparaten als SMS/WhatsApp gateway. Configureerbare API endpoint + API key in .env. Fallback: Twilio voor pure SMS zonder WhatsApp.

2.2 Frontend

Component Keuze / Versie
Language TypeScript 5.9.3
Framework Vue 3.5.22 (Composition API + <script setup>)
Build Vite 7.1.12
UI Template Vuexy 9.5.0 (Vue.js Admin Template) + Vuetify 3.10.8
State Pinia 3.0.3
Router Vue Router 4.5.1
HTTP / API State Axios ^1.13.2 + TanStack Query / Vue Query (te installeren: @tanstack/vue-query)
i18n Vue I18n 11.1.12
Calendar / Timetable FullCalendar (al in stack) — timeline view voor stage timetable
Charts ApexCharts (al in stack)
Rich Text TipTap (al in stack) — voor briefing template builder
Drag & Drop VueDraggable (Sortable.js wrapper) voor form builder en timetable
Maps Leaflet.js of Google Maps embed voor locatie in shift briefing PDF
Forms VeeValidate + Zod (te installeren) voor type-safe form validatie

2.3 Applicatie-frontends

Er zijn meerdere Vue-applicaties in de monorepo. Elke app heeft een eigen scope:

App Doel en doelgroep
admin/ Super Admin dashboard: organisatiebeheer, billing, platform-instellingen. Toegang: Super Admin only.
app/ Hoofdapplicatie voor Organisation Admins, Event Managers en alle interne rollen. Volledig featured.
portal/ Externe portals op basis van subdomain routing: volunteer.crewli.app/[slug], advance.crewli.app/[slug], briefing.crewli.app/[slug]. Server-rendered + PWA.

2.4 Te installeren packages

Package Doel
spatie/laravel-permission Rol- en permissiebeheer (App + Org + Event niveau)
@tanstack/vue-query API state management in Vue frontends (caching, loading states, optimistic updates)
vee-validate + zod Type-safe formuliervalidatie in Vue
vuedraggable@next Drag-and-drop voor form builder, timetable, prioriteitsranking
Zender HTTP API (custom Laravel ZenderService) SMS + WhatsApp verzenden via zelf-gehoste Zender. Config: ZENDER_API_URL + ZENDER_API_KEY.
endroid/qr-code QR-code generatie voor e-tickets en allocatiesheets
spatie/laravel-media-library Bestandsbeheer (uploads, conversions, signed URLs)
barryvdh/laravel-dompdf of spatie/browsershot Server-side PDF generatie (briefings, allocatiesheets)

3. Systeem Architectuur

Multi-tenancy, rollen, en data-isolatie

3.1 Multi-Tenant Data Model

Crewli gebruikt een gedeeld database-schema (shared schema) met organisatie-scoping op alle tabellen. Er is geen row-level security op databaseniveau; scoping wordt afgedwongen via Laravel policies en Global Scopes.

SCOPING REGEL Elke query op event-data MOET een organisation_id scope hebben. Global Scopes in Eloquent models zorgen dat dit automatisch wordt toegepast. Nooit direct queryen zonder organisatiecontext.

3.1.1 Tenancy Hierarchy

De hiërarchie van boven naar beneden:

Platform (Super Admin) └─ Organisation (klant A) └─ Event (evenement 1) └─ Festival Section (Bar, Hospitality, Technical, ...) ├─ Time Slots (DAY1-EARLY-CREW, DAY1-EARLY-VOLUNTEER, ...) └─ Shifts (Bar × DAY1-EARLY-VOLUNTEER, 5 slots)

3.2 Drie-niveau Rol- en Permissiemodel

Gebruikersaccounts zijn platform-breed en uniek per e-mailadres. Eén account geeft toegang tot alle organisaties en evenementen waarvoor een gebruiker is uitgenodigd — elk met een eigen rol. Er is geen aparte registratie per organisatie. Via Spatie laravel-permission worden rollen op drie niveaus beheerd:

Niveau Scope Voorbeeld Rollen
App Level Geldig voor het hele platform, ongeacht organisatie. super_admin, support_agent
Organisation Level Geldig binnen één specifieke organisatie. org_admin, org_member, org_readonly
Event Level Geldig binnen één specifiek evenement. event_manager, artist_manager, staff_coordinator, volunteer_coordinator, accreditation_officer
IMPLEMENTATIE Gebruik Spatie's team-based permissions: elke Organisation is een 'team'. Event-level rollen worden opgeslagen in een pivot tabel: user_event_roles (user_id, event_id, role_id). Middleware: OrganisationRoleMiddleware en EventRoleMiddleware controleren per route.

3.3 Gebruikersaccount Model & Uitnodigingsflow

Crewli hanteert een strict één-account-per-e-mailadres model. Er bestaat geen account per organisatie of per evenement — een account is platform-breed en wordt gekoppeld aan organisaties en evenementen via uitnodigingen.

3.3.1 Account aanmaakstrategieën

Gebruikerstype Account aanmaak methode
Interne medewerkers (staff, coordinatoren) Organisator nodigt uit via e-mail. Ontvanger accepteert uitnodiging via link. Nieuw account aangemaakt óf bestaand account gekoppeld als e-mail al bestaat.
Vrijwilligers Twee paden: (1) Organisator nodigt uit via e-mail (zelfde flow als intern). (2) Vrijwilliger vult publiek registratieformulier in → na goedkeuring door organisator wordt automatisch een account aangemaakt (of gekoppeld als e-mail al bestaat) en login-credentials worden verstuurd.
Artiesten / Tour managers Ontvangen een tokengebaseerde link naar het advance portaal. Geen platform-account vereist — authenticatie via uniek token in URL.
Leveranciers / Partners Ontvangen een tokengebaseerde link naar het leveranciersportaal. Geen platform-account vereist.

3.3.2 Uitnodigingsflow (intern)

  • Organisator voert e-mailadres in en selecteert rol (organisatie- of event-niveau).

  • Systeem controleert: bestaat er al een account met dit e-mailadres?

  • Nee → uitnodigingsmail verstuurd met activatielink (24 uur geldig). Account aangemaakt na activatie.

  • Ja → uitnodigingsmail verstuurd. Na acceptatie wordt de bestaande gebruiker gekoppeld aan de nieuwe organisatie/event-rol. Geen nieuw account.

  • Gebruiker kan in het platform schakelen tussen alle organisaties waar hij/zij toegang toe heeft via een organisatieswitcher in de navigatie.

3.3.3 Vrijwilligersregistratie + goedkeuringsflow

  • Vrijwilliger vult publiek registratieformulier in (multi-step, zie sectie 4.4).

  • Na submit: Person record aangemaakt met status = 'pending'. Organisator ontvangt notificatie.

  • Organisator beoordeelt en keurt goed (of wijst af). Status → 'approved'.

  • Bij goedkeuring: systeem controleert of e-mailadres al een platform-account heeft.

    • Ja → bestaand account gekoppeld aan event als vrijwilliger. Bevestigingsmail met inloglink.

    • Nee → nieuw account aangemaakt. Welkomstmail met tijdelijk wachtwoord of magic link.

  • Vrijwilliger logt in op het volunteer portal en ziet eigen shifts, formulieren en event-informatie.

3.3.4 Claim Shift goedkeuringsflow

Wanneer een vrijwilliger een shift claimt via het portal, wordt een goedkeuringsworkflow geactiveerd:

  • Vrijwilliger claimt shift → shift_assignment aangemaakt met status = 'pending_approval'.

  • Relevante event-beheerder/coordinator ontvangt notificatie (in-app + e-mail).

  • Coordinator keurt goed → status = 'approved'. Vrijwilliger ontvangt bevestigingsmail.

  • Coordinator wijst af → status = 'rejected'. Vrijwilliger ontvangt afwijzingsmail met optionele reden. Slot komt weer beschikbaar.

  • Optioneel per shift: auto-approve instellen. Bij auto-approve: status direct 'approved' na claim, zonder handmatige stap.

SHIFT STATUS ENUM pending_approval → approved → completed | rejected (eindstatus) | cancelled (door vrijwilliger of organisator)

3.4 Event Lifecycle

Elk evenement doorloopt een vaste lifecycle. De dashboard UI en beschikbare acties passen zich automatisch aan de huidige fase aan:

Fase Beschrijving & actieve modules
Draft Evenement aangemaakt maar niet gepubliceerd. Alleen beheerder ziet het. Instellingen worden geconfigureerd.
Published Evenement actief in planning. Alle interne modules beschikbaar. Externe portals nog gesloten.
Registration Open Vrijwilligersregistratie en artist advance portals zijn open. Externe deelnemers kunnen zich aanmelden.
Build-Up Opbouwdagen. Crew-shifts beginnen. Accreditatie uitgifte start.
Show Day(s) Actieve eventdagen. Mission Control actief. Real-time check-in. Timetable live.
Tear-Down Afbouwdagen. Inventaris terugname. Afsluiting van shifts.
Closed Evenement afgerond. Read-only. Rapporten beschikbaar. Data wordt gearchiveerd.

3.5 Core Database Schema

Onderstaand het volledige, productie-waardige datamodel. Alle 12 bevindingen uit de database review (v1.3) zijn verwerkt. Tabellen zijn gegroepeerd per domein.

PRIMAIRE SLEUTELS: ULID Alle tabellen gebruiken ULID (Universally Unique Lexicographically Sortable Identifier) als primaire sleutel — GEEN UUID v4. Reden: UUID v4 is random, wat B-tree index-fragmentatie veroorzaakt in InnoDB bij elke INSERT. ULID is monotoon stijgend (tijd-geordend) en behoudt index-lokaliteit. Laravel: gebruik Str::ulid() of de HasUlids trait. Migraties: $table->ulid('id')->primary(). Extern zichtbare ID's (URL's, barcodes, API) gebruiken ULID. Interne pivot tabellen mogen auto-increment integer PK gebruiken voor join-performance.

3.5.1 Kern (foundation)

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
users id (ULID), name, email, password, timezone, locale, avatar, email_verified_at, deleted_at HasUlids trait. belongsToMany organisations (via organisation_user), belongsToMany events (via event_user_roles). Soft delete: ja.
organisations id (ULID), name, slug, billing_status, settings (JSON: display prefs only), created_at, deleted_at hasMany events, crowd_types, accreditation_categories. settings JSON alleen voor opaque UI-config, niet voor queryable data. Soft delete: ja.
organisation_user id (int AI), user_id, organisation_id, role Pivot. Integer PK voor join-performance. FK: users, organisations. Spatie role via pivot.
user_invitations id (ULID), email, invited_by_user_id, organisation_id, event_id (nullable), role, token (ULID, unique), status (pending|accepted|expired), expires_at Token in uitnodigingsmail. Bij accept: zoek bestaand account op email of maak nieuw aan. INDEX: (token), (email, status).
events id (ULID), organisation_id, name, slug, start_date, end_date, timezone, status (draft|published|registration_open|buildup|showday|teardown|closed), deleted_at belongsTo organisation. hasMany festival_sections, time_slots, persons, artists, briefings. Soft delete: ja. INDEX: (organisation_id, status).
event_user_roles id (int AI), user_id, event_id, role Pivot. Integer PK. FK: users, events.

3.5.2 Locaties (nieuw — oplossing probleem 3)

De locations tabel was gerefereerd door shifts maar niet gedefinieerd. Locaties zijn event-scoped en herbruikbaar over secties.

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
locations id (ULID), event_id, name, address, lat (decimal 10,8), lng (decimal 11,8), description, access_instructions Herbruikbare locaties per event. Gerefereerd door shifts.location_id. INDEX: (event_id).

3.5.3 Festival Sections, Time Slots & Shifts

Drielaags Crescat-model. Kritieke verbetering: time_slot_id gedenormaliseerd naar shift_assignments voor DB-afdwingbare conflictdetectie (probleem 2). Shift-swaps gesplitst in twee tabellen (probleem 10).

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
festival_sections id (ULID), event_id, name, sort_order, deleted_at hasMany shifts. Soft delete: ja. INDEX: (event_id, sort_order).
time_slots id (ULID), event_id, name, person_type (CREW|VOLUNTEER|PRESS|PHOTO|PARTNER), date, start_time, end_time, duration_hours hasMany shifts. person_type bepaalt zichtbaarheid in registratieformulier. INDEX: (event_id, person_type, date).
shifts id (ULID), festival_section_id, time_slot_id, location_id, slots_total, slots_open_for_claiming, assigned_crew_id, events_during_shift (JSON: array of performance_ids), status, deleted_at belongsTo festival_section, time_slot, location. hasMany shift_assignments. JSON hier OK: performance_ids zijn opaque referentie-lijst zonder filtering. INDEX: (festival_section_id, time_slot_id), (time_slot_id, status). Soft delete: ja.
shift_assignments id (ULID), shift_id, person_id, time_slot_id (FK: gedenormaliseerd), status (pending_approval|approved|rejected|cancelled|completed), auto_approved, assigned_by, assigned_at, approved_by, approved_at, rejection_reason, deleted_at UNIEK constraint: UNIQUE(person_id, time_slot_id) — DB-afdwingbare conflictdetectie. time_slot_id gedenormaliseerd van shifts voor deze constraint. Soft delete: ja. INDEX: (shift_id, status), (person_id, status), (person_id, time_slot_id).
volunteer_availabilities id (ULID), person_id, time_slot_id, submitted_at Vrijwilliger kiest beschikbare Time Slots. Basis voor shift-matching. UNIQUE(person_id, time_slot_id). INDEX: (time_slot_id).
shift_absences id (ULID), shift_assignment_id, person_id, reason (sick|personal|other), reported_at, status (open|filled|closed), closed_at Nieuw (splitsing probleem 10). Vrijwilliger meldt zich af — shift_slot komt vrij. Triggert wachtlijst-notificatie. INDEX: (shift_assignment_id), (status).
shift_swap_requests id (ULID), from_assignment_id, to_person_id, message, status (pending|accepted|rejected|cancelled|completed), reviewed_by, reviewed_at, auto_approved Nieuw (splitsing probleem 10). A vraagt B om te ruilen. Na akkoord beide: coordinator bevestigt (of auto-approve). INDEX: (from_assignment_id), (to_person_id, status).
shift_waitlist id (ULID), shift_id, person_id, position (int), added_at, notified_at Wachtlijst per shift. UNIQUE(shift_id, person_id). Bij uitval: positie 1 automatisch aangeschreven. INDEX: (shift_id, position).

3.5.4 Vrijwilligersprofiel & Geschiedenis

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
volunteer_profiles id (ULID), user_id (FK unique), bio, photo_url, tshirt_size, first_aid (bool), driving_licence (bool), allergies, access_requirements, emergency_contact_name, emergency_contact_phone, reliability_score (decimal 3,2), is_ambassador Platform-breed, 1-op-1 met users. reliability_score 0.00-5.00, berekend via scheduled job. UNIQUE(user_id).
volunteer_festival_history id (ULID), user_id, event_id, organisation_id, hours_planned, hours_completed, no_show_count, coordinator_rating (tinyint 1-5), coordinator_notes, would_reinvite (bool) Per gebruiker per festival. Nooit zichtbaar voor vrijwilliger zelf. INDEX: (user_id, event_id), UNIQUE(user_id, event_id).
post_festival_evaluations id (ULID), event_id, person_id, shift_id (nullable), overall_rating (tinyint 1-5), shift_rating (tinyint 1-5), would_return (bool), feedback_text, improvement_suggestion, submitted_at, is_anonymous Vrijwilliger evalueert na afloop. INDEX: (event_id, is_anonymous), (person_id).
festival_retrospectives id (ULID), event_id (unique), generated_at, volunteers_planned (int), volunteers_completed (int), no_show_count (int), no_show_pct (decimal 5,2), avg_overall_satisfaction (decimal 3,2), avg_shift_satisfaction (decimal 3,2), would_return_pct (decimal 5,2), sections_understaffed (int), sections_overstaffed (int), top_feedback (JSON: array of strings), notes (text) Oplossing probleem 8: alle KPIs als concrete kolommen ipv JSON blob. Trendanalyse over meerdere jaren mogelijk. JSON alleen voor vrije-tekst feedback array.

3.5.5 Crowd Types, Persons & Crowd Lists

Oplossing probleem 1 (identiteitsfragmentatie): persons krijgt user_id (nullable) als canonieke koppeling naar platform-account. Oplossing probleem 4: crowd_list_persons pivot toegevoegd. Oplossing probleem 9: persons.email als geindexeerde deduplicatie-sleutel.

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
crowd_types id (ULID), organisation_id, name, system_type (CREW|GUEST|ARTIST|VOLUNTEER|PRESS|PARTNER|SUPPLIER), color, icon, is_active Org-level configuratie. INDEX: (organisation_id, system_type).
persons id (ULID), user_id (nullable FK users), event_id, crowd_type_id, company_id (nullable), name, email, phone, status (invited|applied|pending|approved|rejected|no_show), is_blacklisted, admin_notes, custom_fields (JSON), deleted_at user_id nullable: externe gasten/artiesten hebben geen platform-account. UNIQUE(event_id, user_id) WHERE user_id IS NOT NULL. INDEX: (event_id, crowd_type_id, status), (email, event_id), (user_id, event_id). custom_fields JSON OK: event-specifieke velden, niet queryable. Soft delete: ja.
companies id (ULID), organisation_id, name, type (supplier|partner|agency|venue|other), contact_name, contact_email, contact_phone, deleted_at Gedeeld over events binnen org. Soft delete: ja. INDEX: (organisation_id).
crowd_lists id (ULID), event_id, crowd_type_id, name, type (internal|external), recipient_company_id (nullable), auto_approve (bool), max_persons (int nullable) hasMany persons via crowd_list_persons pivot. INDEX: (event_id, type).
crowd_list_persons id (int AI), crowd_list_id, person_id, added_at, added_by_user_id Nieuw pivot (oplossing probleem 4). Koppelt person aan crowd_list. UNIQUE(crowd_list_id, person_id). INDEX: (person_id).

3.5.6 Accreditatie Engine

Oplossing probleem 5: event_accreditation_items activeert org-level items per event. Accreditatie-items zijn nu org-level geconfigureerd en per event geactiveerd met event-specifieke limieten.

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
accreditation_categories id (ULID), organisation_id, name, sort_order, icon Org-level. Bijv. Wristband, Food & Beverage, Communication, Clothing. INDEX: (organisation_id).
accreditation_items id (ULID), accreditation_category_id, name, is_date_dependent (bool), barcode_type (qr|code128|ean13), ticket_visual_url, cost_price (decimal 8,2), sort_order Org-level items. Worden per event geactiveerd via event_accreditation_items.
event_accreditation_items id (ULID), event_id, accreditation_item_id, max_quantity_per_person (int nullable), total_budget_quantity (int nullable), is_active (bool), notes Nieuw (oplossing probleem 5). Activeert een org-level item voor een specifiek event met event-specifieke limieten. UNIQUE(event_id, accreditation_item_id). INDEX: (event_id, is_active).
accreditation_assignments id (ULID), person_id, accreditation_item_id, event_id, date (nullable, voor date_dependent items), quantity, is_handed_out, handed_out_at, handed_out_by_user_id Bijhouding per persoon per item. FK naar event_accreditation_items voor validatie. INDEX: (person_id, event_id), (accreditation_item_id, is_handed_out).
access_zones id (ULID), event_id, name, zone_code (varchar 20, unique per event), description Bijv. Backstage, VIP, Main Stage. Dag-koppeling via access_zone_days pivot. INDEX: (event_id).
access_zone_days id (int AI), access_zone_id, day_date (date) Nieuw (oplossing probleem 8: vervangt JSON days kolom). Queryable: welke zones zijn actief op datum X? UNIQUE(access_zone_id, day_date). INDEX: (day_date).
person_access_zones id (int AI), person_id, access_zone_id, valid_from (datetime), valid_to (datetime nullable) Pivot: toegangsrecht per persoon per zone. INDEX: (person_id), (access_zone_id).

3.5.7 Artists & Advancing

Oplossing probleem 8: stages.active_days JSON vervangen door stage_days pivot. milestone_flags JSON blijft (opaque toggle-set, nooit gefilterd).

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
artists id (ULID), event_id, name, booking_status (concept|requested|option|confirmed|contracted|cancelled), star_rating (tinyint 1-5), project_leader_id, milestone_flags (JSON), advance_open_from, advance_open_to, portal_token (ULID unique), deleted_at portal_token: toegang artiestenportaal zonder account. hasMany performances, advance_sections, artist_contacts, artist_riders. milestone_flags JSON OK: binaire toggle-set. Soft delete: ja.
performances id (ULID), artist_id, stage_id, date, start_time, end_time, booking_status, check_in_status (expected|checked_in|no_show) B2B detectie via overlap-query op stage_id + datum + tijdvenster. INDEX: (stage_id, date, start_time, end_time).
stages id (ULID), event_id, name, color (hex), capacity (int nullable) Dag-activatie via stage_days pivot (oplossing probleem 8). hasMany performances. INDEX: (event_id).
stage_days id (int AI), stage_id, day_date (date) Nieuw (oplossing probleem 8: vervangt stages.active_days JSON). UNIQUE(stage_id, day_date).
advance_sections id (ULID), artist_id, name, type (guest_list|contacts|production|custom), is_open, open_from, open_to, sort_order Crescat sectie-model. Elke sectie onafhankelijk submitbaar. INDEX: (artist_id, is_open).
advance_submissions id (ULID), advance_section_id, submitted_by_name, submitted_by_email, submitted_at, status (pending|accepted|declined), reviewed_by, reviewed_at, data (JSON) data JSON OK: vrije formulierdata, niet queryable. INDEX: (advance_section_id, status).
artist_contacts id (ULID), artist_id, name, email, phone, role, receives_briefing (bool), receives_infosheet (bool) Tour manager, agent, booker. INDEX: (artist_id).
artist_riders id (ULID), artist_id, category (technical|hospitality), items (JSON) items JSON OK: ongestructureerde rider-data. INDEX: (artist_id, category).
itinerary_items id (ULID), artist_id, type (transfer|pickup|delivery|checkin|performance), datetime, from_location, to_location, notes Vluchten/hotels: Out of Scope. INDEX: (artist_id, datetime).

3.5.8 Communicatie & Briefings

Oplossing probleem 11: broadcast_messages uitgebreid met polymorfisch broadcast_message_targets voor flexibele doelgroep-definitie.

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
briefing_templates id (ULID), event_id, name, type (crowd|artist|volunteer|supplier), blocks (JSON), is_default blocks JSON OK: drag-and-drop blok-configuratie, nooit gefilterd. INDEX: (event_id, type).
briefings id (ULID), event_id, briefing_template_id, name, target_crowd_types (JSON), send_from, send_until, status (draft|queued|sending|sent|paused) target_crowd_types JSON OK: array van crowd_type IDs. INDEX: (event_id, status).
briefing_sends id (ULID), briefing_id, person_id, status (queued|sent|opened|downloaded), sent_at, opened_at Track per persoon per briefing. INDEX: (status, briefing_id) — queue processing. INDEX: (person_id).
communication_campaigns id (ULID), event_id, type (email|sms|whatsapp), name, body, recipient_group (JSON), status (draft|scheduled|sending|sent|cancelled), scheduled_at, sent_at, sent_count, failed_count Bulk campagnes. SMS+WhatsApp via Zender. recipient_group JSON: beschrijving doelgroep-filter. INDEX: (event_id, type, status).
messages id (ULID), event_id, sender_user_id, recipient_person_id, body, urgency (normal|urgent|emergency), channel_used (email|sms|whatsapp), read_at, replied_at, created_at 1-op-1 berichten. urgency bepaalt kanaal via ZenderService. INDEX: (event_id, recipient_person_id), (recipient_person_id, read_at).
message_replies id (ULID), message_id, person_id, body, status_update (on_my_way|arrived|sick|other), created_at Vrijwilliger reageert via portal. INDEX: (message_id).
broadcast_messages id (ULID), event_id, sender_user_id, body, urgency, channel_used, sent_at, recipient_count, read_count Groepsbericht. Doelgroep via broadcast_message_targets (oplossing probleem 11). INDEX: (event_id, sent_at).
broadcast_message_targets id (int AI), broadcast_message_id, target_type (event|section|shift|crowd_type|custom_list), target_id (ULID nullable) Nieuw polymorfisch target model (oplossing probleem 11). Meerdere targets per bericht mogelijk. target_id NULL bij type=event (heel event). INDEX: (broadcast_message_id).

3.5.9 Formulieren, Check-In & Operationeel

Tabel Belangrijkste kolommen Relaties, constraints & opmerkingen
public_forms id (ULID), event_id, name, crowd_type_id, fields (JSON), conditional_logic (JSON), iframe_token (ULID unique), confirmation_email_template, is_active fields + conditional_logic JSON OK: formulier-configuratie, niet gefilterd. INDEX: (event_id, crowd_type_id, is_active).
form_submissions id (ULID), public_form_id, person_id, data (JSON), submitted_at data JSON OK: vrije formulierresultaten. INDEX: (public_form_id, submitted_at), (person_id).
check_ins id (ULID), event_id, person_id, scanned_by_user_id, scanner_id, scanned_at, location_id Immutable audit-record: GEEN soft delete. INDEX: (event_id, person_id, scanned_at), (event_id, scanned_at).
show_day_absence_alerts id (ULID), event_id, shift_id, person_id, alert_sent_at, response_status (no_response|confirmed|absent|late), resolved_at Immutable audit-record: GEEN soft delete. INDEX: (shift_id, response_status), (event_id, alert_sent_at).
scanners id (ULID), event_id, name, type (crowd|zone|accreditation), scope (JSON), pairing_code (varchar 8, unique), last_active_at scope JSON OK: scanner-configuratie. INDEX: (event_id), (pairing_code).
inventory_items id (ULID), event_id, name, item_code (varchar 50), assigned_to_person_id (nullable), assigned_at, returned_at, returned_by_user_id Portofoons, hesjes, sleutels. INDEX: (event_id, assigned_to_person_id), (item_code).
event_info_blocks id (ULID), event_id, type (description|route|parking|contacts|marketing|custom), title, content (text), files (JSON), sort_order, is_published files JSON OK: array van bestandspaden. Zichtbaarheid per crowd_type via event_info_block_crowd_types. INDEX: (event_id, type, is_published).
event_info_block_crowd_types id (int AI), event_info_block_id, crowd_type_id Pivot: welke crowd_types zien welk info-blok. Vervangt visible_to_crowd_types JSON (oplossing probleem 8). UNIQUE(event_info_block_id, crowd_type_id).
production_requests id (ULID), event_id, company_id, title, status (draft|sent|in_progress|submitted|approved|rejected), token (ULID unique), sent_at, submitted_at, reviewed_by, reviewed_at, deleted_at Nieuw (oplossing probleem 3: ontbrekende tabel). Hoofdrecord per leverancier. token: portaaltoegang zonder account. hasMany material_requests. INDEX: (event_id, status), (company_id).
material_requests id (ULID), production_request_id, category (heavy_equipment|tools|vehicles|other), name, description, quantity (int), period_from, period_to, status (requested|approved|rejected|fulfilled), notes Onderdeel van production_request. INDEX: (production_request_id, status).

3.5.10 Database Ontwerpregels & Index Strategie

REGEL 1 — ULID als primaire sleutel Alle business-tabellen: $table->ulid('id')->primary() + HasUlids trait. Pure pivot/koppel-tabellen (geen eigen lifecycle): $table->id() (auto-increment integer) voor join-performance. Nooit UUID v4 — vermijdt InnoDB B-tree fragmentatie.
REGEL 2 — JSON kolommen: wanneer wel, wanneer niet WEL JSON: opaque configuratie (blocks, fields, settings, items), toggle-sets (milestone_flags), vrije tekst arrays (top_feedback). NOOIT JSON voor: datums/periodes, status-waarden, foreign keys, boolean flags, alles waarop je filtert/sorteert/aggregeert. Voorbeelden vervangen: access_zone_days (was days JSON), stage_days (was active_days JSON), broadcast_message_targets (was target JSON), event_info_block_crowd_types (was visible_to_crowd_types JSON), festival_retrospectives kolommen (waren data JSON blob).
REGEL 3 — Soft delete strategie Soft delete (deleted_at) op: organisations, events, festival_sections, shifts, shift_assignments, persons, artists, companies, production_requests. GEEN soft delete op: check_ins, show_day_absence_alerts, briefing_sends, message_replies, audit_log, shift_waitlist, volunteer_festival_history. Rationale: audit-records zijn immutable. Soft delete op audit-records geeft een vals beeld van de werkelijkheid.
REGEL 4 — Verplichte indexes (minimum set) persons: (event_id, crowd_type_id, status), (email, event_id), (user_id, event_id). shift_assignments: UNIQUE(person_id, time_slot_id), (shift_id, status), (person_id, status). check_ins: (event_id, person_id, scanned_at), (event_id, scanned_at). briefing_sends: (status, briefing_id) — queue processing. shift_waitlist: (shift_id, position). performances: (stage_id, date, start_time, end_time) — B2B overlap detectie. Voeg EXPLAIN ANALYZE toe aan queries die > 100ms duren. Target: alle lijst-queries < 50ms.
REGEL 5 — Multi-tenancy scoping Elke query op event-data MOET scopen op organisation_id via Eloquent Global Scope (OrganisationScope). Gebruik Laravel policies voor autorisatie: nooit directe id-checks in controllers. Audit log: Spatie laravel-activitylog op: persons, accreditation_assignments, shift_assignments, check_ins, production_requests.

4. Functionele Modules

Gedetailleerde beschrijving van alle modules

4.1 Dashboard & Event Health Check

Het event dashboard is de landing page voor elke event-context. Het past zijn primaire CTA aan op basis van de huidige event lifecycle fase en toont een overzicht van openstaande acties.

Kerncomponenten van het dashboard:

  • Attention matrix: tabel met crowd types als rijen en 'Pending approval', 'Missing briefing', 'Incomplete accreditation', 'Attention required' als kolommen. Elke cel toont een klikbaar getal. (In2Event patroon)

  • Event Health widget: configuratieproblemen als oranje/rode items met directe actielinks: niet-geverifieerde e-mailadressen, accreditatiegoed keuring achterstand, artiesten met ontbrekende riders, productieaanvragen zonder reactie. (WeezCrew patroon)

  • Fase-aware CTA banner: verandert per event fase. Show Day: 'Ga naar Mission Control'. Planning: 'Openstaande goedkeuringen'. Registratie: 'Vrijwilligersregistraties bekijken'.

  • Bezetting per dag: staafdiagram (ApexCharts) met crew/volunteer bezetting per eventdag. Toont over-/onderbezette dagen in een oogopslag.

  • Shifts Per Day tabel: per sectie en per dag: #Assigned/#Count voor Crew en Volunteers apart. (Crescat Festival Section dashboard patroon)

4.2 Festival Sections

Festival Sections zijn de operationele eenheden binnen een evenement. Elke sectie heeft een eigen tabblad-gebaseerde interface met: Dashboard, Shifts, Scheduler, Crew, Volunteers, Info Section, Accreditation.

Sectie dashboard (per section):

  • Filled Shifts teller: X/Y shifts gevuld. Opgesplitst Crew en Volunteers.

  • Per-dag tabel: #Assigned/#Count, Executed/Planned uren per dag, voor zowel Crew als Volunteers (Responders).

  • Assigned summary rechts: Status (Full/Missing), Missing count, Assigned/Required ratio. Apart voor Crew en Volunteers.

  • 'Open' knop bij Volunteers: opent het publieke vrijwilligersregistratieformulier voor die sectie.

Shifts tab (binnen een sectie):

  • Datum-navigatie aan de linker zijkant: per dag met samenvatting Shifts X/Y, Slots X/Y.

  • Shift lijst: gegroepeerd per Time Slot naam. Per shift: When (tijd + Time Slot naam), Title, Where (locatie), Events (artiesten tijdens shift), Assigned Crew (supervisor + status), Assigned/Slots badge, For Sale count, actieknoppen (Assign, Add Shift, menu).

  • '+ Create Shift' button: maakt een nieuwe shift aan gekoppeld aan een bestaand Time Slot.

  • Assign Shift side panel: Title, Where, Assigned Crew (dropdown). When sectie: Time Slot selector + datum + tijden (overgenomen van Time Slot). Slots sectie: totaal slots input, slots open for claiming input, Assigned counter, Available counter. Assignees grid: tot 6 personen zichtbaar met email/telefoon iconen.

4.3 Time Slots & Shift Planning Model

Dit is een van de meest onderscheidende architecturale keuzes van Crewli, gebaseerd op de Crescat aanpak zoals geanalyseerd uit de screenshots.

HET MODEL IN EEN ZIN Time Slots = het WAN-NEER (event-niveau, per persoonscategorie). Shifts = het WAT+WAAR+HOEVEEL (sectie-niveau, met capaciteit). Vrijwilligers kiezen Time Slots. Organisator maakt Shifts per Time Slot per sectie.

Time Slot aanmaken (organisator):

  • Naam: beschrijvend, incl. persoonscategorie. Bijv. 'DAY 1 - EARLY - VOLUNTEER', 'FRIDAY - PRESS'.

  • Persoonscategorie (person_type): CREW | VOLUNTEER | PRESS | PHOTO | PARTNER. Bepaalt wie dit slot ziet in registratieformulier.

  • Datum, start- en eindtijd, berekende duur.

  • Time Slots worden éénmalig aangemaakt op event-niveau en hergebruikt door alle Festival Sections.

Shift aanmaken (per Festival Section):

  • Selecteer een Time Slot (erft datum + tijden + persoonscategorie).

  • Kies locatie/venue.

  • Stel capaciteit in: slots_total (totaal) en slots_open_for_claiming (claimbaar via portal).

  • Wijs supervisor toe (Assigned Crew).

  • Events during Shift: artiesten die op dat tijdstip spelen (automatisch gevuld vanuit timetable).

Shift claiming (vrijwilliger, via portal):

  • Vrijwilliger ziet alleen shifts waarvan de Time Slot person_type = VOLUNTEER.

  • Vrijwilliger ziet alleen shifts met slots_open_for_claiming > 0.

  • Conflictdetectie: als vrijwilliger al een shift heeft op hetzelfde time_slot_id, is die shift als '(Time Conflict)' gemarkeerd en niet claimbaar.

  • Vrijwilliger ziet welke artiesten er spelen tijdens de shift — dit vergroot betrokkenheid.

Toewijzingsstrategieën (drie opties per shift):

  • Volledig gecontroleerd: slots_open_for_claiming = 0. Organisator wijst handmatig toe. Shift zichtbaar in organisator backend, niet in volunteer portal.

  • Volledig zelfservice: slots_open_for_claiming = slots_total. Vrijwilligers vullen alle plekken zelf.

  • Hybride: slots_open_for_claiming < slots_total. Bijv. 5 plekken totaal, 3 open voor claiming, 2 gereserveerd voor handmatige toewijzing (vaste krachten).

4.4 Vrijwilligersbeheer & Registratieportaal

Vrijwilligers zijn de kern van elke festival-organisatie. Dit module ontlast de organisator maximaal bij de administratie en bouwt binding en trots op bij de vrijwilliger zelf. Vrijwilligers zijn geen datapunten — ze zijn de ambassadeurs van jullie festival.

4.4.1 Publiek registratieformulier (meerdelige structuur):

  • Deel 1 — Over jou: Naam, e-mail, telefoon (met landcode).

  • Deel 2 — Meer over jou: Shirtmaat, EHBO, allergieën, toegangsbehoeften, rijbewijs. Geconfigureerd via de formulierbouwer.

  • Deel 3 — Motivatie: Waarom wil je vrijwilliger zijn? Dropdown + vrije tekst.

  • Deel 4 — Voorkeurssecties: Selecteer secties en rangschik prioriteit 1-5 (drag-to-prioritize).

  • Deel 5 — Beschikbaarheid: Selecteer Time Slots (gefilterd op VOLUNTEER type). Toont minimumurendrempel voor festivalpas.

  • Deel 6 — Admin only: Blacklist toggle, betaalstatus, algemene notities.

4.4.2 Vrijwilligersprofiel (platform-breed, suggestie 1):

Elke vrijwilliger heeft één platform-breed profiel. Eenmalig invullen, herbruikbaar bij elk volgend festival — ook bij andere organisaties.

  • Profielfoto, bio, contactgegevens, EHBO-certificaat, rijbewijs, shirtmaat, noodcontact.

  • Festival-paspoort: visuele tijdlijn van alle deelgenomen festivals. '5 festivals, 84 uur, altijd bij Hospitality.'

  • Intern prestatiebadge systeem (nooit zichtbaar voor vrijwilliger zelf): Betrouwbaar (0 no-shows), Veteraan (5+ festivals), EHBO-gecertificeerd, Terugkerende kracht, Ambassador.

  • reliability_score (0.05.0): automatisch berekend op basis van no-shows, tijdigheid check-in en coordinator-ratings over alle festivals.

  • Bij herinvitatie: organisator ziet reliability score, festival-geschiedenis en 'would_reinvite' oordeel van vorig jaar — direct naast de naam in de lijst.

4.4.3 Organisator back-end (per vrijwilliger):

  • Side panel tabs: General Info, Responder, Shifts, Communication, Accreditations, Openings, Documents, History.

  • Work Hours tracker: Expected / Assigned / Completed uren.

  • History tab: overzicht alle voorgaande festivals. Per festival: geplande uren, voltooide uren, no-shows, coordinator-rating (sterren), notities. Reliability score als visuele scorebalk.

  • Herinvitatie-snelheid indicator: hoe snel reageerde deze persoon op vorige uitnodigingen? Hoge snelheid = stuur als eerste uit.

4.4.4 Vrijwilligersportaal (zelfservice):

  • Home: persoonlijke welkomstboodschap met festivalnaam, shifts-samenvatting en openstaande acties ('Je claim wacht op goedkeuring').

  • My Shifts: per datum. Supervisor, locatie, artiesten tijdens shift. Knop: 'Ik kan toch niet komen' (triggert absence-workflow).

  • Claim Shifts: open shifts per datum. Conflictdetectie rood. Na claimen: 'Wachten op goedkeuring'. Bij vol: wachtlijst-knop. Bij goedkeuring/afwijzing: notificatie via e-mail + WhatsApp (Zender).

  • Dienst wisselen: open swap (iedereen mag reageren) of persoonlijke swap (specifieke collega). Na akkoord beide partijen: eenmalige bevestiging coordinator.

  • Berichten: inbox voor 1-op-1 berichten van coordinatoren. Vrijwilliger kan reageren met kort antwoord of statusupdate: 'Ik ben onderweg' / 'Ik ben er' / 'Ik ben ziek'.

  • Mijn Profiel: gegevens bewerken. Festival-paspoort inzien.

4.4.5 Shift swap & wachtlijst workflow (suggestie 5):

SWAP WORKFLOW Open swap: vrijwilliger meldt zich af → shift_swap.status = open → coordinator notificatie → wachtlijst positie 1 automatisch aangeschreven. Persoonlijke swap: A vraagt B → B accepteert → pending_swap → coordinator keurt goed (of auto-approve als secties identiek) → assignments gewisseld. Wachtlijst: shift vol → 'Op wachtlijst' knop → bij uitval: automatisch aangeschreven op volgorde.

4.4.6 Post-festival evaluatie & retrospectief (suggestie 4):

  • 24u na laatste shift: automatische evaluatiemail naar alle deelgenomen vrijwilligers. Max. 5 vragen: algehele beleving (1-5), shiftkwaliteit (1-5), terugkomen? (ja/nee), vrije feedback, verbeterpunt.

  • Anoniem of op naam: instelbaar per event.

  • Coordinator-beoordeling (parallel, intern): per vrijwilliger een rating (1-5), notities, 'herinviteren?' (ja/nee/misschien). Nooit zichtbaar voor de vrijwilliger.

  • Resultaten worden verwerkt in het festival-retrospectief rapport (zie 4.12).

  • Na afloop: automatisch bedankbericht aan alle vrijwilligers. Gepersonaliseerd: naam, sectie, uren gedraaid, 'Zonder jou was dit festival niet mogelijk.'

4.5 Communicatiehub (suggestie 2)

Communicatie is op show-dag een van de grootste operationele risico's. Het platform biedt een centrale hub met drie niveaus van urgentie. De coordinator kiest urgentie — het systeem kiest het juiste kanaal.

URGENTIENIVEAUS Normaal: e-mail. Voor planningswijzigingen, briefings, updates. Niet-tijdkritisch. Dringend: WhatsApp (Zender). Voor dag-van-het-festival meldingen, last-minute wijzigingen. Noodgeval: SMS + WhatsApp gelijktijdig. Voor acute veiligheidssituaties, volledige evacuatie-achtige scenario's.

Groepsberichten (broadcast):

  • 'Een knop stuur bericht naar ploeg': per shift, per sectie of per heel event. Coordinator kiest urgentie, systeem kiest kanaal.

  • Leesbevestiging: '14 van 17 vrijwilligers hebben dit bericht gelezen.' Live bijgehouden via WebSocket.

  • Reactiemogelijkheid voor vrijwilliger: kort antwoord of statusupdate ('Ik ben onderweg', 'Ik ben er', 'Ik ben ziek'). Coordinator ziet live overzicht van alle reacties per shift.

No-show automatisering (suggestie 3 — show day):

  • 30 minuten voor shiftstart: automatisch WhatsApp-bericht aan iedereen die nog niet is ingecheckt: 'Hey [naam], je dienst bij [sectie] begint over 30 minuten. Laat weten of je er bent.'

  • Vrijwilliger kan direct reageren via WhatsApp of portal. Reactie verschijnt live bij coordinator.

  • show_day_absence_alerts tabel logt elke alert, response-status en afdoening.

  • Coordinator-dashboard toont real-time: per shift hoeveel ingecheckt, hoeveel no-response, hoeveel bevestigd absent.

4.6 Show Day Mode (suggestie 3)

Op de dag van het festival heeft niemand tijd voor complexe navigatie. Show Day Mode activeert automatisch op de event-datum en biedt een radicaal vereenvoudigde interface die werkt op telefoon, tablet en grote schermen.

Activatie:

  • Automatisch: systeem schakelt naar Show Day Mode zodra de datum van de eerste show-dag is bereikt.

  • Handmatig override: event-manager kan de mode vroeger activeren (bijv. tijdens opbouw).

Show Day dashboard (vereenvoudigd):

  • Groot, hoog contrast, werkt op telefoon — ontworpen voor gebruik buiten, in drukte, met handschoenen.

  • Per sectie: grote tegel met naam, # ingecheckt / totaal, gekleurde statusring (rood/oranje/groen).

  • One-tap acties: 'Ping iedereen in deze sectie', 'Bekijk wie er niet is', 'Stuur noodbericht.'

  • Live incheckoverzicht: per minuut bijgewerkt teller. Geen filters, geen tabellen.

Snelle QR check-in (zonder hardware):

  • Vrijwilliger toont QR-code uit eigen portal op telefoon.

  • Coordinator scant met eigen telefoon-camera (geen dedicated scanner hardware nodig voor kleine events).

  • Check-in gelogd, accreditatie-items automatisch als 'uitgedeeld' gemarkeerd indien geconfigureerd.

Artiest show-dag overzicht:

  • Per podium: running order met live checkmarks. Groen = ingecheckt, rood = nog niet gezien.

  • Live statusbericht: '17 artiesten ingecheckt van 32.'

  • One-tap artiest bellen: telefoon direct bellen via opgeslagen contactnummer.

4.7 Staff & Crew Management

Kernfuncties:

  • Crowd pool per organisatie: medewerkers herbruikbaar over evenementen. Cross-event geschiedenis en reliability score.

  • Status KPI tiles: Total, Approved, Pending, Other — klikbare tellers.

  • Quick-action popup: klik op persoon → popup met accreditatie, Approve, Send message.

  • Accreditation matrix view: spreadsheet met items als kolommen, personen als rijen.

  • Externe lijst: deelbaar met partner, binnen organisator-ingestelde limieten.

  • Blacklist toggle, admin-only notities, EHBO/rijbewijs metadata.

4.8 Accreditatie Engine

Configuratie (org-niveau):

  • Categorieën: Wristband, Food & Beverage, Communication (portofoons), Clothing (shirts), Transport, Custom.

  • Items per categorie: naam, date-dependent flag, quantity, barcode type, ticket visual, kostprijs.

  • Templates: herbruikbare sets van items+limieten, activeerbaar per event.

  • Access Zones: Backstage, VIP, Main Stage, Production, Parking — zone_code voor scanners.

Check-in modal (Mission Control):

  • Zoek op naam of scan barcode.

  • Accreditatie tab: items per categorie, checkbox per item, 'Check all items' shortcut.

  • Check-in button → logt check_in, markeert items als uitgedeeld.

4.9 Briefing & Communicatie

Briefings (e-mail + PDF):

  • Briefing template builder: drag-and-drop blokken (tekst, afbeelding, kaart, accreditatielijst). TipTap + VueDraggable.

  • Selectieve blokzichtbaarheid: blok verschijnt alleen als ontvanger een specifiek accreditatie-item heeft.

  • Queue-based verzending: organisator ziet '134 bevestigingsmails wachten'. Handmatige 'Verstuur' trigger.

  • Status tracking: Queued → Sent → Opened → Tickets Downloaded.

  • Tags: __FIRSTNAME__, __LASTNAME__, __COMPANY__, __ARTIST__, __SHIFT__.

Allocatiesheet (PDF per crew/vrijwilliger):

  • Server-side via Laravel Browsershot. Bevat: taaknaam, datum/tijd, beschrijving, materialen, transport, kaart embed, persoonlijke QR-code.

  • Configureerbare opmaak: logo, achtergrondafbeelding, lettertype.

Campagnes:

  • E-mail campagnes: samengesteld bericht naar geselecteerde doelgroep.

  • SMS + WhatsApp campagnes via Zender (zelf-gehoste instantie). Doelgroep selecteerbaar, tekentelling zichtbaar, verzending planbaar.

  • 1-op-1 berichten: gethread berichtenuitwisseling per persoon. Vrijwilliger kan reageren met statusupdate.

4.10 Mission Control (Show Day)

Sub-modules:

  • Crowd Check-In: zoek of scan, modal met accreditatie + items, 'Check all items', print polsband.

  • Artist Handling: live telling ingecheckte artiesten, per-stage running order met live checkmarks.

  • Check-In Insights: live aanwezigheidsanalyse per uur, per crowd type.

  • Scanner beheer: configureer scanstations. Koppeling via QR pairing code.

  • Inventarisbeheer: wijs fysieke items uit, markeer als teruggegeven, bulk-teruggave.

  • Show Day Mode activatie: grote knop bovenaan — schakel naar mobiel-geoptimaliseerde weergave (zie 4.6).

4.11 Formulierbouwer

  • Drag-sorteerbare veldenlijst: sleepgreep, naam, type (Text/Dropdown/Checkbox/Custom), required toggle.

  • Live voorbeeldpaneel: toont formulier real-time tijdens het bouwen.

  • Conditionele logica: veld X is alleen zichtbaar als antwoord op veld Y = waarde Z.

  • Integratiepaneel: deelbare link + <iframe> embed code.

  • E-mailtemplate tab: bevestigingsmail na invullen.

4.12 Suppliers & Productie

  • Productieverzoek workflow: aanmaken → versturen → leverancier vult in → organisator keurt goed.

  • Icon-kolommen: mensen/tech/stroom/materiaal als kolommen met groene vinkjes.

Leveranciersportal — Algemene informatie tab:

  • Eventbeschrijving, route (vrachtwagenvriendelijk), vrachttoegang/laad-lostijden, contacten.

Leveranciersportal — Informatie invullen:

  • Personeelsaccreditatie: medewerkersnamen + accreditatiecategorieën.

  • Stroomvoorziening: type aansluiting, vermogen (kW), aantal aansluitpunten.

  • Materiaalbehoeften: heftruck, manitou, aggregaat, aanhanger, pallet jack. Velden: omschrijving, hoeveelheid, periode (van/tot). Workflow: Requested → Approved/Rejected → Fulfilled.

  • Externe personeelslijst: deelbare spreadsheet met accreditatie-items als bewerkbare kolommen.

4.13 Rapportage & Inzichten

Rapport Inhoud
Accreditatie totalen Aangevraagd / Goedgekeurd / Gescand per dag per item, per bedrijf.
Artiest/Program Rider totalen, riders per artiest, per dag, per kleedkamer.
Bezetting per dag Crew en vrijwilligers per sectie per dag. Staafdiagram + tabel.
Accreditatie type Staafdiagram per type met fill rate.
Aanwezigheid Check-in statistieken per uur, per crowd type, live en historisch.
Inventaris Uitgedeelde items, niet teruggegeven items, per persoon.
Communicatie Leespercentage per campagne, per kanaal (e-mail/SMS/WhatsApp). Response rates.
Shift swaps Aantal swaps per event, reden (absent/ruil), no-show percentage per sectie.
Vrijwilligerstevredenheid Gem. evaluatiescore per sectie, per shift, per event. Trend over meerdere festivals.
Festival retrospectief (suggestie 4) Auto-gegenereerd na afloop: bezetting vs. gepland, no-show %, gem. tevredenheid, secties met tekort/overschot, herinvitatie-aanbeveling, vergelijking vorig jaar.
Financieel Kosten per shift, betaalde accreditatie-items, artiestenkosten.
Duplicaten Personen meerdere keren toegevoegd aan lijsten.

5. API Design & Integraties

REST API, WebSockets, externe koppelingen

5.1 API Architectuurprincipes

  • API-first: alle functionaliteit is beschikbaar via de REST API. De frontend is een API consumer, geen uitzondering.

  • Versioning: /api/v1/ prefix. Breaking changes krijgen een nieuw versienummer.

  • Auth: Laravel Sanctum SPA tokens voor de eigen frontends. API keys (hashed in DB) voor externe integraties.

  • Resource-based routing: standaard Laravel resourcecontrollers. index, show, store, update, destroy.

  • JSON:API-stijl responses: { data: {...}, meta: {...}, links: {...} } voor paginering.

  • Rate limiting: per API key configureerbaar. Standaard 60 req/min voor publieke portals.

5.2 Kern API Routes

Route groep Voorbeeldroutes
Auth POST /auth/login, POST /auth/logout, POST /auth/refresh
Organisations GET|POST /organisations, GET|PUT|DELETE /organisations/{id}
Events GET|POST /organisations/{org}/events, PUT /events/{id}/status
Festival Sections GET|POST /events/{event}/sections, GET /sections/{id}/dashboard
Time Slots GET|POST /events/{event}/time-slots, PUT|DELETE /time-slots/{id}
Shifts GET|POST /sections/{section}/shifts, PUT /shifts/{id}, POST /shifts/{id}/assign
Persons GET|POST /events/{event}/persons, POST /persons/{id}/approve, POST /persons/{id}/checkin
Artists GET|POST /events/{event}/artists, GET /artists/{id}/advance-portal
Advance Sections GET|POST /artists/{id}/sections, POST /sections/{id}/submit
Volunteers GET /events/{event}/volunteers, GET /volunteers/portal/{token}
Accreditations GET|POST /events/{event}/accreditation-items, POST /persons/{id}/accreditations
Briefings GET|POST /events/{event}/briefings, POST /briefings/{id}/send
Campaigns GET|POST /events/{event}/campaigns, POST /campaigns/{id}/send
Mission Control GET /events/{event}/mission-control, POST /persons/{id}/checkin-item
Scanners GET|POST /events/{event}/scanners, POST /scan
Reports GET /events/{event}/reports/{type}
Public (no auth) POST /public/forms/{token}/submit, GET /public/advance/{token}, GET /public/briefing/{token}

5.3 Real-time Events (WebSocket)

Via Laravel Echo + Pusher/Soketi. Alle kritieke statuswijzigingen worden als events uitgezonden:

Event naam Trigger & payload
PersonCheckedIn Na succesvolle check-in. Payload: person_id, name, crowd_type, timestamp.
ShiftFillRateChanged Na elke shift assignment/unassignment. Payload: shift_id, filled, total.
ArtistCheckInStatusChanged Artiest ingecheckt op show dag. Payload: artist_id, performance_id, status.
AdvanceSectionSubmitted Tour manager heeft een advance sectie ingediend. Payload: section_id, artist_id, submitted_by.
AccreditationItemHandedOut Item uitgedeeld via Mission Control. Payload: person_id, item_id, qty.
BriefingSendQueued Briefingverzending in queue geplaatst. Payload: briefing_id, recipient_count.

5.4 Externe Integraties

Integratie Doel & implementatie
Ticketing (Eventix, See Tickets) Import tickets via REST API per event. Tickets worden gekoppeld aan persons voor check-in en accreditatie matching.
SMS + WhatsApp (Zender) Bulk SMS + WhatsApp campagnes via Zender HTTP API. Laravel ZenderService wraps de API calls. Individuele notificaties en bulk campagnes worden beide ondersteund.
E-mail (SendGrid / Postmark) Transactionele e-mail en bulk campagnes. Queue-based via Laravel Mailable.
Google Maps / Leaflet Locatie embed in allocatiesheet PDF en shift location configuratie.
Wristband printers Integratie via USB/network printer API op check-in station.
Externe scanners Hardware koppeling via scanner configuratie + pairing QR code.

6. UX & UI Ontwerppatronen

Gevalideerde patronen uit de concurrentieanalyse

6.1 Statusbadges systeem

Gebruik consistent kleurgecodeerde pill-badges door de hele applicatie:

Badge type Waarden Kleuren
Person status Draft → Invited → Applied → Pending → Approved → Rejected Grijs / Blauw / Geel / Oranje / Groen / Rood
Booking status Concept → Requested → Option → Confirmed → Contracted → Cancelled Grijs / Blauw / Paars / Groen / Donkergroen / Rood
Shift fill status Empty / Partial / Full / Over-quota Rood / Oranje / Groen / Paars
Event status Draft / Published / Reg.Open / Build-Up / Show Day / Tear-Down / Closed Grijs tot groen verloop per fase
Crowd type badge Per crowd type kleur (Volunteer=groen, Artist=paars, Press=blauw, VIP=goud, Staff=blauw) Per type configureerbaar in crowd_types.color

6.2 Quick-action popup patroon

Gebruik compacte overlay-popups (niet volledige paginanavigatie) voor de meest voorkomende acties. Vermijd context-switching bij operationele taken:

  • Klik op persoon in lijst → popup met naam, bedrijf, accreditatiestatus, Approve/Reject knoppen, Send message.

  • Klik op performance blok in timetable → popup met artiestnaam, status, advancing checklist, 'Manage' link.

  • Klik op accreditatie-item in check-in modal → markeer uitgedeeld, inline, geen paginaovergang.

6.3 Fill-rate progress bars

Gebruik kleurgecodeerde horizontale voortgangsbalkjes overal waar capaciteit vs. bezetting relevant is:

  • Groen (≥80%): shift is goed gevuld

  • Oranje (50-79%): gedeeltelijk gevuld

  • Rood (<50%): onvoldoende gevuld

Toepassen op: shift lijst, sectie dashboard, accreditatie allocatie vs. limiet, advancing sectie completering.

6.4 Duaal portal architectuur

Scheiding tussen de interne beheerapplicatie (app/) en externe portals (portal/). Externe portals zijn:

  • advance.crewli.app/[event-slug]/[artist-token] — Artist advance portal

  • volunteers.crewli.app/[event-slug] — Vrijwilligersregistratie en portal

  • briefing.crewli.app/[token] — Persoonlijke briefing pagina (geen login)

  • supplier.crewli.app/[event-slug]/[supplier-token] — Leveranciersportal

Externe portals hebben event-branding (logo, kleuren) maar bieden een vereenvoudigde, rol-beperkte weergave. Ze zijn PWA-geschikt voor mobiel gebruik.

6.5 Inline validatie met 'Missing Items' blok

Formuliervalidatie sectie-voor-sectie. Toon een 'Missing Items' samenvatting onderin elke sectie die persistente zichtbaarheid heeft totdat alle vereiste velden zijn ingevuld. Geen fouten alleen bij submit.

6.6 Kaartintegratie in crew-documenten

Per locatie/shift is een adres, coördinaten en kaartpreview (Google Maps of Leaflet embed) configureerbaar. De kaart wordt opgenomen in de server-side gegenereerde allocatiesheet PDF. Dit is essentieel voor grote outdoor festivallocaties.

7. Ontwikkelroadmap & Prioriteiten

Aanbevolen bouwvolgorde per fase

7.1 Prioriteitsmatrix

Module Reden Prioriteit Fase
Multi-tenant architectuur + Auth Alle overige modules zijn afhankelijk van org/event scoping en rollen. P0 Fundament 1
Gebruikers, Rollen & Permissies Drie-niveau permissiemodel vereist vanaf dag 1. P0 Fundament 1
Organisaties & Evenementbeheer Kerndata-containers voor alle operationele data. P0 Fundament 1
Crowd Types & Segmentatie Basis voor alle deelnemersbeheer. P1 Kern 1
Festival Sections + Time Slots + Shifts Uniek planningsmodel, kern van het platform. P1 Kern 1
Personen & Crowdlijsten Deelnemer data model inclusief externe lijsten. P1 Kern 1
Accreditatie Engine Centraal rechtensysteem, gebruikt door alle modules. P1 Kern 1
Briefings & Communicatie Primair communicatiekanaal naar deelnemers. P1 Kern 2
Staff & Crew Management Primaire use-case voor doelmarkt. P1 Kern 2
Vrijwilligersbeheer + Portaal Zelfregistratie, prioriteitsranking, shift claiming. P2 Hoog 2
Formulierbouwer Geconfigureerd publiek formulier met conditionele logica. P2 Hoog 2
Artist Advancing + Portaal Sectie-gebaseerd advancing, milestone pipeline. P2 Hoog 2
Timetable (stage + drag-drop) FullCalendar timeline, B2B detectie. P2 Hoog 2
Guests & Hospitality VIP beheer, RSVP, paid guest lists. P2 Hoog 3
Suppliers & Productie Leveranciersportal, productieverzoeken. P2 Hoog 3
Mission Control / Show Day Real-time check-in, artiest handling, inventaris. P2 Hoog 3
Communicatiecampagnes (Email + SMS) Bulk email/SMS via Twilio. Bevestigingswachtrij. P3 Medium 3
Allocatiesheet PDF generator Branded PDF per crew met kaart + QR. P3 Medium 3
Scaninfrastructuur Scanstations configureren, hardware koppelen. P3 Medium 3
Rapportage & Inzichten Gebouwd naast elke module. Progressief uitbreiden. P3 Medium 3
Show Day Mode (mobiel-first) Vereenvoudigde show-dag interface, QR check-in op telefoon. P2 Hoog 2
No-show automatisering 30-min alerts voor niet-ingecheckte vrijwilligers via Zender. P2 Hoog 3
Shift swap & wachtlijst Vrijwilliger meldt zich af, wachtlijst automatisch aangeschreven. P2 Hoog 2
Vrijwilligersprofiel + festival-paspoort Platform-breed profiel, reliability score, festival-geschiedenis. P2 Hoog 2
Post-festival evaluatie + retrospectief Automatische enquête, coordinator-beoordeling, gegenereerd rapport. P2 Hoog 3
Communicatiehub met urgentielevels Normaal/Dringend/Noodgeval → juist kanaal, leesbevestiging, reacties. P2 Hoog 2
Real-time WebSocket notificaties Laravel Echo + Soketi. Differentiator. P4 Differentiator 4
Cross-event crew pool + reliabilityscore Hergebruik medewerkers over events. P4 Differentiator 4
Globale zoekfunctie (cmd+K) Cross-entiteit zoeken: artiest, persoon, shift. P4 Differentiator 4
Crew PWA (mobiel) On-site zelfservice, shifts, briefing, clock-in. P4 Differentiator 4
Publieke REST API + webhooks Third-party integraties voor enterprise klanten. P4 Differentiator 4

7.2 Fasering

Fase 1 — Fundament (bouw eerst)

  • Laravel project setup: multi-tenant scoping, Sanctum auth, Spatie permissions.

  • Database migraties: alle kern-tabellen (users, organisations, events, crowd_types, festival_sections, time_slots, shifts).

  • API: auth endpoints, organisaties, evenementen, CRUD voor alle kern-entiteiten.

  • Vue frontend: login, organisatieswitcher, event overview, festival sections overzicht.

  • Aandacht-aware dashboard met event health widget.

Fase 2 — Kern Operaties (bouw tweede)

  • Personen & crowdlijsten (incl. externe lijsten).

  • Accreditatie engine: categorieën, items, toewijzingen.

  • Time Slots & Shifts: volledig planning model incl. Assign Shift panel.

  • Vrijwilligersregistratie: meerdelig formulier, beschikbaarheid, sectievoorkeuren.

  • Vrijwilligersportaal: My Shifts, Claim Shifts met conflictdetectie.

  • Briefing systeem: template builder, queue-based verzending.

Fase 3 — Advancing & Show Day (bouw derde)

  • Artist advancing: sectie-gebaseerd portaal, milestone pipeline, timetable.

  • Mission Control: check-in station, accreditatie uitgifte, artiest handling.

  • Formulierbouwer met conditionele logica en iframe embed.

  • Allocatiesheet PDF generator.

  • Communicatiecampagnes: email + SMS.

  • Leveranciersportal en productieverzoeken.

Fase 4 — Differentiators (bouw vierde)

  • Real-time WebSocket events.

  • Cross-event crew pool met betrouwbaarheidsscore.

  • Globale zoekfunctie.

  • Crew PWA.

  • Publieke REST API + webhook systeem.

  • CO2/duurzaamheidsrapportage.

8. Concurrentieanalyse Samenvatting

Wat te adopteren uit In2Event, Crescat en WeezCrew

8.1 In2Event — Adopteer

  • Dashboard 'Attention required' kolom met klikbare aantallen per crowd type.

  • Check-in modal: categorie-gegroepeerde items, 'Check all items' shortcut.

  • Accreditatie engine: onbeperkte categorieën incl. apparatuur en kleding.

  • Externe crowdlijst: deelbaar spreadsheet met accreditatie-items als bewerkbare kolommen.

  • Mission Control artist handling: live telling, per-stage running order met checkmarks.

  • Productieverzoek icon-kolommen: mensen/tech/stroom/accommodatie.

8.2 Crescat — Adopteer

  • Sectie-gebaseerd advance portaal: elke sectie onafhankelijk submitbaar.

  • Booking milestone pipeline bar: Offer In → Advance Received, klikbaar.

  • Productie-sectie: equipment toggles, riser afmetingen, document uploads.

  • Festival Sections architectuur: per sectie eigen Dashboard/Shifts/Scheduler/Crew/Volunteers tabs.

  • Time Slot + Shift tweelaags planningsmodel (zie sectie 4.3 en 3.4.2).

  • 'Slots open for claiming' (Crescat: 'For Sale'): zichtbaarheid per shift in vrijwilligersportaal.

  • Vrijwilligersregistratie: prioriteitsranking secties, admin-only velden, betaalstatus.

  • Artiest in volunteer portal: toon welke acts spelen tijdens shift.

  • Selectieve announce-zichtbaarheid per artiest in advance portaal.

8.3 WeezCrew — Adopteer

  • Fill-rate progress bars: groen/oranje/rood op alle capaciteitsweergaven.

  • Per-shift geo-locatie: adres, kaartpreview, routetekening.

  • Task manager rollen per shift: Editor vs. Read-only.

  • Formulierbouwer: drag-sorteerbaar, conditionele logica, live preview, iframe embed.

  • SMS + WhatsApp campagnes via Zender (zelf-gehoste instantie).

  • Branded allocatiesheet PDF: taak, beschrijving, kaart, QR-code, configureerbare opmaak.

  • Event health / pending tasks widget op dashboard.

  • Accreditatierapportage: staafdiagram per type + fill-rate lijsten.

  • 'Previously appointed' indicator bij recurrente shifts.

8.4 Differentiators — Geen van de drie heeft dit

  • Real-time WebSocket push notificaties voor alle kritieke statuswijzigingen.

  • Cross-event organisatie-niveau crew pool met betrouwbaarheidsscore en eenklik-heruitnodiging.

  • Globale zoekopdracht (cmd+K) over alle entiteitstypen.

  • Crew PWA: on-site zelfservice, shifts, briefing, clock-in, push notificaties.

  • Open publieke REST API + webhook systeem voor enterprise-integraties.

  • Fase-aware dashboard: CTA en context verandert per event lifecycle fase.

9. Cursor & Claude Code Instructies

Aanbevelingen voor AI-geassisteerde ontwikkeling

9.1 Workspace Rules (.cursorrules / CLAUDE.md)

Maak een CLAUDE.md bestand in de root van de repository met de volgende instructies voor Cursor en Claude Code:

CURSOR / CLAUDE CODE Stack: PHP 8.2 + Laravel 12, TypeScript + Vue 3 + Vuexy/Vuetify + Pinia. Backend: gebruik Resource Controllers, Form Requests voor validatie, API Resources voor responses. Multi-tenancy: alle queries scopen op organisation_id. Gebruik OrganisationScope Global Scope. Rollen: Spatie laravel-permission. Check rollen via $user->hasRole() en policies. Frontend: gebruik <script setup> + Composition API. Pinia voor state. TanStack Query voor API calls. Naamgeving: snake_case DB kolommen, camelCase JS variabelen, PascalCase Vue componenten. Testen: PHPUnit feature tests per API endpoint. Minimum: happy path + unauthorised test. UUIDs: gebruik op alle public-facing ID's (models + migrations).

9.2 Aanbevolen ontwikkelstrategie

  • Begin altijd met de database migratie en het Eloquent model voordat je de controller schrijft.

  • Schrijf API routes en Form Request validaties voor de business logica.

  • Gebruik Laravel API Resources voor consistente JSON responses. Nooit direct model attributes teruggeven.

  • Voor elke module: maak eerst een PHPUnit feature test die de API route test met een factory.

  • Vue componenten: begin met de pagina-shell, dan de API call via useQuery (TanStack), dan de UI.

  • Vuexy/Vuetify componenten altijd gebruiken voor custom CSS te schrijven.

  • Pinia stores per domein: useOrganisationStore, useEventStore, useShiftStore, etc.

9.3 Prioritaire bestanden om eerst te genereren

Vraag Cursor/Claude Code de volgende bestanden in volgorde te genereren voor Fase 1:

Bestand Inhoud
database/migrations/*_create_organisations_table.php Organisations + organisation_user pivot met role kolom.
database/migrations/*_create_events_table.php Events met alle kolommen uit sectie 3.4.1.
database/migrations/*_create_festival_sections_table.php Festival sections + time_slots + shifts + shift_assignments.
database/migrations/*_create_persons_table.php Persons + crowd_types + accreditation tabellen.
app/Models/Organisation.php Met OrganisationScope, hasMany relaties, Spatie Teamable trait.
app/Models/Event.php Met scope, lifecycle status enum, hasMany festival_sections.
app/Models/Shift.php Met time_slot relatie, fill_rate accessor, available_slots accessor.
app/Http/Controllers/Api/V1/ShiftController.php CRUD + assign actie. Gebruikt OrganisationPolicy.
app/Http/Requests/StoreShiftRequest.php Validatie: time_slot_id, slots_total, slots_open_for_claiming.
resources/js/stores/useShiftStore.ts Pinia store met TanStack Query voor shift data.
resources/js/pages/sections/[id]/shifts.vue Shift overzicht pagina met date-navigatie en shift lijst.

10. Out of Scope — Toekomstige Versies

Functionaliteit die bewust buiten de huidige ontwikkelscope valt

De volgende functionaliteiten zijn expliciet buiten scope voor versies 1.0 t/m 1.x. Ze zijn geïdentificeerd als potentiële toevoegingen voor toekomstige versies. Het datamodel houdt waar mogelijk rekening met hun toekomstige integratie (geen breaking changes nodig).

Feature Toelichting & toekomstige aanpak
Reisbeheer (vluchten, bustransport) Vluchten, bus- en treinreizen van artiesten en crew worden niet beheerd. Toekomstig: itinerary_items uitbreiden met type=flight|train|bus, vlucht- of treinnummer, vertrek-/aankomsttijden, airline/carrier.
Accommodatiebeheer (hotels, B&B) Hotelboekingen en kamerindelingen voor artiesten en crew zijn buiten scope. Toekomstig: accommodations tabel met type, locatie, incheck-/uitcheckdatum, kamerindeling, koppeling aan artiest of person.
Travel Party beheer Het beheren van de volledige tourgroep als eenheid voor accreditatie is buiten scope. Toekomstig: travel_party tabel als groep gelinkt aan artiest, met eigen accreditatielimiet.
CO2 / Duurzaamheidsrapportage Emissieberekeningen op basis van transport en energieverbruik zijn buiten scope voor v1. Toekomstig: CO2-berekening per transport-type op basis van afstand en voertuigtype.
Native mobiele apps (iOS / Android) Dedicated native apps voor scanning en crew zelfservice zijn buiten scope. Toekomstig: React Native of Flutter app, of uitbreiding van de PWA met native plugins.
Companion app voor VIP gasten Mobiele app voor VIP gasten om evenementinfo te bekijken en te netwerken is buiten scope voor v1.
Kaartverkoop / Ticketing eigen systeem Crewli beheert accreditaties en e-tickets voor niet-betalende deelnemers. Het verkopen van publieke tickets is buiten scope. Integratie met externe ticketingpartners (Eventix, See Tickets) via API is wel gepland (fase 4).
ONTWERPPRINCIPE Tabellen en velden die gerelateerd zijn aan Out of Scope features worden NIET aangemaakt in de database. Placeholders of lege JSON velden worden ook niet ingebouwd. Wanneer een feature in scope komt, worden nieuwe migraties geschreven.

11. Begrippenlijst

Alle termen gebruikt in dit document en de codebase

Term Definitie
Accreditation Een recht of artikel toegekend aan een persoon: toegangszones, te ontvangen items (polsband, maaltijd, portofoon).
Admin-only field Formulierveld dat alleen voor de organisator zichtbaar is, niet voor de externe indiener.
Advance / Advancing Het verzamelen en bevestigen van alle artistenvereisten voor het evenement via een sectie-gebaseerd portaal.
Allocation sheet Gepersonaliseerd PDF-document per crew/vrijwilliger met taakomschrijving, tijden, locatiekaart en QR-code.
B2B Performance Twee artiesten die tegelijk optreden op hetzelfde podium in hetzelfde tijdslot.
Briefing Gepersonaliseerde e-mailcommunicatie naar eventdeelnemers met e-tickets, informatie en taakbeschrijving.
Claim (shift) Vrijwilliger reserveert zelf een open shift via het zelfserviceportaal.
Crowd Type Classificatie van deelnemers: Crew, Volunteer, Artist, Guest, Press, Partner, Supplier.
Crowd List Gestructureerde lijst van personen. Intern (org-gevuld) of extern (partner-gevuld).
E-ticket Digitaal ticket gegenereerd voor goedgekeurde personen met accreditatiebarcodes.
Event Health Dashboard widget die configuratieproblemen en openstaande acties toont als klikbare items.
Festival Section Operationele eenheid binnen een event: Bar, Hospitality, Technical, Security, etc.
Fill rate Percentage gevulde slots ten opzichte van het totaal voor een shift of accreditatieitem.
For Sale (Crescat) Aantal shift-slots claimbaar via vrijwilligersportaal. In Crewli: 'slots_open_for_claiming'.
Infosheet Gegevensverzamelformulier gestuurd naar artistcontacten voor het ophalen van rider- en logistieke info.
Inventory Item Fysiek item gevolgd op locatie: portofoon, hesje, sleutel, wristband printer.
Itinerary Volledige dagsplanning van een artiest: vluchten, transfers, optredens, activiteiten.
Milestone pipeline Visuele voortgangsbalk met fases in de boekingslevenscyclus (Crescat patroon).
Mission Control Real-time operationele hub op show-dag voor check-in en tracking.
Open for Claiming Aantal shift-slots dat zichtbaar en claimbaar is in het vrijwilligersportaal.
Organisation Een klant/organisatie die Crewli gebruikt. Bevat meerdere evenementen.
Person Individuele deelnemer gekoppeld aan een event via een Crowd Type.
Production Request Digitaal intakeformulier gestuurd naar leveranciers voor het verzamelen van logistieke vereisten.
Rider Lijst van technische en hospitality vereisten van een artiest.
RSVP Bevestigingsstroom waarbij deelnemers een uitnodiging accepteren of weigeren.
Scanner Geconfigureerd scanstation (browser of hardware) gekoppeld aan specifieke crowd types/zones.
Segment Onderverdeling van een crowd type voor granulaire communicatie en rapportage.
Shift Sectie-specifieke toewijzing binnen een Time Slot. Koppelt sectie + tijdvenster + capaciteit.
Slot (capacity) Het aantal personen dat een Shift kan bevatten.
Stage Podiumnaam binnen een event. Heeft performances op specifieke dagen.
Time Slot Event-niveau tijdvenster met persoonscategorie (CREW/VOLUNTEER/PRESS). Aangemaakt per event.
Timetable Visueel drag-and-drop schema met podiums als rijen en tijd op de x-as.
Travel Party De volledige tourgroep van een artiest als een eenheid voor accreditatie.
Zone / Access Zone Gedefinieerd gebiedsonderdeel van het terrein met specifieke toegangspermissies.
Ambassador badge Intern badge voor vrijwilligers die als eerste reageren op uitnodigingen en altijd aanwezig zijn. Nooit zichtbaar voor de vrijwilliger zelf.
Festival-paspoort Visuele tijdlijn in het vrijwilligersprofiel van alle festivals waaraan iemand heeft deelgenomen, met uren en secties.
No-show alert Automatisch WhatsApp-bericht 30 minuten voor shiftstart aan niet-ingecheckte vrijwilligers.
Open swap Dienstwisselverzoek waarbij de vrijwilliger zich afmeldt en de shift vrijkomt voor de wachtlijst of opnieuw voor claiming.
Persoonlijke swap Dienstwisselverzoek waarbij vrijwilliger A een specifieke collega B vraagt om te ruilen. Vereist akkoord van B en bevestiging coordinator.
Reliability score Berekende score (0.05.0) per vrijwilliger op basis van no-shows, tijdigheid check-in en coordinator-ratings over alle festivals.
Retrospectief rapport Automatisch gegenereerd rapport na afloop van een festival: bezetting vs. gepland, no-show percentage, tevredenheid, aanbevelingen voor volgend jaar.
Show Day Mode Radicaal vereenvoudigde, mobiel-first interface die automatisch activeert op de event-datum. Grote tegels, hoog contrast, minimale navigatie.
Urgentieniveau Classificatie van een bericht: Normaal (e-mail), Dringend (WhatsApp), Noodgeval (SMS + WhatsApp). Bepaalt welk kanaal Zender gebruikt.
Wachtlijst (shift) Lijst van vrijwilligers die zich aangemeld hebben voor een volle shift. Bij uitval worden ze automatisch op volgorde aangeschreven.
Zender Zelf-gehoste multi-channel communicatieplatform (codecanyon) dat Android-apparaten gebruikt als SMS en WhatsApp gateway. Aangestuurd via HTTP API.

Crewli Product Design & Technical Specification — Vertrouwelijk

Versie 1.3 — Maart 2026 — Gebaseerd op analyse van In2Event, Crescat & WeezCrew