chore(docs): delete obsolete bootstrap and prompt-template docs

Five files removed, all describing project states that no longer
apply post-WS-TOOLING-001:

- .cursor/instructions.md (8.4 KB): Phase 1-4 roadmap with all
  checkboxes empty; Phase 1 has been done for ~6 months. Broken
  'make portal' target. Content overlaps with CLAUDE.md.
- .cursor/ARCHITECTURE.md (18.9 KB): pre-WS-3 framing (dual SPA,
  dual cookies, dual SANCTUM_STATEFUL_DOMAINS) AND pre-Form-Builder
  schema (volunteer_profiles, public_forms with JSON fields). All
  six sections superseded by SCHEMA.md, AUTH_ARCHITECTURE.md,
  design-document.md, API.md, 102_multi_tenancy.mdc.
- dev-docs/MASTER_PROMPT_CC.md (13 KB): 'paste this above every task'
  workflow superseded by auto-loaded CLAUDE.md and structured
  Claude Chat-authored prompts. Stale dual-SPA + pre-Form-Builder
  assumptions throughout.
- dev-docs/MASTER_PROMPT_CURSOR.md (7.5 KB): same workflow obsoletion;
  Cursor is now IDE-only (Claude Code does all implementation).
  .cursor/rules/ system handles IDE-level guidance.
- dev-docs/dev-guide.md (32 KB): bootstrap-from-scratch document
  containing embedded snapshots of pre-Form-Builder CLAUDE.md,
  pre-Form-Builder SCHEMA.md, pre-Form-Builder API.md as
  copy-paste templates. Section 5 prompts pre-WS-TOOLING-001 era.
  Section 6 (agents) overlaps with CLAUDE_CODE_TOOLING.md.

Total: ~80 KB doc-rot removed.

Cross-reference check found four files outside the deleted set
referencing the deleted paths; all updated in the same commit:

