Dashboard Leads - version limpia para servidor (.env y config de despliegue restaurados)

This commit is contained in:
Panchito
2026-08-20 10:54:03 -05:00
commit 514bcbda45
30 changed files with 9598 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
// Campanita de alertas: avisa de programas/sedes NO identificados en cartera_junta
// (valores que no están en el diccionario alias_normalizacion).
import { useState, useEffect } from "react";
import { api } from "../lib/api";
export default function CampanitaAlertas() {
const [data, setData] = useState({ programa: [], sede: [] });
const [abierto, setAbierto] = useState(false);
useEffect(() => {
let activo = true;
api.alertas()
.then((r) => { if (activo) setData({ programa: r.programa || [], sede: r.sede || [] }); })
.catch(() => { if (activo) setData({ programa: [], sede: [] }); });
return () => { activo = false; };
}, []);
const total = (data.programa?.length || 0) + (data.sede?.length || 0);
const hay = total > 0;
return (
<div style={{ position: "fixed", right: 18, top: 14, zIndex: 80 }}>
<button
onClick={() => setAbierto((v) => !v)}
title={hay ? `${total} valor(es) sin identificar` : "Sin alertas"}
style={{
position: "relative", width: 38, height: 38, borderRadius: 10,
border: "1px solid " + (hay ? "#f59e0b" : "#334155"),
background: hay ? "#78350f" : "rgba(15,23,42,0.9)",
color: hay ? "#fde68a" : "#94a3b8",
cursor: "pointer", fontSize: 18, lineHeight: 1,
display: "flex", alignItems: "center", justifyContent: "center",
boxShadow: "0 2px 8px rgba(0,0,0,0.25)",
}}
>
🔔
{hay && (
<span style={{
position: "absolute", top: -6, right: -6, minWidth: 18, height: 18,
padding: "0 4px", borderRadius: 999, background: "#dc2626", color: "#fff",
fontSize: 11, fontWeight: 700, display: "flex", alignItems: "center",
justifyContent: "center", border: "1px solid #fff",
}}>{total}</span>
)}
</button>
{abierto && (
<div style={{
position: "absolute", right: 0, top: 46, width: 320, maxHeight: 420,
overflowY: "auto", background: "#fff", color: "#0f172a",
border: "1px solid #e2e8f0", borderRadius: 12,
boxShadow: "0 8px 24px rgba(0,0,0,0.18)", padding: 14, fontSize: 13,
}}>
<div style={{ fontWeight: 700, marginBottom: 8 }}>🔔 Alertas de normalización</div>
{!hay ? (
<div style={{ color: "#059669", fontWeight: 600 }}> Todo identificado, nada por revisar.</div>
) : (
<>
{(data.programa?.length > 0) && (
<div style={{ marginBottom: 10 }}>
<div style={{ fontWeight: 600, color: "#b45309", marginBottom: 4 }}>
Nuevo programa no identificado:
</div>
{data.programa.map((p, i) => (
<div key={i} style={{ padding: "3px 0", borderBottom: "1px solid #f1f5f9" }}>
<b>{p.valor || "(vacío)"}</b> <span style={{ color: "#94a3b8" }}>· {p.veces}</span>
</div>
))}
</div>
)}
{(data.sede?.length > 0) && (
<div>
<div style={{ fontWeight: 600, color: "#b45309", marginBottom: 4 }}>
Nueva sede no identificada:
</div>
{data.sede.map((s, i) => (
<div key={i} style={{ padding: "3px 0", borderBottom: "1px solid #f1f5f9" }}>
<b>{s.valor || "(vacío)"}</b> <span style={{ color: "#94a3b8" }}>· {s.veces}</span>
</div>
))}
</div>
)}
<div style={{ marginTop: 10, color: "#64748b", fontSize: 12 }}>
Agrégalos al diccionario (alias_normalizacion) para clasificarlos.
</div>
</>
)}
</div>
)}
</div>
);
}