Convert the documentation files from Word (.docx) to a more gegeneric Markdown format

This commit is contained in:
2026-04-07 17:28:48 +02:00
parent 19a64e97b9
commit 611c311854
6 changed files with 2365 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View 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 docs/SCHEMA.md — Levend schema-document
Maak een Markdown bestand aan in /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 /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 /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 /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 /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 /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

View 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 /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