feat(dashboard): enrich demand calendar locations

This commit is contained in:
2026-03-31 23:12:47 +02:00
parent 79e0fd82f5
commit 92e94f43a7
4 changed files with 255 additions and 2 deletions
@@ -0,0 +1,245 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { SystemRole } from "@capakraken/shared";
import {
createToolContext,
executeTool,
getDashboardDemand,
getDashboardOverview,
getDashboardPeakTimes,
getDashboardTopValueResources,
} from "./assistant-tools-dashboard-test-helpers.js";
describe("assistant dashboard tools detail aggregation", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("routes dashboard detail reads through dashboard router callers", async () => {
vi.mocked(getDashboardOverview).mockResolvedValue({
totalResources: 12,
activeResources: 10,
inactiveResources: 2,
totalProjects: 4,
activeProjects: 3,
inactiveProjects: 1,
totalAllocations: 8,
activeAllocations: 7,
cancelledAllocations: 1,
approvedVacations: 2,
totalEstimates: 5,
budgetSummary: {
totalBudgetCents: 500_000,
totalCostCents: 240_000,
avgUtilizationPercent: 48,
},
budgetBasis: {
remainingBudgetCents: 260_000,
budgetedProjects: 3,
unbudgetedProjects: 1,
trackedAssignmentCount: 8,
windowStart: new Date("2026-01-01T00:00:00.000Z"),
windowEnd: new Date("2026-06-30T00:00:00.000Z"),
},
recentActivity: [],
projectsByStatus: [],
chapterUtilization: [
{
chapter: "Delivery",
resourceCount: 4,
avgChargeabilityTarget: 78,
},
],
});
vi.mocked(getDashboardPeakTimes).mockResolvedValue([
{
period: "2026-03",
groups: [],
totalHours: 320.4,
capacityHours: 400.2,
utilizationPct: 80,
derivation: {
periodStart: "2026-03-01",
periodEnd: "2026-03-31",
calendarContextCount: 1,
resourceCount: 4,
groupCount: 1,
calendarLocations: [
{
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 4,
effectiveAvailableHours: 400.2,
},
],
bookedHours: 320.4,
capacityHours: 400.2,
remainingCapacityHours: 79.8,
overbookedHours: 0,
utilizationPct: 80,
},
},
]);
vi.mocked(getDashboardTopValueResources).mockResolvedValue([
{
id: "res_1",
eid: "pparker",
displayName: "Peter Parker",
chapter: "Delivery",
valueScore: 91,
valueScoreBreakdown: {
skillDepth: 85,
skillBreadth: 74,
costEfficiency: 93,
chargeability: 78,
experience: 88,
total: 91,
},
valueScoreUpdatedAt: new Date("2026-03-03T00:00:00.000Z"),
lcrCents: 9_500,
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
},
]);
vi.mocked(getDashboardDemand).mockResolvedValue([
{
id: "project_1",
name: "Gelddruckmaschine",
shortCode: "GDM",
allocatedHours: 120,
requiredFTEs: 4,
resourceCount: 2,
derivation: {
periodStart: "2026-01-01",
periodEnd: "2026-06-30",
periodWorkingHoursBase: 1040,
requiredHours: 2080,
requiredFTEs: 4,
fillPct: 50,
demandSource: "DEMAND_REQUIREMENTS",
calendarLocations: [
{
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 2,
allocatedHours: 120,
},
],
},
},
]);
const ctx = createToolContext(
{
systemSettings: {
findUnique: vi.fn().mockResolvedValue(null),
},
},
{ userRole: SystemRole.CONTROLLER },
);
const result = await executeTool("get_dashboard_detail", JSON.stringify({ section: "all" }), ctx);
expect(getDashboardOverview).toHaveBeenCalledTimes(1);
expect(getDashboardPeakTimes).toHaveBeenCalledWith(
ctx.db,
expect.objectContaining({
startDate: new Date("2026-01-01T00:00:00.000Z"),
endDate: new Date("2026-06-30T00:00:00.000Z"),
granularity: "month",
groupBy: "project",
}),
);
expect(getDashboardTopValueResources).toHaveBeenCalledWith(
ctx.db,
expect.objectContaining({
limit: 10,
userRole: SystemRole.CONTROLLER,
}),
);
expect(getDashboardDemand).toHaveBeenCalledWith(
ctx.db,
expect.objectContaining({
startDate: new Date("2026-01-01T00:00:00.000Z"),
endDate: new Date("2026-06-30T00:00:00.000Z"),
groupBy: "project",
}),
);
expect(JSON.parse(result.content)).toEqual({
peakTimes: [
{
month: "2026-03",
totalHours: 320.4,
totalHoursPerDay: 320.4,
capacityHours: 400.2,
utilizationPct: 80,
calendarContextCount: 1,
calendarLocations: [
{
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 4,
effectiveAvailableHours: 400.2,
},
],
},
],
topResources: [
{
name: "Peter Parker",
eid: "pparker",
chapter: "Delivery",
lcr: "95,00 EUR",
valueScore: 91,
valueScoreBreakdown: {
skillDepth: 85,
skillBreadth: 74,
costEfficiency: 93,
chargeability: 78,
experience: 88,
total: 91,
},
valueScoreUpdatedAt: "2026-03-03T00:00:00.000Z",
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
},
],
demandPipeline: [
{
project: "Gelddruckmaschine (GDM)",
needed: 2,
requiredFTEs: 4,
allocatedResources: 2,
allocatedHours: 120,
calendarLocations: [
{
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Augsburg",
resourceCount: 2,
allocatedHours: 120,
},
],
},
],
chargeabilityByChapter: [
{
chapter: "Delivery",
headcount: 4,
avgTarget: "78%",
},
],
});
});
});
@@ -202,7 +202,7 @@ describe("dashboard procedure support", () => {
requiredFTEs: 3,
resourceCount: 1,
allocatedHours: 80,
derivation: { calendarLocations: [{ countryCode: "DE" }] },
derivation: { calendarLocations: [{ countryCode: "DE", countryName: "Germany" }] },
},
]);
@@ -250,7 +250,7 @@ describe("dashboard procedure support", () => {
requiredFTEs: 3,
allocatedResources: 1,
allocatedHours: 80,
calendarLocations: [{ countryCode: "DE" }],
calendarLocations: [{ countryCode: "DE", countryName: "Germany" }],
},
],
chargeabilityByChapter: [