- README.md: Documentation table rebuilt around CLAUDE.md +
  dev-docs/* (also dropped stale resources/design/ row pointing
  at a directory that no longer exists, and corrected docs/*
  paths to dev-docs/*)
- dev-docs/CLAUDE_DESKTOP_SETUP.md: dropped MASTER_PROMPT_CC,
  MASTER_PROMPT_CURSOR, dev-guide entries from the
  bewust-verwijderd exclusion list; updated Gerelateerd pointer
  from dev-guide.md -> SETUP.md
- dev-docs/ARCH-CONSOLIDATION-2026-04.md: updated future-distribution
  pointer from dev-guide.md -> SETUP.md (sprint briefing is
  historical so the change is purely doc-hygiene)
- dev-docs/VIBE_CODING_CHECKLIST.md: removed Dev guide row from
  the bestandspaden table

Remaining references in dev-docs/BACKLOG.md (lines 862-869) live
inside the TECH-DOCS-APPS-PORTAL-PURGE entry that closes in the
next commit.

Canonical replacements: dev-docs/SETUP.md (rewritten this PR),
CLAUDE.md, CLAUDE_CODE_TOOLING.md, and the ARCH-*.md series.
This commit is contained in:
2026-05-06 02:14:10 +02:00
parent 2c4d2257ae
commit d33c119d75
9 changed files with 8 additions and 1939 deletions

View File

@@ -4,7 +4,7 @@
> **Doelgroep:** Bert (product owner + solo dev), Claude (architect/PM rol in chat), Claude Code (executie).
> **Positie t.o.v. andere docs:** dit document is tijdelijk. Na afronding van de sprint wordt
> de inhoud gedistribueerd over `/CLAUDE.md`, `/dev-docs/SCHEMA.md`, `/dev-docs/ARCH-*.md` en
> `/dev-docs/dev-guide.md`. Tijdens de sprint is het de enkele bron van waarheid voor wát
> `/dev-docs/SETUP.md`. Tijdens de sprint is het de enkele bron van waarheid voor wát
> we aan het doen zijn en waaróm.
---

View File

@@ -111,11 +111,8 @@ start-guide.md
SETUP.md
SECURITY_AUDIT.md
SCHEMA.md
MASTER_PROMPT_CURSOR.md
MASTER_PROMPT_CC.md
form-builder-migration-playbook.md
form-builder-getting-started.md
dev-guide.md
design-document.md
COPY_CATALOGUE.md
BACKLOG.md
@@ -169,4 +166,4 @@ subprocess-spawn (bij volgende tool call).
## Gerelateerd
- `CLAUDE.md` (repo root) — instructies voor Claude Code, niet Claude Desktop
- `dev-docs/dev-guide.md` — ontwikkelworkflow
- `dev-docs/SETUP.md` — ontwikkelworkflow

View File

@@ -1,294 +0,0 @@
# Crewli — Claude Code Master Prompt
# Plak dit BOVEN elke task. Vervang [TASK] onderaan.
## MANDATORY PREAMBLE — READ BEFORE DOING ANYTHING
Read `/CLAUDE.md` and `/dev-docs/SCHEMA.md` in full before starting. These are
your single source of truth. Do not deviate from them. Do not make assumptions
about the database schema — always verify against SCHEMA.md. Also read
`/dev-docs/API.md` for the existing API contract.
If the task involves a module that already has existing code, read that code
first. Understand the current state before making changes.
## PROJECT CONTEXT
Crewli is a multi-tenant SaaS platform for event and festival management.
- **Backend:** Laravel 12 REST API (no Blade views, no Inertia), Sanctum auth,
Spatie Permission, MySQL 8, Redis
- **Frontend:** Two standalone Vue 3 + TypeScript SPAs on Vuexy 9.5 /
Vuetify 3.10 — `apps/app/` (port 5174), `apps/portal/` (port 5175)
- **State:** Pinia + TanStack Vue Query
- **Forms:** VeeValidate + Zod
- **API base path:** `/api/v1/`
- **Testing:** PHPUnit (backend), Vitest (frontend)
### Repository structure
```
crewli/
├── api/ # Laravel 12 backend
│ ├── app/
│ │ ├── Http/
│ │ │ ├── Controllers/Api/V1/
│ │ │ ├── Middleware/
│ │ │ └── Requests/ # Form Requests per endpoint
│ │ ├── Models/
│ │ ├── Policies/
│ │ ├── Services/ # Business logic (NOT in controllers)
│ │ ├── Enums/ # PHP Enums for all status/type fields
│ │ ├── Events/ + Listeners/
│ │ └── Jobs/ # Queued jobs (briefings, notifications)
│ ├── database/
│ │ ├── migrations/
│ │ ├── factories/
│ │ └── seeders/
│ └── tests/Feature/Api/V1/
├── apps/
│ ├── app/ # Organizer + Platform Admin SPA (main app)
│ │ └── src/
│ │ ├── lib/axios.ts # THE ONLY axios instance
│ │ ├── composables/api/ # TanStack Query composables
│ │ ├── stores/ # Pinia stores
│ │ ├── types/ # TypeScript interfaces
│ │ └── pages/
│ └── portal/ # Dual-mode portal
├── dev-docs/ # Developer documentation (source of truth)
│ ├── SCHEMA.md
│ ├── API.md
│ ├── BACKLOG.md
│ ├── design-document.md
│ ├── dev-guide.md
│ └── start-guide.md
├── docs/ # VitePress end-user documentation (Dutch)
├── CLAUDE.md # This file's companion — workspace rules
└── .cursorrules
```
### Key architectural decisions (final, non-negotiable)
- **ULID** primary keys on all business tables via `HasUlids` — NEVER UUID v4
- **Integer auto-increment** on pure pivot tables only
- **JSON columns** exclusively for opaque config (settings, blocks) — never
for queryable data
- **Multi-tenancy** via `OrganisationScope` Eloquent Global Scope on all
event-related models
- **Soft delete** per table type as documented in SCHEMA.md — immutable audit
records (check_ins, form_submissions, briefing_sends) do NOT get soft delete
- **Portal dual-mode:** Sanctum session for volunteers/crew (persistent
identity), `portal.token` middleware for artists/suppliers/press
(event-specific, no account)
---
## ZERO-COMPROMISE RULES — VIOLATIONS ARE BLOCKERS
These rules are absolute. No workarounds. No "we'll fix it later". No partial
implementations. If something cannot be done properly, STOP and report it.
### Architecture & design
1. **ARCHITECTURE FIRST.** If the task requires a new pattern, data structure,
or integration that isn't documented in CLAUDE.md or SCHEMA.md — stop and
ask. Do not invent architecture. Write an Architecture Decision Record (ADR)
in `/dev-docs/decisions/` if a significant design choice is needed.
2. **DELETE OVER ADAPT.** If you find duplicate logic, conflicting patterns, or
legacy code that does the same thing differently: delete the worse version.
Do not build alongside it. Do not "wrap" it. If two implementations exist,
one must die. Consolidate to one source of truth.
3. **STRICT LAYERING.** Business logic belongs in `app/Services/`, NEVER in
controllers. Controllers handle HTTP concerns only: receive request, call
service, return resource. Data access patterns go through Eloquent models
with proper scopes. No "quick fixes" in the wrong layer.
- Controller → receives FormRequest, calls Service, returns API Resource
- Service → contains business logic, validation beyond FormRequest,
orchestration
- Model → relationships, scopes, accessors, mutators
- Job → async work dispatched by Service
4. **CONTRACT-FIRST.** Before implementing any module:
- Define the PHP Enum(s) in `app/Enums/` for all status/type fields
- Define the API Resource (response shape) first
- Define the Form Request (input validation) first
- Then implement the Service and Controller
Types and contracts define behaviour. Implementation follows.
5. **CONSISTENCY OVER CLEVERNESS.** Use the same pattern for every similar
problem. If existing modules use a specific approach for pagination, error
handling, or resource loading — use that exact same approach. Never
introduce a "better" alternative pattern without refactoring ALL existing
code to match. One pattern per problem type, across the entire codebase.
6. **SINGLE SOURCE OF TRUTH.** Every piece of information exists in exactly
one place:
- Enum values → PHP Enum class (not string literals)
- Validation rules → Form Request (not duplicated in frontend)
- Response shape → API Resource (not ad-hoc arrays)
- Config values → .env / config files (not hardcoded)
- Schema definition → SCHEMA.md (not guessed)
### Code quality
7. **NO TODO / FIXME / HACK.** Zero tolerance. If you cannot complete
something, stop and report it. Do not leave stubs, placeholders, or
"implement later" comments. Every file you touch must be production-ready.
8. **NO UNTYPED CODE.** PHP: `declare(strict_types=1)` on every file. Return
types on all methods. Typed properties. Use PHP Enums (not string literals)
for all status, type, and role fields. No `mixed` where a concrete type
or union type would work.
9. **NO GENERIC NAMES.** Names must be specific and self-documenting:
-`DataService`, `Helper`, `Manager`, `handleData()`, `processItem()`
-`ShiftAssignmentService`, `VolunteerAvailabilityChecker`,
`resolveShiftConflict()`, `calculateFillRate()`
10. **NO SILENT ERROR HANDLING.** No empty catch blocks. No `catch { return
null; }`. Every error must be: logged (via `Log::error()` with context),
or rethrown, or handled with a proper API error response. Use
`report($e)` for unexpected errors.
### Testing
11. **TESTS ARE DESIGN, NOT AFTERTHOUGHT.** Tests define expected behaviour.
Every controller needs feature tests covering:
- 200/201 (happy path for each action)
- 401 (unauthenticated access)
- 403 (wrong organisation — cross-tenant access attempt)
- 403 (insufficient role/permission)
- 422 (validation errors with specific field assertions)
- Edge cases specific to the module (e.g., shift conflict, capacity full)
Run `php artisan test` after EVERY module. Fix failures before proceeding.
Never skip, comment out, or mark tests as incomplete.
12. **FACTORIES ARE REALISTIC.** Use realistic Dutch test data (names,
addresses, company names) that reflects actual usage. Factories must create
valid, complete records — no missing required fields or placeholder values.
### Data & persistence
13. **DATA MODEL IS SACRED.** Never deviate from SCHEMA.md. Every column,
every constraint, every index documented there must be in the migration.
If SCHEMA.md is unclear, ask — do not guess.
14. **EVERY MIGRATION HAS down().** The `down()` method must cleanly reverse
the `up()`. Drop tables, remove columns, restore previous state. No
`down()` methods that throw or do nothing.
15. **INDEXES ARE MANDATORY.** Every foreign key column, every column used in
WHERE/ORDER BY clauses, every unique constraint from SCHEMA.md must have
an explicit index. Verify composite indexes match the documented patterns.
### Security & multi-tenancy
16. **NO UNSCOPED QUERIES.** Every query on event-related models must be
scoped to `organisation_id` via `OrganisationScope`. Mental test: can
User A from Org 1 ever see, modify, or infer the existence of data from
Org 2? If yes → security bug → fix immediately.
17. **POLICIES ARE COMPLETE.** Every policy method checks:
- Does the user belong to the correct organisation?
- Does the user have the required Spatie role/permission for this action?
- No `return true` placeholders. No missing methods.
18. **FORM REQUESTS ARE COMPLETE.** Every store/update has a Form Request
with full validation matching SCHEMA.md constraints: required, nullable,
max length, enum values (referencing the PHP Enum), exists rules with
proper scoping (e.g., `exists:events,id` scoped to organisation).
### API responses
19. **NO BARE MODEL RETURNS.** Every API response goes through an API
Resource. Never `return $model`, `$model->toArray()`, or raw arrays.
Resources define the public contract. Computed fields (fill_rate,
status_label, slot counts) belong in the Resource.
20. **CONSISTENT RESPONSE STRUCTURE.** Follow the existing pattern:
`{ data: {...}, meta: {...} }` for paginated lists. Consistent error
format with `{ message: "...", errors: {...} }` for validation failures.
### Resilience & operations
21. **IDEMPOTENT OPERATIONS.** Every queued job must be safe to retry.
Check-before-act: verify state hasn't changed. Use database transactions
for multi-step mutations. Queued notifications and external API calls
(Zender SMS/WhatsApp) must handle duplicates gracefully.
22. **OBSERVABILITY.** Log significant business events using
`spatie/laravel-activitylog` (already installed). Every create, update,
delete, and status change on business entities must be logged with:
- `causedBy($user)` — who did it
- `performedOn($model)` — what was affected
- `withProperties([...])` — relevant context (old values, new values)
23. **API VERSIONING.** All routes under `/api/v1/`. Controllers in
`App\Http\Controllers\Api\V1\`. Never introduce breaking changes to
existing endpoints — add new fields, don't rename or remove.
### Process
24. **MODULE GENERATION ORDER.** Always follow this sequence. No skipping.
1. PHP Enum(s) for status/type fields (`app/Enums/`)
2. Migration(s) — verify against SCHEMA.md
3. Eloquent Model with: HasUlids, HasFactory, SoftDeletes (if documented),
OrganisationScope (if event-related), relationships, scopes, accessors
4. Factory with realistic Dutch test data
5. Service class for business logic (`app/Services/`)
6. Policy for authorisation
7. Form Request(s) for validation
8. API Resource for response transformation
9. Resource Controller (thin — delegates to Service)
10. Routes in `api/routes/api.php`
11. Feature tests — run them, fix failures
12. Activity log integration in Service methods
13. Update `/dev-docs/API.md` with new routes
25. **GIT.** Auto-commit after each completed module:
`feat(module-name): add backend scaffold with tests`
---
## VERIFICATION CHECKLIST (run before reporting "done")
```bash
# All tests pass
php artisan test
# Database rebuilds cleanly
php artisan migrate:fresh --seed
# New routes are visible
php artisan route:list --path=api/v1
# No forbidden patterns
grep -rn "TODO\|FIXME\|HACK\|dd(\|dump(\|var_dump\|Model::all()" \
api/app/ api/tests/ --include="*.php"
# No UUID v4 in migrations
grep -rn "uuid(" api/database/migrations/ --include="*.php"
# Static analysis (if configured)
./vendor/bin/phpstan analyse
```
Manual verification:
- [ ] Every new model has: HasUlids, HasFactory, correct SoftDeletes, complete
$fillable, all relationships from SCHEMA.md
- [ ] Every new model with event data has OrganisationScope
- [ ] Every controller action is covered by a Policy method
- [ ] Every Service method logs activity via spatie/laravel-activitylog
- [ ] Every Form Request references PHP Enums (not string literals) for
enum validation
- [ ] Business logic is in Service classes, not in Controllers
- [ ] API Resource includes computed fields where applicable
- [ ] No N+1: index actions use `with()` for all accessed relationships
- [ ] `/dev-docs/API.md` updated with new routes
---
## TASK
[INSERT SPECIFIC TASK HERE]

View File

@@ -1,176 +0,0 @@
# Crewli — Cursor Master Prompt
# Plak dit BOVEN elke task. Vervang [TASK] onderaan.
## MANDATORY PREAMBLE
Read `@CLAUDE.md` and `@dev-docs/SCHEMA.md` before starting. Use `@workspace`
for full codebase context. These documents are your source of truth.
If modifying existing code, read the current implementation first. Understand
the patterns already in use before writing anything new.
## PROJECT CONTEXT
Crewli — multi-tenant SaaS for event/festival management.
- **Frontend:** Vue 3 + TypeScript + Vuexy 9.5 (Vuetify 3.10) + Pinia +
TanStack Vue Query + VeeValidate + Zod
- **Two SPAs:**
- `apps/app/` — Organizer + Platform Admin main app (port 5174)
- `apps/portal/` — Dual-mode portal (port 5175)
- **API base:** `VITE_API_URL` from `.env.local` (default `http://localhost:8000`)
- **Axios instance:** `src/lib/axios.ts` — this is the ONLY axios instance.
Never create another. Never import axios directly in components.
- **Backend Enums:** PHP Enums in `api/app/Enums/` define all valid status/type
values. TypeScript equivalents must mirror these exactly in `src/types/`.
### Frontend file structure (per app)
```
src/
├── lib/axios.ts # Singleton axios instance (DO NOT DUPLICATE)
├── types/ # TypeScript interfaces per module
│ └── [module].ts
├── composables/api/ # TanStack Query composables per module
│ └── use[Module].ts
├── stores/ # Pinia stores (cross-component state only)
│ └── use[Module]Store.ts
├── pages/ # Page components
│ └── [module]/
│ ├── index.vue # List view
│ └── [id].vue # Detail view (or side panel)
└── router/ # Vue Router config
```
---
## ZERO-COMPROMISE RULES — VIOLATIONS ARE BLOCKERS
### TypeScript & typing
1. **NO `any` TYPES. EVER.** Every variable, prop, emit, return type, ref,
reactive, computed, and API response is fully typed. If you don't know the
type, check the backend API Resource in `api/app/Http/Resources/` — that
defines the contract. Create matching interfaces in `src/types/[module].ts`.
2. **TYPES FIRST.** Before writing any composable or component for a new
module, create the TypeScript interfaces in `src/types/[module].ts`. These
mirror the backend API Resource shape. Include:
- Entity interface (e.g., `Shift`, `Person`, `Event`)
- Create/Update DTOs (matching backend Form Request fields)
- Enum-like union types or const objects matching backend PHP Enums
- Paginated response wrapper if applicable
3. **ENUMS AS CONST OBJECTS.** Mirror backend PHP Enums as TypeScript const
objects with `as const`, not as loose string unions scattered across files:
```typescript
// src/types/shift.ts
export const ShiftAssignmentStatus = {
PENDING_APPROVAL: 'pending_approval',
APPROVED: 'approved',
REJECTED: 'rejected',
CANCELLED: 'cancelled',
COMPLETED: 'completed',
} as const
export type ShiftAssignmentStatus = typeof ShiftAssignmentStatus[keyof typeof ShiftAssignmentStatus]
```
### Architecture & patterns
4. **NO DIRECT AXIOS IN COMPONENTS.** All API calls go through composables in
`composables/api/use[Module].ts` using TanStack Query (`useQuery` /
`useMutation`). Components never import axios or `src/lib/axios.ts`.
The composable is the only layer that knows about HTTP.
5. **NO OPTIONS API.** Always `<script setup lang="ts">`. Props via
`defineProps<{...}>()`, emits via `defineEmits<{...}>()`, expose via
`defineExpose()`. No `export default { ... }`.
6. **NO PROP DRILLING.** If state needs to cross more than one component
boundary, use a Pinia store (`src/stores/use[Module]Store.ts`). Access
reactive state via `storeToRefs()`. Mutations only through store actions.
7. **DELETE OVER ADAPT.** If you find duplicate composables, overlapping
stores, or conflicting patterns: delete the worse version. Do not build
alongside existing code that does the same thing. One implementation per
concern.
8. **CONSISTENCY OVER CLEVERNESS.** Look at how existing modules handle the
same pattern (table views, form dialogs, detail panels, error handling).
Use the exact same approach. Never introduce a "better" alternative without
refactoring ALL existing modules to match.
9. **SINGLE SOURCE OF TRUTH.** Don't duplicate:
- Validation logic → Zod schema mirrors backend Form Request. One schema
per form, referenced by VeeValidate.
- Enum values → const objects in `src/types/`, matching backend Enums.
Never use raw string literals like `'approved'` in templates or logic.
- API URLs → constructed in composable from module prefix, never
hardcoded in components.
### UI & UX
10. **NO CUSTOM CSS WHERE VUETIFY HAS A SOLUTION.** Before writing any CSS:
check if a Vuetify utility class, component prop, or slot achieves the
result. Custom CSS is a last resort, must be `<style scoped>`, and must
have a comment explaining why Vuetify couldn't handle it.
11. **EVERY PAGE HAS THREE STATES:**
- **Loading:** Vuetify skeleton loader or progress indicator
- **Error:** User-facing message with retry action (`v-alert` with retry
button). Show what went wrong in user terms, not technical jargon.
- **Empty:** Helpful message explaining why the list is empty and what
action to take (not a blank white screen).
Never show only the happy path.
12. **MOBILE-FIRST.** Every component must be usable at 375px width. Use
Vuetify's responsive props (`cols`, `sm`, `md`, `lg` on `v-col`). Never
use fixed pixel widths for layout. Tables on mobile → card views or
horizontal scroll with visual indicator.
13. **FORMS USE VEEVALIDATE + ZOD.** Define the Zod schema matching the
backend Form Request rules. No inline validation logic in templates. Use
`useForm()` and `useField()` from VeeValidate with the Zod resolver.
### Code quality
14. **NO TODO / FIXME / HACK.** Complete the implementation or report the
blocker. No stubs, no "implement later", no placeholder components.
15. **NO ORPHANED IMPORTS OR UNUSED VARIABLES.** Run `npx tsc --noEmit`
after every change. Zero errors, zero warnings. Dead code is deleted
immediately, not commented out.
16. **NO GENERIC NAMES.** Component and composable names must be specific:
- ❌ `DataTable.vue`, `useApi.ts`, `helpers.ts`, `utils.ts`
- ✅ `ShiftAssignmentTable.vue`, `useShifts.ts`,
`formatShiftTimeRange.ts`
### Routing & auth
17. **ROUTER GUARDS.** Protected routes check auth state via navigation
guard. Portal routes validate token. No unguarded routes to
authenticated content. Redirect to login on 401.
### Process
18. **COMPONENT CREATION ORDER.** For every new page/feature:
1. TypeScript types in `src/types/[module].ts`
2. API composable in `src/composables/api/use[Module].ts`
3. Pinia store in `src/stores/use[Module]Store.ts` (if cross-component)
4. Vue page component in `src/pages/[module]/`
5. Router entry in `src/router/`
19. **VERIFY BEFORE DONE:**
- `npx tsc --noEmit` — zero errors
- No `any` types anywhere (search: `grep -rn ": any\|as any" src/`)
- No TODO/FIXME/HACK
- All three states (loading, error, empty) implemented
- Mobile responsive at 375px
- Consistent with existing module patterns
---
## TASK
[INSERT SPECIFIC TASK HERE]

View File

@@ -40,7 +40,6 @@ overzicht en review-checklist.
| Schema definitie | `/dev-docs/SCHEMA.md` | `/docs/SCHEMA.md` |
| API contract | `/dev-docs/API.md` | `/docs/API.md` |
| Design document | `/dev-docs/design-document.md` | `/docs/design-document.md` |
| Dev guide | `/dev-docs/dev-guide.md` | `/docs/dev-guide.md` |
| User docs (VitePress) | `/docs/` | — |
| Workspace rules | `/CLAUDE.md` (root) | — |
| Axios instance (app) | `apps/app/src/lib/axios.ts` | `src/utils/api.ts` etc. |

View File

@@ -1,798 +0,0 @@
# 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/
│ ├── app/ # Organizer + Platform Admin 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/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/ 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 + Platform Admin SPA (hoofdapp, super admin onder /platform/*)
- 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 |
## Gedeelde frontend packages
`apps/portal/` en `apps/app/` delen een klein aantal schema-gedreven modules. Deze sectie legt vast wat wel en niet in die gedeelde laag hoort, en hoe je de aliases opzet.
### Locatie
`packages/form-schema/` — pure TypeScript, **geen** `package.json`, alias-only. Geen npm-package en geen pnpm workspace member; puur geconsumeerd via per-app TypeScript- en Vite-path-aliases.
### Wat hoort erin
- Schema-types die backend PHP-enums en API-resources mirroren (bv. `PublicFormField`, `FormFieldType`, `PublicFormSchema`)
- Schema-gedreven gedrag: validation rule builders, conditional-logic evaluator, step-navigation, stored-value formatters
- Alles wat pure TypeScript is en in portal én app exact dezelfde output moet produceren
### Wat hoort er NIET in
- Vue SFCs (`.vue` bestanden) — elke app rendert zijn eigen UI
- Vuetify/Vuexy-specifieke code, theming of styling
- Vue Router-code, route-definities of navigation guards
- Pinia stores of app-level state
- Axios-instanties of API-client code
- Composables die afhangen van app-runtime (auth, i18n, router)
### Rationale
Portal is submit-facing, app is builder- en review-facing. De twee apps zullen visueel uit elkaar groeien naarmate product-requirements verschillen. Gedeelde Vue-componenten zouden hun stijlen aan elkaar koppelen — daarom delen we het *contract en de logica* (schema-types, gedrag) maar laat elke app zijn eigen UI bezitten.
### Alias-setup (verplicht op drie plekken per app)
Beide apps moeten `@form-schema/*` registreren in álle drie:
- `apps/<app>/tsconfig.json``compilerOptions.paths`
- `apps/<app>/vite.config.ts``resolve.alias`
- `apps/<app>/vitest.config.ts``resolve.alias`
Alle drie zijn nodig omdat `vitest.config.ts` niet overerft van `vite.config.ts`. Eén van de drie vergeten breekt óf de productie-build, óf de dev-server, óf de test-suite. Wanneer een derde app consumer wordt, wire dan alle drie tegelijk.
### Vue-resolutie (dependency-note)
Het gedeelde package heeft geen `package.json` en kan dus geen eigen dependencies declareren. Om `vue` resolvable te maken tijdens TypeScript-compilatie vanuit `packages/form-schema/src/`, voegen beide apps een `"vue": ["./node_modules/vue"]`-entry toe aan `compilerOptions.paths`. Dit is het minimum; als het package ooit niet-triviale externe imports krijgt (bv. `@vueuse/core`, `lodash`), voeg dan per app een vergelijkbare paths-entry toe — níet een `package.json` in het package — of promoveer het package tot workspace-member.
## 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