/* Santos OS — chart primitives + per-metric chart configs for Analítica */
const Tc = window.T;
function HBar({ label, value, max, color, note }) {
const pct = Math.max((value/max)*100, 3);
return (
);
}
// deterministic pseudo-series
function series(base, n, varc, seed=7) {
const out = []; let s = seed;
for (let i=0;i
{data.map((v,i)=>{
const h=Math.max(3,(v/max)*H), x=i*(bw+gap), y=H-h;
const last=i===n-1;
return
{labels && (i%2===0||last) && {labels[i]}}
;
})}
);
}
function AreaLine({ data, color }) {
const max=Math.max(...data), min=Math.min(...data), W=326, H=130, n=data.length;
const pad=(max-min)*0.2||1;
const pts=data.map((v,i)=>[i*(W/(n-1)), H-((v-min+pad)/((max-min)+pad*2))*H]);
const path=pts.map((p,i)=>`${i?'L':'M'}${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
const area=`${path} L${W} ${H} L0 ${H} Z`;
return (
);
}
function Gauge({ pct, color, label }) {
const r=52, c=2*Math.PI*r, off=c*(1-pct/100);
return (
);
}
function Donut({ segments }) {
const total=segments.reduce((a,s)=>a+s.v,0); let acc=0;
const stops=segments.map(s=>{ const a=acc/total*360, b=(acc+s.v)/total*360; acc+=s.v; return `${s.c} ${a}deg ${b}deg`; }).join(',');
return (
{segments.map(s=>(
{s.l}
{s.v}
))}
);
}
const DAYS = Array.from({length:14},(_,i)=>`${10+i}`);
// metric → chart card config
window.CHART_CFG = {
ventas: { title:'Ventas diarias', sub:'Últimos 14 días · Lempiras', render:()=> },
ticket: { title:'Ticket promedio', sub:'Tendencia · 14 días', render:()=> },
captura: { title:'Captura de cliente', sub:'% de ventas con Club Santos', render:()=> <>> },
miembros: { title:'Composición de miembros', sub:'Club Santos por nivel', render:()=> },
uptime: { title:'Disponibilidad POS', sub:'Promedio últimas 24h', render:()=> },
stockouts: { title:'Stockouts críticos', sub:'SKUs vitales sin stock · 14 días', render:()=> },
despachos: { title:'Despachos del CEDI', sub:'Valor enviado a sucursales · 14 días', render:()=> },
ordenes: { title:'Órdenes procesadas', sub:'Pedidos internos · 14 días', render:()=> },
transito: { title:'SKUs en tránsito', sub:'Por destino', render:()=> },
frio: { title:'Cadena de frío', sub:'Temperatura °C · 24h (rango 2–8°C)', render:()=> },
lotes: { title:'Lotes por vencer', sub:'Inventario por ventana de caducidad', render:()=> {[['< 30 días',2,Tc.danger],['30–60 días',2,Tc.warning],['60–90 días',3,Tc.primaryMid]].map(([l,v,c])=>)}
},
};
Object.assign(window, { BarSeries, AreaLine, Gauge, Donut, HBar });