k").
// A series may set { dash: true } for a dashed trend line and name '' to skip its label.
function TrendChart({ series, labels, height = 190, fmt }) {
const w = 760, h = height, padL = 44, padR = 96, padT = 14, padB = 24;
const axisFmt = fmt || (v => '$' + Math.round(v) + 'k');
const labelFmt = fmt || (v => '$' + v + 'k');
const all = series.flatMap(s => s.data);
const lo = Math.min(...all), hi = Math.max(...all);
const min = lo > 0 ? lo * 0.96 : lo, max = hi * 1.03;
const x = i => padL + (i / (series[0].data.length - 1)) * (w - padL - padR);
const y = v => padT + (1 - (v - min) / (max - min)) * (h - padT - padB);
const gridVals = [min, (min + max) / 2, max];
return (
);
}
function SectionHead({ title, action, onAction }) {
return (
{title}
{action ? : null}
);
}
function StatusBadge({ status }) {
const map = {
ok: ['ok', 'In stock'], low: ['warn', 'Low'], reorder: ['bad', 'Reorder now'], watch: ['neutral', 'Count due'],
};
const [tone, label] = map[status] || ['neutral', status];
return {label};
}
function Toast({ msg }) {
if (!msg) return null;
return {msg}
;
}
function useToast() {
const [msg, setMsg] = useState(null);
const show = (m) => {
setMsg(m);
window.clearTimeout(show._t);
show._t = window.setTimeout(() => setMsg(null), 2200);
};
return [msg, show];
}
Object.assign(window, {
fmtMoney, fmtK, pct, Delta, Spark, TrendChart, SectionHead, StatusBadge, Toast, useToast,
});