chore: align migrations, docs, and frontends with crewli.app setup
- Replace dated migrations with ordered 2026_04_07_* chain; fold users update into base migration - Update OrganisationScope, AppServiceProvider, seeders, api routes, and .env.example - Refresh Cursor rules, CLAUDE.md, Makefile, README, and docs (API, SCHEMA, SETUP) - Adjust admin/app/portal HTML, packages, api-client, events types, and theme config - Update docker-compose and VS Code settings; remove stray Office lock files from resources Made-with: Cursor
This commit is contained in:
171
CLAUDE.md
171
CLAUDE.md
@@ -1,129 +1,132 @@
|
||||
# EventCrew — Claude Code Instructies
|
||||
# Crewli — Claude Code Instructions
|
||||
|
||||
## Project Context
|
||||
## Project context
|
||||
|
||||
EventCrew is een multi-tenant SaaS platform voor event- en festivalbeheer.
|
||||
Gebouwd voor een professionele vrijwilligersorganisatie, met SaaS-uitbreidingspotentieel.
|
||||
Design Document: /docs/EventCrew_Design_Document_v1.3.docx
|
||||
Crewli is a multi-tenant SaaS platform for event and festival management.
|
||||
Built for a professional volunteer organisation, with potential to expand as SaaS.
|
||||
Design document: `/resources/design/Crewli_Design_Document_v1.3.docx`
|
||||
|
||||
## Tech Stack
|
||||
## 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
|
||||
## Repository layout
|
||||
|
||||
- api/ Laravel backend
|
||||
- apps/admin/ Super Admin SPA
|
||||
- apps/app/ Organizer SPA (hoofdapp)
|
||||
- apps/portal/ Externe portals (vrijwilliger, artiest, leverancier)
|
||||
- `api/` — Laravel backend
|
||||
- `apps/admin/` — Super Admin SPA
|
||||
- `apps/app/` — Organizer SPA (main product app)
|
||||
- `apps/portal/` — External portal (volunteers, artists, suppliers, etc.)
|
||||
|
||||
## Apps & Portal Architectuur
|
||||
## Apps and portal architecture
|
||||
|
||||
- apps/admin/ = Super Admin — platformbeheer, organisaties aanmaken
|
||||
- apps/app/ = Organizer — event management per organisatie
|
||||
- apps/portal/ = Externe gebruikers — één 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/admin/` — Super Admin: platform management, creating organisations
|
||||
- `apps/app/` — Organizer: event management per organisation
|
||||
- `apps/portal/` — External users: one app, two access modes:
|
||||
- Login-based (`auth:sanctum`): volunteers, crew — persons with `user_id`
|
||||
- Token-based (`portal.token` middleware): artists, suppliers, press — persons without `user_id`
|
||||
|
||||
### CORS
|
||||
|
||||
Drie frontend origins configureren in zowel Laravel (config/cors.php via env) als Vite dev server proxy:
|
||||
- admin: localhost:5173
|
||||
- app: localhost:5174
|
||||
- portal: localhost:5175
|
||||
Configure three frontend origins in both Laravel (`config/cors.php` via env) and the Vite dev server proxy:
|
||||
|
||||
## Backend Regels (STRIKT VOLGEN)
|
||||
- admin: `localhost:5173`
|
||||
- app: `localhost:5174`
|
||||
- portal: `localhost:5175`
|
||||
|
||||
**Production (`crewli.app`):** API `https://api.crewli.app`, SPAs `https://admin.crewli.app`, `https://app.crewli.app`, `https://portal.crewli.app` — see `api/.env.example` for `FRONTEND_*` and `SANCTUM_STATEFUL_DOMAINS`. **`crewli.nl`** is only for a future marketing site; this application stack uses **`crewli.app`** (not `.nl` for API, SPAs, or transactional mail).
|
||||
|
||||
## Backend rules (strict)
|
||||
|
||||
### 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
|
||||
- Every query on event data **must** scope on `organisation_id`
|
||||
- Use `OrganisationScope` as an Eloquent global scope on all event-related models
|
||||
- Never use raw ID checks in controllers — always use 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())
|
||||
- Use resource controllers (`index` / `show` / `store` / `update` / `destroy`)
|
||||
- Namespace: `App\Http\Controllers\Api\V1\`
|
||||
- Return all responses through API resources (never return raw model attributes)
|
||||
- Validate with form requests (never inline `validate()`)
|
||||
|
||||
### Modellen
|
||||
### Models
|
||||
|
||||
- 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
|
||||
- Use the `HasUlids` trait on all business models (not UUID v4)
|
||||
- Soft deletes on: Organisation, Event, FestivalSection, Shift, ShiftAssignment, Person, Artist
|
||||
- No soft deletes on: CheckIn, BriefingSend, MessageReply, ShiftWaitlist (audit records)
|
||||
- JSON columns **only** for opaque configuration — never for queryable/filterable 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
|
||||
- Primary keys: ULID via `HasUlids` (not UUID v4, not auto-increment on business tables)
|
||||
- Create migrations in dependency order: foundation first, then dependent tables
|
||||
- Always add composite indexes as documented in the design document (section 3.5)
|
||||
|
||||
### Rollen & Permissies
|
||||
### Roles and permissions
|
||||
|
||||
- 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.)
|
||||
- Use Spatie `laravel-permission`
|
||||
- Check roles via `$user->hasRole()` and policies — never hard-code role strings in controllers
|
||||
- Three levels: app (`super_admin`), organisation (`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
|
||||
- Write PHPUnit feature tests per controller
|
||||
- Minimum per endpoint: happy path + unauthenticated (401) + wrong organisation (403)
|
||||
- Use factories for all test data
|
||||
- After each module: `php artisan test --filter=ModuleName`
|
||||
|
||||
## Frontend Regels (STRIKT VOLGEN)
|
||||
## Frontend rules (strict)
|
||||
|
||||
### Vue Componenten
|
||||
### Vue components
|
||||
|
||||
- Altijd <script setup lang='ts'> — nooit Options API
|
||||
- Props altijd getypeerd met defineProps<{...}>()
|
||||
- Emits altijd gedeclareerd met defineEmits<{...}>()
|
||||
- Always `<script setup lang="ts">` — never the Options API
|
||||
- Type props with `defineProps<{...}>()`
|
||||
- Declare emits with `defineEmits<{...}>()`
|
||||
|
||||
### API Calls
|
||||
### 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
|
||||
- Use TanStack Query (`useQuery` / `useMutation`) for **all** API calls
|
||||
- Never call axios directly from a component — always via a composable under `composables/`
|
||||
- Use Pinia stores for cross-component state — no prop drilling
|
||||
|
||||
### Naamgeving
|
||||
### Naming
|
||||
|
||||
- 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)
|
||||
- DB columns: `snake_case`
|
||||
- TypeScript / JS variables: `camelCase`
|
||||
- Vue components: PascalCase (e.g. `ShiftAssignPanel.vue`)
|
||||
- Composables: `use` prefix (e.g. `useShifts.ts`)
|
||||
- Pinia stores: `use` prefix + `Store` suffix (e.g. `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
|
||||
- Always use Vuexy/Vuetify for layout, forms, tables, dialogs
|
||||
- Do not write custom CSS when a Vuetify utility class exists
|
||||
- Responsive: mobile-first, usable from 375px width
|
||||
|
||||
## Verboden Patronen
|
||||
## Forbidden patterns
|
||||
|
||||
- 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)
|
||||
- Never: `$user->role === 'admin'` (use policies)
|
||||
- Never: `Model::all()` without a `where` clause (always scope)
|
||||
- Never: leave `dd()` or `var_dump()` in code
|
||||
- Never: hard-code `.env` values in code
|
||||
- Never: use JSON columns for data you need to filter on
|
||||
- Never: UUID v4 as primary key (use `HasUlids`)
|
||||
|
||||
## Volgorde bij elke nieuwe module
|
||||
## Order of work for each new 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
|
||||
1. Create and run migration(s)
|
||||
2. Eloquent model with relationships, scopes, and `HasUlids`
|
||||
3. Factory for test data
|
||||
4. Policy for authorization
|
||||
5. Form request(s) for validation
|
||||
6. API resource for response shaping
|
||||
7. Resource controller
|
||||
8. Register routes in `api.php`
|
||||
9. Write and run PHPUnit feature tests
|
||||
10. Vue composable for API calls (e.g. `useShifts.ts`)
|
||||
11. Pinia store if cross-component state is needed
|
||||
12. Vue page component
|
||||
13. Add route in Vue Router
|
||||
|
||||
Reference in New Issue
Block a user