Files
F-r-ldraledigplannerare/frontend/src/i18n/en/calendar.ts
T
jacobh2 61e5487078 Free days, vacation forfeiture, and global auto-replanning
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
2026-09-20 13:07:36 +02:00

173 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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