From a472347a5e253d7907f44f62ebf0555f07da1997 Mon Sep 17 00:00:00 2001
From: Nessar
Date: Sat, 17 Oct 2026 11:29:31 +0200
Subject: [PATCH] fix(calendar): keep declaration-entry months fixed while
editing
Declaring a date at the edge of the planning period widened the
projection and inserted a disabled month above the grid. Declaration
entry now lists only the planning period's months, while selections
keep the connected-period widening.
Refs #276
Co-Authored-By: Claude Opus 5.5
---
.../components/calendar-year/calendar-year.ts | 9 ++++----
src/app/services/calendar-service.spec.ts | 23 +++++++++++++++++++
src/app/services/calendar-service.ts | 8 ++++++-
3 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/src/app/components/calendar-year/calendar-year.ts b/src/app/components/calendar-year/calendar-year.ts
index e5b3a9b..4813176 100644
--- a/src/app/components/calendar-year/calendar-year.ts
+++ b/src/app/components/calendar-year/calendar-year.ts
@@ -272,11 +272,10 @@ export class CalendarYear {
const horizon = this.calendarHorizon();
if (!horizon) return undefined;
if (this.declarationEntry())
- return this.calendarService.project({
- placements: [],
- horizon,
- declaredDays: this.userInput.declaredDaysSignal(),
- });
+ return this.calendarService.project(
+ { placements: [], horizon, declaredDays: this.userInput.declaredDaysSignal() },
+ { fixedMonths: true },
+ );
const plan = this.displayedPlan();
return this.calendarService.project(
plan ? { ...plan, horizon: plan.horizon ?? horizon } : { placements: [], horizon },
diff --git a/src/app/services/calendar-service.spec.ts b/src/app/services/calendar-service.spec.ts
index 1df7ce3..51a34a3 100644
--- a/src/app/services/calendar-service.spec.ts
+++ b/src/app/services/calendar-service.spec.ts
@@ -216,6 +216,29 @@ describe('CalendarService', () => {
]);
});
+ it('keeps fixed months when an edge declaration widens the projection', () => {
+ const plan = {
+ placements: [],
+ horizon: { start: '2024-07-01', end: '2025-01-04' },
+ declaredDays: [{ date: '2024-07-01', type: 'cp' as const }],
+ };
+ const months = (projection: ReturnType) =>
+ projection.months.map(({ monthIndex, year }) => `${year}-${monthIndex}`);
+ expect(months(service.project(plan))[0]).toBe('2024-5');
+ const fixed = service.project(plan, { fixedMonths: true });
+ expect(months(fixed)).toEqual([
+ '2024-6',
+ '2024-7',
+ '2024-8',
+ '2024-9',
+ '2024-10',
+ '2024-11',
+ '2025-0',
+ ]);
+ expect(fixed.selections.dayTypes(day('2024-06-30'))).toContain(DayType.WEEKEND);
+ expect(fixed.selections.isStart(day('2024-07-01'))).toBe(false);
+ });
+
it('includes the final partial month of a half-open one-year horizon', () => {
const { selections } = project({ start: '2024-01-15', end: '2025-01-15' });
expect(selections.dayTypes(day('2025-01-04'))).toContain(DayType.WEEKEND);
diff --git a/src/app/services/calendar-service.ts b/src/app/services/calendar-service.ts
index cdf5d28..0ea9091 100644
--- a/src/app/services/calendar-service.ts
+++ b/src/app/services/calendar-service.ts
@@ -72,10 +72,14 @@ export class CalendarService {
return { start, end: addPlanningYears(start, 1) };
}
+ /** `fixedMonths` lists only the plan horizon's months, so editing declarations never adds a month. */
project(
plan: CalendarPlan & { horizon: PlanningStateSnapshot['horizon'] },
+ { fixedMonths = false } = {},
): CalendarProjection {
const horizon = calendarProjectionHorizon(plan)!;
+ const firstMonth = plan.horizon.start.slice(0, 7);
+ const lastMonth = addPlanningDays(plan.horizon.end, -1).slice(0, 7);
const first = new Date(`${horizon.start}T00:00:00`);
const last = new Date(`${horizon.end}T00:00:00`);
last.setDate(last.getDate() - 1);
@@ -92,7 +96,9 @@ export class CalendarService {
const cursor = new Date(first);
cursor.setDate(1);
while (cursor <= last) {
- months.push({ monthIndex: cursor.getMonth(), year: cursor.getFullYear() });
+ const month = `${cursor.getFullYear()}-${String(cursor.getMonth() + 1).padStart(2, '0')}`;
+ if (!fixedMonths || (month >= firstMonth && month <= lastMonth))
+ months.push({ monthIndex: cursor.getMonth(), year: cursor.getFullYear() });
for (const week of CalendarService.weeksInMonth(
cursor.getMonth(),
cursor.getFullYear(),