CreaRack-SL

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”.

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 para button.header-server-pill: font inheritance, hover con fondo oscuro.

Dependencias

  • dashboardData.ts (tipos ServiceStatus, 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

  1. Sin re-fetch: el modal recibe los mismos objetos services y monitors que ya carga el pill. No hay delay esperando datos nuevos.
  2. Filtro de monitores pausados: la lógica es idéntica a la del pill. Un monitor en estado paused no cuenta hacia el total de vigilancia activa.
  3. Orden de servidores: constante SERVER_ORDER — si hay un servidor no conocido, se añade al final del grupo en orden de aparición.
  4. Formateo de edades: función formatAge(min) convierte minutos a etiqueta legible (“hace 5 min”, “hace 3 h”, “hace 2 días”).
  5. 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]]