[RFC-ADN-01] Normalizar nomenclatura de proyectos

- Directorios: PascalCase (p2601_dasuten → P2601_Dasuten)
- Planes: numeración pura (P2601_6.1.A → P2601.06.01)
- Sin caracteres especiales en nombres de archivo
- 07_proyectos.md: convención RFC-ADN-01 integrada, P2604/P2605 agregados
- evento:listar: nueva opción --detalle para descripciones completas
This commit is contained in:
Ricardo Monla
2026-03-16 15:15:53 -03:00
parent f608924c6a
commit ba5c65e2a4
33 changed files with 883 additions and 390 deletions
@@ -30,6 +30,21 @@ module.exports = (pool) => {
res.json(rows.map(r => r.fecha));
});
// GET /api/bitacoras/:fecha/version — Devuelve el ID max de entradas para smart refresh
router.get('/:fecha/version', async (req, res) => {
const { fecha } = req.params;
const bResult = await pool.query('SELECT id FROM bitacoras.bitacoras WHERE fecha = $1', [fecha]);
if (bResult.rows.length === 0) {
return res.json({ max_id: 0, count: 0 });
}
const bitacoraId = bResult.rows[0].id;
const { rows } = await pool.query(`
SELECT COALESCE(MAX(id), 0) as max_id, COUNT(*) as count
FROM bitacoras.entradas WHERE bitacora_id = $1
`, [bitacoraId]);
res.json(rows[0]);
});
// GET /api/bitacoras/:fecha/completa — Bitácora completa con entradas agrupadas por nodo
router.get('/:fecha/completa', async (req, res) => {
const { fecha } = req.params;
@@ -285,8 +285,54 @@ function App() {
setSelectedProyecto(proyectos[nextIndex].codigo);
};
// Auto-refresh inteligente: solo actualizar si hay cambios
const [lastVersion, setLastVersion] = useState<{ max_id: number; count: number } | null>(null);
const fetchBitacoraVersion = useCallback(async () => {
try {
const res = await fetch(`${API_URL}/bitacoras/${fecha}/version`);
return await res.json();
} catch { return null; }
}, [fecha]);
const smartRefresh = useCallback(async () => {
const newVersion = await fetchBitacoraVersion();
if (!newVersion) {
fetchBitacora();
fetchFechasActivas();
return;
}
if (!lastVersion || newVersion.max_id !== lastVersion.max_id || Number(newVersion.count) !== Number(lastVersion.count)) {
setLastVersion(newVersion);
fetchBitacora();
fetchFechasActivas();
}
}, [fetchBitacoraVersion, fetchBitacora, fetchFechasActivas, lastVersion]);
useEffect(() => { fetchBitacora(); }, [fetchBitacora]);
useEffect(() => { fetchNodos(); fetchProyectos(); fetchFechasActivas(); }, []);
useEffect(() => { fetchNodos(); fetchProyectos(); }, []);
useEffect(() => { fetchFechasActivas(); }, [fetchFechasActivas]);
// Inicializar versión al cargar
useEffect(() => {
fetchBitacoraVersion().then(v => { if (v) setLastVersion(v); });
}, [fetchBitacoraVersion]);
// Auto-refresh cada 30 segundos (solo si hay cambios)
useEffect(() => {
const interval = setInterval(smartRefresh, 30000);
// También actualizar cuando la página recupera el foco
const handleVisibility = () => {
if (!document.hidden) smartRefresh();
};
document.addEventListener('visibilitychange', handleVisibility);
return () => {
clearInterval(interval);
document.removeEventListener('visibilitychange', handleVisibility);
};
}, [smartRefresh]);
// Rebuild calendar view month when fecha changes
useEffect(() => {