Let a planned day say why it was placed

Every funded day the allocator places already carries a machine-readable
`reason` code — the constants block in allocation_service.py even says it
exists "so the UI can explain itself" — but no user has ever seen one.
The calendar showed what was decided, never why, which is the one question
this product exists to answer.

Clicking a planned day now also raises the existing calendar toast with a
plain-Swedish (and English) sentence for that date and parent: a pinned day
says the user chose it, an allocator-placed day explains the placement, and
anything unrecognised — or a session where no proposal has landed yet —
shows nothing rather than a raw code.

The reason is deliberately not persisted: it is stripped at apply and
`planned_leave_day` has no column for it, so the source is the live
in-memory proposal, merged last-writer-wins from Calendar's own sync
response and PlanSyncProvider's latest. Before the first sync of a session
there is simply no explanation to show.

Also extends the backend's field-parity walk to ProposedDay, which it did
not cover — the reason could have been dropped from the wire without a
single test failing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 07:41:44 +02:00
parent fc90cf9a5f
commit 1a69221040
9 changed files with 384 additions and 1 deletions
+4
View File
@@ -163,6 +163,10 @@ export const calendar = {
'Remove {date} for {parent}? The day stays removed until you restore it.',
'calendar.toastDayExcluded': '{date} removed for {parent}',
'calendar.toastDayRestored': '{date} restored for {parent}',
// Clicking a planned day's band (`handleBandClick`) — explains why that
// day got the type it got, read off the freshest plan proposal held in
// memory (see `frontend/src/utils/planReasons.ts`).
'calendar.toastDayReason': '{date}, {parent}: {reason}',
'calendar.periodDeleteConfirmYear': 'Are you sure you want to remove this period (this year only)?',
'calendar.periodDeleteConfirmAllYears': 'Are you sure you want to remove the period — every year?',
'calendar.wantsDubbeldagLabel': 'Allow double days (dubbeldagar)',