Modal de detalle de servicios vigilados en la cabecera del dashboard
Descripción
El pill “N servicios” de la cabecera del dashboard daba solo el número total y un estado agregado (all green / en rojo si algo caía). Sin embargo, el usuario no sabía qué servicio específico había fallado ni en cuál servidor.
Esta feature hace el pill interactivo: ahora es un botón que al clicar abre un modal que desglosa todos los servicios vigilados, agrupados por servidor (prod, stage, ops, dca) con su nombre legible, estado actual (UP/DOWN/sin datos), código HTTP del error (si aplica) y frescura del dato.
Además, el modal muestra los monitores externos de UptimeRobot con su estado (UP/DOWN/PAUSED), uptime ratio y latencia.
Responsabilidad
- Componente cliente:
ServerStatusPill.tsx(botón en la cabecera) +ServicesModal.tsx(modal). - APIs reutilizadas:
/api/services(sonda servicios-check de OPS, cada 5 min) +/api/uptime(monitores UptimeRobot). - Sin fetch nuevo: el modal consume los mismos datos que el pill ya carga periódicamente. La foto es siempre coherente: el número que el usuario clicar coincide exactamente con los servicios que ve desglosados.
Comportamiento
Pill interactivo (ServerStatusPill)
- Estado visual: LED circular + “N servicios” + etiqueta de salud (all green / warning / sin datos).
- Interactividad: clic abre el modal (botón type=“button” con affordance hover).
- Cierre: escape key, clic fuera del modal, o botón “Cerrar”.
Modal (ServicesModal)
Estructura en dos secciones:
1. Servicios internos
Agrupados por servidor con orden fijo: prod, stage, ops, dca. Cada servidor desconocido cae al final con su clave tal cual.
Cada fila (servicio):
- LED: indicador circular de color (verde=UP, rojo=DOWN, gris=sin datos).
- Naturaleza: nombre legible (
s.label, ej. “MariaDB del cluster”) sobre nombre técnico (s.name, ej. “db-0-prod”). - Estado: “UP” (verde) | “DOWN + código HTTP” (rojo, ej. “DOWN · HTTP 502”) | “sin datos · hace X min” (gris).
2. Monitores externos
UptimeRobot: estado (UP/DOWN/PAUSED) + uptime ratio (%) + latencia (ms).
La naturaleza se deduce del nombre del monitor (patrones como “SSL”, “workspace”, “channel”, “prod”):
- SSL → “Certificado TLS de crearack.com — vigida que no caduque”.
- Workspace → “Este workspace (Cloudflare Pages + D1)”.
- Otro → hostname extraído de la URL (honesto, nunca inventado).
Pie del modal
- Timestamp de la última pasada de la sonda: “Última pasada: hace X min / hace Y h / hace Z días”.
- Botón “Cerrar”.
Archivos modificados
src/components/shell/ServerStatusPill.tsx— convierte el div estático en button interactivo + abre modal.src/components/shell/ServicesModal.tsx— nuevo, 254 LOC. Modal con lógica de agrupación, formateo de estados y naturalización de monitores.src/styles/globals.css— reglas CSS parabutton.header-server-pill: font inheritance, hover con fondo oscuro.
Dependencias
dashboardData.ts(tiposServiceStatus,UptimeMonitor) — sin cambios.- React hooks:
useEffect,useState,createPortal(ya en uso en el proyecto). - Estilos globales:
--danger,--success,--fg-dim,--overlay-4,--border,--border-soft,--space-*,--text-*.
UX/Accesibilidad
- Modal semántico:
role="dialog",aria-labelledby="services-modal-title". - Keyboard: Escape para cerrar.
- Touch: clic fuera cierra (overlay).
- Responsive: ancho máximo 640px (92vw en móvil).
- Color: los LEDs son circulares (no rectangulares) para diferenciación visual.
- Monospace: detalles técnicos (
s.name, timestamp) en font-mono para coherencia con terminal/API.
Notas técnicas
- Sin re-fetch: el modal recibe los mismos objetos
servicesymonitorsque ya carga el pill. No hay delay esperando datos nuevos. - Filtro de monitores pausados: la lógica es idéntica a la del pill. Un monitor en estado
pausedno cuenta hacia el total de vigilancia activa. - Orden de servidores: constante
SERVER_ORDER— si hay un servidor no conocido, se añade al final del grupo en orden de aparición. - Formateo de edades: función
formatAge(min)convierte minutos a etiqueta legible (“hace 5 min”, “hace 3 h”, “hace 2 días”). - Deducción de naturaleza: función
monitorNature(m)busca patrones en el nombre del monitor antes de extraer hostname de la URL.
Véase también
- [[feature—infra—servicios-check-sonda-internos]]
- [[concept—observability—dashboard-monitoring]]
- [[concept—observability—uptime-monitoring]]
- [[entity—src—component—shell]]