feat(dashboard): tighten explainability detail views

This commit is contained in:
2026-03-31 22:50:47 +02:00
parent db50e2e555
commit 7ace137d16
11 changed files with 580 additions and 65 deletions
@@ -156,7 +156,16 @@ describe("dashboard procedure support", () => {
recentActivity: [],
});
vi.mocked(getDashboardPeakTimes).mockResolvedValue([
{ period: "2026-03", totalHours: 160.34, capacityHours: 200.12, utilizationPct: 80 },
{
period: "2026-03",
totalHours: 160.34,
capacityHours: 200.12,
utilizationPct: 80,
derivation: {
calendarContextCount: 1,
calendarLocations: [{ countryCode: "DE", federalState: "BY", metroCityName: "Munich" }],
},
},
]);
vi.mocked(getDashboardTopValueResources).mockResolvedValue([
{
@@ -166,7 +175,17 @@ describe("dashboard procedure support", () => {
chapter: "CGI",
lcrCents: 12345,
valueScore: 98,
valueScoreBreakdown: {
skillDepth: 94,
skillBreadth: 86,
costEfficiency: 82,
chargeability: 88,
experience: 96,
total: 98,
},
valueScoreUpdatedAt: new Date("2026-03-05T00:00:00.000Z"),
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Munich",
},
@@ -193,6 +212,8 @@ describe("dashboard procedure support", () => {
totalHoursPerDay: 160.3,
capacityHours: 200.1,
utilizationPct: 80,
calendarContextCount: 1,
calendarLocations: [{ countryCode: "DE", federalState: "BY", metroCityName: "Munich" }],
},
],
topResources: [
@@ -202,7 +223,17 @@ describe("dashboard procedure support", () => {
chapter: "CGI",
lcr: "123,45 EUR",
valueScore: 98,
valueScoreBreakdown: {
skillDepth: 94,
skillBreadth: 86,
costEfficiency: 82,
chargeability: 88,
experience: 96,
total: 98,
},
valueScoreUpdatedAt: "2026-03-05T00:00:00.000Z",
countryCode: "DE",
countryName: "Germany",
federalState: "BY",
metroCityName: "Munich",
},
@@ -467,6 +467,15 @@ describe("dashboard router", () => {
displayName: "Alice",
chapter: "Delivery",
valueScore: 95,
valueScoreBreakdown: {
skillDepth: 88,
skillBreadth: 79,
costEfficiency: 84,
chargeability: 76,
experience: 90,
total: 95,
},
valueScoreUpdatedAt: new Date("2026-03-01T00:00:00.000Z"),
lcrCents: 12_300,
countryCode: "DE",
countryName: "Germany",
@@ -479,6 +488,15 @@ describe("dashboard router", () => {
displayName: "Bob",
chapter: "Data",
valueScore: 88,
valueScoreBreakdown: {
skillDepth: 80,
skillBreadth: 77,
costEfficiency: 91,
chargeability: 72,
experience: 81,
total: 88,
},
valueScoreUpdatedAt: new Date("2026-03-02T00:00:00.000Z"),
lcrCents: 10_800,
countryCode: "US",
countryName: "United States",
@@ -748,6 +766,15 @@ describe("dashboard router", () => {
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",
@@ -788,6 +815,8 @@ describe("dashboard router", () => {
totalHoursPerDay: 320.4,
capacityHours: 400.2,
utilizationPct: 80,
calendarContextCount: 0,
calendarLocations: [],
},
],
topResources: [
@@ -797,6 +826,15 @@ describe("dashboard router", () => {
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",
@@ -31,6 +31,8 @@ type TopValueResourceRow = {
displayName: string;
chapter: string | null;
valueScore: number | null;
valueScoreBreakdown: import("@capakraken/shared").ValueScoreBreakdown | null;
valueScoreUpdatedAt: Date | null;
lcrCents: number;
countryCode: string | null;
countryName: string | null;
@@ -238,6 +240,8 @@ export async function getDashboardDetail(ctx: DashboardProcedureContext, input:
totalHoursPerDay: round1(entry.totalHours),
capacityHours: round1(entry.capacityHours),
utilizationPct: entry.utilizationPct ?? null,
calendarContextCount: entry.derivation?.calendarContextCount ?? 0,
calendarLocations: entry.derivation?.calendarLocations ?? [],
}));
}
@@ -249,6 +253,8 @@ export async function getDashboardDetail(ctx: DashboardProcedureContext, input:
chapter: resource.chapter ?? null,
lcr: fmtEur(resource.lcrCents),
valueScore: resource.valueScore ?? null,
valueScoreBreakdown: resource.valueScoreBreakdown ?? null,
valueScoreUpdatedAt: resource.valueScoreUpdatedAt?.toISOString() ?? null,
countryCode: resource.countryCode ?? null,
countryName: resource.countryName ?? null,
federalState: resource.federalState ?? null,