Lead the overview with the days about to be lost
The page opened with generic tiles and a bar reading forty-eight of ninety-six weeks, which was neither weeks nor connected to the age-four ceiling it appeared to describe. It is replaced by one headline and a bar per parent, in days, against each parent's own savable limit as the server reports it. The headline has two framings on the same arithmetic. Before anything is planned it says what there is to plan and what will be lost without a plan, because on the first day almost the whole entitlement is technically at risk and saying so bluntly reads as an accusation. Once a plan exists it warns about the days genuinely at stake. A timeline of the years to the fourth birthday answers what the next four years look like, which a month-at-a-time calendar cannot. Shortfalls, surplus advice and balances sit below the fold, read-only: applying is automatic now, so a second apply button would be a way to get two different answers. Rejection messages move to one shared place that resolves a parent's name, so an identifier can no longer surface in their place.
This commit is contained in:
@@ -79,6 +79,10 @@ export interface LeaveWishCreateRequest {
|
||||
windowEndMonth: number
|
||||
windowEndDay: number
|
||||
targetWeeks: number
|
||||
// A calendar-drawn period (dragging a range on the calendar) is not a
|
||||
// whole number of weeks — omitted/`undefined` falls back to
|
||||
// `targetWeeks * 7` server-side. See `LeaveWish.targetDays`.
|
||||
targetDays?: number | null
|
||||
recurrence: 'yearly' | 'once'
|
||||
firstYear: number
|
||||
lastYear?: number | null
|
||||
@@ -152,6 +156,14 @@ export interface PlanApplyResponse {
|
||||
createdDays: object[]
|
||||
}
|
||||
|
||||
// POST /api/plan/sync — the direct-manipulation calendar's one call:
|
||||
// recompute + persist in one step, reporting exactly what changed.
|
||||
export interface PlanSyncResponse {
|
||||
proposal: import('./entities').PlanProposal
|
||||
applyResult: PlanApplyResponse
|
||||
changes: import('./entities').PlanChange[]
|
||||
}
|
||||
|
||||
// Day overrides (calendar exceptions)
|
||||
export interface DayOverrideCreateRequest {
|
||||
workspaceId: string
|
||||
|
||||
@@ -43,6 +43,15 @@ export interface LeaveWish {
|
||||
windowEndMonth: number
|
||||
windowEndDay: number
|
||||
targetWeeks: number
|
||||
// A calendar-drawn period (dragging a band on the calendar UI) is not a
|
||||
// whole number of weeks. `null`/absent (every wish specified in whole
|
||||
// weeks, e.g. via a preset) falls back to `targetWeeks * 7` on the
|
||||
// backend — see `LeaveWish.target_span_days()`
|
||||
// (backend/app/entities/leave_wish.py). Optional (not just nullable) so
|
||||
// an existing hand-rolled `LeaveWish` object literal elsewhere in the
|
||||
// codebase (predating this field) still type-checks without every call
|
||||
// site needing an update.
|
||||
targetDays?: number | null
|
||||
recurrence: 'yearly' | 'once'
|
||||
firstYear: number
|
||||
lastYear: number | null
|
||||
@@ -57,6 +66,33 @@ export interface LeaveWish {
|
||||
updatedAt: string | null
|
||||
}
|
||||
|
||||
/** One (child, parent, date) slot's delta from a `POST /api/plan/sync` call
|
||||
* — see `PlanChange` (backend/app/services/planning_service.py) for the
|
||||
* `kind` vocabulary:
|
||||
*
|
||||
* - 'added' — no row existed for (child, parent, date); one does now.
|
||||
* - 'removed' — a row existed and no longer does (old_* populated, new_*
|
||||
* all `null`).
|
||||
* - 'retyped' — the row survived but `dayType`/`isDubbeldag` changed.
|
||||
* - 'reattributed' — same day and type, but `wishId` changed (the day now
|
||||
* belongs to a different period).
|
||||
*
|
||||
* Structured data only, house style — the frontend composes the toast copy
|
||||
* ("Sommar 2028 flyttades 2 dagar") itself rather than rendering backend prose.
|
||||
*/
|
||||
export interface PlanChange {
|
||||
kind: 'added' | 'removed' | 'retyped' | 'reattributed'
|
||||
date: string // ISO-8601 date
|
||||
parentPersonalDataUuid: string
|
||||
childId: string
|
||||
oldDayType: 'sjukpenning' | 'lagsta' | 'vacation' | null
|
||||
newDayType: 'sjukpenning' | 'lagsta' | 'vacation' | null
|
||||
oldIsDubbeldag: boolean | null
|
||||
newIsDubbeldag: boolean | null
|
||||
oldWishId: string | null
|
||||
newWishId: string | null
|
||||
}
|
||||
|
||||
export interface TierData {
|
||||
sjukpenningTotal: number
|
||||
sjukpenningUsed: number
|
||||
@@ -159,6 +195,11 @@ export interface Age4Projection {
|
||||
projectedLeftAtB4: number
|
||||
savableAfterB4: number
|
||||
expiringAtB4: number
|
||||
// The cap actually applied to `savableAfterB4`/`expiringAtB4` — 96 (per
|
||||
// child) normally, or 48 (per parent) for a `couple` workspace with two
|
||||
// parents. See `resolve_age4_savable_cap` (backend). Render the per-parent
|
||||
// bar against this field directly; never re-derive the 48-vs-96 rule here.
|
||||
savableCapAtB4: number
|
||||
}
|
||||
|
||||
export interface WorkspaceOverview {
|
||||
|
||||
Reference in New Issue
Block a user