101 lines
2.2 KiB
TypeScript
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,
|
|
};
|
|
}
|