// SATMELI — Dashboard con 3 variantes de tarjeta: monitor / ticket / radar.
// ── Radar scope animado (panel oscuro embebido) ───────────────────────────
function RadarScope({ count, active, size = 124, blips = [] }) {
const cx = size / 2, r = size / 2 - 6;
return (
);
}
// blips derivados del id de búsqueda (estables)
function blipsFor(s) {
const out = [];
const n = Math.min(6, Math.max(2, Math.round(s.matches / 3)));
for (let i = 0; i < n; i++) {
out.push({ a: (i * 73 + s.id.charCodeAt(1) * 31) % 360, r: 0.3 + ((i * 0.17 + 0.2) % 0.65), big: i === 0 });
}
return out;
}
function CatChip({ cat }) {
const c = SAT_CATS[cat];
return (
{c.label}
);
}
function priceRangeLabel(s) {
if (s.priceMin && s.priceMax) return `${fmtK(s.priceMin)}–${fmtK(s.priceMax)}`;
if (s.priceMax) return `hasta ${fmtARS(s.priceMax)}`;
if (s.priceMin) return `desde ${fmtARS(s.priceMin)}`;
return 'sin tope';
}
function MenuBtn({ onClick, name = 'dots' }) {
return (
);
}
// ── Variante MONITOR ───────────────────────────────────────────────────────
function CardMonitor({ s, onOpen, onToggle }) {
return (
{s.active ? 'EN VIVO' : 'PAUSADA'}
{priceRangeLabel(s)}
{condLabel(s.condition)}
{s.location}
{s.matches}
coincid.
{s.active ? `revisado ${ago(s.lastCheck)}` : 'monitoreo en pausa'}
{freqLabel(s.freq).toUpperCase()}
);
}
// ── Variante TICKET / RECIBO ───────────────────────────────────────────────
function Perfs({ where }) {
return (
{Array.from({ length: 16 }).map((_, i) => (
))}
);
}
function ReceiptRow({ k, v, accent }) {
return (
{k}
{v}
);
}
function CardTicket({ s, onOpen, onToggle }) {
return (
SATMELI · MONITOR
{s.active ? 'ACTIVA' : 'EN PAUSA'}
{s.term}
#{s.id.toUpperCase()} · alta {ago(s.createdAt * 60).replace('hace ', 'hace ')}
{/* tear line */}
COINCIDENCIAS
{s.matches}
{/* faux barcode */}
{[3,1,2,1,3,2,1,1,3,1,2,3,1,2,1,3,1].map((w, i) => (
))}
);
}
// ── Variante RADAR ─────────────────────────────────────────────────────────
function CardRadar({ s, onOpen, onToggle }) {
return (
{s.active ? '◈ RASTREANDO' : '◈ EN PAUSA'}
{s.term}
{priceRangeLabel(s)}
{condLabel(s.condition)}
{s.active ? `revisado ${ago(s.lastCheck)}` : 'pausada'} · {freqLabel(s.freq)}
);
}
const CARD_VARIANTS = { monitor: CardMonitor, ticket: CardTicket, radar: CardRadar };
// ── Dashboard ──────────────────────────────────────────────────────────────
function Dashboard({ searches, notifs, variant, mode, onOpen, onToggle, onCreate }) {
const Card = CARD_VARIANTS[variant] || CardMonitor;
const active = searches.filter((s) => s.active).length;
const unread = notifs.filter((n) => !n.read).length;
const todayMatches = notifs.filter((n) => n.foundAt < 1440).length;
const desktop = mode === 'desktop';
const stats = [
{ k: 'Centinelas activos', v: `${active}/${searches.length}`, icon: 'radar' },
{ k: 'Hallazgos hoy', v: todayMatches, icon: 'zap', accent: true },
{ k: 'Sin leer', v: unread, icon: 'bell', alert: unread > 0 },
];
return (
{/* header */}
Panel de monitoreo
Tus centinelas
{active} {active === 1 ? 'búsqueda vigilando' : 'búsquedas vigilando'} Mercado Libre por vos.
{desktop &&
Nueva búsqueda}
{/* stats strip */}
{/* cards */}
{searches.map((s, i) => (
onOpen(s.id)} onToggle={() => onToggle(s.id)} />
))}
);
}
Object.assign(window, { Dashboard, RadarScope, CardMonitor, CardTicket, CardRadar, priceRangeLabel, CatChip });