/** * Maps an allocator `reason` code (`ProposedDay.reason` — the * `REASON_*` constants in `backend/app/services/allocation_service.py:62-83`, * §3.5, "machine-readable so the UI can explain itself") to the * `planReasons.` translation key that explains it in the calendar * toast (see `Calendar.tsx`'s `handleBandClick`). * * Follows `frontend/src/utils/rejectionMessages.ts`'s precedent: an * exhaustive `Record` from the backend-defined code to a `TranslationKey`, * so an unrecognised or empty reason returns `null` rather than ever * putting a raw code on screen. Every one of the allocator's 10 reason * codes has an entry here even though only the non-free ones are reachable * in practice (a free day renders no band, so it can never be clicked) — * kept exhaustive so the map can't quietly fall behind the backend's list. */ import type { TranslationKey } from '@/i18n' const REASON_KEYS: Record = { free_weekend: 'planReasons.free_weekend', free_red_day: 'planReasons.free_red_day', vacation_wrap_red_day: 'planReasons.vacation_wrap_red_day', vacation_wrap_weekend: 'planReasons.vacation_wrap_weekend', fp_interior: 'planReasons.fp_interior', fp_lagsta_stretch: 'planReasons.fp_lagsta_stretch', swap_first_half: 'planReasons.swap_first_half', swap_second_half: 'planReasons.swap_second_half', dubbeldag: 'planReasons.dubbeldag', fp_red_day: 'planReasons.fp_red_day', } /** Empty or unrecognised `reason` -> `null`; callers (`Calendar.tsx`) render * nothing rather than a raw code or a generic "unknown" string. */ export function reasonMessageKey(reason: string): TranslationKey | null { if (!reason) return null return REASON_KEYS[reason] ?? null }