// Shared small SVG components for Raudhah KPI dashboard
const { useState, useEffect, useMemo, useRef } = React;
function Sparkline({ data, color = "var(--raudhah-pink)", fill = true, height = 34, strokeWidth = 1.6 }) {
const ref = useRef(null);
const [w, setW] = useState(120);
useEffect(() => {
if (!ref.current) return;
const ro = new ResizeObserver(() => setW(ref.current.clientWidth));
ro.observe(ref.current);
setW(ref.current.clientWidth || 120);
return () => ro.disconnect();
}, []);
const clean = data.filter(v => v != null);
if (!clean.length) return
;
const min = Math.min(...clean), max = Math.max(...clean);
const range = max - min || 1;
const n = data.length;
const pts = data.map((v, i) => {
if (v == null) return null;
const x = (i / (n - 1)) * (w - 4) + 2;
const y = height - 2 - ((v - min) / range) * (height - 4);
return [x, y];
});
const firstIdx = pts.findIndex(p => p);
let d = "";
let lastIdx = -1;
pts.forEach((p, i) => {
if (!p) return;
d += (lastIdx < 0 ? `M${p[0]},${p[1]}` : ` L${p[0]},${p[1]}`);
lastIdx = i;
});
const last = pts[lastIdx];
const fillPath = firstIdx >= 0 && last
? `${d} L${last[0]},${height} L${pts[firstIdx][0]},${height} Z`
: "";
return (
);
}
function BranchChip({ branch, size = "sm" }) {
if (!branch) return null;
const pad = size === "xs" ? "2px 5px" : "3px 6px";
const fs = size === "xs" ? 10 : 11;
return (
{branch.code}
);
}
function Ring({ value, target, size = 70, stroke = 7, color = "var(--raudhah-pink)", track = "var(--panel-sunken)", label = true }) {
const pct = Math.min(1.2, value / target);
const pctDisplay = Math.round((value / target) * 100);
const r = (size - stroke) / 2;
const c = 2 * Math.PI * r;
const offset = c * (1 - Math.min(1, pct));
return (
);
}
function Bullet({ label, value, target, target2, color = "var(--raudhah-pink)", unit = "", onClick }) {
const max = Math.max(target2 || 0, target, value) * 1.05;
const pct = (value / max) * 100;
const tickPct = (target / max) * 100;
const tick2Pct = target2 ? (target2 / max) * 100 : null;
const hit = value >= target;
return (
{label}
{value.toLocaleString()} / {target.toLocaleString()}{unit}
{" "}
{hit ? "HIT" : Math.round((value/target)*100) + "%"}
);
}
function Delta({ value, pct, unit = "", inverse = false }) {
const up = value > 0;
const flat = value === 0;
const cls = flat ? "flat" : (up ? (inverse ? "down" : "up") : (inverse ? "up" : "down"));
const arrow = flat ? "—" : (up ? "▲" : "▼");
return (
{arrow} {up ? "+" : ""}{value.toLocaleString()}{unit}
{pct != null && {pct > 0 ? "+" : ""}{pct}%}
);
}
function StatusPill({ kind, children }) {
return (
{children}
);
}
Object.assign(window, { Sparkline, BranchChip, Ring, Bullet, Delta, StatusPill });