17 KiB
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/)
// 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
// 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
// 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/)
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
// 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)
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/
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)
php artisan make:middleware PortalTokenMiddleware # Registreer in bootstrap/app.php als 'portal.token' -
☐ Update config/cors.php voor drie frontend origins
// 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:
# 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
// 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
# 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
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:
- Crowd Types + Persons + Crowd Lists (basis guest management)
- Festival Sections + Time Slots + Shifts (het hart van het platform)
- Shift Assignments — claim workflow met approval flow
- Vrijwilligers registratie (public form → portal login flow)
- Accreditatie engine + Access Zones
- 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