export interface WorkEffortItem { estimateHours: number; actualHours: number; progress: number; } export interface WorkEffortSummary { estimateHours: number; actualHours: number; progress: number; } export function roundHalfHour(hours: number): number { if (!Number.isFinite(hours) || hours <= 0) return 0; return Math.round(hours * 2) / 2; } export function roundEstimateHours(hours: number): number { if (!Number.isFinite(hours) || hours <= 0) return 0; return Number(hours.toFixed(2)); } export function aggregateWorkEffort(items: WorkEffortItem[]): WorkEffortSummary { if (items.length === 0) return { estimateHours: 0, actualHours: 0, progress: 0 }; const estimateHours = roundEstimateHours(items.reduce((sum, item) => sum + Math.max(0, item.estimateHours || 0), 0)); const actualHours = roundHalfHour(items.reduce((sum, item) => sum + Math.max(0, item.actualHours || 0), 0)); if (estimateHours <= 0) { return { estimateHours, actualHours, progress: Math.round(items.reduce((sum, item) => sum + item.progress, 0) / items.length), }; } const weightedProgress = items.reduce((sum, item) => { const estimate = Math.max(0, item.estimateHours || 0); return sum + estimate * item.progress; }, 0); return { estimateHours, actualHours, progress: Math.round(weightedProgress / estimateHours), }; }