feat: add PlanningDurationService for estimating planning duration

- Implemented PlanningDurationService to provide an indicative wait time before calculation based on user input and calendar settings.
- Introduced duration estimation logic that considers horizon dates, balances, and rules without triggering a calculation.
- Enhanced user input component to display estimated duration using the new service.
- Updated translation files to include new duration estimate strings in multiple languages.
- Added tests for PlanningDurationService to ensure accurate duration estimation and calibration.
- Refactored leave planner service to remove hardcoded deadlines and improve worker responsiveness.
This commit is contained in:
nessar committed 2026-10-05 17:06:17 +02:00
1 parent 2edf2004f5
commit bc8570d8ca
29 files changed
+1064 -123

No files matched your search

+4 -12
View File
@@ -59,7 +59,7 @@ export interface PlanningRequest {
identities?: unknown;
/** Optional caller-owned human-readable labels copied into a calculation snapshot. */
labels?: unknown;
/** A testable deadline in milliseconds. The production default is ten seconds. */
/** Opt-in planning-work budget in milliseconds; omitted means run until completion or cancellation. */
deadlineMs?: number;
progress?: (stage: string) => void;
}
@@ -1235,8 +1235,6 @@ class UserCancelled extends Error {}
type PlanningSteps<T> = Generator<boolean | undefined, T, void>;
const DAY_MS = 86_400_000;
const MAX_PLANNING_MS = 10_000;
const CORE_DEADLINE_MARGIN_MS = 50;
// ponytail: cap the number of feasible obligation branches previewed for a
// loaded year; replace with bounded branch-and-bound if mobile evidence shows
// that this ceiling misses a better lexicographic assignment.
@@ -5735,14 +5733,8 @@ function* calculatePlanningSteps(
calendar;
const started =
typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now();
const configuredLimit =
typeof source.deadlineMs === 'number' ? source.deadlineMs : MAX_PLANNING_MS;
const deadline = Math.min(Math.max(0, configuredLimit), MAX_PLANNING_MS);
// Leave a small scheduling margin so the synchronous SSR/test fallback also
// observes the public ten-second wall-clock ceiling; browser workers have a
// separate watchdog in planInWorker.
const coreDeadline =
deadline === MAX_PLANNING_MS ? deadline - CORE_DEADLINE_MARGIN_MS : deadline;
const deadline =
typeof source.deadlineMs === 'number' ? source.deadlineMs : Number.POSITIVE_INFINITY;
const context = {
horizon,
holidays,
@@ -5754,7 +5746,7 @@ function* calculatePlanningSteps(
workingDays,
initialBalances: normalized.balances,
rules: normalized.rules,
deadline: coreDeadline,
deadline,
started,
request: source,
nodes: 0,