From 26eed5726c34449693ee7723c866a5cd0d79a102 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Mon, 13 Jul 2026 15:27:14 +0000 Subject: [PATCH] feat(alerts): enhance alert handling and client data enrichment in dashboard (#16) - Updated the alert handling logic to include a 'notFound' flag for databases that are not found on the server, allowing for clearer UI representation. - Enriched alert data with client contact information by integrating the `lookupAlertClientData` function, ensuring alerts display relevant client details. - Improved the display of alert dates in the UI to reflect the new 'notFound' status, enhancing user experience and clarity. This update improves the accuracy and usability of the dashboard alerts. Reviewed-on: https://git.aduanasoft.com/ADUANASOFT/PANEL_BASES_ANEXO24/pulls/16 Co-authored-by: AlexeerCT Co-committed-by: AlexeerCT --- src/lib/server/controldesk-pg.ts | 15 +++++++-------- src/lib/server/mssql-nodes.ts | 15 ++++++++++++++- src/routes/+page.svelte | 21 +++++++++++++++------ src/routes/api/dashboard.json/+server.ts | 20 ++++++++++++++++++-- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/lib/server/controldesk-pg.ts b/src/lib/server/controldesk-pg.ts index bd11f69..3d6c258 100644 --- a/src/lib/server/controldesk-pg.ts +++ b/src/lib/server/controldesk-pg.ts @@ -342,18 +342,17 @@ export function matchNodeRowFromBackupStem(stem: string, nodes: any[]): any | nu /** Datos de contacto para alertas (equivalente a la consulta previa sobre Usuarios/BasesDeDatos). */ export async function lookupAlertClientData(nodoName: string): Promise { + // El "Cliente" de la alerta es el nombre de la tabla de bases de datos + // (database_nodes.legal_name), NO el full_name del usuario del portal. Se resuelve el nodo por + // database_name o node_subnode_key; así también funciona para bases sin usuario asociado. const sql = ` SELECT - pu.is_authority_client AS "ClienteAutoridad", - pu.full_name AS "Nombre", - pu.username AS "Usuario", + dn.legal_name AS "Nombre", dn.notification_email AS "CorreoNotificacion", dn.node_subnode_key AS "NodoSubNodo" - FROM ${qUsers()} pu - LEFT JOIN ${qNodes()} dn ON pu.database_node_id = dn.id - WHERE pu.username = $1 - OR dn.database_name = $1 - OR dn.node_subnode_key = $1 + FROM ${qNodes()} dn + WHERE LOWER(TRIM(dn.database_name)) = LOWER(TRIM($1::text)) + OR LOWER(TRIM(dn.node_subnode_key)) = LOWER(TRIM($1::text)) LIMIT 1 `; const r = await pgPool.query(sql, [nodoName]); diff --git a/src/lib/server/mssql-nodes.ts b/src/lib/server/mssql-nodes.ts index d979461..6a58560 100644 --- a/src/lib/server/mssql-nodes.ts +++ b/src/lib/server/mssql-nodes.ts @@ -463,7 +463,9 @@ export async function loadSqlDashboardFromNodes(nodes: CatalogNodeRow[]): Promis queryDatabaseAlertRow(pool, dbn), queryRestoreHistoryForDatabase(pool, dbn) ]); - if (!row) return null; + // Conexión al servidor OK pero la base no existe en él: se marca como alerta + // "no encontrada" (sin métricas ni días sin sincronizar), no se descarta el nodo. + if (!row) return { node, dbn, row: null, notFound: true, alertRow: null, hist: [] }; return { node, dbn, row, alertRow, hist }; } catch (e) { console.error( @@ -480,6 +482,17 @@ export async function loadSqlDashboardFromNodes(nodes: CatalogNodeRow[]): Promis if (!res) continue; const { node, dbn, row, alertRow, hist } = res; + // Base no encontrada en el servidor: solo alerta (sin fila de métricas ni tamaño). + // last_restore_date en null => la UI muestra los días sin sincronizar como N/D. + if ((res as any).notFound) { + alertsData.push({ + visible_name: dbn, + last_restore_date: null, + not_found: true + }); + continue; + } + const visible = String(row.visible_name ?? dbn); const keyLower = visible.toLowerCase(); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0912166..0c0be20 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -736,9 +736,11 @@ const now = new Date(); const csvRows = rows.map((alert: any) => { - const fecha = alert.last_restore_date - ? new Date(alert.last_restore_date).toLocaleString() - : 'Nunca'; + const fecha = alert.not_found + ? 'No encontrada' + : alert.last_restore_date + ? new Date(alert.last_restore_date).toLocaleString() + : 'Nunca'; const dias = alert.last_restore_date ? alert.daysWithout : ''; const cliente = alert.clientData?.Nombre ?? 'N/D'; const correo = alert.clientData?.CorreoNotificacion ?? 'N/D'; @@ -1958,9 +1960,16 @@ {alert.visible_name} - {alert.last_restore_date - ? new Date(alert.last_restore_date).toLocaleString() - : 'Nunca'} + {#if alert.not_found} + + error_outline + No encontrada + + {:else} + {alert.last_restore_date + ? new Date(alert.last_restore_date).toLocaleString() + : 'Nunca'} + {/if} {alert.clientData?.Nombre ?? 'N/D'} {alert.clientData?.CorreoNotificacion ?? 'N/D'} diff --git a/src/routes/api/dashboard.json/+server.ts b/src/routes/api/dashboard.json/+server.ts index f90aa6d..61b4fa8 100644 --- a/src/routes/api/dashboard.json/+server.ts +++ b/src/routes/api/dashboard.json/+server.ts @@ -1,5 +1,5 @@ import { json } from '@sveltejs/kit'; -import { listDatabaseNodesForMssql } from '$lib/server/controldesk-pg'; +import { listDatabaseNodesForMssql, lookupAlertClientData } from '$lib/server/controldesk-pg'; import { loadSqlDashboardFromNodes, type CatalogNodeRow } from '$lib/server/mssql-nodes'; export const GET = async () => { @@ -7,7 +7,23 @@ export const GET = async () => { const nodes = (await listDatabaseNodesForMssql()) as CatalogNodeRow[]; const bundle = await loadSqlDashboardFromNodes(nodes); const { databaseRows, summaryMain, alertsData } = bundle; - return json({ databaseRows, summaryMain, alertsData }); + + // Enriquecer las alertas con datos de cliente/correo (mismo criterio que la carga inicial + // en +page.server.ts). Sin esto, el auto-refresh reemplazaba las alertas por versiones sin + // clientData y la tabla mostraba Cliente y Correo como "N/D" tras el primer refresco. + const enrichedAlerts = await Promise.all( + alertsData.map(async (alert) => { + let clientData: any = null; + try { + clientData = await lookupAlertClientData(String(alert.visible_name)); + } catch { + /* ignore */ + } + return { ...alert, clientData }; + }) + ); + + return json({ databaseRows, summaryMain, alertsData: enrichedAlerts }); } catch (e: any) { console.error('Error refreshing dashboard data:', e); return json({ error: 'Error refreshing dashboard data' }, { status: 500 });