/** iOS-like day timeline: hour grid, now line, colored blocks. */ import type { ScheduledBlock } from "../api"; const DAY_START = 6; const DAY_END = 23; const HOUR_PX = 64; export function parseMinutes(hhmm: string): number { const [h, m] = hhmm.split(":").map(Number); return h * 60 + m; } export function timelineMetrics() { const startMin = DAY_START * 60; const endMin = DAY_END * 60; return { startMin, endMin, hourPx: HOUR_PX, totalPx: ((endMin - startMin) / 60) * HOUR_PX, }; } function blockClass(block: ScheduledBlock): string { const parts = ["timeline-block"]; if (block.priority === "P0" || block.locked) parts.push("p0"); else if (block.intent === "explore") parts.push("explore"); else if (block.intent === "restore_fun") parts.push("restore"); else if (block.kind === "stabilize") parts.push("stabilize"); else if (block.priority === "P1") parts.push("duty"); else parts.push("flex"); if (block.status === "done" || block.status === "partial") parts.push("done"); if (block.status === "skipped" || block.status === "cancelled") parts.push("skipped"); return parts.join(" "); } type Props = { blocks: ScheduledBlock[]; onSelect: (block: ScheduledBlock) => void; now?: Date; }; export function Timeline({ blocks, onSelect, now = new Date() }: Props) { const { startMin, endMin, hourPx, totalPx } = timelineMetrics(); const hours: number[] = []; for (let h = DAY_START; h <= DAY_END; h += 1) hours.push(h); const nowMin = now.getHours() * 60 + now.getMinutes(); const showNow = nowMin >= startMin && nowMin <= endMin; const nowTop = ((nowMin - startMin) / 60) * hourPx; return (