61e5487078
Free days (Sat/Sun/red days, subject to DayOverride) can never be vacation, and can only be parental leave when the workspace opts in via take_pl_on_red_days. The allocator already honoured this; the hand-pin path bypassed it entirely. - PlanningService gains optional workspace_repo/day_override_repo deps and _reject_illegal_free_day, enforced in add_planned_day and update. Reuses allocation_service.is_free_day rather than re-deriving it, and no-ops when the deps are unset so existing callers are unaffected. - update_planned_day had no ValidationError handler at all, so a validation failure there 500'd instead of 422'ing. Fixed. - Calendar filters the pin write loop by the day's isFree; the modal explains free days stay off, and that pinning opts a day out of automatic re-planning. Vacation days at risk of forfeiture were computed and sent over the wire but never rendered: an at-risk column with the age-out/over-cap split, and a dashboard key figure. PlanProposal.warnings was likewise parsed but rendered nowhere, for all three kinds. Adds PlanWarningCard in a Risks section above the tips, with parent names resolved and dates formatted. POST /api/plan/sync now fires from the workspace layer on every plan-input change rather than only from Calendar, so a Settings edit or a pinned-day save re-plans day types instead of merely refetching. Overlapping bumps coalesce into one trailing run; a rejection cannot wedge the in-flight guard. eviction_preview was silently dropped by the frontend despite always being on the wire, and is now surfaced through context. Verified: backend 890 passed (91.49% coverage), frontend 1297 passed, ruff/eslint/tsc clean. Browser-checked in Swedish and English. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JVQx4NUvPbJhn6UcvRd2QY
173 lines
10 KiB
TypeScript
173 lines
10 KiB
TypeScript
export const calendar = {
|
||
'calendar.selectMonthAria': 'Select month',
|
||
'calendar.selectYearAria': 'Select year',
|
||
'calendar.deleteError': 'Failed to delete the planned day',
|
||
'calendar.nextMonthAria': 'Next month',
|
||
'calendar.partner': 'Partner',
|
||
'calendar.pinnedDayAllFreeError':
|
||
'None of the selected days can be saved as that type — free days (weekends/red days) can never be vacation, and parental leave on them needs the Settings toggle turned on.',
|
||
'calendar.prevMonthAria': 'Previous month',
|
||
'calendar.saveError': 'Failed to save the planned days',
|
||
'calendar.today': 'Today',
|
||
'calendar.weekdayFri': 'Fri',
|
||
'calendar.weekdayMon': 'Mon',
|
||
'calendar.weekdaySat': 'Sat',
|
||
'calendar.weekdaySun': 'Sun',
|
||
'calendar.weekdayThu': 'Thu',
|
||
'calendar.weekdayTue': 'Tue',
|
||
'calendar.weekdayWed': 'Wed',
|
||
'calendar.you': 'You',
|
||
'calendarDay.excludedAria': 'removed day — the planner skips it',
|
||
'calendarDay.klamdagAria': 'bridge day',
|
||
'calendarDay.overrideFreeAria': 'marked as a day off (exception)',
|
||
'calendarDay.overrideWorkingAria': 'marked as a working day (exception)',
|
||
'calendarDay.pinnedAria': 'pinned by you — the planner will not retype this day',
|
||
'calendarDay.sundayAria': 'Sunday',
|
||
'calendarEvent.dismissAria': 'Dismiss {leaveType}',
|
||
'plannedDayModal.cancelButton': 'Cancel',
|
||
'plannedDayModal.closeAria': 'Close',
|
||
'plannedDayModal.confirmDelete': 'Are you sure you want to delete this planned day?',
|
||
'plannedDayModal.deleteButton': 'Delete',
|
||
'plannedDayModal.deleteConfirmButton': 'Yes, delete',
|
||
'plannedDayModal.deleteError': 'Failed to delete planned day',
|
||
'plannedDayModal.deletingButton': 'Deleting…',
|
||
'plannedDayModal.doubleDayHint': 'Double days do not apply to vacation',
|
||
'plannedDayModal.doubleDayLabel': 'Double day (both parents taking leave)',
|
||
'plannedDayModal.editTitle': 'Edit Planned Day',
|
||
'plannedDayModal.lagstaOption': 'Lägstanivå (minimum-rate, 90-day tier)',
|
||
'plannedDayModal.leaveTypeLabel': 'Leave Type',
|
||
'plannedDayModal.newTitle': 'Plan a Day',
|
||
'plannedDayModal.parentLabel': 'Parent',
|
||
'plannedDayModal.pinnedNotice': 'This day is pinned by you — the planner will not change it automatically.',
|
||
'plannedDayModal.pinExcludesFromSyncNotice':
|
||
'Pinning excludes this day from the planner’s automatic re-planning.',
|
||
'plannedDayModal.freeDaysStayOffNotice':
|
||
'Free days (weekends/red days) in your selection are left as days off — vacation can never go there, and parental leave needs "Take parental leave on red days" turned on in Settings.',
|
||
'plannedDayModal.excludedNotice': 'You removed this day for {parent}.',
|
||
'plannedDayModal.restoreExcludedButton': 'Put the day back',
|
||
'plannedDayModal.rangeTitle': 'Plan {count} days',
|
||
'plannedDayModal.parentModeLabel': 'Who is this for?',
|
||
'plannedDayModal.wantsDubbeldagLabel': 'Allow double days (dubbeldagar)',
|
||
'plannedDayModal.wantsDubbeldagHint':
|
||
"Both parents are off the same day and each spend one of their own parental-benefit days (no vacation day is used) — only available in the child's first 15 months, capped at 60 dates per child, shared between you.",
|
||
'plannedDayModal.wantsDubbeldagRequiresBothHint': 'Requires both parents to be selected above.',
|
||
'plannedDayModal.showAdvanced': 'Advanced: pick type and parent manually',
|
||
'plannedDayModal.hideAdvanced': 'Hide advanced',
|
||
'plannedDayModal.saveButton': 'Save',
|
||
'plannedDayModal.saveError': 'Failed to save planned day',
|
||
'plannedDayModal.savingButton': 'Saving…',
|
||
'plannedDayModal.sjukpenningOption': 'Sjukpenningnivå (income-based, 390-day tier)',
|
||
'plannedDayModal.vacationOption': 'Vacation',
|
||
'plannedDayModal.autoOption': 'Let the planner choose',
|
||
'redDaysList.effectiveFree': 'Off',
|
||
'redDaysList.effectiveWorking': 'Working day',
|
||
'redDaysList.emptyYear': 'No public holidays or exceptions this year.',
|
||
'redDaysList.loading': 'Loading public holidays...',
|
||
'redDaysList.nextWindow': 'Next 4 years',
|
||
'redDaysList.overrideFree': 'Exception: day off',
|
||
'redDaysList.overrideWorking': 'Exception: working day',
|
||
'redDaysList.prevWindow': 'Previous 4 years',
|
||
'redDaysList.toggleError': 'Failed to update this day. Try again.',
|
||
'redDaysList.toggleToFreeAria': 'Mark {date} as a day off',
|
||
'redDaysList.toggleToWorkingAria': 'Mark {date} as a working day',
|
||
|
||
// §Task7 — the calendar becomes the plan: drag-select, period popover,
|
||
// preset rail, and the sync toast/undo.
|
||
'calendar.periodCreateError': 'Failed to create the period',
|
||
'calendar.syncError': 'Failed to sync the plan',
|
||
'calendar.syncingLabel': 'Syncing…',
|
||
'calendar.toastChangedDays': '{name}: {count} {count|day|days} changed',
|
||
'calendar.toastGenericPeriod': 'The period',
|
||
'calendar.toastShortfallNone':
|
||
'{name} ({start}–{end}): none of the {weeksWished} {weeksWished|week|weeks} requested could be funded — {shortfallDays} {shortfallDays|day|days} short',
|
||
'calendar.toastShortfallPartial':
|
||
'{name} ({start}–{end}): only {weeksFunded} of {weeksWished} weeks funded — {shortfallDays} {shortfallDays|day|days} short',
|
||
'calendar.toastShortfallNoneDays':
|
||
'{name} ({start}–{end}): none of the {daysWished} {daysWished|day|days} requested could be funded — {shortfallDays} {shortfallDays|day|days} short',
|
||
'calendar.toastShortfallPartialDays':
|
||
'{name} ({start}–{end}): only {daysFunded} of {daysWished} days funded — {shortfallDays} {shortfallDays|day|days} short',
|
||
'calendar.toastRedDayShift':
|
||
'{name}: moved to {start} – {end} to catch {days} extra free {days|day|days}',
|
||
'calendar.undoButton': 'Undo',
|
||
'calendar.undoError': 'Failed to undo the change',
|
||
'calendar.dismissToastAria': 'Dismiss the message',
|
||
|
||
// The "?" beside the month title — replaces the legend that used to sit
|
||
// under the grid. One line per mark the calendar draws.
|
||
'calendar.helpAria': 'What the calendar marks mean',
|
||
'calendar.helpTitle': 'Days',
|
||
'calendar.helpBandsTitle': 'Periods',
|
||
'calendar.helpRedDay': 'Public holiday (R) — a red day or a Sunday',
|
||
'calendar.helpKlamdag': 'Bridge day (K)',
|
||
'calendar.helpSaturday': 'Saturday',
|
||
'calendar.helpToday': 'Today',
|
||
'calendar.helpOverride': 'Exception — marked as a working day or a day off',
|
||
'calendar.helpExcluded': 'Removed day — the planner skips it',
|
||
'calendar.helpVacation': 'Vacation',
|
||
'calendar.helpSjukpenning': 'Income-based rate (sjukpenningnivå)',
|
||
'calendar.helpLagsta': 'Minimum rate (lägstanivå)',
|
||
'calendar.helpSelf': 'Your leave',
|
||
'calendar.helpPartner': "Your partner's leave",
|
||
'calendar.helpDubbeldag': 'Double day — both parents on the same day',
|
||
'calendar.helpPinned': 'Pinned by you — the planner leaves the day alone',
|
||
|
||
'calendar.railHeading': 'Add leave',
|
||
'calendar.railHint': 'Click to add — the calendar jumps to wherever it fits best.',
|
||
'calendar.presetAddAria': 'Add {name}',
|
||
'calendar.presetChipsLabel': 'Add leave',
|
||
'calendar.presetWeeksShort': '{count} wk',
|
||
|
||
'calendar.periodAdHocTitle': 'Period',
|
||
'calendar.periodAdHocNotice': 'This period is not linked to a saved wish.',
|
||
'calendar.periodNameLabel': 'Name',
|
||
'calendar.periodNameAria': "The period's name",
|
||
'calendar.periodStartLabel': 'Start date',
|
||
'calendar.periodEndLabel': 'End date',
|
||
'calendar.periodRepeatLabel': 'Repeat',
|
||
'calendar.periodRepeatNever': 'Never',
|
||
'calendar.periodRepeatYearly': 'Yearly',
|
||
'calendar.periodLastYearLabel': 'Last year',
|
||
|
||
// One year vs the whole series. A repeating period is stored as one row per
|
||
// year, so "only this year" can finally be honoured — Easter moves, and a
|
||
// single year has to be able to move without dragging the rest with it.
|
||
// See backend/app/entities/leave_wish.py.
|
||
'calendar.periodEditScopeLabel': 'Apply the change to',
|
||
'calendar.periodEditScopeYear': 'Only this year',
|
||
'calendar.periodEditScopeSeries': 'Every year',
|
||
'calendar.periodEditScopeHint': 'Every year shifts the other years to match, keeping the weekdays.',
|
||
'calendar.periodLastYearNeedsSeriesHint': 'Choose "Every year" to change the last year.',
|
||
'calendar.periodSaveButton': 'Save period',
|
||
'calendar.periodSavingButton': 'Saving…',
|
||
'calendar.periodDeletingButton': 'Deleting…',
|
||
'calendar.periodDeleteConfirmButton': 'Yes, delete',
|
||
'calendar.periodDeleteAdHocButton': 'Delete these days',
|
||
'calendar.periodCancelButton': 'Cancel',
|
||
'calendar.periodCloseAria': 'Close',
|
||
'calendar.periodDeleteMenuButton': 'Remove…',
|
||
|
||
// todo.md #9 — three-way remove: this day / this period (this year) / this
|
||
// period every year.
|
||
'calendar.periodDeleteDayButton': 'This day',
|
||
'calendar.periodDeleteYearButton': 'This period',
|
||
'calendar.periodDeleteAllYearsButton': 'This period, every year',
|
||
// "This day" now works on planner-placed days too: the removal is stored
|
||
// as a `PlannedDayExclusion` and survives every subsequent sync.
|
||
'calendar.periodDeleteDayHint':
|
||
'Only that day is removed. The rest of the period stays, and the planner will not add the day back.',
|
||
'calendar.periodDeleteAllYearsDisabledHint': 'This period is not linked to a recurring plan.',
|
||
'calendar.periodDeleteConfirmDay': 'Are you sure you want to remove this day?',
|
||
// Names both the date and the parent: a day is removed for ONE parent, and
|
||
// the partner's day on the same date stays (see PlannedDayExclusionService).
|
||
'calendar.periodDeleteConfirmDayNamed':
|
||
'Remove {date} for {parent}? The day stays removed until you restore it.',
|
||
'calendar.toastDayExcluded': '{date} removed for {parent}',
|
||
'calendar.toastDayRestored': '{date} restored for {parent}',
|
||
'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)',
|
||
'calendar.wantsDubbeldagHint':
|
||
"Both parents are off the same day and each spend one of their own parental-benefit days (no vacation day is used) — only available in the child's first 15 months, capped at 60 dates per child, shared between you.",
|
||
'calendar.wantsDubbeldagRequiresBothHint': 'Requires the period to apply to both parents.',
|
||
} as const
|