Let a day removed from a period stay removed

Taking one day out of a period the planner placed did nothing that
lasted: the period still asked for a full week, so the next sync put the
day straight back. The option was disabled rather than lying about it.

A removed day is now recorded as its own thing — no leave for this
parent, this child, this date — and the planner honours it on every
later sync. It is kept apart from the working-day overrides, which
answer a different question about the calendar rather than about the
plan.

The day belongs to the parent whose band was clicked, so removing one
parent's day leaves their partner's alone; where both parents were to be
off on one funding arrangement, the partner is simply funded on their
own instead. A removed day shortens what the period is asking for rather
than counting as a day the plan failed to find, so the advice about
closing a shortfall does not start arguing for a day nobody wants.

Nothing is removed without a way back: the toast undoes it, and the day
itself is marked in the calendar and offers to restore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ei7gCZzdd751uZtPzb1Pep
This commit is contained in:
2026-09-19 16:05:17 +02:00
parent 6d92b87bf3
commit 8cfb9c82c6
39 changed files with 2362 additions and 31 deletions
+57 -5
View File
@@ -176,13 +176,14 @@ test('a drag saved without opening Advanced creates a planner-funded wish, and t
await expect(wishBand.first()).toBeVisible()
// Open the wish-linked band. Its own three-way remove: "this day" cannot
// be expressed for a wish (it would just reappear on the next sync) and
// is disabled; "this period" on a standalone (non-repeating) wish — one
// row, no series — removes the whole thing, same as "every year".
// Open the wish-linked band. Its own three-way remove: "this day" now
// works on a planner-placed day too (the removal is recorded as a
// `PlannedDayExclusion` — see the next test); "this period" on a
// standalone (non-repeating) wish — one row, no series — removes the whole
// thing, same as "every year".
await wishBand.first().click()
await expect(page.getByTestId('period-popover__content')).toBeVisible()
await expect(page.getByTestId('period-delete-day-button')).toBeDisabled()
await expect(page.getByTestId('period-delete-day-button')).toBeEnabled()
await page.getByTestId('period-delete-year-button').click()
await expect(page.getByTestId('period-delete-confirm')).toBeVisible()
@@ -193,3 +194,54 @@ test('a drag saved without opening Advanced creates a planner-funded wish, and t
await goToCalendarMonth(page, month)
await expect(page.locator('[data-testid^="calendar-band-wish:"]')).toHaveCount(0)
})
test('removing a single day out of a planner-placed period keeps it removed across syncs', async ({
page,
}) => {
// The user's requirement: "removing a single day should stay permanent.
// Even if it's an auto added period day." The wish behind the band still
// asks for the whole window, so without a stored `PlannedDayExclusion` the
// next `POST /api/plan/sync` — which the calendar runs on every load —
// would simply put the day back.
await registerAndReachOnboarding(page, uniqueEmail('remove-day'), 'Robin Testsson')
await fillChildStep(page, 'Nova')
await continueSoloAndSkipBalances(page)
const { month, dates } = targetRange()
await goToCalendarMonth(page, month)
const wishBand = page.locator('[data-testid^="calendar-band-wish:"]')
await expect(wishBand).toHaveCount(0)
await dragSelectDates(page, dates)
await expect(page.getByTestId('modal-title')).toHaveText('Planera 3 dagar')
await page.getByTestId('save-button').click()
await expect(page.getByTestId('planned-day-modal__content')).not.toBeVisible()
await expect(wishBand.first()).toBeVisible()
// Clicking the band itself targets its first placed date; clicking one of
// its day slots would target that date instead (see CalendarWeek's
// `clickedDate`). Either way the popover's "this day" removes exactly one.
await wishBand.first().click()
await expect(page.getByTestId('period-popover__content')).toBeVisible()
await page.getByTestId('period-delete-day-button').click()
await expect(page.getByTestId('period-delete-confirm')).toBeVisible()
await page.getByTestId('period-delete-confirm-button').click()
await expect(page.getByTestId('period-popover__overlay')).not.toBeVisible()
// There is a way back, offered immediately.
await expect(page.getByTestId('calendar-toast-undo')).toBeVisible()
// The removed date is marked rather than silently empty — a hole in an
// otherwise-funded week would read as a planner bug.
await expect(page.getByTestId('calendar-day-excluded-badge')).toHaveCount(1)
// The rest of the period survives: this removed one day, not the wish.
await expect(wishBand.first()).toBeVisible()
// The real assertion: a reload re-syncs the plan from the wish, and the
// day must still be gone afterwards.
await page.reload()
await goToCalendarMonth(page, month)
await expect(page.getByTestId('calendar-day-excluded-badge')).toHaveCount(1)
await expect(page.locator('[data-testid^="calendar-band-wish:"]').first()).toBeVisible()
})