feat(dashboard): enrich demand calendar locations
This commit is contained in:
@@ -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: [
|
||||
|
||||
Reference in New Issue
Block a user