feat: enhance user input component with new styling and functionality

- Added informational styling to warning cards in user input component.
- Improved remaining days details and history display with new computed properties.
- Updated user input service to manage planning outcomes and traces more effectively.
- Enhanced leave planner service to classify remaining balances with detailed reasons.
- Expanded translation service to support new balance reasoning and suggestions.
- Updated translations for multiple languages to reflect new features and terminology.
This commit is contained in:
nessar committed 2026-10-07 21:49:33 +02:00
1 parent 9851edf906
commit 3dc601982a
15 files changed
+679 -141

No files matched your search

+49 -12
View File
@@ -5044,7 +5044,53 @@ function qualityMetrics(
};
}
function remainingBalances(state: State, context: SearchContext): RemainingBalance[] {
function nonPlacementReason(
balance: NormalizedBalance,
state: State,
context: SearchContext,
completed: boolean,
): PlanningReason {
if (balance.expiryDate < context.horizon.start)
return reason('unavoidable-expiry', 'This balance expired before the planning horizon.');
if (balance.availableFrom >= context.horizon.end)
return reason(
'available-after-horizon',
'This balance becomes available after the last planning date.',
);
const dates = context.workingDays.filter(
(date) => date >= balance.availableFrom && date <= balance.expiryDate,
);
if (!dates.length)
return reason(
'no-working-date',
'There is no working date within both the horizon and this balance’s validity period.',
);
if (dates.every((date) => context.declaredDates.has(date) || state.placements.has(date)))
return reason(
'all-dates-occupied',
'Every working date within this balance’s validity period is already occupied in the displayed plan.',
);
if (!completed)
return reason(
'calculation-incomplete',
'The calculation stopped before completing its planning priorities.',
);
if (balance.expiryDate > context.horizon.end)
return reason(
'preserved',
'This balance expires after the horizon; the planner retained no additional placement under its planning priorities.',
);
return reason(
'not-established',
'The calculation did not establish a precise reason for leaving this balance unspent.',
);
}
function remainingBalances(
state: State,
context: SearchContext,
completed: boolean,
): RemainingBalance[] {
return state.balances
.filter((balance) => balance.remaining > 0)
.map((balance) => {
@@ -5054,8 +5100,6 @@ function remainingBalances(state: State, context: SearchContext): RemainingBalan
: compareDate(balance.expiryDate, context.horizon.end) <= 0
? 'at-risk'
: 'deferred';
const code =
classification === 'unavoidable-expiry' ? 'unavoidable-expiry' : classification;
return {
id: balance.id,
type: balance.type,
@@ -5063,14 +5107,7 @@ function remainingBalances(state: State, context: SearchContext): RemainingBalan
availableFrom: balance.availableFrom,
expiryDate: balance.expiryDate,
classification,
reason: reason(
code,
classification === 'unavoidable-expiry'
? 'This balance expired before the planning horizon.'
: classification === 'at-risk'
? 'This balance expires within the planning horizon.'
: 'This balance remains available after the planning horizon.',
),
reason: nonPlacementReason(balance, state, context, completed),
earned: balance.earned,
sourceRuleId: balance.sourceRuleId,
};
@@ -5442,7 +5479,7 @@ function makePlan(
ruleIds: [...placement.ruleIds].sort(),
supporting: placement.supporting,
}));
const remaining = remainingBalances(state, context);
const remaining = remainingBalances(state, context, stages.includes('dates'));
const windowContributions = timeWindowContributions(state, context);
const patternContributions = ifThenContributions(state, context);
const plan: PlanOutcome = {