Convert the documentation files from Word (.docx) to a more gegeneric Markdown format
This commit is contained in:
457
resources/design/start-guide.md
Normal file
457
resources/design/start-guide.md
Normal file
@@ -0,0 +1,457 @@
|
||||
**Crewli**
|
||||
|
||||
Start — Definitieve Actielijst
|
||||
|
||||
Architectuur + Technologie + Stap-voor-stap naar eerste werkende code
|
||||
|
||||
**Versie:** 1.0 — Definitief | **Datum:** Maart 2026 | **Status:** Klaar om te starten
|
||||
|
||||
## 1 — Definitieve Architectuur
|
||||
|
||||
Dit is de volledige, vastgestelde architectuur van Crewli. Alle beslissingen hierin zijn definitief — verwerk ze in CLAUDE.md en .cursorrules zodat Claude Code dit altijd als context heeft.
|
||||
|
||||
### 1.1 Systeemoverzicht
|
||||
|
||||
| **apps/admin/** | **apps/app/** | **apps/portal/** |
|
||||
|---|---|---|
|
||||
| Super Admin SPA | Organizer SPA | Portal SPA |
|
||||
|
||||
Vuexy + Vue 3 + TypeScript | Pinia + TanStack Query | Axios → CORS → Sanctum Token
|
||||
Alle drie apps zijn Vue 3 SPA's — Vuexy template — communiceren uitsluitend via REST API
|
||||
|
||||
**api/ — Laravel 12 REST API (ENIGE backend — geen Blade views)**
|
||||
PHP 8.2 | Sanctum | Spatie Permission | MySQL 8 | Redis | Queue Workers
|
||||
|
||||
### 1.2 Laravel vs Vue — de harde scheiding
|
||||
|
||||
> **GOUDEN REGEL**
|
||||
>
|
||||
> Laravel doet NIKS met HTML of UI. Geen Blade views, geen Mix, geen Inertia.
|
||||
>
|
||||
> Laravel is uitsluitend een JSON REST API. Elke response is application/json.
|
||||
>
|
||||
> Vue doet ALLES met de gebruikersinterface. De drie SPA's communiceren via HTTPS met de API.
|
||||
|
||||
| **App / Laag** | **Technology** | **Gebruik & verantwoordelijkheid** |
|
||||
|----|----|----|
|
||||
| api/ | Laravel 12 + Sanctum | REST API, authenticatie, business logic, database, queue workers, e-mail, PDF-generatie. Geen enkele HTML pagina. |
|
||||
| apps/admin/ | Vue 3 + Vuexy (vol) | Super Admin SPA: organisations beheren, billing, platform-gebruikers. Klein en eenvoudig. |
|
||||
| apps/app/ | Vue 3 + Vuexy (vol) | Organizer SPA: de hoofdapp. Events, shifts, persons, artists, briefings, Mission Control. 90% van je werk. |
|
||||
| apps/portal/ | Vue 3 + Vuexy (gestript) | Portal SPA: twee toegangsmodi. Login voor vrijwilligers/crew. Token voor artiesten/leveranciers/pers. |
|
||||
|
||||
### 1.3 Vuexy — waar en hoe
|
||||
|
||||
| **App / Laag** | **Technology** | **Gebruik & verantwoordelijkheid** |
|
||||
|----|----|----|
|
||||
| apps/admin/ | Vuexy volledig | Admin template ongewijzigd: sidebar, dark mode, customizer. Weinig aanpassingen nodig. |
|
||||
| apps/app/ | Vuexy volledig | Sidebar nav aanpassen voor Crewli-structuur. Customizer/demo-componenten verwijderen. Full Vuetify component gebruik. |
|
||||
| apps/portal/ | Vuexy gestript | Geen sidebar nav, geen customizer, geen dark mode toggle. Wel: Vuetify componenten, Vuexy SCSS variabelen, Vuexy fonts. Eigen layout: top-bar met event-logo + naam. Mobile-first. |
|
||||
|
||||
### 1.4 Portal: twee toegangsmodi
|
||||
|
||||
Eén portal app met twee modi — op basis van hoe de gebruiker binnenkomt:
|
||||
|
||||
| **Gebruiker** | **Identiteit** | **Toegang** | **Waarom** |
|
||||
|----|:--:|:--:|----|
|
||||
| **Vrijwilliger** | Langdurig | **Login** | Festival-paspoort, reliability score, shift-historie accumuleren over jaren |
|
||||
| **Crew / Staff** | Langdurig | **Login** | Kan ook organizer-rechten hebben; organisatie-medewerker |
|
||||
| **Artiest** | Per event | **Token** | Eenmalige booking-relatie; advancing via gesignde URL |
|
||||
| **Tour manager** | Per event | **Token** | Namens artiest; geen platform-account nodig |
|
||||
| **Leverancier** | Per event | **Token** | Productieaanvraag is event-specifiek; token via production_requests.token |
|
||||
| **Pers / Media** | Per event | **Token** | Accreditatie per event; geen terugkerende relatie |
|
||||
|
||||
**Hoe de router dit afhandelt (apps/portal/)**
|
||||
|
||||
```ts
|
||||
// apps/portal/src/router/guards.ts
|
||||
|
||||
export const accessMode = computed(() => {
|
||||
const token = route.query.token as string | undefined
|
||||
const isAuth = authStore.isAuthenticated
|
||||
|
||||
if (token) return 'token' // artiest, leverancier, pers → token-flow
|
||||
if (isAuth) return 'login' // vrijwilliger, crew → login-flow
|
||||
return 'unauthenticated' // → redirect naar /login
|
||||
})
|
||||
|
||||
// Token-based: geen login nodig, token gevalideerd via API
|
||||
// POST /api/v1/portal/token-auth { token: '...' } → person context terug
|
||||
```
|
||||
|
||||
### 1.5 Hoe de API authorisatie werkt — backend
|
||||
|
||||
```php
|
||||
// api/routes/api.php
|
||||
|
||||
Route::prefix('v1')->group(function () {
|
||||
|
||||
// Publiek (login, token-auth)
|
||||
Route::post('auth/login', [AuthController::class, 'login']);
|
||||
Route::post('portal/token-auth', [PortalTokenController::class, 'auth']);
|
||||
Route::post('portal/form-submit', [PublicFormController::class, 'submit']);
|
||||
|
||||
// Login-based (Sanctum)
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::post('auth/logout', [AuthController::class, 'logout']);
|
||||
Route::get('auth/me', [AuthController::class, 'me']);
|
||||
// ... alle organizer + portal-login routes
|
||||
});
|
||||
|
||||
// Token-based portal (eigen middleware)
|
||||
Route::middleware('portal.token')->group(function () {
|
||||
Route::get('portal/artist', [ArtistPortalController::class, 'index']);
|
||||
Route::post('portal/advancing', [AdvancingController::class, 'submit']);
|
||||
// ... alle token-portal routes
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
> **LET OP — CORS**
|
||||
>
|
||||
> Laravel CORS config (config/cors.php) moet drie origins toestaan:
|
||||
>
|
||||
> admin.crewli.app | app.crewli.app | portal.crewli.app
|
||||
>
|
||||
> In development: http://localhost:5173 | :5174 | :5175
|
||||
|
||||
## 2 — Actielijst: Wat Je Nu Doet
|
||||
|
||||
Voer deze stappen uit in volgorde. Sla niets over — elke stap is input voor de volgende. Tijdsinschatting totaal: 2-4 uur. Daarna kun je de eerste Claude Code prompt sturen.
|
||||
|
||||
### Stap 1 — Repository herstructureren **NU**
|
||||
|
||||
apps/band/ hernoemen, demo-rommel verwijderen
|
||||
|
||||
- ☐ **Hernoem apps/band/ naar apps/portal/**
|
||||
|
||||
```
|
||||
mv apps/band apps/portal
|
||||
# Update package.json naam in apps/portal/package.json:
|
||||
# "name": "crewli-portal"
|
||||
```
|
||||
|
||||
- ☐ **Verwijder Vuexy demo-bestanden uit apps/app/src/**
|
||||
|
||||
- Te verwijderen uit apps/app/src/components/dialogs/:
|
||||
- AddAuthenticatorAppDialog.vue, AddPaymentMethodDialog.vue, CardAddEditDialog.vue
|
||||
- PricingPlanDialog.vue, ReferAndEarnDialog.vue, UserUpgradePlanDialog.vue
|
||||
|
||||
- Te verwijderen uit apps/app/src/@core/components/:
|
||||
- BuyNow.vue — 'buy template' knop
|
||||
- TheCustomizer.vue — theme demo-customizer (niet nodig in productie)
|
||||
|
||||
- Te verwijderen uit apps/app/src/pages/:
|
||||
- second-page.vue — demo pagina
|
||||
|
||||
- Te verwijderen uit apps/app/public/:
|
||||
- mockServiceWorker.js — MSW service worker (alleen in dev nodig, niet committen)
|
||||
|
||||
### Stap 2 — API-laag opruimen in apps/app/ **NU**
|
||||
|
||||
Een centrale axios instance — dubbele laag verwijderen
|
||||
|
||||
- **!** apps/app/ heeft nu drie overlappende API-bestanden: src/lib/api-client.ts, src/utils/api.ts, en src/composables/useApi.ts. Dit moet worden een bestand.
|
||||
|
||||
- ☐ **Bepaal: src/lib/axios.ts wordt de ENIGE axios instance**
|
||||
|
||||
```ts
|
||||
// apps/app/src/lib/axios.ts — de ENIGE axios instance
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL + '/api/v1',
|
||||
withCredentials: true,
|
||||
headers: { 'Accept': 'application/json' },
|
||||
})
|
||||
|
||||
// Request interceptor: voeg Bearer token toe
|
||||
api.interceptors.request.use(config => {
|
||||
const auth = useAuthStore()
|
||||
if (auth.token) config.headers.Authorization = `Bearer ${auth.token}`
|
||||
return config
|
||||
})
|
||||
|
||||
// Response interceptor: 401 → redirect naar login
|
||||
api.interceptors.response.use(
|
||||
res => res,
|
||||
err => {
|
||||
if (err.response?.status === 401) useAuthStore().logout()
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
```
|
||||
|
||||
- ☐ Verwijder daarna: src/lib/api-client.ts en src/utils/api.ts
|
||||
- ☐ Hernoem src/composables/useApi.ts → src/composables/useApiHelpers.ts (algemene helpers)
|
||||
- ☐ Doe hetzelfde voor apps/portal/ en apps/admin/ (zelfde patroon)
|
||||
|
||||
### Stap 3 — TanStack Query installeren **NU**
|
||||
|
||||
Ontbreekt nog — vereist voor alle API state management
|
||||
|
||||
- ☐ **Installeer in alle drie apps (app/, admin/, portal/)**
|
||||
|
||||
```sh
|
||||
cd apps/app && pnpm add @tanstack/vue-query
|
||||
cd ../admin && pnpm add @tanstack/vue-query
|
||||
cd ../portal && pnpm add @tanstack/vue-query
|
||||
```
|
||||
|
||||
- ☐ Registreer in main.ts van elke app
|
||||
|
||||
```ts
|
||||
// apps/app/src/main.ts — voeg toe
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
|
||||
app.use(VueQueryPlugin, {
|
||||
queryClientConfig: {
|
||||
defaultOptions: {
|
||||
queries: { staleTime: 1000 * 60 * 5, retry: 1 },
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- ☐ Installeer ook formuliervalidatie (alle apps)
|
||||
|
||||
```sh
|
||||
pnpm add vee-validate zod @vee-validate/zod
|
||||
```
|
||||
|
||||
### Stap 4 — Backend dependencies installeren **NU**
|
||||
|
||||
Spatie packages zijn vereist voor fase 1
|
||||
|
||||
- ☐ **Installeer Spatie packages in api/**
|
||||
|
||||
```sh
|
||||
cd api
|
||||
composer require spatie/laravel-permission
|
||||
composer require spatie/laravel-activitylog
|
||||
composer require spatie/laravel-medialibrary
|
||||
composer require barryvdh/laravel-dompdf
|
||||
composer require endroid/qr-code
|
||||
|
||||
# Publiceer configs
|
||||
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
|
||||
php artisan vendor:publish --provider="Spatie\LaravelActivitylog\ActivitylogServiceProvider"
|
||||
|
||||
# Voeg HasUlids toe als trait — Laravel native, geen extra package nodig
|
||||
# use Illuminate\Database\Eloquent\Concerns\HasUlids;
|
||||
```
|
||||
|
||||
- ☐ Voeg PortalToken middleware toe (skeleton voor later)
|
||||
|
||||
```sh
|
||||
php artisan make:middleware PortalTokenMiddleware
|
||||
# Registreer in bootstrap/app.php als 'portal.token'
|
||||
```
|
||||
|
||||
- ☐ Update config/cors.php voor drie frontend origins
|
||||
|
||||
```php
|
||||
// config/cors.php
|
||||
'allowed_origins' => [
|
||||
env('FRONTEND_ADMIN_URL', 'http://localhost:5173'),
|
||||
env('FRONTEND_APP_URL', 'http://localhost:5174'),
|
||||
env('FRONTEND_PORTAL_URL','http://localhost:5175'),
|
||||
],
|
||||
```
|
||||
|
||||
### Stap 5 — Helper bestanden aanmaken **NU**
|
||||
|
||||
CLAUDE.md, .cursorrules, docs/ — dit is het belangrijkste wat je doet
|
||||
|
||||
- **!** Dit zijn de vier bestanden die Claude Code altijd laadt als context. Een uur hieraan besteden bespaart honderden uren aan correcties. Gebruik de volledige inhoud uit Dev Guide sectie 3.
|
||||
|
||||
- ☐ **Maak aan in de root van je project:**
|
||||
|
||||
```sh
|
||||
# Root van het project (naast api/ en apps/)
|
||||
touch CLAUDE.md # Dev Guide sectie 3.1 — volledig invullen
|
||||
touch .cursorrules # Dev Guide sectie 3.2 — volledig invullen
|
||||
mkdir -p docs
|
||||
touch docs/SCHEMA.md # Schema uit Design Document v1.3 sectie 3.5
|
||||
touch docs/API.md # API contract — begin met auth + organisations + events
|
||||
```
|
||||
|
||||
- ☐ Voeg toe aan CLAUDE.md (update ten opzichte van Dev Guide v1.0):
|
||||
|
||||
- Portal architectuur: een app, twee toegangsmodi
|
||||
- Login-based (auth:sanctum): vrijwilligers, crew — persons met user_id
|
||||
- Token-based (portal.token middleware): artiesten, leveranciers, pers — persons zonder user_id
|
||||
|
||||
- apps/ mapping: admin/ = Super Admin, app/ = Organizer, portal/ = Externe gebruikers
|
||||
|
||||
- CORS: drie origins configureren in zowel Laravel als Vite dev server
|
||||
|
||||
### Stap 6 — Vite dev ports configureren **NU**
|
||||
|
||||
Elk frontend-app een eigen port zodat CORS werkt
|
||||
|
||||
- ☐ **Pas vite.config.ts aan in elke app**
|
||||
|
||||
```ts
|
||||
// apps/admin/vite.config.ts → port: 5173
|
||||
// apps/app/vite.config.ts → port: 5174
|
||||
// apps/portal/vite.config.ts → port: 5175
|
||||
server: {
|
||||
port: 5174, // aanpassen per app
|
||||
proxy: { // optioneel: proxy API calls in dev
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- ☐ Voeg .env.local toe aan elke app
|
||||
|
||||
```sh
|
||||
# apps/app/.env.local
|
||||
VITE_API_URL=http://localhost:8000
|
||||
VITE_APP_NAME=Crewli
|
||||
|
||||
# apps/portal/.env.local
|
||||
VITE_API_URL=http://localhost:8000
|
||||
VITE_APP_NAME=Crewli Portal
|
||||
```
|
||||
|
||||
### Stap 7 — Eerste Claude Code prompt sturen **NU**
|
||||
|
||||
Je bent klaar om de eerste module te laten genereren
|
||||
|
||||
- **!** Na stappen 1-6 ben je klaar. Onderstaande prompt kun je letterlijk in Claude Code plakken.
|
||||
|
||||
- ☐ **Start Claude Code vanuit je project root**
|
||||
|
||||
```sh
|
||||
cd /pad/naar/crewli
|
||||
claude
|
||||
```
|
||||
|
||||
- ☐ **Plak deze prompt als eerste bericht:**
|
||||
|
||||
```
|
||||
Lees eerst volledig /CLAUDE.md en /docs/SCHEMA.md.
|
||||
|
||||
Bouw daarna Fase 1 — Foundation — in deze volgorde:
|
||||
|
||||
1. Migrations in api/database/migrations/:
|
||||
- update users tabel (voeg timezone, locale, deleted_at toe)
|
||||
- organisations (ULID, name, slug, billing_status, settings JSON, deleted_at)
|
||||
- organisation_user pivot (int PK, user_id, organisation_id, role)
|
||||
- user_invitations (ULID, email, invited_by, organisation_id, event_id nullable,
|
||||
token ULID unique, status enum, expires_at)
|
||||
- events (ULID, organisation_id, name, slug, start_date, end_date,
|
||||
timezone, status enum, deleted_at)
|
||||
- event_user_roles pivot
|
||||
|
||||
2. Models: User (update), Organisation, UserInvitation, Event
|
||||
- HasUlids op alle business modellen
|
||||
- SoftDeletes op Organisation, Event
|
||||
- OrganisationScope global scope op Event
|
||||
- Alle relaties (hasMany, belongsToMany)
|
||||
|
||||
3. Spatie Permission setup:
|
||||
- RoleSeeder: super_admin, org_admin, org_member,
|
||||
event_manager, staff_coordinator, volunteer_coordinator
|
||||
|
||||
4. Auth: LoginController, LogoutController, MeController
|
||||
- Sanctum token-based (geen session)
|
||||
- MeController geeft user + organisations + active event roles terug
|
||||
|
||||
5. Organisations: OrganisationController (index/show/store/update)
|
||||
- OrganisationPolicy
|
||||
- OrganisationRequest (store + update)
|
||||
- OrganisationResource
|
||||
|
||||
6. Events: EventController (index/show/store/update) genest onder organisations
|
||||
- EventPolicy
|
||||
- EventRequest
|
||||
- EventResource
|
||||
|
||||
7. Feature tests voor alles bovenstaande:
|
||||
- Happy path (200/201)
|
||||
- Unauthenticated (401)
|
||||
- Wrong organisation (403)
|
||||
|
||||
Draai na elke stap: php artisan test
|
||||
Los fouten op voor je verdergaat.
|
||||
Stop na stap 7 en rapporteer wat er gebouwd is en of alle tests groen zijn.
|
||||
```
|
||||
|
||||
## 3 — Daarna: Frontend Fase 1 + Fase 2 Planning
|
||||
|
||||
### Stap 8 — Frontend Fase 1 — Auth + Shell **DAARNA**
|
||||
|
||||
Na groene backend tests
|
||||
|
||||
- Auth flow bouwen in apps/app/
|
||||
- stores/useAuthStore.ts — token opslaan, isAuthenticated, me() laden
|
||||
- pages/login.vue — Vuexy login layout gebruiken (al aanwezig als basis)
|
||||
- router guard — redirect naar login als niet authenticated
|
||||
|
||||
- Navigatiestructuur Crewli invullen
|
||||
- src/navigation/vertical/index.ts — verwijder Vuexy demo-items, voeg Crewli items toe
|
||||
- Events → Sections → Shifts, Persons, Artists, Briefings, Rapportage
|
||||
|
||||
- CASL permissions setup
|
||||
- src/plugins/casl.ts koppelen aan Spatie roles vanuit auth/me response
|
||||
- useAbility() gebruiken voor conditionele UI-elementen (aanpassen-knop tonen/verbergen)
|
||||
|
||||
### Stap 9 — Fase 2 — Core module volgorde **DAARNA**
|
||||
|
||||
Na werkende auth en shell
|
||||
|
||||
- **Bouw in deze volgorde — altijd eerst backend, dan frontend:**
|
||||
|
||||
1. Crowd Types + Persons + Crowd Lists (basis guest management)
|
||||
2. Festival Sections + Time Slots + Shifts (het hart van het platform)
|
||||
3. Shift Assignments — claim workflow met approval flow
|
||||
4. Vrijwilligers registratie (public form → portal login flow)
|
||||
5. Accreditatie engine + Access Zones
|
||||
6. Basis briefings (template + send + track)
|
||||
|
||||
- **!** Elke module: gebruik de module-prompt uit Dev Guide sectie 5.2. Altijd: migrations → model → factory → policy → resource → controller → test → composable → pagina.
|
||||
|
||||
### Stap 10 — Portal app inrichten **DAARNA**
|
||||
|
||||
Na werkende apps/app/ basis
|
||||
|
||||
- apps/portal/ strippen tot portal-layout
|
||||
- Verwijder: sidebar nav, customizer, dark mode toggle, demo-dialogen
|
||||
- Maak: PortalLayout.vue — top-bar met event-logo, naam, hamburger menu
|
||||
- Maak: twee router guards — loginGuard en tokenGuard
|
||||
|
||||
- Login-flow (vrijwilligers/crew): zelfde /api/v1/auth/login endpoint als app/
|
||||
- Token-flow (artiesten/leveranciers): POST /api/v1/portal/token-auth
|
||||
- Routing op basis van accessMode computed + person.crowd_type
|
||||
|
||||
## 4 — Checklist: Ben Je Klaar om te Starten?
|
||||
|
||||
| **#** | **Actie** | **Status** |
|
||||
|:--:|----|:--:|
|
||||
| **1** | apps/band/ hernoemd naar apps/portal/ | ☐ Klaar |
|
||||
| **2** | Demo-rommel verwijderd uit apps/app/ | ☐ Klaar |
|
||||
| **3** | Dubbele API-laag opgeruimd → een src/lib/axios.ts per app | ☐ Klaar |
|
||||
| **4** | TanStack Query geinstalleerd in alle drie apps + geregistreerd in main.ts | ☐ Klaar |
|
||||
| **5** | VeeValidate + Zod geinstalleerd in alle drie apps | ☐ Klaar |
|
||||
| **6** | Spatie packages geinstalleerd in api/ + configs gepubliceerd | ☐ Klaar |
|
||||
| **7** | PortalTokenMiddleware skeleton aangemaakt | ☐ Klaar |
|
||||
| **8** | config/cors.php: drie frontend origins geconfigureerd | ☐ Klaar |
|
||||
| **9** | Vite dev ports: admin=5173, app=5174, portal=5175 | ☐ Klaar |
|
||||
| **10** | .env.local aangemaakt per app met VITE_API_URL | ☐ Klaar |
|
||||
| **11** | CLAUDE.md aangemaakt en volledig ingevuld (Dev Guide sectie 3.1 + portal architectuur) | ☐ Klaar |
|
||||
| **12** | .cursorrules aangemaakt (Dev Guide sectie 3.2) | ☐ Klaar |
|
||||
| **13** | docs/SCHEMA.md aangemaakt (schema uit Design Document v1.3 sectie 3.5) | ☐ Klaar |
|
||||
| **14** | docs/API.md aangemaakt met initiele route-lijst | ☐ Klaar |
|
||||
| **15** | claude (Claude Code) gestart en Fase 1 prompt ingevoerd | ☐ Klaar |
|
||||
| **16** | php artisan test — alle tests groen | ☐ Klaar |
|
||||
|
||||
Crewli Start Guide v1.0 — Maart 2026 | Architectuur + Actielijst
|
||||
Reference in New Issue
Block a user