From 3e21125a6b5c8868f4708f02f0cadfe24e7f1611 Mon Sep 17 00:00:00 2001 From: jasonMPM Date: Fri, 6 Mar 2026 00:28:33 -0600 Subject: [PATCH] Add files via upload --- .../components/Calendar/HeatmapDayPanel.jsx | 63 ++++++++----------- .../src/components/Calendar/MainCalendar.jsx | 11 ++-- .../components/Calendar/WorkloadHeatmap.jsx | 38 +++++------ 3 files changed, 45 insertions(+), 67 deletions(-) diff --git a/frontend/src/components/Calendar/HeatmapDayPanel.jsx b/frontend/src/components/Calendar/HeatmapDayPanel.jsx index 03d2edc..300b8d8 100644 --- a/frontend/src/components/Calendar/HeatmapDayPanel.jsx +++ b/frontend/src/components/Calendar/HeatmapDayPanel.jsx @@ -6,16 +6,21 @@ import useUIStore from '../../store/useUIStore' import { updateDeliverable as apiUpdate } from '../../api/deliverables' import DeliverableModal from '../Deliverables/DeliverableModal' -const STATUS_KEYS = ['overdue', 'in_progress', 'upcoming', 'completed'] -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' } -const STATUS_BG = { upcoming: 'bg-blue-400/10 border-blue-400/30 hover:bg-blue-400/20', in_progress: 'bg-amber-400/10 border-amber-400/30 hover:bg-amber-400/20', completed: 'bg-green-400/10 border-green-400/30 hover:bg-green-400/20', overdue: 'bg-red-400/10 border-red-400/30 hover:bg-red-400/20' } -const STATUS_CYCLE = { upcoming: 'in_progress', in_progress: 'completed', completed: 'upcoming', overdue: 'in_progress' } +const STATUS_KEYS = ['overdue', 'in_progress', 'upcoming', 'completed'] +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' } +const STATUS_BG = { + upcoming: 'bg-blue-400/10 border-blue-400/30 hover:bg-blue-400/20', + in_progress: 'bg-amber-400/10 border-amber-400/30 hover:bg-amber-400/20', + completed: 'bg-green-400/10 border-green-400/30 hover:bg-green-400/20', + overdue: 'bg-red-400/10 border-red-400/30 hover:bg-red-400/20', +} +const STATUS_CYCLE = { upcoming: 'in_progress', in_progress: 'completed', completed: 'upcoming', overdue: 'in_progress' } export default function HeatmapDayPanel({ date, onClose }) { - const projects = useProjectStore(s => s.projects) + const projects = useProjectStore(s => s.projects) const storeUpdate = useProjectStore(s => s.updateDeliverable) - const openFocus = useFocusStore(s => s.openFocus) + const openFocus = useFocusStore(s => s.openFocus) const { jumpToCalendarDate } = useUIStore() const [addModal, setAddModal] = useState(false) @@ -23,7 +28,7 @@ export default function HeatmapDayPanel({ date, onClose }) { const dateStr = format(date, 'yyyy-MM-dd') - // Derive items live from store so status cycles update instantly + // Derive live from store so status cycles re-render instantly const items = projects.flatMap(p => (p.deliverables || []) .filter(d => d.due_date === dateStr) @@ -48,27 +53,19 @@ export default function HeatmapDayPanel({ date, onClose }) { } } - const handleJumpToCalendar = () => { - jumpToCalendarDate(dateStr) - onClose() - } - return ( <> {/* Backdrop */} -
+
{/* Slide-in panel */} -
+
{/* Header */}

- {isToday(date) ? '\u2605 Today' : format(date, 'EEEE')} + {isToday(date) ? '★ Today' : format(date, 'EEEE')}

{format(date, 'MMMM d, yyyy')}

@@ -77,17 +74,18 @@ export default function HeatmapDayPanel({ date, onClose }) {

@@ -106,14 +104,13 @@ export default function HeatmapDayPanel({ date, onClose }) {
{items.length === 0 ? (
-
\u25a1
+

Nothing scheduled

Click + Schedule above to add one

) : ( STATUS_KEYS.filter(k => grouped[k].length > 0).map(statusKey => (
- {/* Status group header */}
{STATUS_LABEL[statusKey]} @@ -129,19 +126,11 @@ export default function HeatmapDayPanel({ date, onClose }) { key={deliverable.id} className="flex items-center gap-2 bg-surface rounded-lg px-2.5 py-2 border border-surface-border hover:border-gold/20 transition-colors group" > - {/* Project color dot */} -
- - {/* Title + project */} +

{deliverable.title}

{project.name}

- - {/* Actions */}
{/* Cycle status */} {/* Open Focus View */}
@@ -169,10 +158,10 @@ export default function HeatmapDayPanel({ date, onClose }) { )}
- {/* Footer: cycle legend */} + {/* Footer legend */}

- \u27f3 cycles status  \u00b7  \u25ce opens Focus View  \u00b7  \u2192 Calendar jumps to date + ⟳ cycles status  ·  ◎ opens Focus View  ·  → Calendar jumps to date

diff --git a/frontend/src/components/Calendar/MainCalendar.jsx b/frontend/src/components/Calendar/MainCalendar.jsx index fa021bb..a54b9ea 100644 --- a/frontend/src/components/Calendar/MainCalendar.jsx +++ b/frontend/src/components/Calendar/MainCalendar.jsx @@ -69,7 +69,6 @@ export default function MainCalendar({ onCalendarReady }) { openFocus(projectId, deliverableId) }, [openFocus]) - // Drag-and-drop with 30-second undo toast const handleEventDrop = useCallback(async ({ event, oldEvent }) => { const { deliverableId } = event.extendedProps const newDate = event.startStr.substring(0, 10) @@ -117,11 +116,11 @@ export default function MainCalendar({ onCalendarReady }) { setContextMenu({ x: e.clientX, y: e.clientY, items: [ - { icon: '\u2714\ufe0e', label: 'Edit Deliverable', action: () => setModal({ open: true, deliverable, defaultDate: '' }) }, - { icon: '\u2756', label: 'Open Focus View', action: () => openFocus(projectId, deliverableId) }, - ...(project?.drive_url ? [{ icon: '\u2b21', label: 'Open Drive Folder', action: () => window.open(project.drive_url, '_blank') }] : []), + { icon: '✔︎', label: 'Edit Deliverable', action: () => setModal({ open: true, deliverable, defaultDate: '' }) }, + { icon: '❖', label: 'Open Focus View', action: () => openFocus(projectId, deliverableId) }, + ...(project?.drive_url ? [{ icon: '⬡', label: 'Open Drive Folder', action: () => window.open(project.drive_url, '_blank') }] : []), { separator: true }, - { icon: '\u2715', label: 'Delete Deliverable', danger: true, + { icon: '✕', label: 'Delete Deliverable', danger: true, action: async () => { if (window.confirm(`Delete "${deliverable.title}"?`)) { await deleteDeliverable(deliverableId) @@ -147,7 +146,7 @@ export default function MainCalendar({ onCalendarReady }) { : 'bg-surface-elevated border-surface-border text-text-muted hover:border-gold/40 hover:text-gold' }`} > - {showHeatmap ? '\u2190 Calendar' : '\u2b21 Heatmap'} + {showHeatmap ? '← Calendar' : '⬡ Heatmap'}
diff --git a/frontend/src/components/Calendar/WorkloadHeatmap.jsx b/frontend/src/components/Calendar/WorkloadHeatmap.jsx index 73e8a94..ae67bfe 100644 --- a/frontend/src/components/Calendar/WorkloadHeatmap.jsx +++ b/frontend/src/components/Calendar/WorkloadHeatmap.jsx @@ -3,12 +3,12 @@ import { format, startOfWeek, addDays, addWeeks, isToday } from 'date-fns' import useProjectStore from '../../store/useProjectStore' import HeatmapDayPanel from './HeatmapDayPanel' -const WEEKS = 20 +const WEEKS = 20 const DAY_INIT = ['M','T','W','T','F','S','S'] -const CELL = 16 -const CELL_LG = 40 -const GAP = 2 -const GAP_LG = 4 +const CELL = 16 +const CELL_LG = 40 +const GAP = 2 +const GAP_LG = 4 const STATUS_KEYS = ['upcoming','in_progress','completed','overdue'] const STATUS_LABEL = { upcoming: 'Upcoming', in_progress: 'In Progress', completed: 'Completed', overdue: 'Overdue' } @@ -49,7 +49,6 @@ function getDominantStatus(statusCounts) { return { dominant, total: Object.values(statusCounts).reduce((a, b) => a + b, 0) } } -// 4-cell density legend strip function DensityLegend({ statusKey }) { const c = STATUS_CELL_COLORS[statusKey] return ( @@ -67,8 +66,8 @@ function DensityLegend({ statusKey }) { export default function WorkloadHeatmap() { const projects = useProjectStore(s => s.projects) const [tooltip, setTooltip] = useState(null) - const [selectedDay, setSelectedDay] = useState(null) // Date | null - const [weekOffset, setWeekOffset] = useState(0) // shifts window in weeks + const [selectedDay, setSelectedDay] = useState(null) + const [weekOffset, setWeekOffset] = useState(0) const { weeks, stats } = useMemo(() => { const start = startOfWeek(addWeeks(new Date(), -10 + weekOffset), { weekStartsOn: 1 }) @@ -107,7 +106,7 @@ export default function WorkloadHeatmap() { const windowStart = weeks[0]?.[0]?.date const windowEnd = weeks[WEEKS - 1]?.[6]?.date const windowLabel = windowStart && windowEnd - ? `${format(windowStart, 'MMM d')} \u2013 ${format(windowEnd, 'MMM d, yyyy')}` + ? `${format(windowStart, 'MMM d')} – ${format(windowEnd, 'MMM d, yyyy')}` : '' const monthLabels = useMemo(() => { @@ -131,20 +130,18 @@ export default function WorkloadHeatmap() { return (
- {/* Header */} + {/* Header + window navigation */}

Workload Heatmap

20 weeks of deliverable density by status

- - {/* Window navigation */}
+ >← 4 wks + >4 wks → {windowLabel}
@@ -170,27 +167,21 @@ export default function WorkloadHeatmap() {
{STATUS_KEYS.map((statusKey) => (
- - {/* Stat card */}

{stats[statusKey]}

{STATUS_LABEL[statusKey]}

- - {/* Mini heatmap */}
- {/* Day initials */}
{DAY_INIT.map((d, i) => (
{d}
))}
- {/* Grid */}
{monthLabels.map(({ wi, label }) => ( @@ -247,10 +238,9 @@ export default function WorkloadHeatmap() { {STATUS_LABEL[sk]}
))} - \u00b7 color = highest priority + · color = highest priority
-
{DAY_INIT.map((d, i) => ( @@ -301,7 +291,7 @@ export default function WorkloadHeatmap() { style={{ left: Math.min(tooltip.x + 14, window.innerWidth - 330), top: Math.max(tooltip.y - 100, 8) }} >

- {isToday(tooltip.date) ? 'Today \u2014 ' : ''}{format(tooltip.date, 'EEE, MMM d, yyyy')} + {isToday(tooltip.date) ? 'Today — ' : ''}{format(tooltip.date, 'EEE, MMM d, yyyy')}

{tooltip.combined && tooltip.statusCounts ? (
@@ -314,7 +304,7 @@ export default function WorkloadHeatmap() {
) : (

- {tooltip.statusKey ? STATUS_LABEL[tooltip.statusKey] : ''} \u00b7 {tooltip.items.length} task{tooltip.items.length !== 1 ? 's' : ''} + {tooltip.statusKey ? STATUS_LABEL[tooltip.statusKey] : ''} · {tooltip.items.length} task{tooltip.items.length !== 1 ? 's' : ''}

)}