'use client'; import { useState } from 'react'; export interface DayValue { date: string; // YYYY-MM-DD value: number; } interface Props { data: DayValue[]; days?: number; } function toLocalDateStr(d: Date): string { return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; } const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; export default function HistoryChart({ data, days = 90 }: Props) { const [hovered, setHovered] = useState(null); const byDate = new Map(data.map(d => [d.date, d.value])); const today = new Date(); const filled = Array.from({ length: days }, (_, i) => { const d = new Date(today); d.setDate(d.getDate() - (days - 1 - i)); const date = toLocalDateStr(d); return { date, value: byDate.get(date) ?? 0, month: d.getMonth() }; }); const maxVal = Math.max(...filled.map(d => d.value), 1); const CHART_H = 40; const BAR_UNIT = 100 / days; // Show a month label whenever the month changes const monthLabels: { label: string; pct: number }[] = []; filled.forEach((d, i) => { if (i === 0 || d.month !== filled[i - 1].month) { monthLabels.push({ label: MONTH_ABBR[d.month], pct: (i / days) * 100 }); } }); const hoveredDay = hovered !== null ? filled[hovered] : null; return (
{/* Tooltip */} {hoveredDay && hovered !== null && (
{hoveredDay.value > 0 ? `+${hoveredDay.value}` : '–'} {hoveredDay.date}
)} {filled.map((d, i) => { const barH = d.value > 0 ? Math.max(1.5, (d.value / maxVal) * (CHART_H - 2)) : 1; const x = i * BAR_UNIT + BAR_UNIT * 0.1; const w = BAR_UNIT * 0.8; const y = d.value > 0 ? CHART_H - barH : CHART_H - 1; return ( 0 ? 'fill-ctp-mauve' : 'fill-ctp-surface1'} style={{ opacity: hovered === i ? 0.6 : 1 }} onMouseEnter={() => setHovered(i)} onMouseLeave={() => setHovered(null)} /> ); })}
{/* Month labels */}
{monthLabels.map(({ label, pct }) => ( {label} ))}
); }