inspannings-monitor/lib/planning/meter.ts

101 lines
2.2 KiB
TypeScript

import type {
ActivityImpactLevel,
ActivityRecord,
ActivityStatus,
} from "@/lib/planning/types";
export type PlanningMeterSnapshot = {
totalPoints: number;
activityCount: number;
dailyBudget: number | null;
remainingBudget: number | null;
progressRatio: number | null;
progressPercent: number | null;
isOverBudget: boolean;
};
type ActivityMeterInput = {
durationMinutes: number;
impactLevel: ActivityImpactLevel;
status?: ActivityStatus;
};
function deriveDurationBandPoints(durationMinutes: number) {
if (durationMinutes <= 15) {
return 1;
}
if (durationMinutes <= 45) {
return 2;
}
if (durationMinutes <= 90) {
return 3;
}
return 4;
}
function deriveImpactAdjustment(impactLevel: ActivityImpactLevel) {
if (impactLevel === "laag") {
return -1;
}
if (impactLevel === "hoog") {
return 1;
}
return 0;
}
export function deriveActivityEnergyPoints(input: ActivityMeterInput): number {
if (input.status === "skipped") {
return 0;
}
return Math.max(
1,
deriveDurationBandPoints(input.durationMinutes) + deriveImpactAdjustment(input.impactLevel),
);
}
export function calculateActivityPointsTotal(
activities: Pick<ActivityRecord, "durationMinutes" | "impactLevel" | "status">[],
): number {
return activities.reduce(
(total, activity) => total + deriveActivityEnergyPoints(activity),
0,
);
}
export function calculatePlanningMeterSnapshot(
activities: Pick<ActivityRecord, "durationMinutes" | "impactLevel" | "status">[],
dailyBudget: number | null,
): PlanningMeterSnapshot {
const totalPoints = calculateActivityPointsTotal(activities);
if (dailyBudget === null) {
return {
totalPoints,
activityCount: activities.length,
dailyBudget: null,
remainingBudget: null,
progressRatio: null,
progressPercent: null,
isOverBudget: false,
};
}
const remainingBudget = dailyBudget - totalPoints;
const progressRatio = dailyBudget > 0 ? totalPoints / dailyBudget : 0;
return {
totalPoints,
activityCount: activities.length,
dailyBudget,
remainingBudget,
progressRatio,
progressPercent: Math.min(100, Math.round(progressRatio * 100)),
isOverBudget: remainingBudget < 0,
};
}