Brings apps/portal from 729 vue-tsc errors (≈22 own-code, of which 18 were TS2339 downstream of tiptap; ≈707 in node_modules/@tiptap/) to zero. Tiptap fix-route: Option A — patch upgrade @tiptap/* from 2.27.1 to 2.27.2 to fix tiptap's dist/index.d.ts re-export paths. Sprint commits (this work package, 3 total): -f7bb864fix(portal-deps): upgrade @tiptap/* 2.27.1 → 2.27.2 to fix dist resolution (cleared 707 + 18 errors) -a7ccd2bfix(portal-types): clear residual long-tail tsc errors (cleared the 4 tiptap-independent stragglers: vite.config.ts componentName param, LayoutConfig.title Lowercase<string> over-constraint, @iconify/types missing dev-dep, casl.ts meta string cast) - this commit: close BACKLOG entry; correct the misleading "+4 in tiptap" framing in the original entry (was the ts-reset delta, not the absolute pre-existing count of ~707); seed TECH-PORTAL-TSC-CI-GATE follow-up. apps/portal `pnpm exec vue-tsc --noEmit` exits clean. Vitest: 113/113 passing. Build: 8.52s, succeeded. Pre-commit hook gate not added — the project has no husky/lefthook/ simple-git-hooks setup. Captured as TECH-PORTAL-TSC-CI-GATE follow- up; without that gate the zero state has no enforcement and can drift back. Should land before S3b organizer UI work to keep the "new code introduces no new errors" discipline mechanically enforceable. S3b form-builder organizer UI can now land on top of a verified TypeScript baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1098 lines
46 KiB
Markdown
1098 lines
46 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
|
|
|
|
**Aanleiding:** Post-WS-5d, four near-duplicate scope classes implement the same UNION-over-two-owner-chains shape: `FormFieldBindingScope` (WS-5a), `FormFieldValidationRuleScope` (WS-5b), `FormFieldConfigScope` (WS-5b), `FormFieldOptionScope` (WS-5d). Each is ~40 lines of boilerplate plus the morph-alias names; the `apply()` body and the `resolveOrganisationId()` helper are byte-identical across all four. Base-class extraction was deferred during each WS-5 commit on the principle of "abstraction from N copies is premature when N+1 lands soon"; with the fourth concrete implementation now in place, the "what actually varies" question can be answered empirically.
|
|
**Wat:** Extract `App\Models\Scopes\FormFieldChildTableScope` abstract base. Subclasses declare only the two morph alias strings (`form_field`, `form_field_library`) — actually identical across all four siblings, so even the subclasses become trivial. Keep the four concrete classes as named entry points (the `withoutGlobalScope(FormFieldOptionScope::class)` escape-hatch contract is API surface). Single low-risk refactor PR; full WS-5 test suite must stay green byte-for-byte.
|
|
**Prioriteit:** Middel — quality-of-life refactor. No functional change, no schema change, no contract change. Land before any further child-table morph-pattern is introduced.
|
|
**Related:** Addendum §Q3 WS-5d Uitvoering paragraph "Vier scope-siblings → base-class extractie nu warranted".
|
|
|
|
---
|
|
|
|
### 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-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.
|
|
|
|
---
|
|
|
|
_Laatste update: April 2026_
|
|
_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-, UX-_
|