- TECH-VSCODE-STALE-ADMIN-ENTRY: closed inb9f8f558d1- TECH-DELETE-DEAD-VIEWS: closed inbdbd5b0335Both items shipped; references preserved in git history. Co-Authored-By: Claude <noreply@anthropic.com>
1366 lines
58 KiB
Markdown
1366 lines
58 KiB
Markdown
# 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.
|
||
|
||
## Architectuur consolidatie sprint (actief)
|
||
|
||
Zie `dev-docs/ARCH-CONSOLIDATION-2026-04.md` voor volledige scope, principes en
|
||
werkstroomvolgorde. Sprint gestart april 2026, 8 werkstromen, 22-32 dagen werk
|
||
totaal. Tijdens de sprint worden bestaande backlog-items die door de sprint
|
||
worden opgelost daar expliciet gemarkeerd, en krijgen items die na de sprint
|
||
worden opgepakt een `[post-consolidatie]` tag.
|
||
|
||
---
|
||
|
||
## 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
|
||
|
||
**Aanleiding:** Zonder limiet claimen enthousiaste vrijwilligers 8+ shifts
|
||
(48 uur in één weekend), resulterend in burn-out en no-shows op latere shifts.
|
||
**Wat:** Per event/festival instelbaar minimum en maximum aantal shifts
|
||
dat een vrijwilliger kan claimen.
|
||
**Details:**
|
||
- `events.min_shifts_per_volunteer` (int nullable)
|
||
- `events.max_shifts_per_volunteer` (int nullable)
|
||
- ShiftAssignmentService checkt limiet bij claim/assign
|
||
- Portal toont voortgang: "Je hebt 2 van minimaal 4 shifts geclaimd"
|
||
- Bij bereiken maximum: verdere claims geblokkeerd met melding
|
||
**Prioriteit:** Laag — Nice-to-have. Geen prioriteit op dit moment.
|
||
**Afhankelijk van:** Shift claiming flow
|
||
|
||
---
|
||
|
||
### 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
|
||
|
||
---
|
||
|
||
### ARCH-06 — Locatie-gebaseerd shift-overzicht
|
||
|
||
Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie
|
||
ongeacht programmaonderdeel.
|
||
**Schema:** `locations` tabel en `shifts.location_id` bestaan al.
|
||
**Prioriteit:** Laag
|
||
|
||
---
|
||
|
||
### ARCH-07 — Accreditatie-templates per sectie/dag combinatie
|
||
|
||
MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode.
|
||
Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items.
|
||
Handmatige per-persoon toewijzing is de uitzondering, niet de norm.
|
||
**Schema:** Nieuwe tabel `accreditation_templates` nodig.
|
||
**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module
|
||
|
||
---
|
||
|
||
## 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 / Portal wisselen (admin SPA retired; platform admin in `/platform/*`)
|
||
- 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 ✅
|
||
|
||
---
|
||
|
||
### FORM-02 — TAG_PICKER → user_organisation_tags sync rebuild ✅ Done in S2b (2026-04-17)
|
||
|
||
**Aanleiding:** TagSyncService verwijderd in S2a Form Builder legacy purge. Semantiek (TAG_PICKER-antwoorden syncen naar user_organisation_tags bij registratie-goedkeuring) blijft valide.
|
||
**Wat:** Herbouwen als listener op FormSubmissionSubmitted tegen de nieuwe FormValue + TAG_PICKER field_type. Integreren via PersonIdentityService::confirmMatch zonder directe service-injection in PersonController.
|
||
**Eerdere call-sites (nu verwijderd):** PersonController::approve(), PersonIdentityService::syncRegistrationTags().
|
||
**Landed artefacts:**
|
||
|
||
- `App\Services\FormBuilder\FormTagSyncService::rebuildForPerson` — idempotent union-of-TAG_PICKER-values rebuild, only mutates `source=self_reported` rows, no-op when `person.user_id IS NULL`.
|
||
- `App\Listeners\FormBuilder\SyncTagPickerSelectionsOnSubmit` — ShouldQueue listener on `FormSubmissionSubmitted`, filters to `event_registration` purpose with `subject_type=person` + at least one `TAG_PICKER` value. Logs + swallows errors so sibling listeners (§31.1/§31.3/§31.8) keep running.
|
||
- `App\Services\PersonIdentityService::confirmMatch` — calls `FormTagSyncService::rebuildForPerson` after setting `person.user_id` (deferred-sync path for person who submitted before the user account existed).
|
||
- Contract frozen in ARCH-FORM-BUILDER.md §31.10 (authoritative block) and covered by `tests/Feature/FormBuilder/Integration/TagPickerSyncListenerTest`.
|
||
|
||
**Deferred integration tests (move under FORM-03 if needed):** GdprDeleteCascadeTest, EmailNotificationFlowTest, CodeOfConductGatingTest, SupplierIntakeFlowTest, CrowdListAutoAddTest (§31.9). Only §31.10 ships with S2b; other contracts wait until their feature arrives.
|
||
|
||
---
|
||
|
||
### FORM-BINDING-SNAPSHOT-MULTI — snapshot shape voor multi-binding per field
|
||
|
||
**Aanleiding:** WS-5a legt de relationele `form_field_bindings` tabel neer met een UNIQUE op `(owner_type, owner_id, target_entity, target_attribute)`. Dat laat meerdere bindings per field toe zolang ze op verschillende kolom-paren landen. De snapshot-writer (`FormSubmissionService::buildSnapshot` via `FormFieldBindingService::toJsonShape`) embed op dit moment maar één binding per field — de eerste. `schema_snapshot.fields[*].binding` is een object, geen array.
|
||
**Wat:** Snapshot-shape besluiten voor multi-binding: ofwel `binding` → array-of-objects, ofwel een nieuwe sleutel `bindings`. Migratiepad voor bestaande snapshots (ARCH §4.6.1). Reader-compat behouden.
|
||
**Trigger:** wanneer ARCH §6.1 patroon-scenario's multi-binding op één field rechtvaardigen (bv. Pattern C naar twee target entities tegelijk).
|
||
**Prioriteit:** Laag — out-of-scope van WS-5a, geen huidige user impact.
|
||
|
||
---
|
||
|
||
### FORM-BUILDER-LIBRARY-AUDIT-LOG — Audit FormFieldLibrary-level changes to bindings, validation rules, configs, and options
|
||
|
||
**Aanleiding:** Post-WS-5d, four form-builder child-table services (`FormFieldBindingService`, `FormFieldValidationRuleService`, `FormFieldConfigService`, `FormFieldOptionService`) emit activity-log events on FormField subjects only. Changes to FormFieldLibrary entries — which affect organisation-wide reusable field definitions — land silently in the audit log. This is the consistent behaviour inherited from WS-5a and extended through WS-5b/c/d, but it represents an audit-trail gap for library administration.
|
||
**Wat:** introduce parallel `library.*` activity-log events (`library.bindings_replaced`, `library.validation_rules_replaced`, `library.configs_replaced`, `library.options_replaced`) emitted by the same four services when the owner is a `FormFieldLibrary`. Document the convention addition in `ARCH-FORM-BUILDER.md` §6.7 and §17.4.2 + §17.5.2 + §17.6.3. Single cross-cutting work package.
|
||
**Prioriteit:** Middel — geen blocker; candidate sprint post-WS-5, before any external audit tooling is wired up (consumers shouldn't have to deal with the asymmetry).
|
||
**Related:** WS-5a §6.7 activity log events paragraph; WS-5b §17.4.2 / §17.5.2 paragraphs; WS-5d §17.6.3 paragraph.
|
||
|
||
---
|
||
|
||
### ~~FORM-BUILDER-MORPH-SCOPE-BASE-CLASS — Extract base class across the four WS-5 morph-scope siblings~~
|
||
|
||
**Status: closed 2026-04-25** — `FormFieldChildTableMorphScope`
|
||
abstract base extracted; the four concrete scopes are now marker
|
||
subclasses preserving identity. Phase A diff verification confirmed
|
||
the four concrete `apply()` + `resolveOrganisationId()` bodies were
|
||
byte-equal (zero divergence across three pairwise comparisons). Net
|
||
diff: +165 / −377 lines. Tests 1208 → 1208 (3260 assertions, identical).
|
||
Larastan baseline clean; Rector dry-run 357 → 355.
|
||
See `app/Models/Scopes/FormFieldChildTableMorphScope.php` and
|
||
ARCH-CONSOLIDATION-ADDENDUM-2026-04-24.md §"Uitvoering — base scope-
|
||
class extractie (2026-04-25)".
|
||
|
||
---
|
||
|
||
### FORM-04 — `grace_days` configurable on public_token rotation
|
||
|
||
**Aanleiding:** S2c §10.4 opgeleverd met een hardgecodeerd 7-daagse grace window in `PublicFormTokenResolver`. `rotatePublicToken` endpoint accepteert wel een `grace_days` request param maar schrijft die nergens naartoe; `form_schemas` heeft geen `grace_days` kolom.
|
||
**Wat:**
|
||
- Kolom `form_schemas.public_token_grace_days` (unsignedSmallInteger nullable, default null).
|
||
- `rotatePublicToken` service persisteert de ontvangen `grace_days` value (fallback: config default).
|
||
- `PublicFormTokenResolver::GRACE_DAYS` leest uit `form_schemas.public_token_grace_days ?? config('form_builder.public_token.default_grace_days', 7)`.
|
||
- Test: rotatie met grace_days=3 levert 410 na 4 dagen.
|
||
**Prioriteit:** Laag — operationele tuning, niet frontend-blocking.
|
||
|
||
---
|
||
|
||
### DOC-01 — Scramble / OpenAPI generator voor API.md
|
||
|
||
**Aanleiding:** `dev-docs/API.md` wordt met de hand bijgehouden per sprint — bij snelle iteratie landt hij altijd een slag achter de code. Scramble (of equivalent) genereert OpenAPI uit FormRequest + Resource introspectie zonder annotaties.
|
||
**Wat:** Scramble installeren, publieke form endpoints een dedicated `public` tag geven, CI-hook die de generated spec vergelijkt met een checked-in `dev-docs/api.openapi.yaml`, README link naar de live viewer.
|
||
**Prioriteit:** Middel — verlaagt docs-drift substantieel; past in een "developer-experience" sprint.
|
||
|
||
---
|
||
|
||
### DOC-02 — VitePress docs:build faalt op missing image
|
||
|
||
**Aanleiding:** `/docs/volunteer/je-aanmelden-via-een-link.md` verwijst
|
||
naar `./images/placeholder.png` dat niet bestaat. Dev mode werkt,
|
||
build faalt. Blokkeert CI als die `docs:build` gaat draaien.
|
||
**Wat:** Placeholder afbeelding toevoegen OF de referentie weghalen /
|
||
vervangen door een echte screenshot van het registratieflow.
|
||
**Prioriteit:** Laag — cosmetisch, niet blokkerend voor dev.
|
||
|
||
---
|
||
|
||
### DOC-03 — Formulieren sidebar story is incompleet
|
||
|
||
**Aanleiding:** Tijdens S3a PR 2 is
|
||
`docs/organizer/forms/concepts/wat-is-een-formulier.md` gewired in de
|
||
sidebar, samen met de nieuwe veldtype-pagina's. Maar de bredere
|
||
Formulieren-sidebar mist nog: publicatieflow, inzendingen-overzicht,
|
||
templates, webhook-configuratie, conditionele logica.
|
||
**Wat:** Dedicated docs-sprint voor de Formulieren-module in
|
||
VitePress. Schat: 6-8 pagina's in Nederlands, aimed at organisatoren
|
||
die formulieren configureren.
|
||
**Prioriteit:** Middel — landt best vlak voor/na S3b (organizer
|
||
form-builder UI), omdat screenshots pas zin hebben als de UI staat.
|
||
|
||
---
|
||
|
||
### DOC-04 — `scripts/install-claude-sync-hooks.sh` opnemen in SETUP/onboarding
|
||
|
||
**Aanleiding:** WS-4 pre-flight audit vond dat `scripts/sync-claude-docs.sh`
|
||
bestaat en door de post-commit hook draait, maar de hook-installer
|
||
(`scripts/install-claude-sync-hooks.sh`) is niet terug te vinden in de
|
||
developer onboarding-instructies. Nieuwe clones missen de hook.
|
||
**Wat:** Voeg een regel aan `dev-docs/SETUP.md` (of een post-install
|
||
checklist) toe die nieuwe developers opdraagt `install-claude-sync-hooks.sh`
|
||
te runnen. 1 regel, geen scope-impact.
|
||
**Prioriteit:** Laag — nice-to-have, niet blokkerend voor dev of CI.
|
||
|
||
---
|
||
|
||
### FORM-05 — Smart identity-match on public submission values
|
||
|
||
**Stub-status (S3a PR 2, 2026-04-23):** Public event_registration
|
||
submissions landen al met `identity_match_status='pending'` via de
|
||
bestaande `TriggerPersonIdentityMatchOnFormSubmit` listener. De portal
|
||
`IdentityMatchBanner` leest dit veld en toont de juiste copy. Contract
|
||
ligt vast in `tests/Feature/FormBuilder/Listeners/TriggerPersonIdentityMatchOnFormSubmitTest`.
|
||
|
||
**Resterend werk (de eigenlijke FORM-05):** public form submissions
|
||
(subject_type=null) krijgen momenteel *altijd* 'pending' omdat er nog
|
||
geen Person bestaat om tegen te matchen. Breid uit met:
|
||
|
||
- Nieuwe methode op PersonIdentityService:
|
||
`detectMatchesByValues(array $values, string $organisationId): MatchResult`
|
||
- Een extra tak in `TriggerPersonIdentityMatchOnFormSubmit::resolveStatus`
|
||
die voor public submissions de values uit `FormSubmission->values`
|
||
extraheert (email / first_name / last_name via de schema binding),
|
||
deze methode aanroept, en 'matched' / 'pending' / 'none' schrijft.
|
||
|
||
Zo krijgt de portal-UX een betekenisvol signaal in plaats van een
|
||
constante 'pending'.
|
||
|
||
Prioriteit: Medium. Kan gebundeld worden met de organizer
|
||
`person_identity_matches` UI (ook nog een frontend gap).
|
||
|
||
---
|
||
|
||
### 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~~ RETIRED
|
||
|
||
**Status:** Retired — admin SPA (`apps/admin/`) is afgeschaft. Super admin functionaliteit is verplaatst naar `apps/app/` onder `/platform/*` routes voor `super_admin` gebruikers.
|
||
|
||
---
|
||
|
||
### 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-05 — ESLint configuratie herstellen in apps/app/
|
||
|
||
**Aanleiding:** `npm run lint` faalt omdat `.eslintrc.cjs` niet bestaat
|
||
en er ook geen flat-config equivalent aanwezig is. Effectief draait
|
||
de app zonder lint, wat botst met CLAUDE.md's zero-compromise regels.
|
||
**Wat:** Juiste flat-config installeren en afstemmen op het huidige
|
||
Vuexy 9.5 template. Moet in één keer groen draaien.
|
||
**Prioriteit:** Middel — tooling-gap.
|
||
|
||
---
|
||
|
||
### TECH-06 — ESLint config ontbreekt in apps/portal
|
||
|
||
**Aanleiding:** `npm run lint` faalt in `apps/portal/` omdat
|
||
`.eslintrc.cjs` niet bestaat. Geen flat-config equivalent aanwezig.
|
||
Portal draait dus effectief zonder lint, wat botst met CLAUDE.md's
|
||
zero-compromise regels. Apart van TECH-05 (dat over apps/app gaat).
|
||
**Wat:** Flat-config ESLint installeren in `apps/portal/`, afgestemd
|
||
op Vue 3 + TypeScript + Vuexy 10.11.1. In één keer groen laten
|
||
draaien. Bij voorkeur gedeelde shared-config tussen apps/app en
|
||
apps/portal om drift te voorkomen.
|
||
**Prioriteit:** Middel — tooling-gap, niet user-facing.
|
||
|
||
---
|
||
|
||
### TECH-PORTAL-ESLINT-DEPS — Audit apps/portal/package.json op missing direct ESLint deps
|
||
|
||
**Aanleiding:** Tijdens de Cursor ESLint-integratie fix in `apps/app/`
|
||
(commit `4369806`, 2026-04-30) bleek dat 15 ESLint plugins, parsers en
|
||
configs alleen via pnpm-hoisting werden gevonden, niet als directe
|
||
dependencies in `package.json`. Cursor's ESLint extension gebruikt
|
||
strict module resolution en crashte op elke missing plugin in de
|
||
`@antfu/eslint-config-vue` extends-chain. Aannemelijk dat
|
||
`apps/portal/package.json` hetzelfde patroon heeft, want zelfde antfu-
|
||
config-keten en zelfde pnpm-monorepo-structuur. Zonder fix breekt het
|
||
ESLint-formatter pad voor iedereen die de portal opent in Cursor —
|
||
zelfde 3-uur-diagnose die we vandaag hebben doorgemaakt.
|
||
**Wat:**
|
||
- Run de diagnose-keten van vandaag (`ls node_modules/.pnpm | grep
|
||
"^eslint-plugin-"` vs `ls node_modules | grep "^eslint-plugin-"`,
|
||
plus de scoped variant en `vue-eslint-parser`/`@antfu/eslint-config-*`
|
||
audits) op `apps/portal/`.
|
||
- Voeg alle ontbrekende plugins als directe deps toe via `pnpm add -D`
|
||
met versies die matchen wat in pnpm store zit (zero version shifts).
|
||
- Verifieer in Cursor dat de portal ESLint extension activeert zonder
|
||
errors in Output Channel, en dat save-on-format ESLint correct firet.
|
||
**Prioriteit:** Middel — moet vóór sessie 2 (Pages migration) waar
|
||
portal-files actief in Cursor bewerkt worden. Niet kritisch nu, maar
|
||
de eerste developer die portal in Cursor opent stuit op hetzelfde
|
||
issue als vandaag.
|
||
|
||
---
|
||
|
||
### TECH-ESLINT-V9-MIGRATION — Migreer apps/app + apps/portal naar ESLint v9 + flat config
|
||
|
||
**Aanleiding:** ESLint v8.57.1 is end-of-life sinds eind 2024
|
||
(zie pnpm install warnings: `eslint@8.57.1: This version is no longer
|
||
supported`). Daarnaast zijn meerdere config-pakketten in onze chain
|
||
deprecated en migreren naar flat config: `@antfu/eslint-config-vue`,
|
||
`@antfu/eslint-config-basic`, `@antfu/eslint-config-ts`, en
|
||
`eslint-plugin-markdown`. De huidige `.eslintrc.cjs` legacy-config
|
||
werkt, maar er komen geen security fixes meer voor v8 en de transitieve
|
||
deprecated-warnings groeien per `pnpm install`. Migratie naar v9 + flat
|
||
config (`eslint.config.js`) + modern `@antfu/eslint-config` lost in één
|
||
klap alle deprecated warnings op én moderniseert de toolchain.
|
||
**Wat:**
|
||
- Eigen workstream — niet meeliften op andere sprints want config-
|
||
rewrite raakt 200+ regels.
|
||
- ESLint 8.57.1 → 9.x upgrade.
|
||
- `.eslintrc.cjs` (legacy) → `eslint.config.js` (flat config).
|
||
- `@antfu/eslint-config@0.43.x` (legacy) → `@antfu/eslint-config@latest`
|
||
(flat-config variant).
|
||
- Alle plugins meeschalen naar versies die met v9 + flat config werken.
|
||
- Test op apps/app + apps/portal — de hele lint-baseline moet groen
|
||
blijven (0 problems voor app, baseline voor portal).
|
||
- Hoort vóór WS-3 sessie 2 (pages migration) als de portal-eslint
|
||
baseline ook op 0 staat, anders na alle WS-3 sessies. Schat 1-2
|
||
dagen werk inclusief regression-fixing.
|
||
**Prioriteit:** Middel-Hoog — security-EOL is een doorslaggevend
|
||
argument; uitstel tot na WS-3 acceptabel maar niet onbeperkt. Eigen
|
||
sprint waard, geen meelift-pad.
|
||
|
||
---
|
||
|
||
### TECH-AXIOS-STORE-COUPLING — Decouple lib/axios.ts from stores layer
|
||
|
||
**Aanleiding:** WS-3 sessie 1c (eslint-plugin-boundaries enforcement)
|
||
constateerde dat `apps/app/src/lib/axios.ts` 4 imports heeft uit `stores/`
|
||
(2 statisch op regel 3-4 voor `useNotificationStore` /
|
||
`useOrganisationStore`, 2 dynamisch op regel 61, 72 voor
|
||
`useImpersonationStore` / `useAuthStore` uit 1b-iii). De `lib → stores`
|
||
edge schendt de layered-architecture matrix. Om sessie 1c on-time te
|
||
landen zijn de 4 sites gemarkeerd met `eslint-disable-next-line` +
|
||
verwijzing naar dit backlog-item; de structurele fix is bewust uitgesteld
|
||
naar een dedicated sessie omdat het architectuurwerk is, geen tooling-
|
||
cleanup.
|
||
|
||
**Wat:**
|
||
- Decouple `lib/axios.ts` van stores zodat het puur HTTP-infrastructuur
|
||
wordt. Twee paden, kies bij refactor:
|
||
- **Approach 1 (preferred starting point):** `lib/axios.ts` exporteert
|
||
de axios-instance plus een `registerInterceptors(client, deps)`
|
||
functie die callbacks accepteert (`onAuthFail`,
|
||
`onImpersonationDrop`, `getActiveOrgId`, `notify(message, level)`).
|
||
Een nieuwe `plugins/axios-bindings.ts` (mag `stores` importeren per
|
||
matrix) roept `registerInterceptors` aan bij app-init met closures
|
||
over de stores.
|
||
- **Approach 2 (fallback):** event-bus / callback registry; axios.ts
|
||
emit-eert semantische events (`auth-failed`, `needs-org-header`,
|
||
`notify-error`) en `plugins/axios-bindings.ts` subscribet.
|
||
- Verwijder alle 4 `eslint-disable-next-line` comments uit
|
||
`lib/axios.ts`.
|
||
- Tests moeten dekken: X-Organisation-Id header injection, 401/403
|
||
logout flow, impersonation revocation flow, error toast op 4xx/5xx.
|
||
- Optioneel: meteen ook de static/dynamic import-split in axios.ts
|
||
uniformeren (nu inconsistent om legacy 1b-iii-redenen).
|
||
|
||
**Prioriteit:** Middel — geen blokker voor andere workstreams, maar elke
|
||
maand dat dit blijft staan is een vlek op de boundaries-enforcement
|
||
geloofwaardigheid. Aanbevolen: meelift met de eerste WS-3 PR die `lib/`
|
||
of `plugins/` raakt, of een dedicated 2-3 uur sessie na WS-6 sluiting.
|
||
|
||
---
|
||
|
||
### TECH-WS3-BOUNDARIES-SUBZONES — Sub-zone import-boundaries inside components/ and pages/
|
||
|
||
**Aanleiding:** WS-3 sessie 1c heeft top-level zone-boundaries in
|
||
`apps/app/` neergezet via `eslint-plugin-boundaries`. De `/dev-docs/ARCH-CONSOLIDATION-2026-04.md`
|
||
§4.2 target layout introduceert sub-zones binnen die top-level zones —
|
||
specifiek `components/{organizer,portal,shared}/` en
|
||
`pages/{(auth),portal,register,events,persons,organisations,platform}/`.
|
||
De architecturale intent is dat `components/portal` niet uit
|
||
`components/organizer` mag importeren (en vice versa), met `shared` als
|
||
de gemeenschappelijke uitgang. Sessie 1c heeft die sub-zone
|
||
enforcement bewust uitgesteld omdat de sub-folders nog niet bestaan;
|
||
pre-emptieve rules op niet-bestaande directories worden stille dode
|
||
config die drift.
|
||
|
||
**Wat:**
|
||
- **Precondition:** WS-3 PR-B is gemerged en de §4.2 sub-folder
|
||
structuur is gelandt (`components/{organizer,portal,shared}/` en
|
||
`pages/{(auth),portal,...}/` bestaan fysiek met content).
|
||
- Breid `boundaries/elements` in `apps/app/.eslintrc.cjs` uit met:
|
||
- `{ type: 'components-organizer', pattern: 'src/components/organizer/**' }`
|
||
- `{ type: 'components-portal', pattern: 'src/components/portal/**' }`
|
||
- `{ type: 'components-shared', pattern: 'src/components/shared/**' }`
|
||
- (ontworpen sub-zones voor `pages/` analoog)
|
||
- Voeg per-sub-zone rules toe: `components-portal` en
|
||
`components-organizer` mogen beide uit `components-shared` importeren,
|
||
maar niet uit elkaar. `pages/portal/` mag niet uit `pages/events/`
|
||
(en de andere organizer-pages) importeren, en omgekeerd.
|
||
- Resolve violations die bij eerste activatie naar boven komen.
|
||
- ETA: 1-2 uur zodra precondities ervoor liggen.
|
||
|
||
**Prioriteit:** Middel — preventieve architectuur-discipline voor de
|
||
multi-tenant context-isolatie tussen organizer en portal UI-paden.
|
||
Zonder deze rules is de kans groot dat een ontwikkelaar tijdens een
|
||
PR-B follow-up onbewust portal- en organizer-componenten verstrengelt.
|
||
|
||
---
|
||
|
||
### TECH-WS3-BOUNDARIES-ROUTER-ZONE — Add `router/` zone to boundaries matrix
|
||
|
||
**Aanleiding:** WS-3 sessie 1c audit (§3 forward-compatibility) flagde
|
||
dat de §4.2 target layout `src/plugins/1.router/` vervangt door een
|
||
flat `src/router/`. De huidige boundaries-matrix in
|
||
`apps/app/.eslintrc.cjs` mapt router-files naar de `plugins` zone
|
||
(omdat ze fysiek in `src/plugins/1.router/` zitten). Zodra de
|
||
verhuizing plaatsvindt — geplant in een latere WS-3 PR — moet de
|
||
matrix-config dat reflecteren, anders vallen router-files buiten de
|
||
`boundaries/elements` mapping en flag-stormt de plugin met "no rule
|
||
found".
|
||
|
||
**Wat:** In dezelfde commit/PR die `src/plugins/1.router/` naar
|
||
`src/router/` verhuist:
|
||
|
||
- Voeg toe aan `boundaries/elements` in `apps/app/.eslintrc.cjs`:
|
||
```js
|
||
{ type: 'router', pattern: 'src/router/**' },
|
||
```
|
||
Plaats vóór `plugins` in de array (first-match-wins ordering).
|
||
- Voeg toe aan `boundaries/element-types` rules:
|
||
```js
|
||
{ from: 'router', allow: ['types', 'utils', 'lib', 'plugins', 'stores'] },
|
||
```
|
||
- Verifieer `pnpm lint` blijft op 0 problemen.
|
||
|
||
**Trigger:** "src/plugins/1.router/" → "src/router/" verhuizing (latere
|
||
WS-3 PR, vermoedelijk PR-B als die de router-tree consolideert).
|
||
|
||
**Prioriteit:** Laag — geen actie tot de verhuizing plaatsvindt; dan
|
||
verplicht 5-minute follow-up.
|
||
|
||
---
|
||
|
||
### TECH-08 — Paginated response meta wordt weggegooid in organizer composables
|
||
|
||
**Aanleiding:** `apps/app/src/composables/api/useSections.ts` en
|
||
`apps/app/src/composables/api/useFormSchemas.ts` definiëren beiden een
|
||
lokale `PaginatedResponse<T> = { data: T[] }` shape die alleen de
|
||
`data` array eruit trekt. De Laravel paginator geeft ook `links` en
|
||
`meta` (huidige pagina, totaal, per-page) terug — die informatie gaat
|
||
nu verloren. Voor de huidige consumers geen probleem (geen paginatie-
|
||
controls), maar zodra een lijstweergave in de organizer UI een
|
||
"Volgende"-knop, pagina-selector of totaaltelling wil tonen, loopt de
|
||
composable tegen die beperking aan. PR-b2 (/forms lijst-view) is de
|
||
eerste concrete trigger.
|
||
**Wat:** Upgrade de shared response-shape in beide composables naar
|
||
`{ data: T[], links: { first, last, prev, next }, meta: { current_page, from, last_page, path, per_page, to, total } }` (exacte veldnamen
|
||
conform Laravel's `ResourceCollection` default). Retourneer het hele
|
||
meta-blok mee uit de `useXList` composables zodat de UI kan paginate.
|
||
Bij voorkeur één gedeelde TypeScript interface exporteren uit een
|
||
nieuwe `apps/app/src/types/api.ts` zodat de derde, vierde, ...
|
||
composable die volgt hetzelfde patroon erft. Nieuwe composables voor
|
||
lijst-endpoints moeten vanaf dat moment deze interface gebruiken.
|
||
**Prioriteit:** Middel — blokkeert geen huidige features, maar elke
|
||
composable die zonder paginering-support wordt gebouwd voegt werk toe
|
||
aan de latere migratie. Oplossen vlak vóór PR-b2 paginering-UI
|
||
introduceert is het natuurlijke moment.
|
||
|
||
---
|
||
|
||
### ~~TECH-02 — scopeForFestival helper op Event model~~ ✅ OPGELOST
|
||
|
||
---
|
||
|
||
### ~~TECH-03 — DevSeeder uitbreiden met festival-structuur~~ ✅ OPGELOST
|
||
|
||
---
|
||
|
||
### ~~TECH-04 — EventController.store() redundante ternary~~ ✅ OPGELOST
|
||
|
||
---
|
||
|
||
### ~~TECH-07 — @form-schema transitive dep op @core/utils/validators~~ ✅ OPGELOST — resolved in PR-a1
|
||
|
||
---
|
||
|
||
## Opgeloste items (april 2026)
|
||
|
||
De volgende items zijn geïmplementeerd en afgerond (673+ tests):
|
||
|
||
- ~~TECH-02: scopeForFestival + scopeWithChildren helper scopes op Event model~~ ✅
|
||
- ~~TECH-03: DevSeeder uitgebreid met festival-structuur (secties, tijdsloten, personen)~~ ✅
|
||
- ~~TECH-04: EventController.store() redundante ternary~~ ✅
|
||
- ~~Auth race condition (CTRL+R fix)~~ ✅
|
||
- ~~Section edit dialog bug~~ ✅
|
||
- ~~Time slot duplicate button~~ ✅
|
||
- ~~Browser autocomplete disabled op dialog form fields~~ ✅
|
||
- ~~Category + icon fields op festival_sections~~ ✅
|
||
- ~~IconPicker component~~ ✅
|
||
- ~~Crowd Types beheer-UI~~ ✅
|
||
- ~~Companies CRUD~~ ✅
|
||
- ~~Person tags backend (person_tags + user_organisation_tags)~~ ✅
|
||
- ~~Event status state machine (dedicated transition endpoint, prerequisites, festival cascade)~~ ✅
|
||
- ~~Event status transition buttons (frontend + backend, state machine, cascade)~~ ✅
|
||
- ~~Festival tab-navigatie (uniform tabs, Programmaonderdelen tab)~~ ✅
|
||
- ~~SectionsShiftsPanel extractie als herbruikbaar component~~ ✅
|
||
- ~~Cross-event section auto-redirect~~ ✅
|
||
- ~~Shift claiming in portal (5 endpoints, 26 tests, ClaimenTab + RoosterTab)~~ ✅
|
||
- ~~Cross-app auth isolation (CookieBearerToken per app, 3 isolatietests)~~ ✅
|
||
- ~~Password reset (beide SPAs, custom notification, app-aware links)~~ ✅
|
||
- ~~Email change with verification (self-service + admin, 24h token expiry)~~ ✅
|
||
- ~~Password change while logged in~~ ✅
|
||
- ~~"Lid toevoegen als deelnemer" shortcut (2 endpoints, 11 tests)~~ ✅
|
||
- ~~Person Identity Matching (detect→suggest→confirm, fuzzy name, DOB tiebreaker)~~ ✅
|
||
- ~~Naam-splitsing first_name + last_name (66 files)~~ ✅
|
||
- ~~Date of birth op persons en users~~ ✅
|
||
- ~~Smart assign dialog (tags, preferences, availability, cascading filters)~~ ✅
|
||
- ~~Soft capacity + approve overbook fix~~ ✅
|
||
- ~~Cancellation source tracking + re-assignment~~ ✅
|
||
- ~~VitePress user documentation (3 core pages)~~ ✅
|
||
- ~~Registration settings (show_in_registration)~~ ✅
|
||
- ~~Premium portal wizard (banner, branding, success page)~~ ✅
|
||
- ~~Global error handling (useNotificationStore + axios 422 interceptor)~~ ✅
|
||
- ~~S3a PR 2: TAG_PICKER / AVAILABILITY_PICKER / SECTION_PRIORITY renderen in het publieke registratieformulier. Seeder uitgebreid met twee showcase-velden + parent-level VOLUNTEER time slot + duplicate section name voor dedup-dekking. SECTION_PRIORITY waarde-shape gevalideerd in FormValueService. `FormSubmissionResource` krijgt admin-facing `identity_match` block. 64 nieuwe assertions over backend + Vitest.~~ ✅
|
||
- ~~FORM-09: TriggerPersonIdentityMatchOnFormSubmit sync refactor (eager state transition, async resolution deferred to FORM-05)~~ ✅
|
||
|
||
---
|
||
|
||
## Bekende gaps — nog te bouwen
|
||
|
||
Overzicht van bekende ontbrekende onderdelen die nog niet gebouwd zijn:
|
||
|
||
| Item | Status | Prioriteit |
|
||
|------|--------|-----------|
|
||
| Person Tags frontend UI | Backend compleet, geen organiser UI | Hoog |
|
||
| Accreditatie Engine (SCHEMA 3.5.6, ARCH-07 templates) | Volgende grote module | Hoog |
|
||
| ARCH-03 — Sectie templates / kopiëren van vorig event | Niet gestart | Hoog |
|
||
| Briefings & Communicatie basis | Niet gestart | Middel |
|
||
| Artist Advancing portal | Niet gestart | Middel |
|
||
| UX-01 — Festival setup checklist | Niet gestart | Middel |
|
||
| UX-03 — Personen per sub-event | Niet gestart | Middel |
|
||
| ARCH-06 — Locatie-gebaseerd shift-overzicht | Niet gestart | Laag |
|
||
| ARCH-09 — Artist Eloquent model + migration | Prerequisite for artist_advance purpose | Hoog (blocker voor artist_advance) |
|
||
|
||
---
|
||
|
||
## Nieuwe backlog items
|
||
|
||
### ARCH-06 — Locatie-gebaseerd shift-overzicht
|
||
|
||
Cross sub-event filter op location_id. Toont alle shifts op een fysieke locatie
|
||
ongeacht programmaonderdeel.
|
||
**Prioriteit:** Laag
|
||
|
||
---
|
||
|
||
### ARCH-07 — Accreditatie-templates per sectie/dag combinatie
|
||
|
||
MUST-HAVE bij accreditatie build. Templates worden primaire toewijzingsmethode.
|
||
Per crowd_type + sectie + dag → automatisch voorgestelde accreditatie-items.
|
||
**Prioriteit:** Hoog — direct meebouwen bij accreditatie-module
|
||
|
||
---
|
||
|
||
### ARCH-08 — Recurrence voor time slots
|
||
|
||
Herhalingsfunctie: "genereer 5 time slots in één keer" voor opbouwdagen etc.
|
||
**Prioriteit:** Middel
|
||
|
||
---
|
||
|
||
### ARCH-09 — Artist Eloquent model + migration
|
||
|
||
**Aanleiding:** `artist_advance` purpose is geregistreerd in `PurposeRegistry` (v1.0) met `subject_type = 'artist'`, maar het `App\Models\Artist` model en de `artists` tabel bestaan nog niet. `AppServiceProvider::PURPOSE_SUBJECT_FQCN` bevat `'artist' => 'App\\Models\\Artist'` als string-literal (gedocumenteerd in de constant-docblock) om morph-map-registratie te laten slagen — resolution is lazy en knalt pas bij de eerste echte artist-submission.
|
||
|
||
**Wat:** Artist Eloquent model + migratie + factory, conform het patroon van de overige business-tabellen (ULID PK, `HasUlids`, `OrganisationScope`, soft deletes per SCHEMA §3.5.7). Na het landen van het model: `PURPOSE_SUBJECT_FQCN` omzetten van string-literal naar `Artist::class` import.
|
||
|
||
**Prioriteit:** Hoog — blokkeert elke feature-sprint rond artist_advance.
|
||
|
||
**Afhankelijk van:** SCHEMA §3.5.7 finalisatie (artists, performances, stages etc. — momenteel in `/dev-docs/ARCH-PLANNED-MODULES.md` na WS-8).
|
||
|
||
---
|
||
|
||
### ART-03 — Artist profile met cross-event rider defaults
|
||
|
||
Organisatie-niveau artiest-profiel dat rider-defaults, contacten en interne
|
||
notities opslaat over events heen. "Importeer van vorig jaar" functie.
|
||
**Prioriteit:** Laag
|
||
|
||
---
|
||
|
||
### UX-01 — Festival setup checklist / onboarding wizard
|
||
|
||
Checklist widget op festival dashboard die door de configuratiestappen leidt.
|
||
Items worden groen als ze zijn afgerond.
|
||
**Prioriteit:** Middel
|
||
|
||
---
|
||
|
||
### UX-02 — Aandachtsmatrix op event dashboard
|
||
|
||
**Aanleiding:** Organisator verliest overzicht bij 200+ vrijwilligers en
|
||
30 secties. Kritieke problemen (onderbezette shifts, wachtende goedkeuringen,
|
||
onopgeloste identity matches) worden pas ontdekt als het te laat is.
|
||
**Wat:** Drie metric cards op het event Overzicht-tab:
|
||
- Goedgekeurde personen zonder shift-toewijzing (telling)
|
||
- Wachtende shift-claims (telling)
|
||
- Onopgeloste identiteitsmatches (telling)
|
||
Elke card is klikbaar en navigeert naar de relevante module.
|
||
**Prioriteit:** Hoog — eerste frontend-taak op Overzicht-tab.
|
||
Data is beschikbaar via bestaande endpoints (aggregate queries).
|
||
|
||
---
|
||
|
||
### UX-03 — Personen-tab op sub-event niveau
|
||
|
||
Gefilterde view: alleen personen met shifts in dit programmaonderdeel.
|
||
Met link "Bekijk alle personen op festival-niveau".
|
||
**Prioriteit:** Middel
|
||
|
||
---
|
||
|
||
### COMM-05 — Resend invitation endpoint
|
||
|
||
**Aanleiding:** Uitnodigingen kunnen nu alleen ingetrokken worden of
|
||
verlopen vanzelf. Organisatoren willen een "opnieuw versturen" actie
|
||
voor gevallen waarin de oorspronkelijke mail in de spamfilter belandde,
|
||
gemist werd, of het e-mailadres net gecorrigeerd is.
|
||
**Wat:**
|
||
- Backend: `POST /api/v1/organisations/{org}/invitations/{id}/resend`
|
||
(idempotent: regenereert de mail zonder token of verloopdatum te
|
||
wijzigen). Zelfde endpoint voor `/admin` scope.
|
||
- Frontend: "Opnieuw versturen" actie activeren in de sectie
|
||
openstaande uitnodigingen op `/members` (useMembers heeft al een
|
||
`useResendInvitation` stub-ready).
|
||
**Prioriteit:** Middel — user-requested UX-verbetering.
|
||
|
||
---
|
||
|
||
### UX-04 — Leveranciers-deadline waarschuwing
|
||
|
||
**Aanleiding:** Leveranciers die hun personeelslijst niet tijdig indienen
|
||
veroorzaken last-minute chaos. De organisator heeft geen zicht op welke
|
||
externe lijsten nog niet compleet zijn.
|
||
**Wat:** Op het event dashboard en in de publiekslijsten-tab:
|
||
- Badge "Nog niet compleet" op externe lijsten waar persons_count < max_persons
|
||
- Optioneel: deadline-datum veld op crowd_lists (nieuw kolom)
|
||
- Waarschuwingsbanner X dagen voor de deadline: "3 leveranciers hebben hun
|
||
lijst nog niet compleet ingediend"
|
||
**Prioriteit:** Middel — meebouwen bij leveranciersportaal (SUP-01)
|
||
|
||
---
|
||
|
||
## Larastan reduction sprints
|
||
|
||
Larastan (PHPStan for Laravel) is geïnstalleerd op level 6 met een
|
||
accept-all baseline van 1556 errors over 678 files (41 distinct
|
||
identifiers). Zie `/dev-docs/LARASTAN.md` voor werkmodel. Per-categorie
|
||
reduction-sprints hieronder — elke sprint mikt op één identifier, laat
|
||
de baseline krimpen en regenereert hem aan het einde.
|
||
|
||
### TECH-LARASTAN-01 — property.notFound
|
||
|
||
**Priority:** Middel (post-foundation, incremental)
|
||
**Scope:** baseline-entries met identifier `property.notFound`.
|
||
**Estimate:** 613 errors over 87 files.
|
||
**Completion gate:** category count daalt naar 0 in geregenereerde
|
||
baseline; volledige test suite groen.
|
||
|
||
**Approach:**
|
||
- Merendeel zit op Eloquent-modellen waar `$user->id` of vergelijkbaar
|
||
niet door PHPDoc wordt herkend — los op door `@property` annotaties
|
||
op modellen toe te voegen (of via `php artisan ide-helper:models`
|
||
als dat acceptabel wordt gevonden).
|
||
- Commit per sub-directory als >50 errors.
|
||
|
||
### TECH-LARASTAN-02 — missingType.generics
|
||
|
||
**Priority:** Middel
|
||
**Scope:** baseline-entries met identifier `missingType.generics`.
|
||
**Estimate:** 289 errors over 52 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Zit vooral op factories (`extends Factory<Model>`) en
|
||
`HasFactory`-gebruik zonder template. Voeg type-params toe aan
|
||
class-declaraties en docblocks.
|
||
- Overlapt deels met TYPE_DECLARATION-sprint van Rector.
|
||
|
||
### TECH-LARASTAN-03 — argument.templateType
|
||
|
||
**Priority:** Middel
|
||
**Scope:** baseline-entries met identifier `argument.templateType`.
|
||
**Estimate:** 154 errors over 31 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Voornamelijk `collect(...)` calls waar PHPStan de generieke
|
||
template TKey/TValue niet kan resolven. Typeer de input expliciet
|
||
of gebruik `Collection::make([...])` met generieke annotatie.
|
||
|
||
### TECH-LARASTAN-04 — missingType.iterableValue
|
||
|
||
**Priority:** Middel
|
||
**Scope:** baseline-entries met identifier `missingType.iterableValue`.
|
||
**Estimate:** 98 errors over 61 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Methode-return-types als `array` zonder value-type. Voeg
|
||
`array<string, mixed>` of specifieker toe aan form-requests,
|
||
resource `toArray()` methods, factory `definition()` methods.
|
||
|
||
### TECH-LARASTAN-05 — argument.type
|
||
|
||
**Priority:** Middel
|
||
**Scope:** baseline-entries met identifier `argument.type`.
|
||
**Estimate:** 77 errors over 32 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Reële type-mismatches (bijv. string doorgegeven waar `'strict'|'lax'`
|
||
vereist is). Case-by-case reviewen — niet mechanisch.
|
||
|
||
### TECH-LARASTAN-06 — method.notFound
|
||
|
||
**Priority:** Middel
|
||
**Scope:** baseline-entries met identifier `method.notFound`.
|
||
**Estimate:** 50 errors over 26 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Meestal "Call to an undefined method Illuminate\\…::users()" —
|
||
relationship methods die PHPStan niet kent. Los op via
|
||
`@method` annotaties of generieke relationship-return types.
|
||
|
||
### TECH-LARASTAN-07 — method.childReturnType
|
||
|
||
**Priority:** Laag
|
||
**Scope:** baseline-entries met identifier `method.childReturnType`.
|
||
**Estimate:** 35 errors over 35 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Eén-op-één met factory `definition()` methodes. Smeedt samen met
|
||
TECH-LARASTAN-02 in één sprint indien praktisch.
|
||
|
||
### TECH-LARASTAN-08 — method.unresolvableReturnType
|
||
|
||
**Priority:** Laag
|
||
**Scope:** baseline-entries met identifier
|
||
`method.unresolvableReturnType`.
|
||
**Estimate:** 32 errors over 9 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
### TECH-LARASTAN-09 — assign.propertyType
|
||
|
||
**Priority:** Middel (reële type-bug kans hoger dan bij generics)
|
||
**Scope:** baseline-entries met identifier `assign.propertyType`.
|
||
**Estimate:** 31 errors over 10 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Meestal Carbon vs string mismatch op Eloquent properties —
|
||
modelcasts goed zetten zodat Eloquent de datetime teruggeeft waar
|
||
hij beloofd is.
|
||
|
||
### TECH-LARASTAN-10 — instanceof.alwaysTrue
|
||
|
||
**Priority:** Laag
|
||
**Scope:** baseline-entries met identifier `instanceof.alwaysTrue`.
|
||
**Estimate:** 28 errors over 17 files.
|
||
**Completion gate:** category count naar 0; tests groen.
|
||
|
||
**Approach:**
|
||
- Dead `instanceof`-checks. Prefab voor Rector's `DEAD_CODE`
|
||
sprint — wachten of combineren.
|
||
|
||
### TECH-LARASTAN-CI — CI integration
|
||
|
||
**Priority:** Middel
|
||
**Scope:** wire `composer analyse` als blokkerende PR-gate in CI.
|
||
**Depends on:** CI-infrastructuurkeuze.
|
||
|
||
### TECH-LARASTAN-L8 — level 8 migration
|
||
|
||
**Priority:** Laag
|
||
**Scope:** niveau 6→8 verhogen nadat level-6 baseline op 0 staat.
|
||
**Estimate:** onbekend totdat level 6 leeg is.
|
||
|
||
---
|
||
|
||
## Rector application sprints
|
||
|
||
Rector is geïnstalleerd en geconfigureerd op PHP 8.2 + safe quality
|
||
+ Laravel code-quality rule sets. Dry-run rapporteert **487
|
||
rule-applications over 357 files** verdeeld over ~35 distinct rules.
|
||
Zie `/dev-docs/RECTOR.md` voor werkmodel. Per-ruleset sprints
|
||
hieronder — elke sprint beperkt zich tot één scope, past toe,
|
||
verifieert tests + Larastan, regenereert waar nodig.
|
||
|
||
### TECH-RECTOR-01 — DEAD_CODE sprint
|
||
|
||
**Priority:** Middel
|
||
**Scope:** `SetList::DEAD_CODE` over app/, database/, tests/.
|
||
**Estimate:** Top 13 unused-variable removals via
|
||
`RemoveUnusedVariableAssignRector`, plus verwante dead-code rules.
|
||
Exact totaal: ~30-50 changes.
|
||
**Completion gate:** `composer rector:apply` clean voor deze set;
|
||
test suite groen; Larastan baseline geregenereerd en kleiner (of
|
||
gelijk). Commit per sub-directory indien >50 wijzigingen.
|
||
|
||
**Approach:**
|
||
- Tijdelijk in rector.php alleen DEAD_CODE aanzetten, andere sets
|
||
uitcommentariseren.
|
||
- `composer rector` om diff te reviewen voor zekerheid.
|
||
- `composer rector:apply`.
|
||
- `composer test` + `composer analyse`.
|
||
- Herstel rector.php naar volledige config.
|
||
- Commit.
|
||
|
||
### TECH-RECTOR-02 — TYPE_DECLARATION sprint
|
||
|
||
**Priority:** Middel (hoogste volume — 174+ changes)
|
||
**Scope:** `SetList::TYPE_DECLARATION`.
|
||
**Estimate:** ~174+ changes — top drivers:
|
||
`AddClosureVoidReturnTypeWhereNoReturnRector` (103),
|
||
`AddArrowFunctionReturnTypeRector` (71), plus
|
||
`AddArrayFunctionClosureParamTypeRector` en kleinere.
|
||
**Completion gate:** zie TECH-RECTOR-01. **Extra**: deze set lost
|
||
waarschijnlijk veel `missingType.*` errors in Larastan baseline op —
|
||
regenereer en commit gereduceerde phpstan-baseline.neon mee.
|
||
|
||
### TECH-RECTOR-03 — LARAVEL_CODE_QUALITY + LARAVEL_COLLECTION sprint
|
||
|
||
**Priority:** Middel
|
||
**Scope:** `LaravelSetList::LARAVEL_CODE_QUALITY` +
|
||
`LaravelSetList::LARAVEL_COLLECTION`.
|
||
**Estimate:** ~80 changes — `AppToResolveRector` (51),
|
||
`DispatchToHelperFunctionsRector` (8),
|
||
`EloquentOrderByToLatestOrOldestRector` (7),
|
||
`CarbonToDateFacadeRector` (4), plus collection-idioms.
|
||
**Completion gate:** zie TECH-RECTOR-01.
|
||
|
||
**Approach:**
|
||
- Splits in twee commits (één per set) als het totaal te groot is
|
||
om in één review te laten passen.
|
||
|
||
### TECH-RECTOR-04 — CODE_QUALITY + EARLY_RETURN + PRIVATIZATION
|
||
|
||
**Priority:** Laag
|
||
**Scope:** resterende quality-sets (~80 changes).
|
||
**Estimate:** `ConvertStaticToSelfRector` (34),
|
||
`ReadOnlyClassRector` (27), `ReturnBinaryOrToEarlyReturnRector` (16),
|
||
`ClosureToArrowFunctionRector` (9), etc.
|
||
**Completion gate:** zie TECH-RECTOR-01.
|
||
|
||
### TECH-RECTOR-05 — Laravel modernisation sprint
|
||
|
||
**Priority:** Laag
|
||
**Scope:** review en selectief enable van
|
||
`LaravelLevelSetList::UP_TO_LARAVEL_*` in rector.php.
|
||
**Estimate:** onbekend tot per-set dry-runs zijn bekeken.
|
||
**Completion gate:** per-set applicatie als gescoopte commits.
|
||
|
||
### TECH-RECTOR-CI — CI integration
|
||
|
||
**Priority:** Laag
|
||
**Scope:** `composer rector` (dry-run) als PR-comment-surface. Apply
|
||
blijft handmatig.
|
||
|
||
---
|
||
|
||
## Frontend type-safety
|
||
|
||
### TECH-TS-IMPERSONATION — runtime validation of impersonation state
|
||
|
||
**Aanleiding:** ts-reset install (april 2026) surfaced dat
|
||
`apps/app/src/stores/useImpersonationStore.ts` blindly vertrouwt
|
||
op `JSON.parse(sessionStorage.getItem(...))`. De return is nu
|
||
`unknown` in plaats van `any`; we fixten dit door twee `as
|
||
ImpersonationState` casts toe te voegen. Die maken de bestaande
|
||
trust expliciet maar valideren de shape niet.
|
||
**Wat:** vervang beide casts (lines 19 + 123) door een
|
||
narrowing-helper `parseStoredState(raw: string | null):
|
||
ImpersonationState | null` die het JSON parseert, de verwachte
|
||
keys controleert (incl. geneste `impersonatedUser: AdminUser`
|
||
fields), en `null` teruggeeft als de shape niet klopt. Zelfde
|
||
helper gebruiken bij beide call sites. sessionStorage is in theorie
|
||
tamperbaar door lokale users, dus dit is ook een kleine security
|
||
verbetering.
|
||
**Prioriteit:** Laag — defensive hardening, geen user-impact.
|
||
|
||
### ~~TECH-TS-PORTAL-TSC — `tsc --noEmit` baseline reduction (apps/portal)~~
|
||
|
||
**Status: closed 2026-04-25** — `pnpm exec vue-tsc --noEmit` exits
|
||
clean in `apps/portal/`. Sprint commits: `f7bb864` (tiptap 2.27.2
|
||
upgrade) + `a7ccd2b` (4 long-tail fixes).
|
||
|
||
**Correction to the original framing:** the entry initially read
|
||
"22 pre-existing tsc errors in own code + 4 in tiptap node_modules".
|
||
The "4" was the **delta** that ts-reset added in foundation-tooling
|
||
commit 3 (ts-reset's `JSON.parse → unknown` tightening surfaced 4
|
||
new tiptap errors), **not** the absolute pre-existing tiptap baseline.
|
||
The actual absolute count was **~707 tiptap node_modules errors**,
|
||
silently invisible because the project's CI runs build + vitest only,
|
||
not `vue-tsc`.
|
||
|
||
**Root cause of the 707:** tiptap 2.27.1's `dist/index.d.ts`
|
||
re-exported from `'../src/CommandManager.js'` and ~22 similar lines
|
||
that referenced `.js` files which did not exist (only `.ts` source).
|
||
With `moduleResolution: "Bundler"`, vue-tsc fell through and pulled
|
||
tiptap's entire uncompiled source tree into the program. Tiptap
|
||
2.27.2 (a patch release) fixed the dist exports to use sibling-
|
||
relative paths (`./CommandManager.js`) that resolve correctly to
|
||
the existing `dist/*.d.ts` siblings.
|
||
|
||
**Why `skipLibCheck` did not help:** `skipLibCheck: true` was
|
||
already set in `apps/portal/tsconfig.json` (and `apps/app/`)
|
||
**but only suppresses checking of `.d.ts` declaration files**.
|
||
Tiptap's uncompiled `.ts` source files in `node_modules/.../src/`
|
||
were raw `.ts`, not `.d.ts`, so they bypassed `skipLibCheck` once
|
||
the import graph reached them. The 2.27.2 packaging fix made the
|
||
import graph stop there, no exclusion needed.
|
||
|
||
**Final error tally:**
|
||
- Pre: 729 vue-tsc errors (~22 own-code, of which 18 were TS2339
|
||
downstream of tiptap's broken types; ~707 in node_modules/@tiptap)
|
||
- Post-tiptap-upgrade: 4 own-code (the tiptap-independent stragglers:
|
||
`vite.config.ts` TS7006, `themeConfig.ts` TS2322 via
|
||
`LayoutConfig.title` `Lowercase<string>` over-constraint,
|
||
`build-icons.ts` TS2307 missing `@iconify/types`,
|
||
`casl.ts` TS2345 vue-router meta cast)
|
||
- Post-long-tail-fixes: 0 own-code, 0 node_modules
|
||
|
||
**Aftermath:**
|
||
- The S3b form-builder organizer UI now lands on a verified
|
||
zero-error baseline.
|
||
- The "new code introduces no new errors" discipline still depends
|
||
on a CI gate — see TECH-PORTAL-TSC-CI-GATE follow-up.
|
||
|
||
### TECH-PORTAL-TSC-CI-GATE — vue-tsc as a blocking gate for apps/portal
|
||
|
||
**Aanleiding:** TECH-TS-PORTAL-TSC reached zero in commits `f7bb864`
|
||
+ `a7ccd2b`, but the project has no pre-commit infrastructure
|
||
(no husky, lefthook, or simple-git-hooks) and CI does not run
|
||
`vue-tsc` either. Without a blocking gate, the baseline can drift
|
||
back to non-zero between commits — exactly the discipline gap
|
||
that produced the 22 pre-existing errors in the first place.
|
||
**Wat:**
|
||
- Add `pnpm exec vue-tsc --noEmit` as a CI step in the workflow that
|
||
runs portal build/vitest (most natural location).
|
||
- OR introduce a pre-commit infrastructure (lefthook is lighter than
|
||
husky for monorepos) and run vue-tsc on portal-touching commits.
|
||
- Either path: the gate must fail the run on any new vue-tsc error
|
||
in `apps/portal/`.
|
||
**Out of scope:** extending the same gate to `apps/app/` — that
|
||
SPA still has a different mix of errors and would be a separate
|
||
sprint (out of scope today, would land alongside TECH-APP-VITEST or
|
||
later).
|
||
**Prioriteit:** Middel — without the gate, TECH-TS-PORTAL-TSC's
|
||
zero state has no enforcement. Should land before S3b organizer UI
|
||
work to keep that sprint's "new code introduces no new errors"
|
||
discipline mechanically enforceable.
|
||
|
||
### TECH-APP-VITEST — apps/app Vitest setup
|
||
|
||
**Priority:** medium → **high before S3b organizer UI lands.**
|
||
**Scope:** install Vitest + config + sample test in `apps/app/`.
|
||
Mirror `apps/portal/` setup. Add `test` script to package.json.
|
||
|
||
**Trigger to upgrade priority to "must-fix-now":** any S3b
|
||
form-builder organizer UI commit. apps/portal has 113 Vitest tests
|
||
as of foundation-tooling commit 0; apps/app has zero. Launching
|
||
new organizer UI uncovered while the portal SPA is well-tested is
|
||
asymmetric quality, exactly the discipline gap that bites during
|
||
post-launch debugging.
|
||
|
||
**Setup outline (1-2 hours, isolated commit):**
|
||
1. `cd apps/app && pnpm add -D vitest @vue/test-utils @testing-library/vue happy-dom`
|
||
2. Mirror `apps/portal/vitest.config.ts` adapted for apps/app paths
|
||
3. Mirror `apps/portal/tests/setup.ts` if relevant
|
||
4. Add `"test": "vitest"` and `"test:run": "vitest run"` to
|
||
`apps/app/package.json` scripts
|
||
5. Write one sample test against an existing apps/app component
|
||
(any simple Vue component with a clear input → output mapping —
|
||
confirm the harness works end-to-end). `useImpersonationStore.ts`
|
||
is a natural early target (no runtime test today; the
|
||
ts-reset-surfaced TECH-TS-IMPERSONATION runtime-validation work
|
||
would benefit from that coverage)
|
||
6. Foundation-tooling-style return deliverable: confirm
|
||
`pnpm test --run` exits 0 with at least one test passing
|
||
|
||
**Out of scope:** writing comprehensive tests for existing apps/app
|
||
code. This sprint sets up the harness; per-feature test coverage
|
||
follows organically as features land or are touched.
|
||
|
||
---
|
||
|
||
### WS-6 Deferred
|
||
|
||
#### ARTIST-ADV-SECTION-APPLY
|
||
|
||
Section-level binding apply runtime activation.
|
||
**Removal trigger:** when artist_advance feature work begins (post-S5).
|
||
**Action:** set `FORM_BUILDER_SECTION_APPLY=true`, write section-scoped tests,
|
||
activate `ApplyBindingsOnFormSectionSubmitted` listener registration, remove
|
||
the feature-flag early-return guard from the listener.
|
||
**Refs:** RFC-WS-6.md §3 Q10.
|
||
|
||
#### FORM-BINDING-COMPOSITE-IDENTITY
|
||
|
||
Multi-attribute identity-key resolution (e.g. `email OR (first_name+last_name+DOB)`).
|
||
**Trigger:** when a purpose requires composite identity matching that single
|
||
binding cannot satisfy. Currently `MaxOneIdentityKeyPerTargetEntity` guard
|
||
enforces single-key only.
|
||
**Refs:** RFC-WS-6.md §3 Q8, §6.
|
||
|
||
#### FORM-LIBRARY-RESYNC
|
||
|
||
Admin action to propagate `FormFieldLibrary` binding updates to existing
|
||
field instances. Currently library-bindings are copy-on-instantiation
|
||
only (see `ARCH-BINDINGS.md §1`).
|
||
**Trigger:** when organisations report friction updating shared field
|
||
templates.
|
||
**Refs:** RFC-WS-6.md §3 Q11.
|
||
|
||
#### FORM-FAILURE-DAILY-DIGEST
|
||
|
||
Daily digest mailable for orgs with open `FormSubmissionActionFailure` rows
|
||
above a threshold.
|
||
**Deferred until** notification framework lands (post-accreditation engine).
|
||
Requires NotificationBell + NotificationCenter infrastructure.
|
||
**Refs:** RFC-WS-6.md §3 Q5.
|
||
|
||
#### LOAD-TEST-FOUNDATION
|
||
|
||
Wall-clock concurrent load testing infrastructure (k6 or equivalent) against
|
||
staging-API. Separate workstream from WS-6.
|
||
**Trigger:** pre-release hardening phase.
|
||
**Refs:** RFC-WS-6.md §4 V4.
|
||
|
||
#### ARTIST-ADV-BINDING-MODEL
|
||
|
||
WS-6 v1 omits artist binding-target registry entries entirely. The
|
||
artist_advance purpose accepts schemas without bindings (Artist subject
|
||
resolved via portal_token, not via field-to-attribute mapping). For v2,
|
||
decide:
|
||
|
||
- Should there be an `Artist` Eloquent model class? Currently the
|
||
`artists` table exists but no class — only the morph map alias points
|
||
at `App\Models\Artist` (a string).
|
||
- Which artist attributes are bindable from form data? The advance form
|
||
is OUTPUT-shaped: it gathers info FROM the artist (rider, hospitality,
|
||
technical needs); it does not provision Artist attributes the way
|
||
event_registration provisions Person attributes.
|
||
- Is the binding model even the right abstraction for advance forms,
|
||
or do they use a different sync mechanism (e.g. typed AdvanceSection
|
||
fields)?
|
||
|
||
**Trigger:** when v2 design discussions for artist_advance feature
|
||
work begin. May result in registry entries, an Artist model, OR a
|
||
domain-specific alternative to bindings.
|
||
**Refs:** RFC-WS-6.md §3 Q9 v1.2 addendum, ARCH-BINDINGS.md appendix.
|
||
|
||
#### FORM-BINDING-JSON-PATH
|
||
|
||
WS-6 v1's binding-target registry handles only top-level model columns.
|
||
JSON-path attributes (e.g. `persons.custom_fields.dietary_preferences`)
|
||
are not bindable in v1. Adding support requires:
|
||
|
||
- Registry shape extension (path-string syntax:
|
||
`'custom_fields.dietary_preferences'`)
|
||
- Applicator's `setAttribute` change → typed json_set / array_set helper
|
||
- Conflict resolution: do JSON-path siblings resolve independently?
|
||
- Type-validation: dietary_preferences is a list, but custom_fields
|
||
itself is JSON — what does identity_key_eligible mean here?
|
||
|
||
For v1 the recommendation: model dietary_preferences (and similar
|
||
`custom_fields` properties) as a `TAG_PICKER` form_field with a
|
||
tag_categories config. The TAG_PICKER → user_organisation_tags sync
|
||
(per ARCH-FORM-BUILDER §31.10) handles this without requiring
|
||
binding-target column mapping.
|
||
|
||
**Trigger:** when an organisation requests dietary_preferences (or
|
||
other `custom_fields` properties) as a form binding target. May
|
||
coincide with Crewli's v2 dietary management feature work.
|
||
**Refs:** RFC-WS-6.md §3 Q9 v1.2 addendum, ARCH-BINDINGS.md appendix.
|
||
|
||
---
|
||
|
||
_Laatste update: April 2026_
|
||
_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-, UX-_
|