87 lines · 4.0 KB
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 (
    <div className="space-y-3 px-4 py-4">
      <div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-kumo-subtle">
        <span className="flex items-center gap-1.5">
          <span aria-hidden className="size-2.5 rounded-sm bg-accent-500" />
          <span className="text-kumo-default">{def?.label ?? meter}</span>
          <span>per day</span>
        </span>
        <span>
          Total <span className="font-mono text-kumo-default">{formatQuantity(unit, total)}</span>
        </span>
        {max > 0 && peak && (