Añadir frontend/src/components/IndicadorActualizacion.jsx

This commit is contained in:
2026-07-10 14:34:59 -05:00
parent c4b6be1810
commit 4b5f0f8d77

View File

@@ -0,0 +1,97 @@
// src/components/IndicadorActualizacion.jsx
// Indicador (arriba-derecha) con la hora de la última actualización + botón de
// recarga. Al pulsar: re-lee las bases para la vista actual y recachea el resto.
import { useState, useEffect, useCallback } from "react";
import { api } from "../lib/api";
import { getVistaActual, emitirRefresco } from "../lib/vistaActual";
function formatearHora(iso) {
if (!iso) return "—";
try {
const d = new Date(iso);
return d.toLocaleString("es-PE", {
day: "2-digit", month: "2-digit",
hour: "2-digit", minute: "2-digit",
});
} catch {
return "—";
}
}
export default function IndicadorActualizacion() {
const [hora, setHora] = useState(null);
const [cargando, setCargando] = useState(false);
const consultar = useCallback(async () => {
try {
const r = await api.ultimaActualizacion();
setHora(r?.hora || null);
} catch { /* silencioso */ }
}, []);
useEffect(() => {
consultar();
const id = setInterval(consultar, 60000);
return () => clearInterval(id);
}, [consultar]);
async function recargar() {
if (cargando) return;
setCargando(true);
try {
const r = await api.cacheRefreshManual(getVistaActual());
setHora(r?.hora || null);
emitirRefresco();
} catch { /* silencioso */ }
finally { setCargando(false); }
}
return (
<>
<style>{`
@keyframes spin-actualiza { to { transform: rotate(360deg); } }
.ind-actualiza-btn { transition: color .15s, transform .15s; }
.ind-actualiza-btn:hover:not(:disabled) { color:#7dd3fc; transform: scale(1.15); }
.ind-actualiza-spin { animation: spin-actualiza .9s linear infinite; }
`}</style>
<div
style={{
position: "fixed", right: 16, top: 14, zIndex: 60,
display: "flex", alignItems: "center", gap: 8,
background: "rgba(15,23,42,0.9)",
color: cargando ? "#7dd3fc" : "#cbd5e1",
border: "1px solid " + (cargando ? "#0ea5e9" : "#334155"),
borderRadius: 20, padding: "6px 12px",
fontSize: 12, lineHeight: 1, fontWeight: 500,
boxShadow: "0 2px 8px rgba(0,0,0,0.28)",
backdropFilter: "blur(4px)",
userSelect: "none",
}}
title="Última actualización de las bases de datos"
>
<span style={{ opacity: cargando ? 1 : 0.85 }}>
{cargando ? "Actualizando…" : formatearHora(hora)}
</span>
<button
onClick={recargar}
disabled={cargando}
className={"ind-actualiza-btn" + (cargando ? " ind-actualiza-spin" : "")}
title={cargando ? "Actualizando datos…" : "Actualizar ahora"}
style={{
background: "transparent", border: "none",
cursor: cargando ? "default" : "pointer",
color: "#38bdf8", fontSize: 15, padding: 0, lineHeight: 1,
display: "flex", alignItems: "center",
}}
>
<svg width="15" height="15" viewBox="0 0 24 24" fill="none"
stroke="currentColor" strokeWidth="2.4"
strokeLinecap="round" strokeLinejoin="round">
<path d="M21 12a9 9 0 1 1-2.64-6.36" />
<path d="M21 3v6h-6" />
</svg>
</button>
</div>
</>
);
}