/* KPI Ribbon ======================================================== */
function KPICard({ def, value, delta, sparkData, tone, format, live }){
const [hover, setHover] = useState(false);
const positive = delta > 0;
const goodIsUp = def.good === 'up';
const deltaGood = goodIsUp ? positive : !positive;
const deltaColor = deltaGood ? 'text-emerald-600 dark:text-emerald-400' : 'text-rose-600 dark:text-rose-400';
return (
setHover(true)}
onMouseLeave={()=>setHover(false)}
className="group relative rounded-xl border border-ink-200/80 dark:border-white/10 bg-white dark:bg-ink-900/60 shadow-card p-4 transition hover:shadow-float hover:border-ink-300/80 dark:hover:border-white/20 cursor-default overflow-hidden">
{/* Top: label + live dot */}
{/* Value */}
{Math.abs(delta).toFixed(1)}%
{/* Sparkline that appears on hover (slides in) */}
24h
{def.label.split(' ').slice(-1)[0]}
{/* Hover overlay accent */}
);
}
function KPIRibbon({ accountId, platformId, customCols=[], onAddCustomCol }){
const snap = useMemo(()=>kpiSnapshot(accountId, platformId), [accountId, platformId]);
const tone = (PLATFORMS.find(p=>p.id===platformId)||PLATFORMS[0]).tone;
const colors = ['#8351ff', '#06b6d4', '#10b981', '#f59e0b', '#ec4899', '#3b82f6'];
/* Real values only — no synthetic drift. */
const liveValues = snap;
return (
{KPI_DEFS.map((def, i) => (
))}
{/* Custom column add slot */}
);
}
Object.assign(window, { KPIRibbon });