{{ liveMessage() }}

@if (isLoading()) {
{{ loadingProgress() }}%

{{ isCancelling() ? tr().planning.cancelling : tr().loading }}

}

{{ tr().planning.title }}

@if (planningOutcome()?.kind === 'InvalidInput') { } @else if (planningOutcome()?.kind === 'PlanningConflict') { } @else if (planningOutcome()?.kind === 'PlanningIncomplete') {

{{ outcomeLabel(planningOutcome()!) }}

} @else if (planningOutcome()?.kind === 'Plan') {

{{ tr().planning.complete }}

}
@if (displayedPlan()) { }
@if (planningOutcome(); as outcome) { @if (outcome.kind === 'InvalidInput') { } } @if (planningExplanation(); as outcomeExplanation) { @if (outcomeExplanation.kind === 'ConflictExplanation') {

{{ tr().planning.conflictDetails }}

{{ tr().planning.conflictingRules }}:

{{ tr().planning.bottleneck }}: {{ outcomeExplanation.bottleneck.message }}

} @else if (outcomeExplanation.kind === 'IncompleteExplanation') {

{{ tr().planning.incompleteDetails }}

{{ outcomeExplanation.reason.message }}

{{ tr().planning.completedStages }}
{{ outcomeExplanation.completedStages.length ? outcomeExplanation.completedStages.join(', ') : tr().planning.none }}
{{ tr().planning.uncompletedStages }}
{{ outcomeExplanation.uncompletedStages.length ? outcomeExplanation.uncompletedStages.join(', ') : tr().planning.none }}
} } @if (displayedPlan(); as plan) {
{{ tr().planning.daysPlaced }}
{{ plan.placements.length }}
{{ tr().planning.daysGained }}
{{ plan.metrics.nonWorkingDayGain }}
{{ tr().planning.longestWorkSequence }}
{{ plan.metrics.longestWorkSequence }}
@for (contribution of plan.timeWindowContributions ?? []; track contribution.ruleId) {

{{ contribution.label }}

{{ contribution.declaredDays.length }} {{ tr().bookedDays.booked }} + {{ contribution.placementDates.length }} {{ tr().planning.explanation.leaveDays }} = {{ contribution.declaredDays.length + contribution.placementDates.length }} / {{ contribution.requested }}

@for (day of contribution.declaredDays; track day.date) { {{ displayDate(day.date) }} · {{ tr().dayTypes[day.type] }} }

{{ tr().bookedDays.fixed }}

} @if (plan.calculationDate) {

{{ tr().planning.calculationDate }} : {{ displayDate(plan.calculationDate) }}

} @for ( contribution of plan.ifThenContributions ?? []; track contribution.ruleId + contribution.occurrence ) {

{{ contribution.label }} · {{ displayDate(contribution.occurrence) }}

{{ contribution.declaredDays.length }} {{ tr().bookedDays.booked }} + {{ contribution.placementDates.length }} {{ tr().planning.explanation.leaveDays }} = {{ contribution.requested }} / {{ contribution.requested }}

@for (day of contribution.declaredDays; track day.date) { {{ displayDate(day.date) }} · {{ tr().dayTypes[day.type] }} } @if (contribution.declaredDays.length) {

{{ tr().bookedDays.fixed }}

} @if (contribution.earnedEntitlement; as earned) {

{{ earned.added ? tr().planning.futureEntitlement : tr().planning.includedEntitlement }} : {{ earned.quantity }} {{ tr().dayTypes[earned.type] }}

{{ tr().planning.details.available }} {{ displayDate(earned.availableFrom) }} · {{ tr().planning.details.expires }} {{ displayDate(earned.expiryDate) }}

}
} @for (period of plan.timeOffPeriods; track period.start) { @if (period.declaredDates?.length) {

{{ tr().planning.explanation.breakTitle }} · {{ displayDate(period.start) }} – {{ displayDate(period.end) }}

{{ period.declaredDates!.length }} {{ tr().bookedDays.booked }} + {{ period.placementDates.length }} {{ tr().planning.explanation.leaveDays }} + {{ period.nonWorkingDates.length }} {{ tr().planning.explanation.freeDays }} = {{ period.duration }} {{ tr().planning.explanation.daysOff }}

{{ tr().bookedDays.fixed }}

} } }
@if (selectedExplanation(); as selected) {
{{ tr().planning.whyPlacement }}{{ tr().planning.explanationHelp }}

{{ displayDate(selected.placement.date) }} · {{ balanceLabel(selected) }}

@if (planExplanation()!.incomplete; as interrupted) {
{{ tr().planning.incumbentExplanation }}

{{ interrupted.reason.message }}

{{ tr().planning.completedStages }}: {{ interrupted.completedStages.length ? interrupted.completedStages.join(', ') : tr().planning.none }}

{{ tr().planning.uncompletedStages }}: {{ interrupted.uncompletedStages.length ? interrupted.uncompletedStages.join(', ') : tr().planning.none }}

} @if (selected.timeOffPeriod; as period) {

{{ tr().planning.explanation.breakTitle }} · {{ displayDate(period.start) }} – {{ displayDate(period.end) }}

@if (period.declaredDates?.length) { {{ period.declaredDates!.length }}{{ tr().bookedDays.booked }} + } {{ period.placementDates.length }}{{ tr().planning.explanation.leaveDays }} + {{ period.nonWorkingDates.length }}{{ tr().planning.explanation.freeDays }} = {{ period.duration }}{{ tr().planning.explanation.daysOff }}

{{ tr().planning.explanation.wholeBreak }}

}

{{ leaveUsed(selected) }} @if (selected.balance) { {{ tr().planning.explanation.expires }} {{ displayDate(planningDate(selected.balance.expiryDate)) }}. }

@if (selected.ruleIds.length) {

{{ tr().planning.explanation.linkedRules }} @for (ruleId of selected.ruleIds; track ruleId; let last = $last) { {{ ruleLabel(ruleId) }}{{ last ? '.' : ', ' }} }

} @if (hasAtRiskContribution(selected)) {

{{ tr().planning.explanation.atRiskContribution }}

} @if (selected.supporting) {

{{ tr().planning.details.supporting }}

}

{{ tr().planning.remaining }}: {{ selected.remainingQuantity }} {{ tr().userInput.daysSuffix }} @if (selected.remainingBalance; as remaining) { · {{ translationService.getRemainingBalanceReason( remaining, displayedPlan(), planningOutcome()?.kind === 'PlanningIncomplete' ) }} }

@let decisiveObjective = selected.firstDecisiveObjective ?? selected.comparison.firstDecisiveObjective; @if (decisiveObjective) {

{{ tr().planning.details.firstDifference }}: {{ objectiveCopy(decisiveObjective).label }}

}

{{ tr().planning.explanation.impact }}

{{ tr().planning.explanation.impactHelp }}

@if (placementChanges().length) { @for (change of placementChanges(); track change.id) { }
{{ tr().planning.explanation.criterion }} {{ tr().planning.explanation.withoutDay }} {{ tr().planning.explanation.withDay }}
{{ change.label }}{{ change.help }} @if (change.id === decisiveObjective) { {{ tr().planning.details.firstDifference }} } {{ change.before }} {{ change.after }}
} @else {

{{ tr().planning.explanation.noChange }}

}
{{ tr().planning.technicalDetails }}

{{ tr().planning.details.intro }}

{{ tr().planning.details.balance }} · {{ balanceLabel(selected) }}

@if (selected.balance?.availableFrom; as available) {
{{ tr().planning.details.available }}
{{ displayDate(planningDate(available)) }}
} @if (selected.balance; as balance) {
{{ tr().planning.details.expires }}
{{ displayDate(planningDate(balance.expiryDate)) }}
}
{{ tr().planning.remaining }}
{{ selected.remainingQuantity }} {{ tr().userInput.daysSuffix }}
@if (selected.remainingBalance; as remaining) {

{{ translationService.getRemainingBalanceReason( remaining, displayedPlan(), planningOutcome()?.kind === 'PlanningIncomplete' ) }}

}
} @else if (explanationError()) { }

