Dashboard Leads - version limpia para servidor (.env y config de despliegue restaurados)
This commit is contained in:
92
frontend/src/components/CampanitaAlertas.jsx
Normal file
92
frontend/src/components/CampanitaAlertas.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user