feat(api): include project health in dashboard detail
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
getDashboardDemand,
|
||||
getDashboardOverview,
|
||||
getDashboardPeakTimes,
|
||||
getDashboardProjectHealth,
|
||||
getDashboardTopValueResources,
|
||||
} from "./assistant-tools-dashboard-test-helpers.js";
|
||||
|
||||
@@ -172,6 +173,42 @@ describe("assistant dashboard tools detail aggregation", () => {
|
||||
watchlist: [],
|
||||
month: "2026-03",
|
||||
});
|
||||
vi.mocked(getDashboardProjectHealth).mockResolvedValue([
|
||||
{
|
||||
id: "project_1",
|
||||
projectName: "Gelddruckmaschine",
|
||||
shortCode: "GDM",
|
||||
status: "ACTIVE",
|
||||
clientId: "client_1",
|
||||
clientName: "Acme",
|
||||
budgetHealth: 58,
|
||||
staffingHealth: 46,
|
||||
timelineHealth: 61,
|
||||
compositeScore: 55,
|
||||
budgetUtilizationPercent: 73,
|
||||
remainingBudgetCents: 88_000,
|
||||
demandHeadcountTotal: 4,
|
||||
demandHeadcountFilled: 2,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: new Date("2026-09-30T00:00:00.000Z"),
|
||||
daysUntilEndDate: 183,
|
||||
timelineStatus: "DUE_SOON",
|
||||
derivation: {
|
||||
periodStart: "2026-01-01",
|
||||
periodEnd: "2026-06-30",
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 2,
|
||||
fallbackAssignmentCount: 0,
|
||||
baseSpentCents: 140_000,
|
||||
adjustedSpentCents: 132_000,
|
||||
publicHolidayDayEquivalent: 1,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceDayEquivalent: 0.5,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const ctx = createToolContext(
|
||||
{
|
||||
@@ -320,6 +357,33 @@ describe("assistant dashboard tools detail aggregation", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
projectHealth: [
|
||||
{
|
||||
project: "Gelddruckmaschine (GDM)",
|
||||
status: "ACTIVE",
|
||||
overall: 55,
|
||||
rating: "at_risk",
|
||||
budget: 58,
|
||||
staffing: 46,
|
||||
timeline: 61,
|
||||
timelineStatus: "DUE_SOON",
|
||||
daysUntilEndDate: 183,
|
||||
demandHeadcountOpen: 2,
|
||||
explainability: {
|
||||
demandHeadcountTotal: 4,
|
||||
demandHeadcountFilled: 2,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: "2026-09-30T00:00:00.000Z",
|
||||
budgetUtilizationPercent: 73,
|
||||
remainingBudgetCents: 88_000,
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 2,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -257,6 +257,42 @@ describe("dashboard procedure support", () => {
|
||||
watchlist: [],
|
||||
month: "2026-03",
|
||||
});
|
||||
vi.mocked(getDashboardProjectHealth).mockResolvedValue([
|
||||
{
|
||||
id: "project_1",
|
||||
projectName: "Apollo",
|
||||
shortCode: "APO",
|
||||
status: "ACTIVE",
|
||||
clientId: "client_1",
|
||||
clientName: "Acme",
|
||||
budgetHealth: 62,
|
||||
staffingHealth: 55,
|
||||
timelineHealth: 48,
|
||||
compositeScore: 54,
|
||||
budgetUtilizationPercent: 81,
|
||||
remainingBudgetCents: 45_000,
|
||||
demandHeadcountTotal: 5,
|
||||
demandHeadcountFilled: 3,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: new Date("2026-07-31T00:00:00.000Z"),
|
||||
daysUntilEndDate: 138,
|
||||
timelineStatus: "DUE_SOON",
|
||||
derivation: {
|
||||
periodStart: "2026-03-01",
|
||||
periodEnd: "2026-06-30",
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 3,
|
||||
fallbackAssignmentCount: 0,
|
||||
baseSpentCents: 170_000,
|
||||
adjustedSpentCents: 165_000,
|
||||
publicHolidayDayEquivalent: 1,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceDayEquivalent: 0.5,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
try {
|
||||
const result = await getDashboardDetail(createContext(), { section: "all" });
|
||||
@@ -354,6 +390,33 @@ describe("dashboard procedure support", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
projectHealth: [
|
||||
{
|
||||
project: "Apollo (APO)",
|
||||
status: "ACTIVE",
|
||||
overall: 54,
|
||||
rating: "at_risk",
|
||||
budget: 62,
|
||||
staffing: 55,
|
||||
timeline: 48,
|
||||
timelineStatus: "DUE_SOON",
|
||||
daysUntilEndDate: 138,
|
||||
demandHeadcountOpen: 2,
|
||||
explainability: {
|
||||
demandHeadcountTotal: 5,
|
||||
demandHeadcountFilled: 3,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: "2026-07-31T00:00:00.000Z",
|
||||
budgetUtilizationPercent: 81,
|
||||
remainingBudgetCents: 45_000,
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 3,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(getDashboardChargeabilityOverview).toHaveBeenCalledWith(
|
||||
|
||||
@@ -870,6 +870,42 @@ describe("dashboard router", () => {
|
||||
watchlist: [],
|
||||
month: "2026-03",
|
||||
});
|
||||
vi.mocked(getDashboardProjectHealth).mockResolvedValue([
|
||||
{
|
||||
id: "project_1",
|
||||
projectName: "Gelddruckmaschine",
|
||||
shortCode: "GDM",
|
||||
status: "ACTIVE",
|
||||
clientId: "client_1",
|
||||
clientName: "Acme",
|
||||
budgetHealth: 58,
|
||||
staffingHealth: 46,
|
||||
timelineHealth: 61,
|
||||
compositeScore: 55,
|
||||
budgetUtilizationPercent: 73,
|
||||
remainingBudgetCents: 88_000,
|
||||
demandHeadcountTotal: 4,
|
||||
demandHeadcountFilled: 2,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: new Date("2026-09-30T00:00:00.000Z"),
|
||||
daysUntilEndDate: 183,
|
||||
timelineStatus: "DUE_SOON",
|
||||
derivation: {
|
||||
periodStart: "2026-01-01",
|
||||
periodEnd: "2026-06-30",
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 2,
|
||||
fallbackAssignmentCount: 0,
|
||||
baseSpentCents: 140_000,
|
||||
adjustedSpentCents: 132_000,
|
||||
publicHolidayDayEquivalent: 1,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceDayEquivalent: 0.5,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const caller = createControllerCaller({});
|
||||
const result = await caller.getDetail({ section: "all" });
|
||||
@@ -985,6 +1021,33 @@ describe("dashboard router", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
projectHealth: [
|
||||
{
|
||||
project: "Gelddruckmaschine (GDM)",
|
||||
status: "ACTIVE",
|
||||
overall: 55,
|
||||
rating: "at_risk",
|
||||
budget: 58,
|
||||
staffing: 46,
|
||||
timeline: 61,
|
||||
timelineStatus: "DUE_SOON",
|
||||
daysUntilEndDate: 183,
|
||||
demandHeadcountOpen: 2,
|
||||
explainability: {
|
||||
demandHeadcountTotal: 4,
|
||||
demandHeadcountFilled: 2,
|
||||
demandHeadcountOpen: 2,
|
||||
demandRequirementCount: 2,
|
||||
plannedEndDate: "2026-09-30T00:00:00.000Z",
|
||||
budgetUtilizationPercent: 73,
|
||||
remainingBudgetCents: 88_000,
|
||||
calendarContextCount: 1,
|
||||
holidayAwareAssignmentCount: 2,
|
||||
publicHolidayCostDeductionCents: 5_000,
|
||||
absenceCostDeductionCents: 3_000,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(getDashboardPeakTimes).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
@@ -1003,6 +1066,7 @@ describe("dashboard router", () => {
|
||||
watchlistThreshold: 15,
|
||||
}),
|
||||
);
|
||||
expect(getDashboardProjectHealth).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user