{{ declarationEntry() ? tr().bookedDays.help : tr().planning.calendarHelp }}

@if (declarationEntry() && !calendarHorizon()) {

{{ tr().settings.invalidHorizon }}

}
{{ tr().planning.legend }}
@for (month of months(); track month.year + '-' + month.monthIndex) { }

{{ tr().planning.details.criteria }}

{{ tr().planning.details.criteriaHelp }}

@for ( objective of objectiveRows(comparison.before, comparison.after); track objective.id; let index = $index ) { @if (objective.numeric) { } }
{{ tr().planning.explanation.criterion }} {{ beforeLabel }} {{ afterLabel }}
{{ index + 1 }}. {{ objective.label }}

{{ objective.help }}

{{ objective.direction === 'maximize' ? tr().planning.details.higher : tr().planning.details.lower }} @if (objective.id === comparison.firstDecisiveObjective) { {{ tr().planning.details.firstDifference }} }
{{ objective.before ?? tr().planning.none }} {{ objective.value }}
@if (hasTieBreaks(comparison.after)) {
{{ tr().planning.details.tieBreaks }}
    @for ( objective of objectiveRows(comparison.before, comparison.after); track objective.id ) { @if (!objective.numeric) {
  1. {{ objective.label }} @if (objective.id === comparison.firstDecisiveObjective) { {{ tr().planning.details.firstDifference }} }
  2. } }
}
{{ tr().planning.details.audit }}

