"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 (
{segments.map((s, i) => { const frac = s.value / sum; const len = on ? frac * c : 0; const dash = `${len} ${c - len}`; const el = ( ); offset += frac; return el; })}
{total}
{label}
); } /* ---- 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 (
); }