Files
crewli/resources/design/start-guide.md

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:
  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