{{ tr().planning.details.auditHelp }}

{{ data | json }}

{{ tr().planning.statistics.title }}

@if (displayedPlan(); as plan) { @if (planningOutcome()?.kind === 'PlanningIncomplete') {

{{ tr().planning.statistics.partialPlan }}

}

{{ tr().planning.statistics.help }}

{{ tr().planning.statistics.leaveDays }}
{{ plan.placements.length }}
{{ tr().planning.daysGained }} ({{ tr().planning.statistics.calendarDays }})
{{ formatStatistic(plan.metrics.nonWorkingDayGain) }}
{{ tr().planning.statistics.averageWorkSequence }} ({{ tr().planning.statistics.workingDays }})
{{ formatStatistic(plan.metrics.averageWorkSequence) }}
{{ tr().planning.longestWorkSequence }} ({{ tr().planning.statistics.workingDays }})
{{ formatStatistic(plan.metrics.longestWorkSequence) }}
{{ tr().planning.statistics.averageTimeOffPeriod }} ({{ tr().planning.statistics.calendarDays }})
{{ formatStatistic(plan.metrics.averageTimeOffPeriod) }}
{{ tr().planning.statistics.longestTimeOffPeriod }} ({{ tr().planning.statistics.calendarDays }})
{{ formatStatistic(plan.metrics.longestTimeOffPeriod) }}
{{ tr().planning.statistics.weekendConnectedHolidays }} ({{ tr().planning.statistics.calendarDays }})
{{ formatStatistic(weekendConnectedHolidays()) }}
{{ tr().planning.statistics.earnedLeave }} ({{ tr().planning.statistics.leaveDaysUnit }})
{{ formatStatistic(earnedLeave().total) }}
@if (earnedLeave().contributions.length) {

{{ tr().planning.statistics.earnedLeaveByRule }} ({{ tr().planning.statistics.leaveDaysUnit }})

@for ( contribution of earnedLeave().contributions; track contribution.ruleId ) {
{{ contribution.label }}
{{ formatStatistic(contribution.quantity) }}
}
} }

{{ tr().planning.comparison.title }}

{{ tr().planning.explanation.moveHelp }}

@if (counterfactualLoading()) {

{{ tr().planning.counterfactualLoading }}

} @else if (counterfactualError()) { } @else if (counterfactual(); as analysis) {
{{ tr().planning.explanation.plannedDate }} {{ displayDate(analysis.placement.date) }}
{{ tr().planning.explanation.tryDate }}
@if (analysis.results[0]; as firstDate) {

{{ tr().planning.comparison.dateRange }} {{ displayDate(firstDate.date) }} – {{ displayDate(analysis.results.at(-1)!.date) }}

} @if (invalidComparisonDate()) { } @if (nearbyComparisonDates().length) {
{{ tr().planning.comparison.nearby }} @for (alternative of nearbyComparisonDates(); track alternative.date) { }
} @else {

{{ tr().planning.comparison.noAlternatives }}

} @if (selectedCounterfactualResult(); as result) {

{{ displayDate(result.date) }} · {{ result.status === 'eligible' ? tr().planning.comparison.available : tr().planning.comparison.unavailable }}

@if (result.status === 'ineligible') {

{{ tr().planning.comparison.unavailable }}

@if (result.reason) {

{{ tr().planning.explanation.unavailable[ result.reason.code ] ?? result.reason.message }}

}
} @else if (result.date === analysis.placement.date) {

{{ tr().planning.explanation.sameDate }}

} @else {

{{ tr().planning.comparison.changes }}

{{ tr().planning.comparison.available }}
@if (dateComparison()) { @if (dateChanges().length) { @for (change of dateChanges(); track change.id) { }
{{ tr().planning.explanation.criterion }} {{ tr().planning.explanation.currentPlan }} {{ tr().planning.explanation.alternative }}
{{ change.label }} {{ change.before }} {{ change.after }}
} @else {

{{ tr().planning.explanation.noChange }}

} } @else {

{{ tr().planning.explanation.comparisonUnavailable }}

} }
{{ tr().planning.technicalDetails }}
@if (dateComparison(); as comparison) { } @else { }
} @else if (!invalidComparisonDate()) {

{{ tr().planning.comparison.chooseDate }}

} }