"use client";
import React from "react";
import { BRAND_600 } from "@/lib/theme";
export type TrendDatum = { label: string; value: number };
// Dependency-free single-series area/line chart, following the same
// hand-rolled-SVG approach as reports/charts/HorizontalBarChart.tsx. A
// single continuous series over time calls for a line/area (dataviz skill
// form heuristic), so this uses one brand-consistent color rather than the
// bar chart's multi-category palette.
const WIDTH = 600;
const HEIGHT = 220;
const PADDING_LEFT = 48; // room for y-axis value labels
const PADDING_RIGHT = 8;
const PADDING_TOP = 12;
const PADDING_BOTTOM = 24;
const GRIDLINES = 4;
const defaultAxisFormatter = (v: number) => new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 }).format(v);
export function AreaTrendChart({
data,
valueFormatter,
axisFormatter,
}: {
data: TrendDatum[];
/** Formats the headline "Latest" value — full precision is fine here. */
valueFormatter?: (v: number) => string;
/** Formats the y-axis gridline labels — should stay compact (little horizontal room). Defaults to a compact number (e.g. "1.5k"). */
axisFormatter?: (v: number) => string;
}) {
const fmt = valueFormatter || ((v: number) => String(v));
const axisFmt = axisFormatter || defaultAxisFormatter;
if (data.length === 0) {
return
No data to chart.
;
}
const max = Math.max(1, ...data.map(d => d.value));
const min = Math.min(0, ...data.map(d => d.value));
const range = max - min || 1;
const plotWidth = WIDTH - PADDING_LEFT - PADDING_RIGHT;
const plotHeight = HEIGHT - PADDING_TOP - PADDING_BOTTOM;
const baseline = PADDING_TOP + plotHeight;
const points = data.map((d, i) => {
const x = data.length === 1 ? PADDING_LEFT + plotWidth / 2 : PADDING_LEFT + (i / (data.length - 1)) * plotWidth;
const y = PADDING_TOP + plotHeight - ((d.value - min) / range) * plotHeight;
return { x, y, value: d.value };
});
const linePath = points.map((p, i) => (i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`)).join(" ");
const areaPath = `${linePath} L ${points[points.length - 1].x} ${baseline} L ${points[0].x} ${baseline} Z`;
// Horizontal gridlines from 0 up to the max, evenly spaced, with a value label on each.
const gridlines = Array.from({ length: GRIDLINES + 1 }, (_, i) => {
const value = min + (range * i) / GRIDLINES;
const y = PADDING_TOP + plotHeight - (i / GRIDLINES) * plotHeight;
return { y, value };
});
// Show at most ~6 x-axis labels so long series don't crowd the axis.
const labelStep = Math.max(1, Math.ceil(data.length / 6));
const visibleLabels = data.filter((_, i) => i % labelStep === 0 || i === data.length - 1);
const latest = data[data.length - 1];
return (