"use client";
import { useEffect, useState } from "react";
/* ---- vertical bar chart (HTML/CSS, animated heights) ---- */
export function BarsChart({
labels,
series,
max,
yTicks,
height = 132,
highlight,
barWidth = 7,
}: {
labels: string[];
series: { color: string; values: number[] }[];
max: number;
yTicks?: string[];
height?: number;
highlight?: number; // single-series: index to highlight, others muted
barWidth?: number;
}) {
const [on, setOn] = useState(false);
useEffect(() => {
const t = setTimeout(() => setOn(true), 40);
return () => clearTimeout(t);
}, []);
return (
{yTicks && (
{yTicks.map((t) => {t})}
)}
{labels.map((lab, i) => (
{series.map((s, si) => {
const muted = highlight !== undefined && i !== highlight;
return (
);
})}
{lab}
))}
);
}
/* ---- mini sparkline bars (stat cards) ---- */
export function Spark({ values, color, height = 40, width = 8 }: { values: number[]; color: string; height?: number; width?: number }) {
const [on, setOn] = useState(false);
useEffect(() => { const t = setTimeout(() => setOn(true), 40); return () => clearTimeout(t); }, []);
const max = Math.max(...values, 1);
return (
{values.map((v, i) => (
))}
);
}
/* ---- donut chart ---- */
export function Donut({
segments, total, label, size = 150, thickness = 18,
}: {
segments: { value: number; color: string }[];
total: string;
label: string;
size?: number;
thickness?: number;
}) {
const [on, setOn] = useState(false);
useEffect(() => { const t = setTimeout(() => setOn(true), 60); return () => clearTimeout(t); }, []);
const r = (size - thickness) / 2;
const c = 2 * Math.PI * r;
const sum = segments.reduce((a, s) => a + s.value, 0) || 1;
let offset = 0;
return (
);
}
/* ---- semicircle gauge ---- */
export function Gauge({
value, max = 100, color, size = 150, label, sub,
}: {
value: number; max?: number; color: string; size?: number; label?: string; sub?: string;
}) {
const [on, setOn] = useState(false);
useEffect(() => { const t = setTimeout(() => setOn(true), 60); return () => clearTimeout(t); }, []);
const sw = 14;
const r = (size - sw) / 2;
const cx = size / 2, cy = size / 2;
const semi = Math.PI * r; // half circumference
const frac = Math.min(1, value / max);
const len = on ? frac * semi : 0;
return (
{(label || sub) && (
{label &&
{label}
}
{sub &&
{sub}
}
)}
);
}
/* ---- progress bar ---- */
export function Progress({ value, color, track }: { value: number; color: string; track?: boolean }) {
const [on, setOn] = useState(false);
useEffect(() => { const t = setTimeout(() => setOn(true), 50); return () => clearTimeout(t); }, []);
return (
);
}
/* ---- horizontal segmented bar (pipeline) ---- */
export function HBar({ value, max, color }: { value: number; max: number; color: string }) {
const [on, setOn] = useState(false);
useEffect(() => { const t = setTimeout(() => setOn(true), 50); return () => clearTimeout(t); }, []);
return (
);
}