Files
bert.hausmans 1eee1f9415 fix(timetable): align Zod decimal fields with backend wire format (decimal-as-string per Laravel cast) (B5)
Phase A diagnosed the "Kon timetable niet laden" browser symptom as Zod
schema drift. The prompt's hypothesis (enum {value, label} mismatch) was
incorrect — the schema already uses the enumLabel() wrapper for every
enum field. The actual drift is decimal-cast columns: Laravel serialises
`decimal(N,M)` columns as strings to preserve precision, but the schema
expected numbers, so the very first response triggered a ZodError.

Affected fields, all on `artist_engagements`:
  fee_amount         decimal(10,2)  → wire `"11503.58"`, schema was z.number()
  buma_percentage    decimal(5,2)   → wire `"7.00"`,     schema was z.number()
  vat_percentage     decimal(5,2)   → wire `"21.00"`,    schema was z.number()
  deposit_percentage decimal(5,2)   → wire `"…"`,        schema was z.number()

Backend has no explicit `decimal:N` cast on these columns
(api/app/Models/ArtistEngagement.php:64-85 — the `casts()` method covers
the enums + booleans + dates + integers, but skips decimals).

Per the strategic decision (frontend adapts, backend stays):
  - schemas/timetable.ts: four fields → z.string().nullable()
  - types/timetable.ts: matching ArtistEngagement interface fields →
    `string | null`
  - PerformancePopover.vue:129: only consumer doing arithmetic on a
    decimal field; coerce at the use site via Number(...).toFixed(2).
    Single line.
  - tests/component/PerformanceBlock.test.ts + tests/a11y/axe.test.ts:
    spot-checked mocks; the two with hand-built engagement payloads
    flipped fee_amount/buma_percentage/vat_percentage from numbers to
    strings to match the new schema. No other mocks needed updating.

The {value, label} enum wrapper claim in the prompt was specifically
debunked in Phase A — every consumer (Wachtrij, PerformanceBlock,
WachtrijCard, PerformancePopover, AddPerformanceDialog, page entry)
already uses .value/.label access against an enumLabel-wrapped schema.

B6 will lock the wire-format contract with a real-API fixture
regression test.

All 397 tests still pass; typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 00:33:13 +02:00
..