fix: remove redundant top-row heatmaps, keep labeled status rows only
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { format, startOfWeek, addDays, addWeeks, parseISO, isToday } from 'date-fns'
|
import { format, startOfWeek, addDays, addWeeks, isToday } from 'date-fns'
|
||||||
import useProjectStore from '../../store/useProjectStore'
|
import useProjectStore from '../../store/useProjectStore'
|
||||||
import useFocusStore from '../../store/useFocusStore'
|
import useFocusStore from '../../store/useFocusStore'
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ export default function WorkloadHeatmap() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-surface overflow-auto">
|
<div className="flex flex-col h-full bg-surface overflow-auto">
|
||||||
{/* Header with spacing that clears FABDASH corner logo */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between px-8 pt-6 pb-4">
|
<div className="flex items-center justify-between px-8 pt-6 pb-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-gold font-bold text-lg tracking-wide">Workload Heatmap</h2>
|
<h2 className="text-gold font-bold text-lg tracking-wide">Workload Heatmap</h2>
|
||||||
@@ -92,96 +92,7 @@ export default function WorkloadHeatmap() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stat cards + aligned status heatmaps */}
|
{/* Status heatmaps */}
|
||||||
<div className="grid grid-cols-4 gap-4 px-8 pb-8">
|
|
||||||
{STATUS_KEYS.map((statusKey) => (
|
|
||||||
<div key={statusKey} className="flex flex-col gap-3">
|
|
||||||
{/* Stat card */}
|
|
||||||
<div className="bg-surface-elevated border border-surface-border rounded-xl p-4 text-center">
|
|
||||||
<p className={`text-2xl font-bold ${STATUS_COLOR[statusKey]}`}>
|
|
||||||
{stats[statusKey]}
|
|
||||||
</p>
|
|
||||||
<p className="text-text-muted text-xs mt-1">{STATUS_LABEL[statusKey]}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Heatmap for this status */}
|
|
||||||
<div className="bg-surface-elevated/40 border border-surface-border rounded-xl p-3 flex-1 min-h-[160px]">
|
|
||||||
<div className="flex gap-2 overflow-x-auto pb-1">
|
|
||||||
{/* Day labels */}
|
|
||||||
<div className="flex flex-col flex-shrink-0 pt-6" style={{ gap: GAP }}>
|
|
||||||
{DAY_INIT.map((d, i) => (
|
|
||||||
<div key={i} style={{ height: CELL }} className="flex items-center text-[9px] text-text-muted/50 font-mono w-3">
|
|
||||||
{d}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Grid */}
|
|
||||||
<div className="flex flex-col flex-shrink-0">
|
|
||||||
{/* Month labels */}
|
|
||||||
<div className="relative h-4 mb-1">
|
|
||||||
{monthLabels.map(({ wi, label }) => (
|
|
||||||
<span
|
|
||||||
key={label+wi}
|
|
||||||
className="absolute text-[9px] text-text-muted/60 font-medium"
|
|
||||||
style={{ left: wi * (CELL + GAP) }}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex" style={{ gap: GAP }}>
|
|
||||||
{weeks.map((week, wi) => (
|
|
||||||
<div key={wi} className="flex flex-col flex-shrink-0" style={{ gap: GAP }}>
|
|
||||||
{week.map(({ date, key, items, statusCounts }) => {
|
|
||||||
const countForStatus = (statusCounts || {})[statusKey] || 0
|
|
||||||
const baseDensity = countForStatus
|
|
||||||
const hoverRing =
|
|
||||||
statusKey === 'upcoming' ? 'hover:ring-blue-300/80 hover:shadow-[0_0_0_1px_rgba(147,197,253,0.8)]' :
|
|
||||||
statusKey === 'in_progress' ? 'hover:ring-amber-300/80 hover:shadow-[0_0_0_1px_rgba(252,211,77,0.8)]' :
|
|
||||||
statusKey === 'completed' ? 'hover:ring-green-300/80 hover:shadow-[0_0_0_1px_rgba(74,222,128,0.8)]' :
|
|
||||||
statusKey === 'overdue' ? 'hover:ring-red-400/90 hover:shadow-[0_0_0_1px_rgba(248,113,113,0.9)]' :
|
|
||||||
'hover:ring-white/60';
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={key + statusKey}
|
|
||||||
style={{ width: CELL, height: CELL }}
|
|
||||||
className={`rounded-sm border cursor-pointer transition-transform duration-100 hover:scale-125 hover:z-10 relative
|
|
||||||
${getCellClass(baseDensity, statusCounts || {})}
|
|
||||||
${isToday(date) ? 'ring-1 ring-white/60' : ''}
|
|
||||||
${hoverRing}
|
|
||||||
`}
|
|
||||||
onClick={() => {
|
|
||||||
if (!items || !items.length) return
|
|
||||||
const match = items.find(({ deliverable }) => deliverable.status === statusKey) || items[0]
|
|
||||||
if (match) openFocus(match.project.id, match.deliverable.id)
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => {
|
|
||||||
const filtered = (items || []).filter(({ deliverable }) => deliverable.status === statusKey)
|
|
||||||
const showItems = filtered.length ? filtered : items || []
|
|
||||||
setTooltip({
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
date,
|
|
||||||
statusKey,
|
|
||||||
items: showItems,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onMouseLeave={() => setTooltip(null)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{/* Multi-row heatmaps by status */}
|
|
||||||
<div className="flex flex-col gap-6 px-8 pb-8">
|
<div className="flex flex-col gap-6 px-8 pb-8">
|
||||||
{STATUS_KEYS.map((statusKey) => (
|
{STATUS_KEYS.map((statusKey) => (
|
||||||
<div key={statusKey} className="bg-surface-elevated/40 border border-surface-border rounded-xl p-4">
|
<div key={statusKey} className="bg-surface-elevated/40 border border-surface-border rounded-xl p-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user