feat(api): include project health in dashboard detail

This commit is contained in:
2026-03-31 23:36:29 +02:00
parent 703406a76b
commit 2de5a0eede
5 changed files with 235 additions and 2 deletions
@@ -293,6 +293,12 @@ function mapChargeabilityByChapter(
));
}
function getProjectHealthRating(overall: number): "healthy" | "at_risk" | "critical" {
if (overall >= 80) return "healthy";
if (overall >= 50) return "at_risk";
return "critical";
}
export async function getDashboardOverviewRead(ctx: DashboardProcedureContext) {
return getOverviewCached(ctx.db);
}
@@ -438,6 +444,39 @@ export async function getDashboardDetail(ctx: DashboardProcedureContext, input:
);
}
if (section === "all" || section === "project_health") {
const projectHealth = await getDashboardProjectHealthRead(ctx);
result.projectHealth = [...projectHealth]
.sort((left, right) => left.compositeScore - right.compositeScore)
.slice(0, 10)
.map((project) => ({
project: `${project.projectName} (${project.shortCode})`,
status: project.status,
overall: project.compositeScore,
rating: getProjectHealthRating(project.compositeScore),
budget: project.budgetHealth,
staffing: project.staffingHealth,
timeline: project.timelineHealth,
timelineStatus: project.timelineStatus ?? "UNSCHEDULED",
daysUntilEndDate: project.daysUntilEndDate ?? null,
demandHeadcountOpen: project.demandHeadcountOpen ?? 0,
explainability: {
demandHeadcountTotal: project.demandHeadcountTotal ?? 0,
demandHeadcountFilled: project.demandHeadcountFilled ?? 0,
demandHeadcountOpen: project.demandHeadcountOpen ?? 0,
demandRequirementCount: project.demandRequirementCount ?? 0,
plannedEndDate: project.plannedEndDate?.toISOString() ?? null,
budgetUtilizationPercent: project.budgetUtilizationPercent ?? null,
remainingBudgetCents: project.remainingBudgetCents ?? null,
calendarContextCount: project.derivation?.calendarContextCount ?? 0,
holidayAwareAssignmentCount: project.derivation?.holidayAwareAssignmentCount ?? 0,
publicHolidayCostDeductionCents:
project.derivation?.publicHolidayCostDeductionCents ?? 0,
absenceCostDeductionCents: project.derivation?.absenceCostDeductionCents ?? 0,
},
}));
}
return result;
}