docs: design-document v1.8, dev-docs restructure, VitePress user docs scaffold, backlog update
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
165
dev-docs/API.md
Normal file
165
dev-docs/API.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Crewli API Contract
|
||||
|
||||
Base path: `/api/v1/`
|
||||
|
||||
Auth: Bearer token (Sanctum)
|
||||
|
||||
## Auth
|
||||
|
||||
- `POST /auth/login`
|
||||
- `POST /auth/logout`
|
||||
- `GET /auth/me`
|
||||
|
||||
## Organisations
|
||||
|
||||
- `GET /organisations` — list (super admin)
|
||||
- `POST /organisations` — create
|
||||
- `GET /organisations/{org}` — show
|
||||
- `PUT /organisations/{org}` — update
|
||||
- `GET /organisations/{org}/members` — members
|
||||
- `POST /organisations/{org}/invite` — invite user
|
||||
|
||||
## Events
|
||||
|
||||
- `GET /organisations/{org}/events` — list (top-level only by default)
|
||||
- `GET /organisations/{org}/events?include_children=true` — include sub-events nested in response
|
||||
- `GET /organisations/{org}/events?type=festival` — filter by event_type (festival|series|event)
|
||||
- `POST /organisations/{org}/events` — create (supports `parent_event_id` for sub-events)
|
||||
- `GET /organisations/{org}/events/{event}` — detail (includes children and parent if loaded)
|
||||
- `PUT /organisations/{org}/events/{event}` — update (does NOT accept `status` — use transition endpoint)
|
||||
- `POST /organisations/{org}/events/{event}/transition` — change event status via state machine (see below)
|
||||
- `GET /organisations/{org}/events/{event}/children` — list sub-events of a festival/series
|
||||
|
||||
### Event Status Transitions
|
||||
|
||||
`POST /organisations/{org}/events/{event}/transition`
|
||||
|
||||
Body: `{ "status": "published" }`
|
||||
|
||||
Enforces a state machine: only valid forward (and select backward) transitions are allowed.
|
||||
Returns 422 with `errors`, `current_status`, `requested_status`, and `allowed_transitions` when the transition is invalid or prerequisites are missing.
|
||||
|
||||
**Prerequisites checked:**
|
||||
- `→ published`: name, start_date, end_date required
|
||||
- `→ registration_open`: at least one time slot and one section required
|
||||
|
||||
**Festival cascade:** Transitioning a festival parent to `showday`, `teardown`, or `closed` automatically cascades to all children in an earlier status.
|
||||
|
||||
**EventResource** includes `allowed_transitions` (array of valid next statuses) so the frontend knows which buttons to show.
|
||||
|
||||
## Crowd Types
|
||||
|
||||
- `GET /organisations/{org}/crowd-types`
|
||||
- `POST /organisations/{org}/crowd-types`
|
||||
- `PUT /organisations/{org}/crowd-types/{type}`
|
||||
- `DELETE /organisations/{org}/crowd-types/{type}`
|
||||
|
||||
## Companies
|
||||
|
||||
- `GET /organisations/{org}/companies`
|
||||
- `POST /organisations/{org}/companies`
|
||||
- `PUT /organisations/{org}/companies/{company}`
|
||||
- `DELETE /organisations/{org}/companies/{company}`
|
||||
|
||||
## Section Categories
|
||||
|
||||
- `GET /organisations/{org}/section-categories` — distinct categories used across the organisation's events (for autocomplete). Returns `{ "data": ["Bar", "Podium", ...] }`
|
||||
|
||||
## Festival Sections
|
||||
|
||||
- `GET /events/{event}/sections`
|
||||
- `POST /events/{event}/sections`
|
||||
- `PUT /events/{event}/sections/{section}`
|
||||
- `DELETE /events/{event}/sections/{section}`
|
||||
- `POST /events/{event}/sections/reorder`
|
||||
|
||||
> **Festival context:** `{event}` can be a festival parent or a sub-event.
|
||||
> On a festival parent, sections are for operational planning (build-up, teardown).
|
||||
> For sub-events, `GET` automatically includes `cross_event` sections from the parent festival.
|
||||
> Shifts on cross_event sections must use the **parent festival's event_id** in API calls,
|
||||
> since the section's `event_id` points to the parent.
|
||||
|
||||
## Time Slots
|
||||
|
||||
- `GET /events/{event}/time-slots`
|
||||
- `POST /events/{event}/time-slots`
|
||||
- `PUT /events/{event}/time-slots/{timeSlot}`
|
||||
- `DELETE /events/{event}/time-slots/{timeSlot}`
|
||||
|
||||
> **Festival context:** `{event}` can be a festival parent or a sub-event.
|
||||
> Festival-level time slots (operational: build-up, teardown, transitions) are separate
|
||||
> from sub-event time slots (program-specific).
|
||||
>
|
||||
> `GET /events/{event}/time-slots` returns only the specified event's own time slots by
|
||||
> default. For sub-events, pass `?include_parent=true` to also include the parent festival's
|
||||
> time slots — each time slot is marked with a `source` field (`sub_event` or `festival`)
|
||||
> and includes `event_name` for display grouping. This parameter has no effect on festivals
|
||||
> or flat events.
|
||||
>
|
||||
> Shifts on sub-event sections may reference parent festival time slots (e.g. for build-up
|
||||
> shifts). The `time_slot_id` validation accepts time slots from the sub-event itself or
|
||||
> its parent festival.
|
||||
|
||||
## Shifts
|
||||
|
||||
- `GET /events/{event}/sections/{section}/shifts`
|
||||
- `POST /events/{event}/sections/{section}/shifts`
|
||||
- `PUT /events/{event}/sections/{section}/shifts/{shift}`
|
||||
- `DELETE /events/{event}/sections/{section}/shifts/{shift}`
|
||||
- `POST /events/{event}/sections/{section}/shifts/{shift}/assign`
|
||||
- `POST /events/{event}/sections/{section}/shifts/{shift}/claim`
|
||||
|
||||
> **Festival context:** When managing shifts on a `cross_event` section, the `{event}`
|
||||
> in the URL must be the parent festival's ID (matching `section.event_id`), not the
|
||||
> sub-event's ID.
|
||||
|
||||
## Persons
|
||||
|
||||
- `GET /events/{event}/persons`
|
||||
- `POST /events/{event}/persons`
|
||||
- `GET /events/{event}/persons/{person}`
|
||||
- `PUT /events/{event}/persons/{person}`
|
||||
- `POST /events/{event}/persons/{person}/approve`
|
||||
- `DELETE /events/{event}/persons/{person}`
|
||||
|
||||
## Crowd Lists
|
||||
|
||||
- `GET /events/{event}/crowd-lists`
|
||||
- `POST /events/{event}/crowd-lists`
|
||||
- `PUT /events/{event}/crowd-lists/{list}`
|
||||
- `DELETE /events/{event}/crowd-lists/{list}`
|
||||
- `POST /events/{event}/crowd-lists/{list}/persons`
|
||||
- `DELETE /events/{event}/crowd-lists/{list}/persons/{person}`
|
||||
|
||||
## Locations
|
||||
|
||||
- `GET /events/{event}/locations`
|
||||
- `POST /events/{event}/locations`
|
||||
- `PUT /events/{event}/locations/{location}`
|
||||
- `DELETE /events/{event}/locations/{location}`
|
||||
|
||||
## Person Tags (Organisation Settings)
|
||||
|
||||
- `GET /organisations/{org}/person-tags` — list active tags (ordered)
|
||||
- `POST /organisations/{org}/person-tags` — create tag
|
||||
- `PUT /organisations/{org}/person-tags/{tag}` — update tag
|
||||
- `DELETE /organisations/{org}/person-tags/{tag}` — deactivate tag (soft: sets `is_active = false`)
|
||||
- `GET /organisations/{org}/person-tag-categories` — distinct categories for autocomplete
|
||||
|
||||
## User Tag Assignments
|
||||
|
||||
- `GET /organisations/{org}/users/{user}/tags` — list all tags for user in organisation
|
||||
- `POST /organisations/{org}/users/{user}/tags` — assign a tag
|
||||
- `DELETE /organisations/{org}/users/{user}/tags/{tagAssignment}` — remove assignment
|
||||
- `PUT /organisations/{org}/users/{user}/tags/sync` — sync tags by source
|
||||
|
||||
> **Sync endpoint:** Replaces tags of the specified `source` only.
|
||||
> Body: `{ "tag_ids": ["ulid1", "ulid2"], "source": "self_reported" }`
|
||||
> Removes `self_reported` tags not in the list, adds new ones, leaves `organiser_assigned` untouched (and vice versa).
|
||||
|
||||
### Person list tag filtering
|
||||
|
||||
- `GET /events/{event}/persons?tag={person_tag_id}` — filter persons by single tag
|
||||
- `GET /events/{event}/persons?tags=ulid1,ulid2` — filter persons by multiple tags (AND logic: must have all)
|
||||
|
||||
_(Extend this contract per module as endpoints are implemented.)_
|
||||
485
dev-docs/BACKLOG.md
Normal file
485
dev-docs/BACKLOG.md
Normal file
@@ -0,0 +1,485 @@
|
||||
# Crewli — Product Backlog
|
||||
|
||||
> Gedocumenteerde wensen en features die bewust zijn uitgesteld.
|
||||
> Bijgewerkt: April 2026
|
||||
>
|
||||
> **Gebruik:** Voeg nieuwe items toe als ze tijdens development ontstaan.
|
||||
> Geef elk item een prioriteit en fase zodra je het gaat oppakken.
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Geplande features
|
||||
|
||||
### ARCH-01 — Recurrence / Terugkerende events
|
||||
|
||||
**Aanleiding:** Schaatsbaan use case — 8 weken, elke za+zo openingsdagen.
|
||||
**Wat:** Organisator definieert één template sub-event met RRULE.
|
||||
Platform genereert automatisch alle instanties.
|
||||
**Details:**
|
||||
|
||||
- RRULE formaat (RFC 5545): `FREQ=WEEKLY;BYDAY=SA,SU;UNTIL=20270126`
|
||||
- `events.recurrence_rule` (string nullable) — al gereserveerd in schema
|
||||
- `events.recurrence_exceptions` (JSON) — cancelled + modified dates
|
||||
- UI: "Genereer openingsdagen" wizard
|
||||
- Aanpassen van één instantie raakt template niet
|
||||
- "Alleen deze dag" / "Alle volgende dagen" / "Alle dagen" (Google Calendar patroon)
|
||||
**Schema:** Kolommen al aanwezig in v1.7. Alleen generator-logica ontbreekt.
|
||||
|
||||
---
|
||||
|
||||
### ARCH-02 — Min/max shifts per vrijwilliger per serie
|
||||
|
||||
**Aanleiding:** Schaatsbaan — eerlijke verdeling, minimum commitment.
|
||||
**Wat:** Per festival/serie instelbaar minimum en maximum aantal shifts
|
||||
dat een vrijwilliger kan claimen.
|
||||
**Details:**
|
||||
|
||||
- `festivals.min_shifts_per_volunteer` (int nullable)
|
||||
- `festivals.max_shifts_per_volunteer` (int nullable)
|
||||
- Portal toont voortgang: "Jij hebt 2 van minimaal 4 shifts geclaimd"
|
||||
- Bij bereiken maximum: verdere claims geblokkeerd
|
||||
**Afhankelijk van:** ARCH-01 (recurrence), Portal self-service
|
||||
|
||||
---
|
||||
|
||||
### ARCH-03 — Sectie templates / kopiëren van vorig event
|
||||
|
||||
**Aanleiding:** Organisatoren die elk jaar dezelfde secties en shifts opzetten.
|
||||
**Wat:** "Kopieer secties van vorig festival" functie in de UI.
|
||||
Kopieert festival_sections + shifts structuur (zonder toewijzingen).
|
||||
**Details:**
|
||||
|
||||
- UI: dropdown "Kopieer structuur van..." bij aanmaken festival
|
||||
- Optie: kopieer alleen secties / secties + shifts / alles
|
||||
- Tijden worden proportioneel aangepast aan nieuwe datums
|
||||
**Prioriteit:** Hoog — bespaart veel handmatig werk bij terugkerende festivals
|
||||
|
||||
---
|
||||
|
||||
### ARCH-04 — Cross-festival conflictdetectie
|
||||
|
||||
**Aanleiding:** Vrijwilliger die bij twee festivals van dezelfde organisatie
|
||||
op dezelfde dag ingepland staat.
|
||||
**Wat:** Waarschuwing (geen blokkade) als iemand al actief is op een
|
||||
ander festival van dezelfde organisatie op dezelfde datum.
|
||||
**Details:**
|
||||
|
||||
- Soft check — waarschuwing tonen, niet blokkeren
|
||||
- Relevant bij organisaties met meerdere festivals tegelijk
|
||||
- Query: `shift_assignments` cross-festival op person_id + datum
|
||||
|
||||
---
|
||||
|
||||
### ARCH-05 — Shift fairness / prioriteitswachtrij
|
||||
|
||||
**Aanleiding:** Populaire shifts worden direct volgeboekt door snelle vrijwilligers.
|
||||
**Wat:** Optionele wachtrij-modus waarbij het systeem eerlijk verdeelt
|
||||
op basis van: reliability score, aantal uren al ingepland, aanmeldvolgorde.
|
||||
**Details:**
|
||||
|
||||
- `shifts.assignment_mode` (enum: first_come | fair_queue | manual)
|
||||
- Fair queue: systeem wijst toe op basis van algoritme
|
||||
- Organisator keurt resultaat goed voor publicatie
|
||||
**Prioriteit:** Middel — nice-to-have voor grote festivals
|
||||
|
||||
---
|
||||
|
||||
### ARCH-06 — Locatie-gebaseerd shift-overzicht
|
||||
|
||||
Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie
|
||||
ongeacht programmaonderdeel.
|
||||
**Schema:** `locations` tabel en `shifts.location_id` bestaan al.
|
||||
**Prioriteit:** Laag
|
||||
|
||||
---
|
||||
|
||||
### ARCH-07 — Accreditatie-templates per sectie/dag combinatie
|
||||
|
||||
MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode.
|
||||
Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items.
|
||||
Handmatige per-persoon toewijzing is de uitzondering, niet de norm.
|
||||
**Schema:** Nieuwe tabel `accreditation_templates` nodig.
|
||||
**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Communicatie & Notificaties
|
||||
|
||||
### COMM-01 — Real-time WebSocket notificaties
|
||||
|
||||
**Aanleiding:** Differentiator — geen van de concurrenten heeft dit.
|
||||
**Wat:** Push notificaties via Laravel Echo + Soketi voor:
|
||||
|
||||
- Nieuwe vrijwilliger aanmelding
|
||||
- Shift geclaimd
|
||||
- Uitnodiging geaccepteerd
|
||||
- Shift niet gevuld (waarschuwing)
|
||||
- No-show alert op show-dag
|
||||
**Tech:** Laravel Echo + Soketi (zelf-gehoste WebSocket server)
|
||||
**Frontend:** Notificatie bell in topbar activeren
|
||||
|
||||
---
|
||||
|
||||
### COMM-02 — Topbar volledig activeren
|
||||
|
||||
**Aanleiding:** Vuexy topbar staat er maar is niet aangesloten op Crewli.
|
||||
**Wat:**
|
||||
|
||||
- Zoekbalk (CTRL+K) aansluiten op Crewli-entiteiten
|
||||
(personen, events, secties zoeken)
|
||||
- Notificatie bell koppelen aan COMM-01
|
||||
- App switcher: Organizer / Admin / Portal wisselen
|
||||
- User avatar: gekoppeld aan ingelogde gebruiker (deels al gedaan)
|
||||
**Prioriteit:** Middel — werkt zonder maar verbetert UX significant
|
||||
|
||||
---
|
||||
|
||||
### COMM-03 — Globale zoekfunctie (cmd+K)
|
||||
|
||||
**Aanleiding:** Differentiator — cross-entiteit zoeken.
|
||||
**Wat:** Modal zoekbalk die zoekt over:
|
||||
personen, events, artiesten, secties, shifts
|
||||
**Tech:** Meilisearch of database full-text search
|
||||
**Prioriteit:** Laag — Fase 4
|
||||
|
||||
---
|
||||
|
||||
### COMM-04 — SMS + WhatsApp campagnes via Zender
|
||||
|
||||
**Aanleiding:** WeezCrew heeft dit als sterk punt.
|
||||
**Wat:** Bulk communicatie via Zender (zelf-gehoste SMS/WhatsApp gateway)
|
||||
|
||||
- Normal urgency → email
|
||||
- Urgent → WhatsApp
|
||||
- Emergency → SMS + WhatsApp parallel
|
||||
**Tech:** ZenderService (al gedocumenteerd in dev guide)
|
||||
**Afhankelijk van:** Communicatie module backend
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Show Day & Operationeel
|
||||
|
||||
### OPS-01 — Mission Control
|
||||
|
||||
**Aanleiding:** In2Event's sterkste feature.
|
||||
**Wat:** Real-time operationele hub op show-dag:
|
||||
|
||||
- Live check-in overzicht per sectie
|
||||
- Artiest handling (aankomst, soundcheck, performance status)
|
||||
- No-show alerts met automatische opvolging
|
||||
- Inventaris uitgifte (portofoons, hesjes)
|
||||
**Prioriteit:** Hoog voor show-dag gebruik
|
||||
|
||||
---
|
||||
|
||||
### OPS-02 — No-show automatisering
|
||||
|
||||
**Aanleiding:** 30-minuten alert voor niet-ingecheckte vrijwilligers.
|
||||
**Wat:** Automatische WhatsApp/SMS via Zender als vrijwilliger
|
||||
niet is ingecheckt 30 min na shift-starttijd.
|
||||
**Schema:** `show_day_absence_alerts` al aanwezig ✅
|
||||
**Afhankelijk van:** COMM-04 (Zender), OPS-01 (Mission Control)
|
||||
|
||||
---
|
||||
|
||||
### OPS-03 — Allocatiesheet PDF generator
|
||||
|
||||
**Aanleiding:** WeezCrew heeft branded PDF per crew.
|
||||
**Wat:** Gepersonaliseerde PDF per vrijwilliger/crew:
|
||||
taakbeschrijving, tijden, locatie, QR-code voor check-in.
|
||||
**Tech:** DomPDF (al geïnstalleerd)
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
### OPS-04 — Scanner infrastructuur
|
||||
|
||||
**Aanleiding:** QR check-in op locatie.
|
||||
**Wat:** Scanstations configureren, koppelen aan hardware.
|
||||
`scanners` tabel al aanwezig in schema ✅
|
||||
**Prioriteit:** Laag — Fase 4
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Vrijwilligers & Portal
|
||||
|
||||
### VOL-01 — apps/portal/ vrijwilliger self-service
|
||||
|
||||
**Aanleiding:** Vrijwilligers moeten zichzelf kunnen aanmelden en
|
||||
shifts claimen zonder toegang tot de Organizer app.
|
||||
**Wat:**
|
||||
|
||||
- Publiek registratieformulier (multi-step)
|
||||
- Login portal voor vrijwilligers
|
||||
- Beschikbaarheid opgeven (time slots kiezen)
|
||||
- My Shifts overzicht
|
||||
- Shift claimen met conflictdetectie
|
||||
- "Ik kan toch niet komen" workflow
|
||||
**Afhankelijk van:** Sections + Shifts backend (al klaar ✅)
|
||||
|
||||
---
|
||||
|
||||
### VOL-02 — Vrijwilliger paspoort + reliability score
|
||||
|
||||
**Aanleiding:** Platform-breed profiel dat accumuleert over jaren.
|
||||
**Wat:**
|
||||
|
||||
- Festival-paspoort: visuele tijdlijn van deelgenomen festivals
|
||||
- Reliability score (0.0-5.0): berekend via scheduled job
|
||||
- Coordinator-beoordeling per festival (intern, nooit zichtbaar)
|
||||
- "Would reinvite" indicator bij heruitnodiging
|
||||
**Schema:** `volunteer_profiles`, `volunteer_festival_history` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### VOL-03 — Post-festival evaluatie + retrospectief
|
||||
|
||||
**Aanleiding:** Automatische feedback na het festival.
|
||||
**Wat:**
|
||||
|
||||
- 24u na laatste shift: evaluatiemail naar vrijwilligers
|
||||
- Max 5 vragen (beleving, shift kwaliteit, terugkomen?)
|
||||
- Gegenereerd retrospectief rapport per festival
|
||||
- Coordinator-beoordeling parallel (intern)
|
||||
**Schema:** `post_festival_evaluations`, `festival_retrospectives` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### VOL-04 — Shift swap workflow (portal)
|
||||
|
||||
**Aanleiding:** Vrijwilliger wil shift ruilen met collega.
|
||||
**Wat:**
|
||||
|
||||
- Open swap: iedereen mag reageren
|
||||
- Persoonlijke swap: specifieke collega vragen
|
||||
- Na akkoord beide: coordinator bevestigt (of auto-approve)
|
||||
- Wachtlijst: bij uitval automatisch aanschrijven
|
||||
**Schema:** `shift_swap_requests`, `shift_absences`, `shift_waitlist` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Artiesten & Advancing
|
||||
|
||||
### ART-01 — Artist advancing portal (apps/portal/)
|
||||
|
||||
**Aanleiding:** Crescat's sterkste feature.
|
||||
**Wat:**
|
||||
|
||||
- Sectie-gebaseerd advance portal via gesignde URL
|
||||
- Per sectie onafhankelijk submitbaar (Guest List, Contacts, Production)
|
||||
- Milestone pipeline: Offer In → Advance Received
|
||||
- Per-artiest zichtbaarheidscontrole van advance secties
|
||||
- Submission diff tracking (created/updated/untouched/deleted)
|
||||
**Schema:** `advance_sections`, `advance_submissions` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### ART-02 — Timetable (stage + drag-drop)
|
||||
|
||||
**Aanleiding:** FullCalendar timeline view voor podia-planning.
|
||||
**Wat:**
|
||||
|
||||
- Timeline view per podium
|
||||
- Drag-and-drop performances
|
||||
- B2B detectie (twee artiesten op zelfde podium zelfde tijd)
|
||||
**Tech:** FullCalendar (al in stack ✅)
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Formulieren & Leveranciers
|
||||
|
||||
### FORM-01 — Formulierbouwer
|
||||
|
||||
**Aanleiding:** WeezCrew heeft een krachtige drag-sorteerbare builder.
|
||||
**Wat:**
|
||||
|
||||
- Drag-sorteerbaar, conditionele logica
|
||||
- Live preview
|
||||
- Iframe embed voor externe websites
|
||||
- Configureerbare velden per crowd type
|
||||
**Schema:** `public_forms` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### SUP-01 — Leveranciersportal + productieverzoeken
|
||||
|
||||
**Aanleiding:** Leveranciers moeten productie-informatie kunnen indienen.
|
||||
**Wat:**
|
||||
|
||||
- Token-gebaseerde portal toegang (geen account nodig)
|
||||
- Productieverzoek indienen (mensen, tech, stroom, voertuigen)
|
||||
- Crowd list indienen voor hun crew
|
||||
**Schema:** `production_requests`, `material_requests` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
## Fase 4 — Differentiators
|
||||
|
||||
### DIFF-01 — Cross-event crew pool + reliability score
|
||||
|
||||
**Aanleiding:** Vrijwilligers hergebruiken over events van dezelfde organisatie.
|
||||
**Wat:** Eén klik heruitnodiging op basis van vorig jaar.
|
||||
Reliability score zichtbaar naast naam in de lijst.
|
||||
|
||||
---
|
||||
|
||||
### DIFF-02 — Crew PWA (mobiel)
|
||||
|
||||
**Aanleiding:** On-site zelfservice voor crew op hun telefoon.
|
||||
**Wat:** Progressive Web App voor:
|
||||
shifts bekijken, briefing lezen, clock-in, push notificaties.
|
||||
|
||||
---
|
||||
|
||||
### DIFF-03 — Publieke REST API + webhooks
|
||||
|
||||
**Aanleiding:** Enterprise integraties.
|
||||
**Wat:** Gedocumenteerde publieke API + webhook systeem
|
||||
voor third-party integraties (ticketing, HR, etc.)
|
||||
|
||||
---
|
||||
|
||||
### DIFF-04 — CO2 / Duurzaamheidsrapportage
|
||||
|
||||
**Aanleiding:** Toenemende focus op duurzame events.
|
||||
**Wat:** Emissieberekeningen op basis van transport en energieverbruik.
|
||||
**Status:** Expliciet out of scope voor v1.x
|
||||
|
||||
---
|
||||
|
||||
## Apps & Platforms
|
||||
|
||||
### APPS-01 — apps/admin/ volledig bouwen
|
||||
|
||||
**Aanleiding:** Super Admin panel voor platform-beheer.
|
||||
**Wat:**
|
||||
|
||||
- Alle organisaties beheren
|
||||
- Billing status wijzigen
|
||||
- Platform-gebruikers beheren
|
||||
- Usage statistieken
|
||||
|
||||
---
|
||||
|
||||
### APPS-02 — OrganisationSwitcher ingeklapte staat fix
|
||||
|
||||
**Aanleiding:** Flikkering/hover-bug bij ingeklapte sidebar.
|
||||
**Wat:** Correcte weergave en animatie in ingeklapte staat.
|
||||
**Prioriteit:** Low — cosmetisch, werkt functioneel wel
|
||||
|
||||
---
|
||||
|
||||
## Technische schuld
|
||||
|
||||
### TECH-01 — Bestaande tests bijwerken na festival/event refactor
|
||||
|
||||
**Aanleiding:** Na toevoegen parent_event_id worden bestaande tests
|
||||
mogelijk fragiel door gewijzigde factory-setup.
|
||||
**Wat:** Alle Feature tests reviewen en bijwerken waar nodig.
|
||||
|
||||
---
|
||||
|
||||
### TECH-02 — scopeForFestival helper op Event model
|
||||
|
||||
**Aanleiding:** Queries die door parent/child heen moeten werken.
|
||||
**Wat:** `Event::scopeWithChildren()` en `Event::scopeForFestival()`
|
||||
helper scopes zodat queries automatisch parent + children bevatten.
|
||||
|
||||
---
|
||||
|
||||
### TECH-03 — DevSeeder uitbreiden met festival-structuur
|
||||
|
||||
**Aanleiding:** Na festival/event refactor heeft de DevSeeder
|
||||
realistische testdata nodig met parent/child events.
|
||||
**Wat:** DevSeeder aanpassen met:
|
||||
|
||||
- Test festival (parent)
|
||||
- 2-3 sub-events (children)
|
||||
- Personen op festival-niveau
|
||||
|
||||
---
|
||||
|
||||
### ~~TECH-04 — EventController.store() redundante ternary~~ ✅ OPGELOST
|
||||
|
||||
---
|
||||
|
||||
## Opgeloste items (april 2026)
|
||||
|
||||
De volgende items zijn geïmplementeerd en afgerond:
|
||||
|
||||
- ~~TECH-04: EventController.store() redundante ternary~~ ✅
|
||||
- ~~Auth race condition (CTRL+R fix)~~ ✅
|
||||
- ~~Section edit dialog bug~~ ✅
|
||||
- ~~Time slot duplicate button~~ ✅
|
||||
- ~~Browser autocomplete disabled op dialog form fields~~ ✅
|
||||
- ~~Category + icon fields op festival_sections~~ ✅
|
||||
- ~~IconPicker component~~ ✅
|
||||
- ~~Crowd Types beheer-UI~~ ✅
|
||||
- ~~Companies CRUD~~ ✅
|
||||
- ~~Person tags backend (person_tags + user_organisation_tags)~~ ✅
|
||||
- ~~Event status state machine (dedicated transition endpoint, prerequisites, festival cascade)~~ ✅
|
||||
- ~~Festival tab-navigatie (uniform tabs, Programmaonderdelen tab)~~ ✅
|
||||
- ~~SectionsShiftsPanel extractie als herbruikbaar component~~ ✅
|
||||
- ~~Cross-event section auto-redirect~~ ✅
|
||||
|
||||
---
|
||||
|
||||
## Nieuwe backlog items
|
||||
|
||||
### ARCH-06 — Locatie-gebaseerd shift-overzicht
|
||||
|
||||
Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie
|
||||
ongeacht programmaonderdeel.
|
||||
**Prioriteit:** Laag
|
||||
|
||||
---
|
||||
|
||||
### ARCH-07 — Accreditatie-templates per sectie/dag combinatie
|
||||
|
||||
MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode.
|
||||
Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items.
|
||||
**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module
|
||||
|
||||
---
|
||||
|
||||
### ARCH-08 — Recurrence voor time slots
|
||||
|
||||
Herhalingsfunctie: "genereer 5 time slots in één keer" voor opbouwdagen etc.
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
### ART-03 — Artist profile met cross-event rider defaults
|
||||
|
||||
Organisatie-niveau artiest-profiel dat rider-defaults, contacten en interne
|
||||
notities opslaat over events heen. "Importeer van vorig jaar" functie.
|
||||
**Prioriteit:** Laag
|
||||
|
||||
---
|
||||
|
||||
### UX-01 — Festival setup checklist / onboarding wizard
|
||||
|
||||
Checklist widget op festival dashboard die door de configuratiestappen leidt.
|
||||
Items worden groen als ze zijn afgerond.
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
### UX-02 — Aandachtsmatrix dashboard
|
||||
|
||||
Dashboard widget: hoeveel personen approved maar zonder shift? Hoeveel
|
||||
shift-claims wachten op goedkeuring? Hoeveel pending identity matches?
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
### UX-03 — Personen-tab op sub-event niveau
|
||||
|
||||
Gefilterde view: alleen personen met shifts in dit programmaonderdeel.
|
||||
Met link "Bekijk alle personen op festival-niveau".
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
_Laatste update: April 2026_
|
||||
_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-, UX-_
|
||||
1491
dev-docs/SCHEMA.md
Normal file
1491
dev-docs/SCHEMA.md
Normal file
File diff suppressed because it is too large
Load Diff
286
dev-docs/SETUP.md
Normal file
286
dev-docs/SETUP.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Crewli - Setup Guide
|
||||
|
||||
This guide walks you through setting up the Crewli project from scratch.
|
||||
|
||||
## Cursor AI Configuration
|
||||
|
||||
The project includes comprehensive AI instructions:
|
||||
|
||||
```
|
||||
.cursor/
|
||||
├── instructions.md # Quick start and common prompts
|
||||
├── ARCHITECTURE.md # System design and database schema
|
||||
└── rules/
|
||||
├── 001_workspace.mdc # Project structure and conventions
|
||||
├── 100_laravel.mdc # Laravel API patterns
|
||||
├── 101_vue.mdc # Vue + Vuexy patterns
|
||||
└── 200_testing.mdc # Testing strategies
|
||||
```
|
||||
|
||||
**Read these files first!** They contain everything Cursor needs to generate code correctly.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install these before starting:
|
||||
|
||||
### macOS (Homebrew)
|
||||
|
||||
```bash
|
||||
# PHP 8.3
|
||||
brew install php@8.3
|
||||
brew link php@8.3
|
||||
|
||||
# Composer
|
||||
brew install composer
|
||||
|
||||
# Node.js (via fnm)
|
||||
brew install fnm
|
||||
fnm install 20
|
||||
fnm use 20
|
||||
|
||||
# pnpm
|
||||
npm install -g pnpm
|
||||
|
||||
# Docker Desktop
|
||||
# Download from: https://www.docker.com/products/docker-desktop/
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
php -v # Should show 8.3.x
|
||||
composer -V # Should show 2.x
|
||||
node -v # Should show v20.x
|
||||
pnpm -v # Should show 8.x or 9.x
|
||||
docker -v # Should show Docker version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Start Docker Services
|
||||
|
||||
```bash
|
||||
cd crewli
|
||||
make services
|
||||
```
|
||||
|
||||
This starts:
|
||||
- **MySQL 8.0** on port 3306
|
||||
- **Redis** on port 6379
|
||||
- **Mailpit** on port 8025 (email testing UI)
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create Laravel API
|
||||
|
||||
Open the project in Cursor and use this prompt:
|
||||
|
||||
```
|
||||
Create a new Laravel 12 project in the api/ folder.
|
||||
|
||||
Requirements:
|
||||
- Use the command: composer create-project laravel/laravel api
|
||||
- After creation, install Sanctum: composer require laravel/sanctum
|
||||
- Configure for API-only (we don't need web routes)
|
||||
- Set up CORS for localhost:5173, localhost:5174, localhost:5175
|
||||
- Use MySQL with these credentials:
|
||||
- Host: 127.0.0.1
|
||||
- Database: crewli
|
||||
- Username: crewli
|
||||
- Password: secret
|
||||
|
||||
Follow the conventions in .cursor/rules for code style.
|
||||
```
|
||||
|
||||
### Manual Alternative
|
||||
|
||||
```bash
|
||||
cd crewli
|
||||
composer create-project laravel/laravel api
|
||||
cd api
|
||||
composer require laravel/sanctum
|
||||
php artisan install:api
|
||||
```
|
||||
|
||||
Then configure `api/.env`:
|
||||
```env
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=crewli
|
||||
DB_USERNAME=crewli
|
||||
DB_PASSWORD=secret
|
||||
|
||||
FRONTEND_ADMIN_URL=http://localhost:5173
|
||||
FRONTEND_APP_URL=http://localhost:5174
|
||||
FRONTEND_PORTAL_URL=http://localhost:5175
|
||||
SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:5174,localhost:5175
|
||||
SESSION_DOMAIN=localhost
|
||||
```
|
||||
|
||||
**Production (domain `crewli.app`):** set `APP_URL=https://api.crewli.app`, point `FRONTEND_ADMIN_URL` / `FRONTEND_APP_URL` / `FRONTEND_PORTAL_URL` to `https://admin.crewli.app`, `https://app.crewli.app`, and `https://portal.crewli.app`, and `SANCTUM_STATEFUL_DOMAINS=admin.crewli.app,app.crewli.app,portal.crewli.app` (hostnames only). Each SPA build should use `VITE_API_URL=https://api.crewli.app/api/v1`. Full template: `api/.env.example`. The product uses **`crewli.app`** only; **`crewli.nl`** is for a future public marketing site, not this API or SPAs.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Vuexy frontends (this repo)
|
||||
|
||||
This monorepo already contains three SPAs under `apps/`:
|
||||
|
||||
| Directory | Role | Typical Vuexy source |
|
||||
|-----------|------|----------------------|
|
||||
| `apps/admin/` | Super Admin | full-version (TypeScript) |
|
||||
| `apps/app/` | Organizer (main product) | full-version or customized starter |
|
||||
| `apps/portal/` | External portal (volunteers, token links) | stripped starter / custom layout |
|
||||
|
||||
If you ever need to re-copy from a Vuexy ZIP, use the paths above — not legacy `apps/band` or `apps/customers`.
|
||||
|
||||
```bash
|
||||
# Example only — adjust to your Vuexy download path
|
||||
cp -r ~/Downloads/vuexy/typescript-version/full-version/* apps/admin/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Configure SPAs
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
cd apps/admin && pnpm install
|
||||
cd ../app && pnpm install
|
||||
cd ../portal && pnpm install
|
||||
```
|
||||
|
||||
### Create Environment Files
|
||||
|
||||
**apps/admin/.env.local**
|
||||
```env
|
||||
VITE_API_URL=http://localhost:8000/api/v1
|
||||
VITE_APP_NAME="Crewli Admin"
|
||||
```
|
||||
|
||||
**apps/app/.env.local**
|
||||
```env
|
||||
VITE_API_URL=http://localhost:8000/api/v1
|
||||
VITE_APP_NAME="Crewli Organizer"
|
||||
```
|
||||
|
||||
**apps/portal/.env.local**
|
||||
```env
|
||||
VITE_API_URL=http://localhost:8000/api/v1
|
||||
VITE_APP_NAME="Crewli Portal"
|
||||
```
|
||||
|
||||
### Dev server ports
|
||||
|
||||
From the repo root, `make admin`, `make app`, and `make portal` start Vite on **5173**, **5174**, and **5175** respectively. If you run `pnpm dev` manually, configure the same ports in each app’s `vite.config.ts` under `server.port`.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: API client in SPAs
|
||||
|
||||
`apps/admin/src/lib/api-client.ts`, `apps/app/src/lib/api-client.ts`, and `apps/portal/src/lib/api-client.ts` share the same pattern: `VITE_API_URL` base, Bearer token from the `accessToken` cookie, 401 → clear cookies and redirect to `/login`. Build new composables on `apiClient`; keep Vuexy `useApi` for template demos only.
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Create database schema
|
||||
|
||||
Implement migrations from the canonical schema, not a legacy intranet model:
|
||||
|
||||
- **`docs/SCHEMA.md`** — table list, columns, indexes
|
||||
- **`.cursor/ARCHITECTURE.md`** — overview and relationships
|
||||
- **`.cursor/rules/103_database.mdc`** — ULIDs, soft deletes, index rules
|
||||
|
||||
**Checked-in foundation (this repo):** Laravel defaults (`users`, `cache`, `jobs`) then `2026_04_07_*` migrations: Sanctum tokens → Spatie permission → activity log → `organisations` → `organisation_user` → `events` → `user_invitations` → `event_user_roles`. New modules should append migrations with a later timestamp in dependency order.
|
||||
|
||||
Typical next expansion order from `103_database.mdc`: festival sections, time slots, persons, shifts, …
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
cd api && php artisan migrate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Start development
|
||||
|
||||
Open separate terminals (or use the Makefile from the repo root):
|
||||
|
||||
```bash
|
||||
# Tab 1: Services (Docker)
|
||||
make services
|
||||
|
||||
# Tab 2: Laravel API
|
||||
make api
|
||||
|
||||
# Tab 3: Admin SPA (optional)
|
||||
make admin
|
||||
|
||||
# Tab 4: Organizer SPA (optional)
|
||||
make app
|
||||
|
||||
# Tab 5: Portal SPA (optional)
|
||||
make portal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building features
|
||||
|
||||
Use Cursor with **`CLAUDE.md`** and **`.cursor/instructions.md`**. Example directions:
|
||||
|
||||
### Authentication
|
||||
|
||||
```
|
||||
Wire Sanctum API auth: login, logout, me; form requests; API resources; Vue apps use axios + token storage (see .cursor/rules).
|
||||
```
|
||||
|
||||
### Events module (Crewli)
|
||||
|
||||
```
|
||||
Events nested under organisations: ULID PK, OrganisationScope, policies, EventResource, feature tests (200/401/403/422).
|
||||
```
|
||||
|
||||
### Portal token flow
|
||||
|
||||
```
|
||||
Portal token middleware and routes for artist/supplier contexts; document links on https://portal.crewli.app/... (see .cursor/rules/102_multi_tenancy.mdc).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### MySQL Connection Refused
|
||||
```bash
|
||||
# Check if Docker is running
|
||||
docker ps
|
||||
|
||||
# Restart services
|
||||
make services-stop
|
||||
make services
|
||||
```
|
||||
|
||||
### CORS Errors
|
||||
Check `api/config/cors.php` allows your frontend origins.
|
||||
|
||||
### Vuexy TypeScript Errors
|
||||
```bash
|
||||
cd apps/admin
|
||||
pnpm install
|
||||
pnpm type-check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next steps
|
||||
|
||||
1. Services running (Docker)
|
||||
2. Laravel API configured and migrated
|
||||
3. SPAs installed (`apps/admin`, `apps/app`, `apps/portal`)
|
||||
4. Environment files for API + each SPA
|
||||
5. Authentication and organisation switching
|
||||
6. Events, sections, time slots, shifts
|
||||
7. Persons, crowd types, portal flows
|
||||
8. Accreditation, briefings, operational modules per roadmap in `.cursor/instructions.md`
|
||||
122
dev-docs/TEST_SCENARIO.md
Normal file
122
dev-docs/TEST_SCENARIO.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Crewli — End-to-End Testscenario Fase 2
|
||||
|
||||
## Scenario: "Test Festival BV organiseert Echt Feesten 2026"
|
||||
|
||||
Dit scenario valideert de complete operationele kern van Crewli.
|
||||
Voer dit uit na elke Fase 2 module om regressie te voorkomen.
|
||||
|
||||
---
|
||||
|
||||
## Stap 1 — Voorbereiding (al werkend na Fase 1)
|
||||
|
||||
- [ ] Login als orgadmin@crewli.test / password
|
||||
- [ ] Organisatie "Test Festival BV" is automatisch actief
|
||||
- [ ] Navigeer naar Evenementen
|
||||
- [ ] Maak aan: "Echt Feesten 2026" | slug: echt-feesten-2026
|
||||
start: 10-07-2026 | eind: 12-07-2026 | timezone: Europe/Amsterdam
|
||||
- [ ] Evenement verschijnt in lijst met status "draft"
|
||||
- [ ] Klik door naar detail pagina — header toont naam, datum, status
|
||||
|
||||
---
|
||||
|
||||
## Stap 2 — Festival Secties (Fase 2 Module 1)
|
||||
|
||||
- [ ] Navigeer naar evenement detail > tab "Secties"
|
||||
- [ ] Maak sectie aan: "Bar" (sort_order: 1)
|
||||
- [ ] Maak sectie aan: "Security" (sort_order: 2)
|
||||
- [ ] Maak sectie aan: "Hospitality" (sort_order: 3)
|
||||
- [ ] Drie secties zichtbaar in lijst, correct gesorteerd
|
||||
|
||||
---
|
||||
|
||||
## Stap 3 — Time Slots (Fase 2 Module 1)
|
||||
|
||||
- [ ] Navigeer naar evenement detail > tab "Time Slots"
|
||||
- [ ] Maak time slot aan:
|
||||
Naam: "Vrijdag Avond" | type: VOLUNTEER
|
||||
datum: 10-07-2026 | start: 18:00 | eind: 02:00
|
||||
- [ ] Maak time slot aan:
|
||||
Naam: "Zaterdag Dag" | type: VOLUNTEER
|
||||
datum: 11-07-2026 | start: 10:00 | eind: 18:00
|
||||
- [ ] Maak time slot aan:
|
||||
Naam: "Zaterdag Avond" | type: VOLUNTEER
|
||||
datum: 11-07-2026 | start: 18:00 | eind: 02:00
|
||||
- [ ] Maak time slot aan:
|
||||
Naam: "Zondag" | type: CREW
|
||||
datum: 12-07-2026 | start: 10:00 | eind: 20:00
|
||||
- [ ] Vier time slots zichtbaar, correct gesorteerd op datum/tijd
|
||||
|
||||
---
|
||||
|
||||
## Stap 4 — Shifts aanmaken (Fase 2 Module 2)
|
||||
|
||||
- [ ] Navigeer naar sectie "Bar"
|
||||
- [ ] Maak shift aan: Time Slot "Vrijdag Avond" | slots_total: 4
|
||||
slots_open_for_claiming: 3
|
||||
- [ ] Maak shift aan: Time Slot "Zaterdag Dag" | slots_total: 5
|
||||
slots_open_for_claiming: 4
|
||||
- [ ] Maak shift aan: Time Slot "Zaterdag Avond" | slots_total: 4
|
||||
slots_open_for_claiming: 3
|
||||
- [ ] Navigeer naar sectie "Security"
|
||||
- [ ] Maak shift aan: Time Slot "Vrijdag Avond" | slots_total: 3
|
||||
slots_open_for_claiming: 2
|
||||
- [ ] Maak shift aan: Time Slot "Zaterdag Avond" | slots_total: 3
|
||||
slots_open_for_claiming: 2
|
||||
- [ ] Shifts tonen fill_rate: 0/4, 0/5, etc.
|
||||
|
||||
---
|
||||
|
||||
## Stap 5 — Personen aanmaken (Fase 2 Module 3)
|
||||
|
||||
- [ ] Navigeer naar Personen (event-scoped)
|
||||
- [ ] Maak 5 personen aan als crowd_type "Volunteer": 1. Jan de Vries | jan@test.nl 2. Lisa Bakker | lisa@test.nl 3. Ahmed Hassan | ahmed@test.nl 4. Sara Jansen | sara@test.nl 5. Tom Visser | tom@test.nl
|
||||
- [ ] Vijf personen zichtbaar in lijst, status "pending"
|
||||
|
||||
---
|
||||
|
||||
## Stap 6 — Shift toewijzing (Fase 2 Module 2)
|
||||
|
||||
- [ ] Wijs Jan de Vries toe aan: Bar > Vrijdag Avond
|
||||
- [ ] Wijs Jan de Vries toe aan: Bar > Zaterdag Dag
|
||||
- [ ] Wijs Lisa Bakker toe aan: Bar > Vrijdag Avond
|
||||
- [ ] Wijs Ahmed Hassan toe aan: Security > Vrijdag Avond
|
||||
- [ ] Fill rate Bar > Vrijdag Avond: 2/4 ✓
|
||||
|
||||
---
|
||||
|
||||
## Stap 7 — Conflictdetectie (Fase 2 Module 2)
|
||||
|
||||
- [ ] Probeer Jan de Vries toe te wijzen aan:
|
||||
Security > Vrijdag Avond (zelfde time slot!)
|
||||
- [ ] Systeem weigert met foutmelding:
|
||||
"Jan de Vries is al ingepland voor Vrijdag Avond"
|
||||
- [ ] Conflictdetectie werkt op DB-niveau (UNIQUE constraint)
|
||||
|
||||
---
|
||||
|
||||
## Stap 8 — Overzicht validatie
|
||||
|
||||
- [ ] Event dashboard tiles tonen correcte aantallen:
|
||||
Secties: 3 | Shifts: 5 | Personen: 5
|
||||
- [ ] Sectie "Bar" toont 3 shifts, totaal 13 slots
|
||||
- [ ] Persoon "Jan de Vries" toont 2 shift-toewijzingen
|
||||
- [ ] Geen console errors in browser DevTools
|
||||
|
||||
---
|
||||
|
||||
## Regressie — controleer na elke nieuwe module
|
||||
|
||||
- [ ] Login werkt nog (admin@crewli.test en orgadmin@crewli.test)
|
||||
- [ ] Organisatie switcher werkt nog
|
||||
- [ ] Events lijst laadt zonder errors
|
||||
- [ ] php artisan test → alle tests groen
|
||||
|
||||
## Openstaande FK constraints (worden toegevoegd bij persons module)
|
||||
|
||||
- shift_assignments.person_id → persons
|
||||
- shift_check_ins.person_id → persons
|
||||
- volunteer_availabilities.person_id → persons
|
||||
person_id kolommen zonder FK constraint in:
|
||||
- shift_assignments
|
||||
- shift_check_ins
|
||||
- volunteer_availabilities
|
||||
1291
dev-docs/design-document.md
Normal file
1291
dev-docs/design-document.md
Normal file
File diff suppressed because it is too large
Load Diff
759
dev-docs/dev-guide.md
Normal file
759
dev-docs/dev-guide.md
Normal file
@@ -0,0 +1,759 @@
|
||||
# Crewli Development Guide
|
||||
|
||||
Cursor & Claude Code — Van Leeg Project naar Productie
|
||||
|
||||
**Versie:** 1.0 | **Datum:** Maart 2026 | **Stack:** Laravel 12 + Vue 3 | **AI Tools:** Cursor + Claude Code
|
||||
|
||||
## 1. Strategie & Mindset
|
||||
|
||||
Cursor vs Claude Code — wanneer gebruik je wat?
|
||||
|
||||
Voordat je begint met ontwikkelen is het belangrijk te begrijpen hoe Cursor en Claude Code zich tot elkaar verhouden. Ze zijn complementair — niet concurrerend.
|
||||
|
||||
| **Tool** | **Wanneer inzetten** |
|
||||
|----|----|
|
||||
| Cursor (IDE) | Dagelijks coderen. Inline autocomplete, context-aware suggesties, kleine refactors, code reviews, directe file-edits. Beste voor: een specifiek component bouwen, een bug fixen, een test schrijven. |
|
||||
| Claude Code (Terminal) | Grote, multi-file taken. Scaffolding van een volledig module (migrations + model + controller + tests + Vue-pagina). Autonome agent die zelfstandig werkt, tests uitvoert en fouten corrigeert. Beste voor: 'Bouw het volledige shift-module end-to-end.' |
|
||||
| Samen | Aanbevolen workflow: Claude Code genereert het skelet en alle bestanden. Cursor verfijnt, debugt en voegt details toe. Claude Code draait de test-suite. Cursor doet code review en stijlcorrecties. |
|
||||
|
||||
> **KERNPRINCIPE**
|
||||
>
|
||||
> Claude Code is je senior developer die grote blokken werk autonoom uitvoert.
|
||||
>
|
||||
> Cursor is je pair programmer die naast je zit terwijl jij zelf ook werkt.
|
||||
>
|
||||
> Jij bent de architect en product owner: jij beslist, zij bouwen.
|
||||
|
||||
## 2. De Eerste Stappen
|
||||
|
||||
Wat je vandaag doet voordat je één regel code schrijft
|
||||
|
||||
### 2.1 Repository structuur definitief maken
|
||||
|
||||
Controleer en bevestig de folderstructuur
|
||||
|
||||
Jouw huidige setup heeft al een goede basis. Bevestig of maak de volgende structuur:
|
||||
|
||||
```
|
||||
crewli/ # Monorepo root
|
||||
├── api/ # Laravel 12 backend
|
||||
│ ├── app/
|
||||
│ │ ├── Http/
|
||||
│ │ │ ├── Controllers/Api/V1/
|
||||
│ │ │ ├── Middleware/
|
||||
│ │ │ └── Requests/ # Form Requests per endpoint
|
||||
│ │ ├── Models/
|
||||
│ │ ├── Policies/ # Laravel Policies per model
|
||||
│ │ ├── Services/ # Business logic buiten controllers
|
||||
│ │ ├── Events/ + Listeners/
|
||||
│ │ └── Jobs/ # Queue jobs (briefings, PDF, notifs)
|
||||
│ ├── database/
|
||||
│ │ ├── migrations/
|
||||
│ │ ├── factories/
|
||||
│ │ └── seeders/
|
||||
│ └── tests/Feature/Api/V1/ # PHPUnit feature tests per controller
|
||||
├── apps/
|
||||
│ ├── admin/ # Super Admin SPA (Vuexy)
|
||||
│ ├── app/ # Organizer SPA (Vuexy) -- HOOFDAPP
|
||||
│ └── portal/ # Externe portals (vrijwilliger, artiest, leverancier)
|
||||
├── docs/ # Design document, API docs, ERD
|
||||
│ ├── design-document.md
|
||||
│ └── dev-guide.md
|
||||
└── .cursorrules # Cursor workspace rules
|
||||
```
|
||||
|
||||
### 2.2 Dependencies installeren
|
||||
|
||||
Backend en frontend klaarstomen
|
||||
|
||||
**Backend (api/)**
|
||||
|
||||
```bash
|
||||
cd api
|
||||
|
||||
# Spatie permissions (rollen/permissies)
|
||||
composer require spatie/laravel-permission
|
||||
|
||||
# Audit log
|
||||
composer require spatie/laravel-activitylog
|
||||
|
||||
# Media library (bestandsbeheer)
|
||||
composer require spatie/laravel-medialibrary
|
||||
|
||||
# PDF generatie
|
||||
composer require barryvdh/laravel-dompdf
|
||||
|
||||
# QR codes
|
||||
composer require endroid/qr-code
|
||||
|
||||
# Publiceer Spatie configs
|
||||
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
|
||||
php artisan vendor:publish --provider="Spatie\LaravelActivitylog\ActivitylogServiceProvider"
|
||||
```
|
||||
|
||||
**Frontend — alle apps (apps/app/, apps/admin/, apps/portal/)**
|
||||
|
||||
```bash
|
||||
# TanStack Query voor API state management
|
||||
npm install @tanstack/vue-query
|
||||
|
||||
# Formuliervalidatie
|
||||
npm install vee-validate zod @vee-validate/zod
|
||||
|
||||
# Drag-and-drop (form builder, timetable, prioriteitsranking)
|
||||
npm install vuedraggable@next
|
||||
|
||||
# In apps/app/ en apps/admin/ ook:
|
||||
npm install @fullcalendar/vue3 @fullcalendar/timeline @fullcalendar/resource-timeline
|
||||
```
|
||||
|
||||
### 2.3 CLAUDE.md aanmaken
|
||||
|
||||
Het belangrijkste bestand in je hele repo
|
||||
|
||||
CLAUDE.md is de instructieset voor Claude Code. Het wordt automatisch geladen bij elke sessie. Dit bestand is de meest impactvolle investering die je doet — een uur hieraan besteden bespaart honderden uren aan correcties.
|
||||
|
||||
Maak aan: /crewli/CLAUDE.md (root niveau, zodat het voor alle sub-projecten geldt)
|
||||
|
||||
## 3. Helper Files — Volledige Inhoud
|
||||
|
||||
De exacte bestanden die je aanmaakt voor de eerste prompt
|
||||
|
||||
### 3.1 CLAUDE.md — Root niveau
|
||||
|
||||
Dit is de volledige, aanbevolen inhoud voor je CLAUDE.md. Kopieer dit letterlijk en pas aan waar nodig.
|
||||
|
||||
```
|
||||
# Crewli — Claude Code Instructies
|
||||
|
||||
## Project Context
|
||||
Crewli is een multi-tenant SaaS platform voor event- en festivalbeheer.
|
||||
Gebouwd voor een professionele vrijwilligersorganisatie, met SaaS-uitbreidingspotentieel.
|
||||
Design Document: /resources/design/design-document.md
|
||||
|
||||
## Tech Stack
|
||||
- Backend: PHP 8.2+, Laravel 12, Sanctum, Spatie Permission, MySQL 8, Redis
|
||||
- Frontend: TypeScript, Vue 3 (Composition API), Vuexy/Vuetify, Pinia, TanStack Query
|
||||
- Testing: PHPUnit (backend), Vitest (frontend)
|
||||
|
||||
## Repository Structuur
|
||||
- api/ Laravel backend
|
||||
- apps/app/ Organizer SPA (hoofdapp)
|
||||
- apps/admin/ Super Admin SPA
|
||||
- apps/portal/ Externe portals (vrijwilliger, artiest, leverancier)
|
||||
|
||||
## Backend Regels (STRIKT VOLGEN)
|
||||
|
||||
### Multi-tenancy
|
||||
- ELKE query op event-data MOET scoperen op organisation_id
|
||||
- Gebruik OrganisationScope als Eloquent Global Scope op alle event-gerelateerde modellen
|
||||
- Nooit directe id-checks in controllers — gebruik altijd Policies
|
||||
|
||||
### Controllers
|
||||
- Gebruik Resource Controllers (index/show/store/update/destroy)
|
||||
- Namespace: App\Http\Controllers\Api\V1\
|
||||
- Alle responses via API Resources (nooit model-attributen direct teruggeven)
|
||||
- Validatie via Form Requests (nooit inline validate())
|
||||
|
||||
### Modellen
|
||||
- Gebruik HasUlids trait op alle business-modellen (GEEN UUID v4)
|
||||
- Soft deletes op: Organisation, Event, FestivalSection, Shift, ShiftAssignment, Person, Artist
|
||||
- GEEN soft deletes op: CheckIn, BriefingSend, MessageReply, ShiftWaitlist (audit-records)
|
||||
- JSON kolommen ALLEEN voor opaque configuratie — nooit voor queryable data
|
||||
|
||||
### Database
|
||||
- Primaire sleutels: ULID via HasUlids (niet UUID v4, niet auto-increment voor business tables)
|
||||
- Elke migratie in volgorde aanmaken: eerst foundation, dan afhankelijke tabellen
|
||||
- ALTIJD composite indexes toevoegen zoals gedocumenteerd in het design document sectie 3.5
|
||||
|
||||
### Rollen & Permissies
|
||||
- Gebruik Spatie laravel-permission
|
||||
- Check rollen via $user->hasRole() en Policies — nooit hardcoded role strings in controllers
|
||||
- Drie niveaus: app (super_admin), organisatie (org_admin/org_member), event (event_manager etc.)
|
||||
|
||||
### Testing
|
||||
- Schrijf PHPUnit Feature Tests per controller
|
||||
- Minimaal per endpoint: happy path + unauthenticated (401) + wrong organisation (403)
|
||||
- Gebruik factories voor alle test-data
|
||||
- Draai tests NA elke module: php artisan test --filter=ModuleNaam
|
||||
|
||||
## Frontend Regels (STRIKT VOLGEN)
|
||||
|
||||
### Vue Componenten
|
||||
- Altijd <script setup lang='ts'> — nooit Options API
|
||||
- Props altijd getypeerd met defineProps<{...}>()
|
||||
- Emits altijd gedeclareerd met defineEmits<{...}>()
|
||||
|
||||
### API Calls
|
||||
- Gebruik TanStack Query (useQuery / useMutation) voor ALLE API calls
|
||||
- Nooit direct axios in een component — altijd via een composable in composables/api/
|
||||
- Pinia stores voor cross-component state — nooit prop drilling
|
||||
|
||||
### Naamgeving
|
||||
- DB kolommen: snake_case
|
||||
- TypeScript/JS variabelen: camelCase
|
||||
- Vue componenten: PascalCase (bijv. ShiftAssignPanel.vue)
|
||||
- Composables: use-prefix (bijv. useShifts.ts)
|
||||
- Pinia stores: use-suffix store (bijv. useEventStore.ts)
|
||||
|
||||
### UI
|
||||
- Gebruik ALTIJD Vuexy/Vuetify componenten voor layout, forms, tabellen, dialogen
|
||||
- Nooit custom CSS schrijven als een Vuetify klasse bestaat
|
||||
- Responsief: mobile-first, minimaal werkend op 375px breedte
|
||||
|
||||
## Verboden Patronen
|
||||
- NOOIT: $user->role === 'admin' (gebruik policies)
|
||||
- NOOIT: Model::all() zonder where-clausule (altijd scopen)
|
||||
- NOOIT: dd() of var_dump() achterlaten in code
|
||||
- NOOIT: .env waarden hardcoden in code
|
||||
- NOOIT: JSON kolommen gebruiken voor data waarop gefilterd wordt
|
||||
- NOOIT: UUID v4 als primaire sleutel (gebruik HasUlids)
|
||||
|
||||
## Volgorde bij elke nieuwe module
|
||||
1. Migratie(s) aanmaken en draaien
|
||||
2. Eloquent Model met relaties, scopes en HasUlids
|
||||
3. Factory voor test-data
|
||||
4. Policy voor autorisatie
|
||||
5. Form Request(s) voor validatie
|
||||
6. API Resource voor response transformatie
|
||||
7. Resource Controller
|
||||
8. Routes registreren in api.php
|
||||
9. PHPUnit Feature Test schrijven en draaien
|
||||
10. Vue composable voor API calls (useModuleNaam.ts)
|
||||
11. Pinia store indien cross-component state nodig
|
||||
12. Vue pagina component
|
||||
13. Route toevoegen in Vue Router
|
||||
```
|
||||
|
||||
### 3.2 .cursorrules — Root niveau
|
||||
|
||||
Dit is het equivalent van CLAUDE.md maar voor Cursor's autocomplete en inline AI. Korter en meer gefocust op directe code-stijl.
|
||||
|
||||
```
|
||||
# Crewli Cursor Rules
|
||||
|
||||
## Stack
|
||||
PHP 8.2 + Laravel 12 | TypeScript + Vue 3 + Vuexy/Vuetify | Pinia + TanStack Query
|
||||
|
||||
## Laravel
|
||||
- Resource Controllers, Form Requests, API Resources — altijd
|
||||
- HasUlids op business modellen, HasFactory, SoftDeletes waar gedocumenteerd
|
||||
- Global Scope OrganisationScope op event-gerelateerde modellen
|
||||
- Policies voor autorisatie, nooit inline role checks
|
||||
|
||||
## Vue 3
|
||||
- <script setup lang='ts'> altijd
|
||||
- TanStack Query voor API state, Pinia voor UI state
|
||||
- Vuetify componenten eerst, custom CSS als laatste redmiddel
|
||||
|
||||
## Naamgeving
|
||||
- snake_case DB | camelCase JS | PascalCase Vue | use* composables | use*Store Pinia
|
||||
|
||||
## Tests
|
||||
- PHPUnit Feature Test per controller, minimaal: 200 + 401 + 403
|
||||
```
|
||||
|
||||
### 3.3 dev-docs/SCHEMA.md — Levend schema-document
|
||||
|
||||
Maak een Markdown bestand aan in /dev-docs/ dat de tabel-definitie bevat als platte tekst. Claude Code gebruikt dit als primaire referentie bij het genereren van migraties.
|
||||
|
||||
```
|
||||
# Crewli Database Schema
|
||||
# Versie: 1.3 | Gegenereerd vanuit Design Document
|
||||
|
||||
## Regels
|
||||
- Primaire sleutels: ULID via HasUlids (nooit UUID v4)
|
||||
- Soft delete: zie lijst per tabel hieronder
|
||||
- JSON kolommen: alleen voor opaque config
|
||||
|
||||
## Tabellen
|
||||
|
||||
### users
|
||||
- id (ulid, PK)
|
||||
- name (string)
|
||||
- email (string, unique)
|
||||
- password (string)
|
||||
- timezone (string, default: Europe/Amsterdam)
|
||||
- locale (string, default: nl)
|
||||
- avatar (string, nullable)
|
||||
- email_verified_at (timestamp, nullable)
|
||||
- deleted_at (timestamp, nullable) -- soft delete
|
||||
|
||||
### organisations
|
||||
- id (ulid, PK)
|
||||
- name (string)
|
||||
- slug (string, unique)
|
||||
- billing_status (enum: trial|active|suspended|cancelled, default: trial)
|
||||
- settings (json, nullable) -- UI display prefs only
|
||||
- deleted_at (timestamp, nullable)
|
||||
|
||||
# ... (volledig schema uit Design Document sectie 3.5)
|
||||
```
|
||||
|
||||
### 3.4 docs/API.md — API contract
|
||||
|
||||
Een simpele route-lijst die Claude Code gebruikt als referentie bij het genereren van controllers en Vue composables.
|
||||
|
||||
```
|
||||
# Crewli API Contract
|
||||
# Base: /api/v1/
|
||||
# Auth: Bearer token (Sanctum)
|
||||
|
||||
## Auth
|
||||
POST /auth/login
|
||||
POST /auth/logout
|
||||
GET /auth/me
|
||||
|
||||
## Organisations
|
||||
GET /organisations -- lijst (super admin)
|
||||
POST /organisations -- aanmaken
|
||||
GET /organisations/{org} -- detail
|
||||
PUT /organisations/{org} -- bijwerken
|
||||
GET /organisations/{org}/members -- leden
|
||||
POST /organisations/{org}/invite -- uitnodigen
|
||||
|
||||
## Events
|
||||
GET /organisations/{org}/events
|
||||
POST /organisations/{org}/events
|
||||
GET /organisations/{org}/events/{event}
|
||||
PUT /organisations/{org}/events/{event}
|
||||
|
||||
## Festival Sections
|
||||
GET /events/{event}/sections
|
||||
POST /events/{event}/sections
|
||||
GET /events/{event}/sections/{section}
|
||||
|
||||
## Time Slots
|
||||
GET /events/{event}/time-slots
|
||||
POST /events/{event}/time-slots
|
||||
|
||||
## Shifts
|
||||
GET /events/{event}/sections/{section}/shifts
|
||||
POST /events/{event}/sections/{section}/shifts
|
||||
PUT /events/{event}/sections/{section}/shifts/{shift}
|
||||
POST /events/{event}/sections/{section}/shifts/{shift}/assign
|
||||
POST /events/{event}/sections/{section}/shifts/{shift}/claim
|
||||
|
||||
## Persons
|
||||
GET /events/{event}/persons
|
||||
POST /events/{event}/persons
|
||||
GET /events/{event}/persons/{person}
|
||||
PUT /events/{event}/persons/{person}
|
||||
POST /events/{event}/persons/{person}/approve
|
||||
|
||||
# ... (volledig API contract uitbreiden per module)
|
||||
```
|
||||
|
||||
## 4. Development Workflow
|
||||
|
||||
Hoe je van leeg project naar werkende feature gaat
|
||||
|
||||
Elke feature volgt dezelfde drielaagse workflow. Commit altijd per voltooide laag — nooit halfafgebouwde code in main.
|
||||
|
||||
| **Laag** | **Wat je doet en met welk tool** |
|
||||
|----|----|
|
||||
| Laag 1 — Backend (API) | Claude Code genereert: migratie + model + factory + policy + form request + resource + controller + test. Jij reviewt en draait tests. |
|
||||
| Laag 2 — Frontend (Vue) | Claude Code genereert: composable + Pinia store + Vue pagina + router entry. Cursor verfijnt de UI met Vuexy componenten. |
|
||||
| Laag 3 — Integration | Cursor: verbind frontend met backend. Test end-to-end. Fix type-errors. Review mobile weergave. |
|
||||
|
||||
### 4.1 De Module-generatie volgorde
|
||||
|
||||
Altijd in deze volgorde. Nooit stappen overslaan — later toevoegen kost meer tijd dan nu correct doen.
|
||||
|
||||
| **Stap** | **Commando / Actie** |
|
||||
|----|----|
|
||||
| 1. Migratie | php artisan make:migration create_shifts_table |
|
||||
| 2. Model | php artisan make:model Shift -mfp (migration + factory + policy) |
|
||||
| 3. Form Request | php artisan make:request StoreShiftRequest + UpdateShiftRequest |
|
||||
| 4. API Resource | php artisan make:resource ShiftResource + ShiftCollection |
|
||||
| 5. Controller | php artisan make:controller Api/V1/ShiftController --api |
|
||||
| 6. Registreer routes | In api/routes/api.php toevoegen |
|
||||
| 7. Test | php artisan make:test ShiftControllerTest + draaien |
|
||||
| 8. Composable | apps/app/src/composables/api/useShifts.ts aanmaken |
|
||||
| 9. Store (indien nodig) | apps/app/src/stores/useShiftStore.ts |
|
||||
| 10. Vue pagina | apps/app/src/pages/sections/[id]/shifts.vue |
|
||||
| 11. Route | apps/app/src/router/index.ts |
|
||||
|
||||
### 4.2 Fase-planning: wat bouw je wanneer
|
||||
|
||||
| **Fase** | **Inhoud** |
|
||||
|----|----|
|
||||
| Fase 1 — Foundation (nu) | Auth (login/logout/me), Organisations CRUD, Events CRUD, User invitations, Multi-tenant scope, Roles & permissions setup, Basis dashboard shell |
|
||||
| Fase 2 — Core Operations | Persons & Crowd Types, Festival Sections + Time Slots + Shifts, Shift claiming + goedkeuring, Vrijwilligers registratie + portaal, Accreditatie engine, Basis briefings |
|
||||
| Fase 3 — Advancing & Show Day | Artist advancing + portaal, Timetable, Mission Control, Formulierbouwer, Post-festival evaluatie, PDF allocatiesheet, Campagnes (email + WhatsApp via Zender) |
|
||||
| Fase 4 — Differentiators | Real-time WebSockets, Show Day Mode, Vrijwilligersprofiel + festival-paspoort, Shift swap & wachtlijst, Retrospectief rapport, Leveranciersportaal uitgebreid |
|
||||
|
||||
## 5. Prompt Bibliotheek
|
||||
|
||||
Kant-en-klare prompts voor elke ontwikkelstap
|
||||
|
||||
Gebruik deze prompts letterlijk of als basis. De meest effectieve prompts zijn: specifiek, contextueel en taak-gebaseerd. Verwijs altijd naar de docs/ bestanden die je hebt aangemaakt.
|
||||
|
||||
### 5.1 Kickstart prompts
|
||||
|
||||
> **Fase 1 kickstart — Alles genereren in een sweep**
|
||||
>
|
||||
> Lees /resources/design/design-document.md sectie 3.5 (schema) en /CLAUDE.md.
|
||||
>
|
||||
> Genereer alle Fase 1 componenten in de juiste volgorde:
|
||||
>
|
||||
> 1. Migrations voor: users (update), organisations, organisation_user, user_invitations, events, event_user_roles
|
||||
> 2. Eloquent modellen met HasUlids, relaties, OrganisationScope global scope waar van toepassing
|
||||
> 3. Factories met realistic test data
|
||||
> 4. Spatie Permission seeder: maak rollen aan (super_admin, org_admin, org_member, event_manager, staff_coordinator, volunteer_coordinator)
|
||||
> 5. Auth controller (login/logout/me) met Sanctum
|
||||
> 6. Organisations controller (CRUD) met Policy en Feature Test
|
||||
> 7. Events controller (CRUD) met Policy en Feature Test
|
||||
>
|
||||
> Draai na elke stap: php artisan test. Los fouten op voor je verder gaat.
|
||||
|
||||
> **Module genereren — Shifts als voorbeeld**
|
||||
>
|
||||
> Lees /CLAUDE.md en /dev-docs/SCHEMA.md voor de shifts tabel definitie.
|
||||
>
|
||||
> Bouw het volledige Shifts module in de volgorde uit CLAUDE.md sectie 'Volgorde bij elke nieuwe module'.
|
||||
>
|
||||
> Specifieke eisen voor Shifts:
|
||||
>
|
||||
> - time_slot_id MOET gedenormaliseerd worden in shift_assignments voor de UNIQUE(person_id, time_slot_id) constraint
|
||||
> - ShiftAssignment heeft een status machine: pending_approval > approved/rejected/cancelled/completed
|
||||
> - Auto-approve is configureerbaar per shift (auto_approved bool op shift niveau)
|
||||
> - Bij approve: stuur notificatie naar vrijwilliger (queued job, gebruik ZenderService voor WhatsApp)
|
||||
> - ShiftResource moet slots_filled (count van approved assignments) en fill_rate (percentage) berekend teruggeven
|
||||
>
|
||||
> Eindig met: php artisan test --filter=Shift
|
||||
|
||||
### 5.2 Backend prompts
|
||||
|
||||
> **Migration genereren**
|
||||
>
|
||||
> Genereer een Laravel migratie voor de tabel [TABELNAAM] op basis van /dev-docs/SCHEMA.md.
|
||||
>
|
||||
> Gebruik $table->ulid('id')->primary() als PK.
|
||||
>
|
||||
> Voeg alle indexes toe zoals gedocumenteerd (composite indexes, unique constraints).
|
||||
>
|
||||
> Voeg timestamps() en softDeletes() toe indien van toepassing per CLAUDE.md.
|
||||
>
|
||||
> Gebruik constrained() op alle foreign keys voor cascade-gedrag.
|
||||
|
||||
> **Model met alle features**
|
||||
>
|
||||
> Genereer het Eloquent model voor [MODELNAAM].
|
||||
>
|
||||
> Gebruik: HasUlids, HasFactory, SoftDeletes (indien van toepassing).
|
||||
>
|
||||
> Voeg toe: OrganisationScope global scope, alle relaties (hasMany, belongsTo, belongsToMany),
|
||||
> computed accessors (fill_rate, available_slots), status-gerelateerde scopes (scopePending, scopeApproved),
|
||||
> en $fillable of $guarded array.
|
||||
>
|
||||
> Schrijf ook de factory met realistic Nederlandse testdata.
|
||||
|
||||
> **API Resource met computed velden**
|
||||
>
|
||||
> Genereer een Laravel API Resource voor [MODELNAAM].
|
||||
>
|
||||
> Voeg toe: alle relevante velden, computed velden (fill_rate, status_label),
|
||||
> conditioneel geladen relaties (whenLoaded), en wanneer van toepassing: when() voor permissie-afhankelijke velden.
|
||||
>
|
||||
> De Resource mag NOOIT model-attributen direct weggeven zonder transformatie.
|
||||
|
||||
> **Feature test schrijven**
|
||||
>
|
||||
> Schrijf een PHPUnit Feature Test voor [CONTROLLERNAAM].
|
||||
>
|
||||
> Dek minimaal af: index (200), show (200), store (201), update (200), destroy (204),
|
||||
> unauthenticated (401 op alle routes), wrong organisation (403), validatiefouten (422).
|
||||
>
|
||||
> Gebruik RefreshDatabase, ActingAs met correcte rol via Spatie Permission.
|
||||
>
|
||||
> Maak test data via factories — nooit hardcoded IDs.
|
||||
|
||||
> **ZenderService aanmaken (WhatsApp/SMS)**
|
||||
>
|
||||
> Maak app/Services/ZenderService.php aan.
|
||||
>
|
||||
> Zender is een self-hosted SMS/WhatsApp gateway (CodeCanyon product).
|
||||
>
|
||||
> Config: ZENDER_API_URL en ZENDER_API_KEY uit .env.
|
||||
>
|
||||
> Methoden: sendSms(string $to, string $message): bool
|
||||
> sendWhatsApp(string $to, string $message): bool
|
||||
> sendByUrgency(string $to, string $message, string $urgency): bool
|
||||
>
|
||||
> urgency: normal=email only, urgent=whatsapp, emergency=sms+whatsapp parallel
|
||||
>
|
||||
> Gebruik Laravel HTTP Client (Http::). Log alle sends via activitylog.
|
||||
>
|
||||
> Schrijf ook een ZenderServiceTest met HTTP fake.
|
||||
|
||||
### 5.3 Frontend prompts
|
||||
|
||||
> **Vue pagina voor een lijst-overzicht**
|
||||
>
|
||||
> Maak apps/app/src/pages/[module]/index.vue.
|
||||
>
|
||||
> Gebruik \<script setup lang='ts'\>.
|
||||
>
|
||||
> API calls via useQuery() uit TanStack Query — niet direct axios.
|
||||
>
|
||||
> Tabel via VDataTable van Vuetify — niet custom HTML.
|
||||
>
|
||||
> Bovenaan: status KPI-tiles (totaal, goedgekeurd, pending) als klikbare VCard componenten.
|
||||
>
|
||||
> Rij klik: opent een side panel (niet navigeert naar nieuwe pagina) met detail-informatie.
|
||||
>
|
||||
> Loading state: VSkeleton loader. Error state: VAlert met retry knop.
|
||||
>
|
||||
> Mobiel: tabel collapst naar een VList op viewport < 768px.
|
||||
|
||||
> **Composable voor API calls**
|
||||
>
|
||||
> Maak apps/app/src/composables/api/use[Module].ts.
|
||||
>
|
||||
> Exporteer: use[Module]List (useQuery), use[Module]Detail (useQuery met id param),
|
||||
> useCreate[Module] (useMutation), useUpdate[Module] (useMutation), useDelete[Module] (useMutation).
|
||||
>
|
||||
> Gebruik axios via de centrale api.ts instance (met Sanctum CSRF en auth header).
|
||||
>
|
||||
> Mutations invalideren automatisch de relevante query keys na succes.
|
||||
>
|
||||
> Alle response types volledig getypeerd via TypeScript interfaces in types/[module].ts.
|
||||
|
||||
> **Pinia store aanmaken**
|
||||
>
|
||||
> Maak apps/app/src/stores/use[Module]Store.ts.
|
||||
>
|
||||
> Gebruik defineStore met Setup syntax (niet Options syntax).
|
||||
>
|
||||
> Sla op: geselecteerde IDs, UI state (open sidepanel, actief tab), filters.
|
||||
>
|
||||
> NIET in Pinia: server data (dat zit in TanStack Query). Pinia is alleen voor UI state.
|
||||
>
|
||||
> Exporteer: alle state als readonly via storeToRefs.
|
||||
|
||||
> **Shift claim workflow — volledig end-to-end**
|
||||
>
|
||||
> Bouw de volledige shift claim workflow:
|
||||
>
|
||||
> Backend: POST /shifts/{shift}/claim endpoint in ShiftController.
|
||||
>
|
||||
> - Valideer: shift heeft slots_open_for_claiming beschikbaar
|
||||
> - Valideer: geen bestaande approved assignment voor zelfde time_slot_id voor deze person
|
||||
> - Maak ShiftAssignment aan met status=pending_approval
|
||||
> - Dispatch NotifyCoordinatorOfClaimJob (queued)
|
||||
> - Return ShiftAssignmentResource
|
||||
>
|
||||
> Frontend: 'Claim' knop in portal/shifts/index.vue.
|
||||
>
|
||||
> - Disable knop als conflict of geen slots beschikbaar
|
||||
> - Na claim: toon 'Wachten op goedkeuring' badge
|
||||
> - Optioneel: 'Op wachtlijst' knop als shift vol is
|
||||
|
||||
### 5.4 Agent-aanstuurprompts
|
||||
|
||||
> **Grote module — een prompt voor alles**
|
||||
>
|
||||
> Je bent een senior fullstack developer die werkt aan Crewli. Lees /CLAUDE.md volledig.
|
||||
>
|
||||
> Bouw het volledige [MODULE] module:
|
||||
>
|
||||
> Backend (in volgorde):
|
||||
>
|
||||
> 1. Migrations voor alle tabellen uit /dev-docs/SCHEMA.md sectie [X.X]
|
||||
> 2. Models met alle relaties, scopes en accessors
|
||||
> 3. Factories
|
||||
> 4. Policies
|
||||
> 5. Form Requests
|
||||
> 6. API Resources
|
||||
> 7. Controllers
|
||||
> 8. Routes
|
||||
> 9. Feature tests — draai ze, los fouten op
|
||||
>
|
||||
> Frontend:
|
||||
>
|
||||
> 10. TypeScript types in apps/app/src/types/[module].ts
|
||||
> 11. Composables in apps/app/src/composables/api/
|
||||
> 12. Vue pagina's (lijst + detail side panel)
|
||||
> 13. Router entries
|
||||
>
|
||||
> Stop na elke laag en vraag bevestiging voor je doorgaat.
|
||||
>
|
||||
> Als een test faalt: los het op voor je verdergaat — nooit overslaan.
|
||||
|
||||
> **Bug fix prompt**
|
||||
>
|
||||
> Er is een probleem met [BESCHRIJVING VAN HET PROBLEEM].
|
||||
>
|
||||
> Relevante bestanden: [BESTANDSPADEN].
|
||||
>
|
||||
> Foutmelding: [PLAK EXACTE ERROR].
|
||||
>
|
||||
> Verwacht gedrag: [WAT ZOU ER MOETEN GEBEUREN].
|
||||
>
|
||||
> Analyseer de oorzaak, schrijf een failing test die het probleem reproduceert,
|
||||
> fix het probleem, en bevestig dat de test slaagt.
|
||||
|
||||
> **Code review prompt**
|
||||
>
|
||||
> Review de code in [BESTANDSPAD] als senior Laravel/Vue developer.
|
||||
>
|
||||
> Check specifiek op:
|
||||
>
|
||||
> - Multi-tenancy: wordt organisation_id correct gescopeerd?
|
||||
> - Security: worden Policies gebruikt? Geen directe role-checks?
|
||||
> - Performance: ontbrekende eager loading (N+1), ontbrekende indexes?
|
||||
> - Conventies: volgt het CLAUDE.md regels?
|
||||
> - Types: zijn alle TypeScript types volledig (geen any)?
|
||||
>
|
||||
> Geef concrete verbeterpunten met codevoorbeelden.
|
||||
|
||||
## 6. Agents — Autonome Ontwikkeling
|
||||
|
||||
Hoe je Claude Code en Cursor agents effectief inzet
|
||||
|
||||
### 6.1 Claude Code als Agent
|
||||
|
||||
Claude Code kan volledig autonoom werken: bestanden lezen, aanmaken, aanpassen, tests draaien en fouten corrigeren — zonder dat jij elke stap bevestigt. Dit is het krachtigste en snelste werkmode.
|
||||
|
||||
| **Mode** | **Wanneer gebruiken** |
|
||||
|----|----|
|
||||
| Interactief (standaard) | Als je wil meekijken en goedkeuren. Claude Code stelt elke actie voor en wacht. Gebruik voor: eerste keer een module bouwen, complexe refactors. |
|
||||
| Autonoom (--dangerously-skip-permissions) | Als je een grote taak wil delegeren en wegloopt. Claude Code werkt door tot klaar. Gebruik voor: routine-modules die je eerder hebt gebouwd, test-driven fixes. |
|
||||
| Aanbevolen aanpak | Start autonoom voor scaffolding. Schakel naar interactief bij UI-componenten of business-logica die project-specifieke kennis vereist. |
|
||||
|
||||
**Claude Code opstarten**
|
||||
|
||||
```bash
|
||||
# Installeer Claude Code (eenmalig)
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
|
||||
# Start in je project root
|
||||
cd /pad/naar/crewli
|
||||
claude
|
||||
|
||||
# Of: direct met een taak
|
||||
claude --print 'Genereer de Shift migration op basis van CLAUDE.md'
|
||||
|
||||
# Autonoom mode (voorzichtig gebruiken)
|
||||
claude --dangerously-skip-permissions
|
||||
```
|
||||
|
||||
### 6.2 Cursor Agent Mode
|
||||
|
||||
Cursor heeft een ingebouwde Agent mode die vergelijkbaar is met Claude Code maar geintegreerd in de IDE. Activeer via Cmd+Shift+P > 'Cursor: Open Agent'.
|
||||
|
||||
| **Feature** | **Gebruik** |
|
||||
|----|----|
|
||||
| @workspace | Geeft de agent toegang tot je hele codebase als context. Altijd meegeven bij module-niveau taken. |
|
||||
| @file | Verwijs naar een specifiek bestand. Bijv: @CLAUDE.md @api/app/Models/Shift.php |
|
||||
| @docs | Verwijs naar externe documentatie (Laravel docs, Vuetify docs). Cursor indexeert deze. |
|
||||
| Composer mode | Meerdere bestanden tegelijk bewerken. Ideaal voor: tegelijk model + controller + test aanpassen. |
|
||||
|
||||
### 6.3 De optimale agent-workflow per dag
|
||||
|
||||
> **DAGELIJKSE ROUTINE**
|
||||
>
|
||||
> Ochtend: Open Claude Code. Geef de taak voor die dag: 'Bouw het volledig Persons module op basis van CLAUDE.md en SCHEMA.md.' Laat autonoom draaien.
|
||||
>
|
||||
> Middag: Review de gegenereerde code in Cursor. Check: volgt het de conventies? Zijn de tests groen? Zijn de TypeScript types compleet?
|
||||
>
|
||||
> Namiddag: Cursor voor UI fijnafstelling, Vuexy componenten integratie, visuele correcties.
|
||||
>
|
||||
> Einde dag: php artisan test (alle tests groen). Commit alles met duidelijke commit messages.
|
||||
|
||||
### 6.4 Context window management
|
||||
|
||||
Claude Code heeft een beperkt context window. Bij grote taken verliest het de context van eerdere bestanden. Beheer dit proactief:
|
||||
|
||||
- Begin elke nieuwe sessie met: 'Lees /CLAUDE.md voor je begint.'
|
||||
|
||||
- Verwijs expliciet naar relevante bestanden: 'Zie /dev-docs/SCHEMA.md voor de tabel definitie.'
|
||||
|
||||
- Splits grote modules: 'Bouw eerst alleen de backend (stappen 1-9). Stop dan.'
|
||||
|
||||
- Na een context-verlies: geef een samenvatting: 'We bouwen Crewli. Tot nu toe klaar: auth, organisations, events. Nu: Shifts module backend.'
|
||||
|
||||
- Gebruik /dev-docs/API.md als levend document — houd bij wat er al gebouwd is.
|
||||
|
||||
## 7. Tips, Valkuilen & Best Practices
|
||||
|
||||
Geleerd van ervaring — lees dit voordat je begint
|
||||
|
||||
### 7.1 De grootste tijdverspillers
|
||||
|
||||
| **Valkuil** | **Oplossing** |
|
||||
|----|----|
|
||||
| Te brede prompts: 'Bouw de hele app' | Altijd per module. Per module maximaal 1 laag tegelijk. Breed = vaag = slechte output. |
|
||||
| CLAUDE.md niet up-to-date houden | Na elke architectuurbeslissing: update CLAUDE.md. Dit is je bron van waarheid. Verouderde regels leiden tot inconsistente code. |
|
||||
| Tests overslaan 'want het werkt toch' | Schrijf tests altijd. AI-gegenereerde code heeft subtiele bugs die pas later opduiken. Tests vangen dit vroeg. |
|
||||
| Alle gegenereerde code blindelings accepteren | Review altijd: check multi-tenancy scoping, check indexes, check error handling. AI mist soms subtiele business logica. |
|
||||
| Frontend en backend tegelijk bouwen | Backend eerst, compleet en getest. Dan frontend. Nooit parallel — je verliest overzicht. |
|
||||
| Geen versiecontrole per module | Commit na elke voltooide module (backend + frontend + tests). Kleine commits = makkelijk terugdraaien. |
|
||||
|
||||
### 7.2 Prompts die altijd goed werken
|
||||
|
||||
- **Geef altijd context: 'Crewli is een multi-tenant SaaS voor festival-organisatie...'**
|
||||
|
||||
- **Verwijs naar bestanden: 'Op basis van CLAUDE.md en SCHEMA.md...'**
|
||||
|
||||
- **Specificeer de output: 'Genereer X, Y en Z. Niets anders.'**
|
||||
|
||||
- **Vraag om uitleg: 'Leg uit waarom je deze aanpak kiest voor de shift conflict-check.'**
|
||||
|
||||
- **Gebruik iteratief: 'Dit klopt niet omdat... Pas aan en draai tests opnieuw.'**
|
||||
|
||||
### 7.3 Kwaliteitscontrole checklist
|
||||
|
||||
Gebruik dit als checklist voor elke voltooide module voordat je verder gaat:
|
||||
|
||||
| **Check** | **Wat je controleert** |
|
||||
|----|----|
|
||||
| Tests | php artisan test draait groen. Minimaal: 200, 401, 403 per endpoint. |
|
||||
| Multi-tenancy | Elke query heeft organisation_id scope. Controleer via tinker. |
|
||||
| N+1 queries | Gebruik Laravel Debugbar of query logging. Geen N+1 in lijst-endpoints. |
|
||||
| TypeScript | Geen 'any' types in Vue composables en components. npx tsc --noEmit groen. |
|
||||
| Mobile | Pagina is bruikbaar op 375px. Open Chrome DevTools en check. |
|
||||
| Error states | Wat ziet een gebruiker bij: lege lijst, API fout, netwerk timeout? |
|
||||
| CLAUDE.md | Geen verboden patronen (Model::all, hardcoded roles, UUID v4). |
|
||||
|
||||
### 7.4 Handige Laravel commando's
|
||||
|
||||
```bash
|
||||
# Alles in een keer voor een nieuwe model
|
||||
php artisan make:model Shift -a # model + migration + factory + seeder + policy + controller + resource
|
||||
|
||||
# Tests draaien
|
||||
php artisan test # alle tests
|
||||
php artisan test --filter=ShiftTest # specifieke test class
|
||||
php artisan test --coverage # met coverage rapport
|
||||
|
||||
# Database
|
||||
php artisan migrate:fresh --seed # reset + migreer + seed
|
||||
php artisan tinker # REPL voor quick checks
|
||||
|
||||
# Routes inspecteren
|
||||
php artisan route:list --path=api/v1 # alle API routes
|
||||
|
||||
# Queue (voor briefings, notificaties)
|
||||
php artisan queue:work --queue=notifications,briefings,default
|
||||
```
|
||||
|
||||
### 7.5 Eerste dag: exacte volgorde
|
||||
|
||||
| **#** | **Actie** |
|
||||
|----|----|
|
||||
| 1 | Repository structuur controleren (sectie 2, stap 01) |
|
||||
| 2 | Dependencies installeren (sectie 2, stap 02) |
|
||||
| 3 | CLAUDE.md aanmaken en invullen (sectie 3.1) |
|
||||
| 4 | .cursorrules aanmaken (sectie 3.2) |
|
||||
| 5 | docs/SCHEMA.md aanmaken met volledig schema uit design document |
|
||||
| 6 | docs/API.md aanmaken met initiiele routes |
|
||||
| 7 | Claude Code starten: 'Lees CLAUDE.md. Daarna: genereer Fase 1 — auth, organisations, events.' |
|
||||
| 8 | Tests draaien: php artisan test — los fouten op |
|
||||
| 9 | Commit: 'feat: fase 1 foundation — auth, organisations, events' |
|
||||
| 10 | Morgen: Fase 2 starten met Persons & Crowd Types |
|
||||
|
||||
---
|
||||
|
||||
Crewli Development Guide v1.0 — Maart 2026
|
||||
457
dev-docs/start-guide.md
Normal file
457
dev-docs/start-guide.md
Normal file
@@ -0,0 +1,457 @@
|
||||
**Crewli**
|
||||
|
||||
Start — Definitieve Actielijst
|
||||
|
||||
Architectuur + Technologie + Stap-voor-stap naar eerste werkende code
|
||||
|
||||
**Versie:** 1.0 — Definitief | **Datum:** Maart 2026 | **Status:** Klaar om te starten
|
||||
|
||||
## 1 — Definitieve Architectuur
|
||||
|
||||
Dit is de volledige, vastgestelde architectuur van Crewli. Alle beslissingen hierin zijn definitief — verwerk ze in CLAUDE.md en .cursorrules zodat Claude Code dit altijd als context heeft.
|
||||
|
||||
### 1.1 Systeemoverzicht
|
||||
|
||||
| **apps/admin/** | **apps/app/** | **apps/portal/** |
|
||||
|---|---|---|
|
||||
| Super Admin SPA | Organizer SPA | Portal SPA |
|
||||
|
||||
Vuexy + Vue 3 + TypeScript | Pinia + TanStack Query | Axios → CORS → Sanctum Token
|
||||
Alle drie apps zijn Vue 3 SPA's — Vuexy template — communiceren uitsluitend via REST API
|
||||
|
||||
**api/ — Laravel 12 REST API (ENIGE backend — geen Blade views)**
|
||||
PHP 8.2 | Sanctum | Spatie Permission | MySQL 8 | Redis | Queue Workers
|
||||
|
||||
### 1.2 Laravel vs Vue — de harde scheiding
|
||||
|
||||
> **GOUDEN REGEL**
|
||||
>
|
||||
> Laravel doet NIKS met HTML of UI. Geen Blade views, geen Mix, geen Inertia.
|
||||
>
|
||||
> Laravel is uitsluitend een JSON REST API. Elke response is application/json.
|
||||
>
|
||||
> Vue doet ALLES met de gebruikersinterface. De drie SPA's communiceren via HTTPS met de API.
|
||||
|
||||
| **App / Laag** | **Technology** | **Gebruik & verantwoordelijkheid** |
|
||||
|----|----|----|
|
||||
| api/ | Laravel 12 + Sanctum | REST API, authenticatie, business logic, database, queue workers, e-mail, PDF-generatie. Geen enkele HTML pagina. |
|
||||
| apps/admin/ | Vue 3 + Vuexy (vol) | Super Admin SPA: organisations beheren, billing, platform-gebruikers. Klein en eenvoudig. |
|
||||
| apps/app/ | Vue 3 + Vuexy (vol) | Organizer SPA: de hoofdapp. Events, shifts, persons, artists, briefings, Mission Control. 90% van je werk. |
|
||||
| apps/portal/ | Vue 3 + Vuexy (gestript) | Portal SPA: twee toegangsmodi. Login voor vrijwilligers/crew. Token voor artiesten/leveranciers/pers. |
|
||||
|
||||
### 1.3 Vuexy — waar en hoe
|
||||
|
||||
| **App / Laag** | **Technology** | **Gebruik & verantwoordelijkheid** |
|
||||
|----|----|----|
|
||||
| apps/admin/ | Vuexy volledig | Admin template ongewijzigd: sidebar, dark mode, customizer. Weinig aanpassingen nodig. |
|
||||
| apps/app/ | Vuexy volledig | Sidebar nav aanpassen voor Crewli-structuur. Customizer/demo-componenten verwijderen. Full Vuetify component gebruik. |
|
||||
| apps/portal/ | Vuexy gestript | Geen sidebar nav, geen customizer, geen dark mode toggle. Wel: Vuetify componenten, Vuexy SCSS variabelen, Vuexy fonts. Eigen layout: top-bar met event-logo + naam. Mobile-first. |
|
||||
|
||||
### 1.4 Portal: twee toegangsmodi
|
||||
|
||||
Eén portal app met twee modi — op basis van hoe de gebruiker binnenkomt:
|
||||
|
||||
| **Gebruiker** | **Identiteit** | **Toegang** | **Waarom** |
|
||||
|----|:--:|:--:|----|
|
||||
| **Vrijwilliger** | Langdurig | **Login** | Festival-paspoort, reliability score, shift-historie accumuleren over jaren |
|
||||
| **Crew / Staff** | Langdurig | **Login** | Kan ook organizer-rechten hebben; organisatie-medewerker |
|
||||
| **Artiest** | Per event | **Token** | Eenmalige booking-relatie; advancing via gesignde URL |
|
||||
| **Tour manager** | Per event | **Token** | Namens artiest; geen platform-account nodig |
|
||||
| **Leverancier** | Per event | **Token** | Productieaanvraag is event-specifiek; token via production_requests.token |
|
||||
| **Pers / Media** | Per event | **Token** | Accreditatie per event; geen terugkerende relatie |
|
||||
|
||||
**Hoe de router dit afhandelt (apps/portal/)**
|
||||
|
||||
```ts
|
||||
// apps/portal/src/router/guards.ts
|
||||
|
||||
export const accessMode = computed(() => {
|
||||
const token = route.query.token as string | undefined
|
||||
const isAuth = authStore.isAuthenticated
|
||||
|
||||
if (token) return 'token' // artiest, leverancier, pers → token-flow
|
||||
if (isAuth) return 'login' // vrijwilliger, crew → login-flow
|
||||
return 'unauthenticated' // → redirect naar /login
|
||||
})
|
||||
|
||||
// Token-based: geen login nodig, token gevalideerd via API
|
||||
// POST /api/v1/portal/token-auth { token: '...' } → person context terug
|
||||
```
|
||||
|
||||
### 1.5 Hoe de API authorisatie werkt — backend
|
||||
|
||||
```php
|
||||
// api/routes/api.php
|
||||
|
||||
Route::prefix('v1')->group(function () {
|
||||
|
||||
// Publiek (login, token-auth)
|
||||
Route::post('auth/login', [AuthController::class, 'login']);
|
||||
Route::post('portal/token-auth', [PortalTokenController::class, 'auth']);
|
||||
Route::post('portal/form-submit', [PublicFormController::class, 'submit']);
|
||||
|
||||
// Login-based (Sanctum)
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::post('auth/logout', [AuthController::class, 'logout']);
|
||||
Route::get('auth/me', [AuthController::class, 'me']);
|
||||
// ... alle organizer + portal-login routes
|
||||
});
|
||||
|
||||
// Token-based portal (eigen middleware)
|
||||
Route::middleware('portal.token')->group(function () {
|
||||
Route::get('portal/artist', [ArtistPortalController::class, 'index']);
|
||||
Route::post('portal/advancing', [AdvancingController::class, 'submit']);
|
||||
// ... alle token-portal routes
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
> **LET OP — CORS**
|
||||
>
|
||||
> Laravel CORS config (config/cors.php) moet drie origins toestaan:
|
||||
>
|
||||
> admin.crewli.app | app.crewli.app | portal.crewli.app
|
||||
>
|
||||
> In development: http://localhost:5173 | :5174 | :5175
|
||||
|
||||
## 2 — Actielijst: Wat Je Nu Doet
|
||||
|
||||
Voer deze stappen uit in volgorde. Sla niets over — elke stap is input voor de volgende. Tijdsinschatting totaal: 2-4 uur. Daarna kun je de eerste Claude Code prompt sturen.
|
||||
|
||||
### Stap 1 — Repository herstructureren **NU**
|
||||
|
||||
apps/band/ hernoemen, demo-rommel verwijderen
|
||||
|
||||
- ☐ **Hernoem apps/band/ naar apps/portal/**
|
||||
|
||||
```
|
||||
mv apps/band apps/portal
|
||||
# Update package.json naam in apps/portal/package.json:
|
||||
# "name": "crewli-portal"
|
||||
```
|
||||
|
||||
- ☐ **Verwijder Vuexy demo-bestanden uit apps/app/src/**
|
||||
|
||||
- Te verwijderen uit apps/app/src/components/dialogs/:
|
||||
- AddAuthenticatorAppDialog.vue, AddPaymentMethodDialog.vue, CardAddEditDialog.vue
|
||||
- PricingPlanDialog.vue, ReferAndEarnDialog.vue, UserUpgradePlanDialog.vue
|
||||
|
||||
- Te verwijderen uit apps/app/src/@core/components/:
|
||||
- BuyNow.vue — 'buy template' knop
|
||||
- TheCustomizer.vue — theme demo-customizer (niet nodig in productie)
|
||||
|
||||
- Te verwijderen uit apps/app/src/pages/:
|
||||
- second-page.vue — demo pagina
|
||||
|
||||
- Te verwijderen uit apps/app/public/:
|
||||
- mockServiceWorker.js — MSW service worker (alleen in dev nodig, niet committen)
|
||||
|
||||
### Stap 2 — API-laag opruimen in apps/app/ **NU**
|
||||
|
||||
Een centrale axios instance — dubbele laag verwijderen
|
||||
|
||||
- **!** apps/app/ heeft nu drie overlappende API-bestanden: src/lib/api-client.ts, src/utils/api.ts, en src/composables/useApi.ts. Dit moet worden een bestand.
|
||||
|
||||
- ☐ **Bepaal: src/lib/axios.ts wordt de ENIGE axios instance**
|
||||
|
||||
```ts
|
||||
// apps/app/src/lib/axios.ts — de ENIGE axios instance
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL + '/api/v1',
|
||||
withCredentials: true,
|
||||
headers: { 'Accept': 'application/json' },
|
||||
})
|
||||
|
||||
// Request interceptor: voeg Bearer token toe
|
||||
api.interceptors.request.use(config => {
|
||||
const auth = useAuthStore()
|
||||
if (auth.token) config.headers.Authorization = `Bearer ${auth.token}`
|
||||
return config
|
||||
})
|
||||
|
||||
// Response interceptor: 401 → redirect naar login
|
||||
api.interceptors.response.use(
|
||||
res => res,
|
||||
err => {
|
||||
if (err.response?.status === 401) useAuthStore().logout()
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
```
|
||||
|
||||
- ☐ Verwijder daarna: src/lib/api-client.ts en src/utils/api.ts
|
||||
- ☐ Hernoem src/composables/useApi.ts → src/composables/useApiHelpers.ts (algemene helpers)
|
||||
- ☐ Doe hetzelfde voor apps/portal/ en apps/admin/ (zelfde patroon)
|
||||
|
||||
### Stap 3 — TanStack Query installeren **NU**
|
||||
|
||||
Ontbreekt nog — vereist voor alle API state management
|
||||
|
||||
- ☐ **Installeer in alle drie apps (app/, admin/, portal/)**
|
||||
|
||||
```sh
|
||||
cd apps/app && pnpm add @tanstack/vue-query
|
||||
cd ../admin && pnpm add @tanstack/vue-query
|
||||
cd ../portal && pnpm add @tanstack/vue-query
|
||||
```
|
||||
|
||||
- ☐ Registreer in main.ts van elke app
|
||||
|
||||
```ts
|
||||
// apps/app/src/main.ts — voeg toe
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
|
||||
app.use(VueQueryPlugin, {
|
||||
queryClientConfig: {
|
||||
defaultOptions: {
|
||||
queries: { staleTime: 1000 * 60 * 5, retry: 1 },
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- ☐ Installeer ook formuliervalidatie (alle apps)
|
||||
|
||||
```sh
|
||||
pnpm add vee-validate zod @vee-validate/zod
|
||||
```
|
||||
|
||||
### Stap 4 — Backend dependencies installeren **NU**
|
||||
|
||||
Spatie packages zijn vereist voor fase 1
|
||||
|
||||
- ☐ **Installeer Spatie packages in api/**
|
||||
|
||||
```sh
|
||||
cd api
|
||||
composer require spatie/laravel-permission
|
||||
composer require spatie/laravel-activitylog
|
||||
composer require spatie/laravel-medialibrary
|
||||
composer require barryvdh/laravel-dompdf
|
||||
composer require endroid/qr-code
|
||||
|
||||
# Publiceer configs
|
||||
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
|
||||
php artisan vendor:publish --provider="Spatie\LaravelActivitylog\ActivitylogServiceProvider"
|
||||
|
||||
# Voeg HasUlids toe als trait — Laravel native, geen extra package nodig
|
||||
# use Illuminate\Database\Eloquent\Concerns\HasUlids;
|
||||
```
|
||||
|
||||
- ☐ Voeg PortalToken middleware toe (skeleton voor later)
|
||||
|
||||
```sh
|
||||
php artisan make:middleware PortalTokenMiddleware
|
||||
# Registreer in bootstrap/app.php als 'portal.token'
|
||||
```
|
||||
|
||||
- ☐ Update config/cors.php voor drie frontend origins
|
||||
|
||||
```php
|
||||
// config/cors.php
|
||||
'allowed_origins' => [
|
||||
env('FRONTEND_ADMIN_URL', 'http://localhost:5173'),
|
||||
env('FRONTEND_APP_URL', 'http://localhost:5174'),
|
||||
env('FRONTEND_PORTAL_URL','http://localhost:5175'),
|
||||
],
|
||||
```
|
||||
|
||||
### Stap 5 — Helper bestanden aanmaken **NU**
|
||||
|
||||
CLAUDE.md, .cursorrules, docs/ — dit is het belangrijkste wat je doet
|
||||
|
||||
- **!** Dit zijn de vier bestanden die Claude Code altijd laadt als context. Een uur hieraan besteden bespaart honderden uren aan correcties. Gebruik de volledige inhoud uit Dev Guide sectie 3.
|
||||
|
||||
- ☐ **Maak aan in de root van je project:**
|
||||
|
||||
```sh
|
||||
# Root van het project (naast api/ en apps/)
|
||||
touch CLAUDE.md # Dev Guide sectie 3.1 — volledig invullen
|
||||
touch .cursorrules # Dev Guide sectie 3.2 — volledig invullen
|
||||
mkdir -p docs
|
||||
touch docs/SCHEMA.md # Schema uit Design Document v1.3 sectie 3.5
|
||||
touch docs/API.md # API contract — begin met auth + organisations + events
|
||||
```
|
||||
|
||||
- ☐ Voeg toe aan CLAUDE.md (update ten opzichte van Dev Guide v1.0):
|
||||
|
||||
- Portal architectuur: een app, twee toegangsmodi
|
||||
- Login-based (auth:sanctum): vrijwilligers, crew — persons met user_id
|
||||
- Token-based (portal.token middleware): artiesten, leveranciers, pers — persons zonder user_id
|
||||
|
||||
- apps/ mapping: admin/ = Super Admin, app/ = Organizer, portal/ = Externe gebruikers
|
||||
|
||||
- CORS: drie origins configureren in zowel Laravel als Vite dev server
|
||||
|
||||
### Stap 6 — Vite dev ports configureren **NU**
|
||||
|
||||
Elk frontend-app een eigen port zodat CORS werkt
|
||||
|
||||
- ☐ **Pas vite.config.ts aan in elke app**
|
||||
|
||||
```ts
|
||||
// apps/admin/vite.config.ts → port: 5173
|
||||
// apps/app/vite.config.ts → port: 5174
|
||||
// apps/portal/vite.config.ts → port: 5175
|
||||
server: {
|
||||
port: 5174, // aanpassen per app
|
||||
proxy: { // optioneel: proxy API calls in dev
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- ☐ Voeg .env.local toe aan elke app
|
||||
|
||||
```sh
|
||||
# apps/app/.env.local
|
||||
VITE_API_URL=http://localhost:8000
|
||||
VITE_APP_NAME=Crewli
|
||||
|
||||
# apps/portal/.env.local
|
||||
VITE_API_URL=http://localhost:8000
|
||||
VITE_APP_NAME=Crewli Portal
|
||||
```
|
||||
|
||||
### Stap 7 — Eerste Claude Code prompt sturen **NU**
|
||||
|
||||
Je bent klaar om de eerste module te laten genereren
|
||||
|
||||
- **!** Na stappen 1-6 ben je klaar. Onderstaande prompt kun je letterlijk in Claude Code plakken.
|
||||
|
||||
- ☐ **Start Claude Code vanuit je project root**
|
||||
|
||||
```sh
|
||||
cd /pad/naar/crewli
|
||||
claude
|
||||
```
|
||||
|
||||
- ☐ **Plak deze prompt als eerste bericht:**
|
||||
|
||||
```
|
||||
Lees eerst volledig /CLAUDE.md en /dev-docs/SCHEMA.md.
|
||||
|
||||
Bouw daarna Fase 1 — Foundation — in deze volgorde:
|
||||
|
||||
1. Migrations in api/database/migrations/:
|
||||
- update users tabel (voeg timezone, locale, deleted_at toe)
|
||||
- organisations (ULID, name, slug, billing_status, settings JSON, deleted_at)
|
||||
- organisation_user pivot (int PK, user_id, organisation_id, role)
|
||||
- user_invitations (ULID, email, invited_by, organisation_id, event_id nullable,
|
||||
token ULID unique, status enum, expires_at)
|
||||
- events (ULID, organisation_id, name, slug, start_date, end_date,
|
||||
timezone, status enum, deleted_at)
|
||||
- event_user_roles pivot
|
||||
|
||||
2. Models: User (update), Organisation, UserInvitation, Event
|
||||
- HasUlids op alle business modellen
|
||||
- SoftDeletes op Organisation, Event
|
||||
- OrganisationScope global scope op Event
|
||||
- Alle relaties (hasMany, belongsToMany)
|
||||
|
||||
3. Spatie Permission setup:
|
||||
- RoleSeeder: super_admin, org_admin, org_member,
|
||||
event_manager, staff_coordinator, volunteer_coordinator
|
||||
|
||||
4. Auth: LoginController, LogoutController, MeController
|
||||
- Sanctum token-based (geen session)
|
||||
- MeController geeft user + organisations + active event roles terug
|
||||
|
||||
5. Organisations: OrganisationController (index/show/store/update)
|
||||
- OrganisationPolicy
|
||||
- OrganisationRequest (store + update)
|
||||
- OrganisationResource
|
||||
|
||||
6. Events: EventController (index/show/store/update) genest onder organisations
|
||||
- EventPolicy
|
||||
- EventRequest
|
||||
- EventResource
|
||||
|
||||
7. Feature tests voor alles bovenstaande:
|
||||
- Happy path (200/201)
|
||||
- Unauthenticated (401)
|
||||
- Wrong organisation (403)
|
||||
|
||||
Draai na elke stap: php artisan test
|
||||
Los fouten op voor je verdergaat.
|
||||
Stop na stap 7 en rapporteer wat er gebouwd is en of alle tests groen zijn.
|
||||
```
|
||||
|
||||
## 3 — Daarna: Frontend Fase 1 + Fase 2 Planning
|
||||
|
||||
### Stap 8 — Frontend Fase 1 — Auth + Shell **DAARNA**
|
||||
|
||||
Na groene backend tests
|
||||
|
||||
- Auth flow bouwen in apps/app/
|
||||
- stores/useAuthStore.ts — token opslaan, isAuthenticated, me() laden
|
||||
- pages/login.vue — Vuexy login layout gebruiken (al aanwezig als basis)
|
||||
- router guard — redirect naar login als niet authenticated
|
||||
|
||||
- Navigatiestructuur Crewli invullen
|
||||
- src/navigation/vertical/index.ts — verwijder Vuexy demo-items, voeg Crewli items toe
|
||||
- Events → Sections → Shifts, Persons, Artists, Briefings, Rapportage
|
||||
|
||||
- CASL permissions setup
|
||||
- src/plugins/casl.ts koppelen aan Spatie roles vanuit auth/me response
|
||||
- useAbility() gebruiken voor conditionele UI-elementen (aanpassen-knop tonen/verbergen)
|
||||
|
||||
### Stap 9 — Fase 2 — Core module volgorde **DAARNA**
|
||||
|
||||
Na werkende auth en shell
|
||||
|
||||
- **Bouw in deze volgorde — altijd eerst backend, dan frontend:**
|
||||
|
||||
1. Crowd Types + Persons + Crowd Lists (basis guest management)
|
||||
2. Festival Sections + Time Slots + Shifts (het hart van het platform)
|
||||
3. Shift Assignments — claim workflow met approval flow
|
||||
4. Vrijwilligers registratie (public form → portal login flow)
|
||||
5. Accreditatie engine + Access Zones
|
||||
6. Basis briefings (template + send + track)
|
||||
|
||||
- **!** Elke module: gebruik de module-prompt uit Dev Guide sectie 5.2. Altijd: migrations → model → factory → policy → resource → controller → test → composable → pagina.
|
||||
|
||||
### Stap 10 — Portal app inrichten **DAARNA**
|
||||
|
||||
Na werkende apps/app/ basis
|
||||
|
||||
- apps/portal/ strippen tot portal-layout
|
||||
- Verwijder: sidebar nav, customizer, dark mode toggle, demo-dialogen
|
||||
- Maak: PortalLayout.vue — top-bar met event-logo, naam, hamburger menu
|
||||
- Maak: twee router guards — loginGuard en tokenGuard
|
||||
|
||||
- Login-flow (vrijwilligers/crew): zelfde /api/v1/auth/login endpoint als app/
|
||||
- Token-flow (artiesten/leveranciers): POST /api/v1/portal/token-auth
|
||||
- Routing op basis van accessMode computed + person.crowd_type
|
||||
|
||||
## 4 — Checklist: Ben Je Klaar om te Starten?
|
||||
|
||||
| **#** | **Actie** | **Status** |
|
||||
|:--:|----|:--:|
|
||||
| **1** | apps/band/ hernoemd naar apps/portal/ | ☐ Klaar |
|
||||
| **2** | Demo-rommel verwijderd uit apps/app/ | ☐ Klaar |
|
||||
| **3** | Dubbele API-laag opgeruimd → een src/lib/axios.ts per app | ☐ Klaar |
|
||||
| **4** | TanStack Query geinstalleerd in alle drie apps + geregistreerd in main.ts | ☐ Klaar |
|
||||
| **5** | VeeValidate + Zod geinstalleerd in alle drie apps | ☐ Klaar |
|
||||
| **6** | Spatie packages geinstalleerd in api/ + configs gepubliceerd | ☐ Klaar |
|
||||
| **7** | PortalTokenMiddleware skeleton aangemaakt | ☐ Klaar |
|
||||
| **8** | config/cors.php: drie frontend origins geconfigureerd | ☐ Klaar |
|
||||
| **9** | Vite dev ports: admin=5173, app=5174, portal=5175 | ☐ Klaar |
|
||||
| **10** | .env.local aangemaakt per app met VITE_API_URL | ☐ Klaar |
|
||||
| **11** | CLAUDE.md aangemaakt en volledig ingevuld (Dev Guide sectie 3.1 + portal architectuur) | ☐ Klaar |
|
||||
| **12** | .cursorrules aangemaakt (Dev Guide sectie 3.2) | ☐ Klaar |
|
||||
| **13** | docs/SCHEMA.md aangemaakt (schema uit Design Document v1.3 sectie 3.5) | ☐ Klaar |
|
||||
| **14** | docs/API.md aangemaakt met initiele route-lijst | ☐ Klaar |
|
||||
| **15** | claude (Claude Code) gestart en Fase 1 prompt ingevoerd | ☐ Klaar |
|
||||
| **16** | php artisan test — alle tests groen | ☐ Klaar |
|
||||
|
||||
Crewli Start Guide v1.0 — Maart 2026 | Architectuur + Actielijst
|
||||
Reference in New Issue
Block a user