import { useEffect, useState } from "react"; import { formatQuantity, meterDef } from "~/lib/meters"; import type { MeterDef } from "~/lib/meters"; type Day = { day: string; meter: string; quantity: number }; function daysOf(period: string): string[] { const [year, month] = period.split("-").map(Number); const count = new Date(Date.UTC(year, month, 0)).getUTCDate(); return Array.from({ length: count }, (_, index) => `${period}-${String(index + 1).padStart(2, "0")}`); } const LABEL_DAYS = new Set([1, 8, 15, 22]); /** * Daily usage of one meter across the whole billing period: one slot per calendar day, so a single day of usage * is a single bar instead of a full-width block. Includes a legend, a y-axis maximum and day labels. */ export function UsageChart({ days, meter, period }: { days: Day[]; meter: string; period: string }) { const def = meterDef(meter); const unit: MeterDef["unit"] = def?.unit ?? "count"; const byDay = new Map(days.filter((day) => day.meter === meter).map((day) => [day.day, day.quantity])); const slots = daysOf(period).map((day) => ({ day, quantity: byDay.get(day) ?? 0 })); const total = slots.reduce((sum, slot) => sum + slot.quantity, 0); const peak = slots.reduce((best, slot) => (slot.quantity > best.quantity ? slot : best), slots[0]); const max = Math.max(peak?.quantity ?? 0, 0); const [today, setToday] = useState("9999-12-31"); useEffect(() => setToday(new Date().toISOString().slice(0, 10)), []); return (
{def?.label ?? meter} per day Total {formatQuantity(unit, total)} {max > 0 && peak && ( Peak {formatQuantity(unit, max)} on {peak.day.slice(5)} )}
{total === 0 ? (

No usage recorded for this meter in {period}.

) : (
{formatQuantity(unit, max)} 0
{slots.map((slot) => (
0 ? "w-full max-w-4 rounded-t-sm bg-accent-500 hover:bg-accent-400" : slot.day > today ? "h-px w-full max-w-4 bg-transparent" : "h-px w-full max-w-4 bg-kumo-interact" } style={slot.quantity > 0 ? { height: `${Math.max(2, (slot.quantity / max) * 100)}%` } : undefined} />
))}
{slots.map((slot, index) => ( {LABEL_DAYS.has(index + 1) || index === slots.length - 1 ? index + 1 : ""} ))}
)}
); }