diff --git a/frontend/src/components/Calendar/WorkloadHeatmap.jsx b/frontend/src/components/Calendar/WorkloadHeatmap.jsx index 3cd383f..27226fa 100644 --- a/frontend/src/components/Calendar/WorkloadHeatmap.jsx +++ b/frontend/src/components/Calendar/WorkloadHeatmap.jsx @@ -1,16 +1,30 @@ import { useMemo, useState } from 'react' -import { format, startOfWeek, addDays, addWeeks, isSameDay, parseISO, isToday } from 'date-fns' +import { format, startOfWeek, addDays, addWeeks, parseISO, isToday } from 'date-fns' import useProjectStore from '../../store/useProjectStore' import useFocusStore from '../../store/useFocusStore' -import Badge from '../UI/Badge' -const WEEKS = 20 -const DAY_INIT = ['M','T','W','T','F','S','S'] +const WEEKS = 20 +const DAY_INIT = ['M','T','W','T','F','S','S'] -function getCellStyle(count) { - if (count === 0) return 'bg-surface border-surface-border' - if (count === 1) return 'bg-gold/25 border-gold/40' - if (count === 2) return 'bg-gold/55 border-gold/70' +const STATUS_KEYS = ['upcoming','in_progress','completed','overdue'] +const STATUS_LABEL = { + upcoming: 'Upcoming', + in_progress: 'In Progress', + completed: 'Completed', + overdue: 'Overdue', +} +const STATUS_COLOR = { + upcoming: 'text-blue-400', + in_progress: 'text-amber-400', + completed: 'text-green-400', + overdue: 'text-red-400', +} + +function getCellClass(baseDensity, statusCounts) { + const total = Object.values(statusCounts).reduce((a, b) => a + b, 0) + if (total === 0) return 'bg-surface border-surface-border' + if (baseDensity === 1) return 'bg-gold/25 border-gold/40' + if (baseDensity === 2) return 'bg-gold/55 border-gold/70' return 'bg-gold border-gold shadow-gold' } @@ -22,10 +36,14 @@ export default function WorkloadHeatmap() { const { weeks, stats } = useMemo(() => { const start = startOfWeek(addWeeks(new Date(), -10), { weekStartsOn: 1 }) const map = {} + projects.forEach(p => { (p.deliverables || []).forEach(d => { - if (!map[d.due_date]) map[d.due_date] = [] - map[d.due_date].push({ deliverable: d, project: p }) + const key = d.due_date + if (!map[key]) map[key] = { items: [], statusCounts: { upcoming: 0, in_progress: 0, completed: 0, overdue: 0 } } + map[key].items.push({ deliverable: d, project: p }) + const s = (d.status || 'upcoming') + if (map[key].statusCounts[s] !== undefined) map[key].statusCounts[s]++ }) }) @@ -33,17 +51,18 @@ export default function WorkloadHeatmap() { Array.from({ length: 7 }, (_, di) => { const date = addDays(start, wi * 7 + di) const key = format(date, 'yyyy-MM-dd') - return { date, key, items: map[key] || [] } + const entry = map[key] || { items: [], statusCounts: { upcoming: 0, in_progress: 0, completed: 0, overdue: 0 } } + return { date, key, ...entry } }) ) const all = projects.flatMap(p => p.deliverables || []) const stats = { total: all.length, - overdue: all.filter(d => d.status === 'overdue').length, + upcoming: all.filter(d => d.status === 'upcoming').length, in_progress: all.filter(d => d.status === 'in_progress').length, completed: all.filter(d => d.status === 'completed').length, - upcoming: all.filter(d => d.status === 'upcoming').length, + overdue: all.filter(d => d.status === 'overdue').length, } return { weeks: grid, stats } }, [projects]) @@ -57,90 +76,138 @@ export default function WorkloadHeatmap() { return labels }, [weeks]) - const CELL = 20, GAP = 3 + const CELL = 18 + const GAP = 3 return ( -
{WEEKS} weeks of deliverable density
+20 weeks of deliverable density by status
{value}
+ { key: 'total', label: 'Total', color: 'text-text-primary' }, + { key: 'upcoming', label: 'Upcoming', color: 'text-blue-400' }, + { key: 'in_progress', label: 'In Progress', color: 'text-amber-400' }, + { key: 'completed', label: 'Completed', color: 'text-green-400' }, + { key: 'overdue', label: 'Overdue', color: 'text-red-400' }, + ].map(({ key, label, color }) => ( +{stats[key]}
{label}
{isToday(tooltip.date) ? 'Today — ' : ''}{format(tooltip.date, 'EEE, MMM d, yyyy')}
++ {STATUS_LABEL[tooltip.statusKey]} · {tooltip.items.length} task{tooltip.items.length !== 1 ? 's' : ''} +
{tooltip.items.length === 0 ? (No deliverables
) : ( @@ -161,6 +228,7 @@ export default function WorkloadHeatmap() { )}