refactor(planning): give planning calculations one state owner

PlanningCalculationService now builds the submitted request, owns the
pending and accepted calculation state, and delivers explanations for
the accepted calculation only. The duration estimator reads the same
request. The calendar and leave-balance summary consume the accepted
outcome instead of coordinating writable state on the input service.

Refs #275

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nessarandClaude Opus 5.5 committed 2026-10-17 11:11:37 +02:00
1 parent b69b3c3690
commit 5cb0b93339
12 files changed
+708 -322

No files matched your search

+2 -20
View File
@@ -1,4 +1,4 @@
import { computed, Injectable, signal } from '@angular/core';
import { Injectable, signal } from '@angular/core';
import { SelectableDayType, VacDay } from './calendar-service';
import dayjs from 'dayjs';
import {
@@ -6,7 +6,7 @@ import {
isWorkingPlanningDate,
parseDeclaredDays,
} from './declared-non-working-days';
import type { LeaveType, PlanningOutcome, PlanningSnapshot } from './leave-planner.service';
import type { LeaveType } from './leave-planner.service';
@Injectable({
providedIn: 'root',
@@ -161,19 +161,6 @@ export class UserInputService {
readonly vacationNumberSignal = signal<VacDay[]>(
this.getSavedValues() ?? this.getInitialValues(),
);
readonly planningOutcomeSignal = signal<PlanningOutcome | null>(null);
readonly planningSnapshotSignal = signal<PlanningSnapshot | null>(null);
readonly displayedPlanSignal = computed(() => {
const outcome = this.planningOutcomeSignal();
return outcome?.kind === 'Plan'
? outcome
: outcome?.kind === 'PlanningIncomplete'
? outcome.incumbent
: undefined;
});
readonly computeTriggerSignal = signal(0);
readonly computationRequestedAtMs = signal<number | null>(null);
public updateBalances(updater: (balances: VacDay[]) => VacDay[]): void {
this.vacationNumberSignal.update(updater);
try {
@@ -193,9 +180,4 @@ export class UserInputService {
console.warn('Failed to save leave balances to localStorage.', error);
}
}
public requestComputation(): void {
this.computationRequestedAtMs.set(performance.now());
this.computeTriggerSignal.update((count) => count + 1);
}
}