Say something when planning a free day changes nothing
Planning a day through the default "Planera en dag" dialog on a weekend or red day accepted the save, returned 200, and did nothing visible: the allocator marks a free day as 'free' before a type is chosen, the shortfall count excludes it, and planning_service strips it before persisting — so the sync came back accepted with an empty batch and the toast builder had nothing to say. The click looked broken. The modal already had the right words for this; they were only wired to the advanced/pinned path. They now show in the default path too, so the user is told before clicking. And an accepted sync that changed nothing now says so instead of staying silent — opt-in at the one call site that needs it, since the preset rail deliberately stays quiet on a genuine no-op. No change to the allocator, to planning_service, or to the policy that free days stay off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -130,16 +130,24 @@ export function PlannedDayModal({
|
||||
const showPinnedControls = forcedAdvanced || soloDirect || isAdvancedOpen
|
||||
|
||||
// A concrete type is about to be pinned (not the 'auto' planner-decides
|
||||
// choice) — the two notices below only make sense once that's true.
|
||||
// choice) — the pin-exclusion notice below only makes sense once that's
|
||||
// true.
|
||||
const isPinningConcreteType = pinnedPath && dayType !== 'auto'
|
||||
// The chosen type would leave every free day in the selection (weekend/red
|
||||
// day) as a day off: always true for `vacation`, and true for a PL type
|
||||
// unless the workspace opted in via `takePlOnRedDays` — mirrors
|
||||
// `Calendar.tsx`'s `handleSavePinnedDay` filter exactly.
|
||||
// Would every free day (weekend/red day) in the selection stay OFF under
|
||||
// the save path this dialog is currently set up to take? Pinning a
|
||||
// concrete type: always for `vacation`, and for a PL type unless the
|
||||
// workspace opted in via `takePlOnRedDays` — mirrors `Calendar.tsx`'s
|
||||
// `handleSavePinnedDay` filter exactly. The default/auto (wish) path:
|
||||
// `allocation_service.py`'s `is_free_day` branch fires BEFORE any type is
|
||||
// chosen and marks the day 'free' unless `takePlOnRedDays`, regardless of
|
||||
// parentMode/dubbeldag — so free days stay off there too, always, unless
|
||||
// that same opt-in is on. Shown regardless of `isPinningConcreteType` so
|
||||
// the default path warns just as loudly as the advanced one (see the
|
||||
// "silent no-op" fix: planning a free day through the default dialog used
|
||||
// to accept with nothing changed and no explanation).
|
||||
const freeDaysWillStayOff =
|
||||
isPinningConcreteType &&
|
||||
freeDatesInSelection.length > 0 &&
|
||||
(dayType === 'vacation' || !takePlOnRedDays)
|
||||
(pinnedPath ? dayType === 'vacation' || !takePlOnRedDays : !takePlOnRedDays)
|
||||
// Pinning excludes the day from `sync_plan`'s automatic re-planning —
|
||||
// worth saying prospectively (about to pin) even when the day isn't
|
||||
// already pinned; `existingDay?.isPinned` below already covers the
|
||||
@@ -348,6 +356,17 @@ export function PlannedDayModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* The default path's own copy of the "will stay off" notice —
|
||||
* `showPinnedControls` is false here (Advanced isn't open), so
|
||||
* the copy inside that section below never renders for this
|
||||
* case. Gated on `!showPinnedControls` rather than re-deriving
|
||||
* "default path" so the two copies can never both render. */}
|
||||
{!forcedAdvanced && hasMultipleParents && !showPinnedControls && freeDaysWillStayOff && (
|
||||
<p className="planned-day-modal__hint" data-testid="free-days-stay-off-notice">
|
||||
{t('plannedDayModal.freeDaysStayOffNotice')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!forcedAdvanced && hasMultipleParents && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user