feat: schema v1.7 + sections/shifts frontend
- Universeel festival/event model (parent_event_id, event_type) - event_person_activations pivot tabel - Event model: parent/children relaties + helper scopes - DevSeeder: festival structuur met sub-events - Sections & Shifts frontend (twee-kolom layout) - BACKLOG.md aangemaakt met 22 gedocumenteerde wensen
This commit is contained in:
384
docs/BACKLOG.md
Normal file
384
docs/BACKLOG.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Crewli — Product Backlog
|
||||
|
||||
> Gedocumenteerde wensen en features die bewust zijn uitgesteld.
|
||||
> Bijgewerkt: April 2026
|
||||
>
|
||||
> **Gebruik:** Voeg nieuwe items toe als ze tijdens development ontstaan.
|
||||
> Geef elk item een prioriteit en fase zodra je het gaat oppakken.
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Geplande features
|
||||
|
||||
### ARCH-01 — Recurrence / Terugkerende events
|
||||
|
||||
**Aanleiding:** Schaatsbaan use case — 8 weken, elke za+zo openingsdagen.
|
||||
**Wat:** Organisator definieert één template sub-event met RRULE.
|
||||
Platform genereert automatisch alle instanties.
|
||||
**Details:**
|
||||
|
||||
- RRULE formaat (RFC 5545): `FREQ=WEEKLY;BYDAY=SA,SU;UNTIL=20270126`
|
||||
- `events.recurrence_rule` (string nullable) — al gereserveerd in schema
|
||||
- `events.recurrence_exceptions` (JSON) — cancelled + modified dates
|
||||
- UI: "Genereer openingsdagen" wizard
|
||||
- Aanpassen van één instantie raakt template niet
|
||||
- "Alleen deze dag" / "Alle volgende dagen" / "Alle dagen" (Google Calendar patroon)
|
||||
**Schema:** Kolommen al aanwezig in v1.7. Alleen generator-logica ontbreekt.
|
||||
|
||||
---
|
||||
|
||||
### ARCH-02 — Min/max shifts per vrijwilliger per serie
|
||||
|
||||
**Aanleiding:** Schaatsbaan — eerlijke verdeling, minimum commitment.
|
||||
**Wat:** Per festival/serie instelbaar minimum en maximum aantal shifts
|
||||
dat een vrijwilliger kan claimen.
|
||||
**Details:**
|
||||
|
||||
- `festivals.min_shifts_per_volunteer` (int nullable)
|
||||
- `festivals.max_shifts_per_volunteer` (int nullable)
|
||||
- Portal toont voortgang: "Jij hebt 2 van minimaal 4 shifts geclaimd"
|
||||
- Bij bereiken maximum: verdere claims geblokkeerd
|
||||
**Afhankelijk van:** ARCH-01 (recurrence), Portal self-service
|
||||
|
||||
---
|
||||
|
||||
### ARCH-03 — Sectie templates / kopiëren van vorig event
|
||||
|
||||
**Aanleiding:** Organisatoren die elk jaar dezelfde secties en shifts opzetten.
|
||||
**Wat:** "Kopieer secties van vorig festival" functie in de UI.
|
||||
Kopieert festival_sections + shifts structuur (zonder toewijzingen).
|
||||
**Details:**
|
||||
|
||||
- UI: dropdown "Kopieer structuur van..." bij aanmaken festival
|
||||
- Optie: kopieer alleen secties / secties + shifts / alles
|
||||
- Tijden worden proportioneel aangepast aan nieuwe datums
|
||||
**Prioriteit:** Hoog — bespaart veel handmatig werk bij terugkerende festivals
|
||||
|
||||
---
|
||||
|
||||
### ARCH-04 — Cross-festival conflictdetectie
|
||||
|
||||
**Aanleiding:** Vrijwilliger die bij twee festivals van dezelfde organisatie
|
||||
op dezelfde dag ingepland staat.
|
||||
**Wat:** Waarschuwing (geen blokkade) als iemand al actief is op een
|
||||
ander festival van dezelfde organisatie op dezelfde datum.
|
||||
**Details:**
|
||||
|
||||
- Soft check — waarschuwing tonen, niet blokkeren
|
||||
- Relevant bij organisaties met meerdere festivals tegelijk
|
||||
- Query: `shift_assignments` cross-festival op person_id + datum
|
||||
|
||||
---
|
||||
|
||||
### ARCH-05 — Shift fairness / prioriteitswachtrij
|
||||
|
||||
**Aanleiding:** Populaire shifts worden direct volgeboekt door snelle vrijwilligers.
|
||||
**Wat:** Optionele wachtrij-modus waarbij het systeem eerlijk verdeelt
|
||||
op basis van: reliability score, aantal uren al ingepland, aanmeldvolgorde.
|
||||
**Details:**
|
||||
|
||||
- `shifts.assignment_mode` (enum: first_come | fair_queue | manual)
|
||||
- Fair queue: systeem wijst toe op basis van algoritme
|
||||
- Organisator keurt resultaat goed voor publicatie
|
||||
**Prioriteit:** Middel — nice-to-have voor grote festivals
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Communicatie & Notificaties
|
||||
|
||||
### COMM-01 — Real-time WebSocket notificaties
|
||||
|
||||
**Aanleiding:** Differentiator — geen van de concurrenten heeft dit.
|
||||
**Wat:** Push notificaties via Laravel Echo + Soketi voor:
|
||||
|
||||
- Nieuwe vrijwilliger aanmelding
|
||||
- Shift geclaimd
|
||||
- Uitnodiging geaccepteerd
|
||||
- Shift niet gevuld (waarschuwing)
|
||||
- No-show alert op show-dag
|
||||
**Tech:** Laravel Echo + Soketi (zelf-gehoste WebSocket server)
|
||||
**Frontend:** Notificatie bell in topbar activeren
|
||||
|
||||
---
|
||||
|
||||
### COMM-02 — Topbar volledig activeren
|
||||
|
||||
**Aanleiding:** Vuexy topbar staat er maar is niet aangesloten op Crewli.
|
||||
**Wat:**
|
||||
|
||||
- Zoekbalk (CTRL+K) aansluiten op Crewli-entiteiten
|
||||
(personen, events, secties zoeken)
|
||||
- Notificatie bell koppelen aan COMM-01
|
||||
- App switcher: Organizer / Admin / Portal wisselen
|
||||
- User avatar: gekoppeld aan ingelogde gebruiker (deels al gedaan)
|
||||
**Prioriteit:** Middel — werkt zonder maar verbetert UX significant
|
||||
|
||||
---
|
||||
|
||||
### COMM-03 — Globale zoekfunctie (cmd+K)
|
||||
|
||||
**Aanleiding:** Differentiator — cross-entiteit zoeken.
|
||||
**Wat:** Modal zoekbalk die zoekt over:
|
||||
personen, events, artiesten, secties, shifts
|
||||
**Tech:** Meilisearch of database full-text search
|
||||
**Prioriteit:** Laag — Fase 4
|
||||
|
||||
---
|
||||
|
||||
### COMM-04 — SMS + WhatsApp campagnes via Zender
|
||||
|
||||
**Aanleiding:** WeezCrew heeft dit als sterk punt.
|
||||
**Wat:** Bulk communicatie via Zender (zelf-gehoste SMS/WhatsApp gateway)
|
||||
|
||||
- Normal urgency → email
|
||||
- Urgent → WhatsApp
|
||||
- Emergency → SMS + WhatsApp parallel
|
||||
**Tech:** ZenderService (al gedocumenteerd in dev guide)
|
||||
**Afhankelijk van:** Communicatie module backend
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Show Day & Operationeel
|
||||
|
||||
### OPS-01 — Mission Control
|
||||
|
||||
**Aanleiding:** In2Event's sterkste feature.
|
||||
**Wat:** Real-time operationele hub op show-dag:
|
||||
|
||||
- Live check-in overzicht per sectie
|
||||
- Artiest handling (aankomst, soundcheck, performance status)
|
||||
- No-show alerts met automatische opvolging
|
||||
- Inventaris uitgifte (portofoons, hesjes)
|
||||
**Prioriteit:** Hoog voor show-dag gebruik
|
||||
|
||||
---
|
||||
|
||||
### OPS-02 — No-show automatisering
|
||||
|
||||
**Aanleiding:** 30-minuten alert voor niet-ingecheckte vrijwilligers.
|
||||
**Wat:** Automatische WhatsApp/SMS via Zender als vrijwilliger
|
||||
niet is ingecheckt 30 min na shift-starttijd.
|
||||
**Schema:** `show_day_absence_alerts` al aanwezig ✅
|
||||
**Afhankelijk van:** COMM-04 (Zender), OPS-01 (Mission Control)
|
||||
|
||||
---
|
||||
|
||||
### OPS-03 — Allocatiesheet PDF generator
|
||||
|
||||
**Aanleiding:** WeezCrew heeft branded PDF per crew.
|
||||
**Wat:** Gepersonaliseerde PDF per vrijwilliger/crew:
|
||||
taakbeschrijving, tijden, locatie, QR-code voor check-in.
|
||||
**Tech:** DomPDF (al geïnstalleerd)
|
||||
**Prioriteit:** Middel
|
||||
|
||||
---
|
||||
|
||||
### OPS-04 — Scanner infrastructuur
|
||||
|
||||
**Aanleiding:** QR check-in op locatie.
|
||||
**Wat:** Scanstations configureren, koppelen aan hardware.
|
||||
`scanners` tabel al aanwezig in schema ✅
|
||||
**Prioriteit:** Laag — Fase 4
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Vrijwilligers & Portal
|
||||
|
||||
### VOL-01 — apps/portal/ vrijwilliger self-service
|
||||
|
||||
**Aanleiding:** Vrijwilligers moeten zichzelf kunnen aanmelden en
|
||||
shifts claimen zonder toegang tot de Organizer app.
|
||||
**Wat:**
|
||||
|
||||
- Publiek registratieformulier (multi-step)
|
||||
- Login portal voor vrijwilligers
|
||||
- Beschikbaarheid opgeven (time slots kiezen)
|
||||
- My Shifts overzicht
|
||||
- Shift claimen met conflictdetectie
|
||||
- "Ik kan toch niet komen" workflow
|
||||
**Afhankelijk van:** Sections + Shifts backend (al klaar ✅)
|
||||
|
||||
---
|
||||
|
||||
### VOL-02 — Vrijwilliger paspoort + reliability score
|
||||
|
||||
**Aanleiding:** Platform-breed profiel dat accumuleert over jaren.
|
||||
**Wat:**
|
||||
|
||||
- Festival-paspoort: visuele tijdlijn van deelgenomen festivals
|
||||
- Reliability score (0.0-5.0): berekend via scheduled job
|
||||
- Coordinator-beoordeling per festival (intern, nooit zichtbaar)
|
||||
- "Would reinvite" indicator bij heruitnodiging
|
||||
**Schema:** `volunteer_profiles`, `volunteer_festival_history` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### VOL-03 — Post-festival evaluatie + retrospectief
|
||||
|
||||
**Aanleiding:** Automatische feedback na het festival.
|
||||
**Wat:**
|
||||
|
||||
- 24u na laatste shift: evaluatiemail naar vrijwilligers
|
||||
- Max 5 vragen (beleving, shift kwaliteit, terugkomen?)
|
||||
- Gegenereerd retrospectief rapport per festival
|
||||
- Coordinator-beoordeling parallel (intern)
|
||||
**Schema:** `post_festival_evaluations`, `festival_retrospectives` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### VOL-04 — Shift swap workflow (portal)
|
||||
|
||||
**Aanleiding:** Vrijwilliger wil shift ruilen met collega.
|
||||
**Wat:**
|
||||
|
||||
- Open swap: iedereen mag reageren
|
||||
- Persoonlijke swap: specifieke collega vragen
|
||||
- Na akkoord beide: coordinator bevestigt (of auto-approve)
|
||||
- Wachtlijst: bij uitval automatisch aanschrijven
|
||||
**Schema:** `shift_swap_requests`, `shift_absences`, `shift_waitlist` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Artiesten & Advancing
|
||||
|
||||
### ART-01 — Artist advancing portal (apps/portal/)
|
||||
|
||||
**Aanleiding:** Crescat's sterkste feature.
|
||||
**Wat:**
|
||||
|
||||
- Sectie-gebaseerd advance portal via gesignde URL
|
||||
- Per sectie onafhankelijk submitbaar (Guest List, Contacts, Production)
|
||||
- Milestone pipeline: Offer In → Advance Received
|
||||
- Per-artiest zichtbaarheidscontrole van advance secties
|
||||
- Submission diff tracking (created/updated/untouched/deleted)
|
||||
**Schema:** `advance_sections`, `advance_submissions` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### ART-02 — Timetable (stage + drag-drop)
|
||||
|
||||
**Aanleiding:** FullCalendar timeline view voor podia-planning.
|
||||
**Wat:**
|
||||
|
||||
- Timeline view per podium
|
||||
- Drag-and-drop performances
|
||||
- B2B detectie (twee artiesten op zelfde podium zelfde tijd)
|
||||
**Tech:** FullCalendar (al in stack ✅)
|
||||
|
||||
---
|
||||
|
||||
## Fase 3 — Formulieren & Leveranciers
|
||||
|
||||
### FORM-01 — Formulierbouwer
|
||||
|
||||
**Aanleiding:** WeezCrew heeft een krachtige drag-sorteerbare builder.
|
||||
**Wat:**
|
||||
|
||||
- Drag-sorteerbaar, conditionele logica
|
||||
- Live preview
|
||||
- Iframe embed voor externe websites
|
||||
- Configureerbare velden per crowd type
|
||||
**Schema:** `public_forms` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
### SUP-01 — Leveranciersportal + productieverzoeken
|
||||
|
||||
**Aanleiding:** Leveranciers moeten productie-informatie kunnen indienen.
|
||||
**Wat:**
|
||||
|
||||
- Token-gebaseerde portal toegang (geen account nodig)
|
||||
- Productieverzoek indienen (mensen, tech, stroom, voertuigen)
|
||||
- Crowd list indienen voor hun crew
|
||||
**Schema:** `production_requests`, `material_requests` al aanwezig ✅
|
||||
|
||||
---
|
||||
|
||||
## Fase 4 — Differentiators
|
||||
|
||||
### DIFF-01 — Cross-event crew pool + reliability score
|
||||
|
||||
**Aanleiding:** Vrijwilligers hergebruiken over events van dezelfde organisatie.
|
||||
**Wat:** Eén klik heruitnodiging op basis van vorig jaar.
|
||||
Reliability score zichtbaar naast naam in de lijst.
|
||||
|
||||
---
|
||||
|
||||
### DIFF-02 — Crew PWA (mobiel)
|
||||
|
||||
**Aanleiding:** On-site zelfservice voor crew op hun telefoon.
|
||||
**Wat:** Progressive Web App voor:
|
||||
shifts bekijken, briefing lezen, clock-in, push notificaties.
|
||||
|
||||
---
|
||||
|
||||
### DIFF-03 — Publieke REST API + webhooks
|
||||
|
||||
**Aanleiding:** Enterprise integraties.
|
||||
**Wat:** Gedocumenteerde publieke API + webhook systeem
|
||||
voor third-party integraties (ticketing, HR, etc.)
|
||||
|
||||
---
|
||||
|
||||
### DIFF-04 — CO2 / Duurzaamheidsrapportage
|
||||
|
||||
**Aanleiding:** Toenemende focus op duurzame events.
|
||||
**Wat:** Emissieberekeningen op basis van transport en energieverbruik.
|
||||
**Status:** Expliciet out of scope voor v1.x
|
||||
|
||||
---
|
||||
|
||||
## Apps & Platforms
|
||||
|
||||
### APPS-01 — apps/admin/ volledig bouwen
|
||||
|
||||
**Aanleiding:** Super Admin panel voor platform-beheer.
|
||||
**Wat:**
|
||||
|
||||
- Alle organisaties beheren
|
||||
- Billing status wijzigen
|
||||
- Platform-gebruikers beheren
|
||||
- Usage statistieken
|
||||
|
||||
---
|
||||
|
||||
### APPS-02 — OrganisationSwitcher ingeklapte staat fix
|
||||
|
||||
**Aanleiding:** Flikkering/hover-bug bij ingeklapte sidebar.
|
||||
**Wat:** Correcte weergave en animatie in ingeklapte staat.
|
||||
**Prioriteit:** Low — cosmetisch, werkt functioneel wel
|
||||
|
||||
---
|
||||
|
||||
## Technische schuld
|
||||
|
||||
### TECH-01 — Bestaande tests bijwerken na festival/event refactor
|
||||
|
||||
**Aanleiding:** Na toevoegen parent_event_id worden bestaande tests
|
||||
mogelijk fragiel door gewijzigde factory-setup.
|
||||
**Wat:** Alle Feature tests reviewen en bijwerken waar nodig.
|
||||
|
||||
---
|
||||
|
||||
### TECH-02 — scopeForFestival helper op Event model
|
||||
|
||||
**Aanleiding:** Queries die door parent/child heen moeten werken.
|
||||
**Wat:** `Event::scopeWithChildren()` en `Event::scopeForFestival()`
|
||||
helper scopes zodat queries automatisch parent + children bevatten.
|
||||
|
||||
---
|
||||
|
||||
### TECH-03 — DevSeeder uitbreiden met festival-structuur
|
||||
|
||||
**Aanleiding:** Na festival/event refactor heeft de DevSeeder
|
||||
realistische testdata nodig met parent/child events.
|
||||
**Wat:** DevSeeder aanpassen met:
|
||||
|
||||
- Test festival (parent)
|
||||
- 2-3 sub-events (children)
|
||||
- Personen op festival-niveau
|
||||
|
||||
---
|
||||
|
||||
_Laatste update: April 2026_
|
||||
_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-_
|
||||
178
docs/SCHEMA.md
178
docs/SCHEMA.md
@@ -1,14 +1,21 @@
|
||||
# Crewli — Core Database Schema
|
||||
|
||||
> Source: Design Document v1.3 — Section 3.5
|
||||
> **Version: 1.6** — Updated April 2026
|
||||
> **Version: 1.7** — Updated April 2026
|
||||
>
|
||||
> **Changelog:**
|
||||
>
|
||||
> - v1.3: Original — 12 database review findings incorporated
|
||||
> - v1.4: Competitor analysis amendments (Crescat, WeezCrew, In2Event)
|
||||
> - v1.5: Concept Event Structure review + final decisions
|
||||
> - v1.6: Removed `festival_sections.shift_follows_events` — feature does not fit Crewli's vision (staff planning is independent of artist/timetable planning)
|
||||
> - v1.6: Removed `festival_sections.shift_follows_events`
|
||||
> - v1.7: Festival/Event architecture — universal event model supporting
|
||||
> single events, multi-day festivals, multi-location events, event series
|
||||
> and periodic operations (recurrence). Added `parent_event_id`,
|
||||
> `event_type`, `sub_event_label`, `is_recurring`, `recurrence_rule`,
|
||||
> `recurrence_exceptions` to `events`. Added `event_person_activations`
|
||||
> pivot. Changed `persons.event_id` to reference festival-level event.
|
||||
> Added `event_type_label` for UI terminology customisation.
|
||||
|
||||
---
|
||||
|
||||
@@ -110,23 +117,72 @@
|
||||
|
||||
### `events`
|
||||
|
||||
| Column | Type | Notes |
|
||||
| ----------------- | ------------------ | ------------------------------------------------------------------------- |
|
||||
| `id` | ULID | PK |
|
||||
| `organisation_id` | ULID FK | → organisations |
|
||||
| `name` | string | |
|
||||
| `slug` | string | |
|
||||
| `start_date` | date | |
|
||||
| `end_date` | date | |
|
||||
| `timezone` | string | default: Europe/Amsterdam |
|
||||
| `status` | enum | `draft\|published\|registration_open\|buildup\|showday\|teardown\|closed` |
|
||||
| `deleted_at` | timestamp nullable | Soft delete |
|
||||
> **v1.7:** Universal event model supporting all event types:
|
||||
> single events, multi-day festivals, multi-location events,
|
||||
> event series, and periodic operations (schaatsbaan use case).
|
||||
>
|
||||
> **Architecture:**
|
||||
>
|
||||
> - A **flat event** has no parent and no children → behaves as a normal single event
|
||||
> - A **festival/series** has no parent but has children → container level
|
||||
> - A **sub-event** has a `parent_event_id` → operational unit within a festival
|
||||
> - A **single event** = flat event where festival and operational unit are the same
|
||||
>
|
||||
> **UI behaviour:** If an event has no children, all tabs are shown at the
|
||||
> event level (flat mode). Once children are added, the event becomes a
|
||||
> festival container and children get the operational tabs.
|
||||
|
||||
**Relations:** `belongsTo` organisation, `hasMany` festival_sections, time_slots, persons, artists, briefings
|
||||
**Indexes:** `(organisation_id, status)`, `UNIQUE(organisation_id, slug)`
|
||||
| Column | Type | Notes |
|
||||
| ----------------------- | ------------------ | ------------------------------------------------------------------------------------------ |
|
||||
| `id` | ULID | PK |
|
||||
| `organisation_id` | ULID FK | → organisations |
|
||||
| `parent_event_id` | ULID FK nullable | **v1.7** → events (nullOnDelete). NULL = top-level event or festival |
|
||||
| `name` | string | |
|
||||
| `slug` | string | |
|
||||
| `start_date` | date | |
|
||||
| `end_date` | date | |
|
||||
| `timezone` | string | default: Europe/Amsterdam |
|
||||
| `status` | enum | `draft\|published\|registration_open\|buildup\|showday\|teardown\|closed` |
|
||||
| `event_type` | enum | **v1.7** `event\|festival\|series` — default: event |
|
||||
| `event_type_label` | string nullable | **v1.7** UI label chosen by organiser: "Festival", "Evenement", "Serie" |
|
||||
| `sub_event_label` | string nullable | **v1.7** How to call children: "Dag", "Programmaonderdeel", "Editie" |
|
||||
| `is_recurring` | bool | **v1.7** default: false. True = generated from recurrence rule |
|
||||
| `recurrence_rule` | string nullable | **v1.7** RRULE (RFC 5545): "FREQ=WEEKLY;BYDAY=SA,SU;UNTIL=20270126" |
|
||||
| `recurrence_exceptions` | JSON nullable | **v1.7** Array of {date, type: cancelled\|modified, overrides: {}}. JSON OK: opaque config |
|
||||
| `deleted_at` | timestamp nullable | Soft delete |
|
||||
|
||||
**Relations:**
|
||||
|
||||
- `belongsTo` Organisation
|
||||
- `belongsTo` Event as parent (`parent_event_id`)
|
||||
- `hasMany` Event as children (`parent_event_id`)
|
||||
- `hasMany` FestivalSection, TimeSlot, Artist, Briefing (on sub-event or flat event)
|
||||
- `hasMany` Person (on festival/top-level event)
|
||||
|
||||
**Indexes:** `(organisation_id, status)`, `(parent_event_id)`, `UNIQUE(organisation_id, slug)`
|
||||
**Soft delete:** yes
|
||||
|
||||
> **v1.5 note:** `volunteer_min_hours_for_pass` removed — not applicable for Crewli use cases.
|
||||
**Helper scopes (Laravel):**
|
||||
|
||||
```php
|
||||
scopeTopLevel() // WHERE parent_event_id IS NULL
|
||||
scopeChildren() // WHERE parent_event_id IS NOT NULL
|
||||
scopeWithChildren() // includes self + all children
|
||||
scopeFestivals() // WHERE event_type IN ('festival', 'series')
|
||||
```
|
||||
|
||||
**Event type behaviour:**
|
||||
| event_type | Has parent? | Description |
|
||||
|---|---|---|
|
||||
| `event` | No | Flat single event — all modules at this level |
|
||||
| `event` | Yes | Sub-event (operational unit within festival) |
|
||||
| `festival` | No | Multi-day festival — children are the days |
|
||||
| `series` | No | Recurring series — children are the editions |
|
||||
|
||||
> **Recurrence note (BACKLOG ARCH-01):** `recurrence_rule` and
|
||||
> `recurrence_exceptions` are reserved for the future recurrence generator.
|
||||
> For now, sub-events are created manually. The generator will auto-create
|
||||
> sub-events from the RRULE when built.
|
||||
|
||||
---
|
||||
|
||||
@@ -537,21 +593,26 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date;
|
||||
|
||||
### `persons`
|
||||
|
||||
| Column | Type | Notes |
|
||||
| ---------------- | ------------------ | -------------------------------------------------------------------- |
|
||||
| `id` | ULID | PK |
|
||||
| `user_id` | ULID FK nullable | → users — nullable: external guests/artists have no platform account |
|
||||
| `event_id` | ULID FK | → events |
|
||||
| `crowd_type_id` | ULID FK | → crowd_types |
|
||||
| `company_id` | ULID FK nullable | → companies |
|
||||
| `name` | string | |
|
||||
| `email` | string | Indexed deduplication key |
|
||||
| `phone` | string nullable | |
|
||||
| `status` | enum | `invited\|applied\|pending\|approved\|rejected\|no_show` |
|
||||
| `is_blacklisted` | bool | |
|
||||
| `admin_notes` | text nullable | |
|
||||
| `custom_fields` | JSON | Event-specific fields — not queryable |
|
||||
| `deleted_at` | timestamp nullable | Soft delete |
|
||||
> **v1.7:** `event_id` now always references the top-level event (festival or
|
||||
> flat event). For sub-events, persons register at the festival level.
|
||||
> Activation per sub-event is tracked via `event_person_activations` pivot
|
||||
> and/or derived from shift assignments.
|
||||
|
||||
| Column | Type | Notes |
|
||||
| ---------------- | ------------------ | ------------------------------------------------------------------------------ |
|
||||
| `id` | ULID | PK |
|
||||
| `user_id` | ULID FK nullable | → users — nullable: external guests/artists have no platform account |
|
||||
| `event_id` | ULID FK | → events — **v1.7** always references top-level event (festival or flat event) |
|
||||
| `crowd_type_id` | ULID FK | → crowd_types |
|
||||
| `company_id` | ULID FK nullable | → companies |
|
||||
| `name` | string | |
|
||||
| `email` | string | Indexed deduplication key |
|
||||
| `phone` | string nullable | |
|
||||
| `status` | enum | `invited\|applied\|pending\|approved\|rejected\|no_show` |
|
||||
| `is_blacklisted` | bool | |
|
||||
| `admin_notes` | text nullable | |
|
||||
| `custom_fields` | JSON | Event-specific fields — not queryable |
|
||||
| `deleted_at` | timestamp nullable | Soft delete |
|
||||
|
||||
**Unique constraint:** `UNIQUE(event_id, user_id) WHERE user_id IS NOT NULL`
|
||||
**Indexes:** `(event_id, crowd_type_id, status)`, `(email, event_id)`, `(user_id, event_id)`
|
||||
@@ -610,6 +671,29 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date;
|
||||
|
||||
---
|
||||
|
||||
### `event_person_activations`
|
||||
|
||||
> **v1.7 New table.** Tracks which sub-events a person is active on,
|
||||
> independent of shift assignments. Used for:
|
||||
>
|
||||
> - Suppliers/crew present at all sub-events without shifts
|
||||
> - Festival-wide crew who need accreditation per day
|
||||
> - Persons manually activated on specific sub-events by coordinator
|
||||
>
|
||||
> For volunteers: activation is derived from shift assignments (no manual entry needed).
|
||||
> For fixed crew and suppliers: use this pivot for explicit activation.
|
||||
|
||||
| Column | Type | Notes |
|
||||
| ----------- | ------- | --------------------------------- |
|
||||
| `id` | int AI | PK — integer for join performance |
|
||||
| `event_id` | ULID FK | → events (the sub-event) |
|
||||
| `person_id` | ULID FK | → persons |
|
||||
|
||||
**Unique constraint:** `UNIQUE(event_id, person_id)`
|
||||
**Indexes:** `(person_id)`, `(event_id)`
|
||||
|
||||
---
|
||||
|
||||
## 3.5.6 Accreditation Engine
|
||||
|
||||
### `accreditation_categories`
|
||||
@@ -1236,10 +1320,42 @@ $effectiveDate = $shift->end_date ?? $shift->timeSlot->date;
|
||||
|
||||
- Every query on event data **MUST** scope on `organisation_id` via `OrganisationScope` Eloquent Global Scope
|
||||
- Use Laravel policies — never direct id-checks in controllers
|
||||
- **v1.7:** For festival queries, use `scopeWithChildren()` to include parent + all sub-events
|
||||
- **Audit log:** Spatie `laravel-activitylog` on: `persons`, `accreditation_assignments`, `shift_assignments`, `check_ins`, `production_requests`
|
||||
|
||||
---
|
||||
|
||||
### Rule 8 — Festival/Event Model (v1.7)
|
||||
|
||||
```
|
||||
Registration level → top-level event (festival or flat event)
|
||||
Operational level → sub-event (child event)
|
||||
Planning level → festival_section + shift
|
||||
|
||||
A person:
|
||||
- Registers once at festival/top-level event
|
||||
- Is active on 1 or more sub-events
|
||||
- Has shifts within those sub-events
|
||||
|
||||
Determined by:
|
||||
- Volunteer: via shift assignments (automatic)
|
||||
- Fixed crew: via event_person_activations (manual)
|
||||
- Supplier crew: via event_person_activations (manual)
|
||||
- Artist: always linked to one sub-event
|
||||
|
||||
Flat event (no children):
|
||||
- All modules at event level
|
||||
- persons.event_id = the event itself
|
||||
- No sub-event navigation shown in UI
|
||||
|
||||
Festival/series (has children):
|
||||
- persons.event_id = the parent (festival) event
|
||||
- festival_sections, shifts, artists = on child events
|
||||
- UI shows festival overview + child event tabs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Rule 6 — Shift Time Resolution
|
||||
|
||||
```php
|
||||
|
||||
Reference in New Issue
Block a user