Implement ST-404 day overview
This commit is contained in:
parent
67b4461e8a
commit
c5ab2a40e4
6 changed files with 319 additions and 7 deletions
68
lib/planning/day-overview.test.ts
Normal file
68
lib/planning/day-overview.test.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { calculateDayOverviewSnapshot } from "./day-overview";
|
||||
|
||||
describe("calculateDayOverviewSnapshot", () => {
|
||||
it("berekent gepland versus werkelijk met statusverdeling", () => {
|
||||
const snapshot = calculateDayOverviewSnapshot([
|
||||
{
|
||||
source: "planned",
|
||||
status: "completed",
|
||||
durationMinutes: 30,
|
||||
impactLevel: "midden",
|
||||
},
|
||||
{
|
||||
source: "planned",
|
||||
status: "adjusted",
|
||||
durationMinutes: 60,
|
||||
impactLevel: "laag",
|
||||
},
|
||||
{
|
||||
source: "planned",
|
||||
status: "skipped",
|
||||
durationMinutes: 90,
|
||||
impactLevel: "hoog",
|
||||
},
|
||||
{
|
||||
source: "ad_hoc",
|
||||
status: "completed",
|
||||
durationMinutes: 15,
|
||||
impactLevel: "laag",
|
||||
},
|
||||
{
|
||||
source: "planned",
|
||||
status: "planned",
|
||||
durationMinutes: 45,
|
||||
impactLevel: "midden",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(snapshot.totalActivities).toBe(5);
|
||||
expect(snapshot.plannedActivityCount).toBe(4);
|
||||
expect(snapshot.adHocActivityCount).toBe(1);
|
||||
expect(snapshot.openActivityCount).toBe(1);
|
||||
expect(snapshot.completedActivityCount).toBe(2);
|
||||
expect(snapshot.adjustedActivityCount).toBe(1);
|
||||
expect(snapshot.skippedActivityCount).toBe(1);
|
||||
expect(snapshot.executedActivityCount).toBe(3);
|
||||
expect(snapshot.plannedPoints).toBe(10);
|
||||
expect(snapshot.actualPoints).toBe(5);
|
||||
expect(snapshot.pointDifference).toBe(-5);
|
||||
});
|
||||
|
||||
it("laat ongeplande activiteiten meetellen in werkelijk, niet in gepland", () => {
|
||||
const snapshot = calculateDayOverviewSnapshot([
|
||||
{
|
||||
source: "ad_hoc",
|
||||
status: "completed",
|
||||
durationMinutes: 120,
|
||||
impactLevel: "hoog",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(snapshot.plannedActivityCount).toBe(0);
|
||||
expect(snapshot.adHocActivityCount).toBe(1);
|
||||
expect(snapshot.plannedPoints).toBe(0);
|
||||
expect(snapshot.actualPoints).toBe(5);
|
||||
expect(snapshot.pointDifference).toBe(5);
|
||||
});
|
||||
});
|
||||
72
lib/planning/day-overview.ts
Normal file
72
lib/planning/day-overview.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { deriveActivityEnergyPoints } from "./meter";
|
||||
import type { ActivityRecord } from "./types";
|
||||
|
||||
type ActivityOverviewInput = Pick<
|
||||
ActivityRecord,
|
||||
"source" | "status" | "durationMinutes" | "impactLevel"
|
||||
>;
|
||||
|
||||
export type DayOverviewSnapshot = {
|
||||
totalActivities: number;
|
||||
plannedActivityCount: number;
|
||||
adHocActivityCount: number;
|
||||
openActivityCount: number;
|
||||
completedActivityCount: number;
|
||||
adjustedActivityCount: number;
|
||||
skippedActivityCount: number;
|
||||
executedActivityCount: number;
|
||||
plannedPoints: number;
|
||||
actualPoints: number;
|
||||
pointDifference: number;
|
||||
};
|
||||
|
||||
export function calculateDayOverviewSnapshot(
|
||||
activities: ActivityOverviewInput[],
|
||||
): DayOverviewSnapshot {
|
||||
return activities.reduce<DayOverviewSnapshot>(
|
||||
(snapshot, activity) => {
|
||||
snapshot.totalActivities += 1;
|
||||
|
||||
if (activity.source === "planned") {
|
||||
snapshot.plannedActivityCount += 1;
|
||||
// Planned points should reflect the intended activity load, regardless of later status.
|
||||
snapshot.plannedPoints += deriveActivityEnergyPoints({
|
||||
durationMinutes: activity.durationMinutes,
|
||||
impactLevel: activity.impactLevel,
|
||||
});
|
||||
} else {
|
||||
snapshot.adHocActivityCount += 1;
|
||||
}
|
||||
|
||||
if (activity.status === "planned") {
|
||||
snapshot.openActivityCount += 1;
|
||||
} else if (activity.status === "completed") {
|
||||
snapshot.completedActivityCount += 1;
|
||||
snapshot.executedActivityCount += 1;
|
||||
snapshot.actualPoints += deriveActivityEnergyPoints(activity);
|
||||
} else if (activity.status === "adjusted") {
|
||||
snapshot.adjustedActivityCount += 1;
|
||||
snapshot.executedActivityCount += 1;
|
||||
snapshot.actualPoints += deriveActivityEnergyPoints(activity);
|
||||
} else if (activity.status === "skipped") {
|
||||
snapshot.skippedActivityCount += 1;
|
||||
}
|
||||
|
||||
snapshot.pointDifference = snapshot.actualPoints - snapshot.plannedPoints;
|
||||
return snapshot;
|
||||
},
|
||||
{
|
||||
totalActivities: 0,
|
||||
plannedActivityCount: 0,
|
||||
adHocActivityCount: 0,
|
||||
openActivityCount: 0,
|
||||
completedActivityCount: 0,
|
||||
adjustedActivityCount: 0,
|
||||
skippedActivityCount: 0,
|
||||
executedActivityCount: 0,
|
||||
plannedPoints: 0,
|
||||
actualPoints: 0,
|
||||
pointDifference: 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue