Servicios Centralizados Frontend
IMPORTANTE: Antes de crear un nuevo servicio/utility, consulta este documento. Los servicios centralizados evitan duplicación de código y facilitan el mantenimiento.
Última actualización: 02-04-2026 Versión: v1.0.8
Ubicación de Servicios
static/js/
├── editor/ # Utilidades de editor (compartidas globalmente)
│ └── modal_helper.js # ✅ Modal open/close/toggle centralizado
├── services/ # Servicios principales (API, logging, notificaciones)
│ ├── ApiService.js # ✅ HTTP requests con CSRF
│ ├── EChartsService.js # ✅ Factory para gráficas Apache ECharts
│ ├── ChartGuideService.js # ✅ Modal centralizado de guía de interpretación de gráficas
│ ├── ScopedInsightTab.js # ✅ Reusable CNS (AI Insights) tab — scoped by targetIds (Observatory, Wireless, UPS)
│ ├── InsightIndicatorService.js # ✅ Sidebar pulsing dot indicators for active AI insights
│ ├── QRLabelService.js # ✅ Centralized QR label generation + print (dashboard + editor)
│ ├── NavModuleService.js # ✅ Module nav same-tab/new-tab preference + first-use popover
│ ├── ToastService.js # ✅ Notificaciones toast
│ └── Logger.js # ✅ Sistema de logging con niveles
├── utils/ # Utilidades auxiliares (validación, helpers)
│ ├── TabHelper.js # ✅ "New Tab" standalone mode
│ ├── IpValidator.js # ✅ Validación IPs RFC 1918
│ ├── DeviceFilterService.js # ✅ Device list filtering (search, status, group, rack)
│ ├── InsightDetailModal.js # ✅ AI Insight detail modal + conversation history + revise (shared Observatory + Editor)
│ ├── InsightUtils.js # ✅ Shared constants (RISK_COLORS, RISK_PRIORITY) + helpers (escHtml, timeAgo)
│ ├── InsightActions.js # ✅ Shared insight action handlers (handleExplain, handleRevise, buildInsightActions)
│ ├── SidebarResize.js # ✅ Draggable sidebar resize (Terminal, Observatory)
│ └── GuideService.js # ✅ App Guide slideshow: open/close/reset, auto-detect slide from URL, keyboard nav
├── state_core/ # Gestión de estado global
│ ├── StateManager.js # ✅ Undo/Redo global
│ └── PersistenceManager.js # ✅ LocalStorage wrapper
└── constants/ # Constantes globales
├── Timing.js # ✅ Timeouts, debounce
└── Typography.js # ✅ Fuentes autorizadas
1. ApiService.js
Ubicación: static/js/services/ApiService.js
Propósito: Wrapper centralizado para HTTP requests con manejo automático de CSRF tokens.
Cuándo Usar
- ✅ SIEMPRE para llamadas a la API backend
- ✅ Cuando necesites GET, POST, PUT, DELETE a endpoints Django
Cuándo NO Usar
- ❌ Para fetch() a APIs externas (usar fetch nativo)
- ❌ Para WebSocket connections
API
import { ApiService } from '../services/ApiService.js';
// GET request
const data = await ApiService.get('/api/endpoint');
// POST request
const result = await ApiService.post('/api/endpoint', { key: 'value' });
// PUT request
await ApiService.put('/api/endpoint/123', { updated: true });
// DELETE request
await ApiService.delete('/api/endpoint/123');
Características
- ✅ CSRF token automático (lectura desde cookie)
- ✅ Headers JSON automáticos
- ✅ Error handling con throw
- ✅ Singleton pattern (window.ApiService)
Ejemplo de Uso Incorrecto
// ❌ NO HACER ESTO
fetch('/api/devices', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
// ✅ HACER ESTO
await ApiService.post('/api/devices', data);
2. TabHelper.js
Ubicación: static/js/utils/TabHelper.js
Propósito: Utilidad centralizada para abrir páginas en “New Tab” con modo standalone.
Cuándo Usar
- ✅ Para implementar botones “New Tab” en cualquier página
- ✅ Para ocultar sidebar/elementos en modo standalone
- ✅ Para detectar si una página está en standalone mode
Cuándo NO Usar
- ❌ Para navegación normal entre páginas (usar
<a>owindow.location) - ❌ Para abrir modales
API
import {
openInNewTab,
applyStandaloneMode,
isStandaloneMode,
openTerminalInNewTab,
openObservatoryInNewTab,
openObservatoryOverviewInNewTab
} from '../utils/TabHelper.js';
// Función genérica
openInNewTab({
baseUrl: '/my-page/',
params: { item_id: 123, name: 'Item' },
onClose: () => closeInternalTab()
});
// Wrappers específicos
openTerminalInNewTab(deviceId, deviceName, () => closeTab(tabId));
openObservatoryInNewTab(deviceId, deviceName, () => closeTab(tabId));
openObservatoryOverviewInNewTab();
// Aplicar standalone mode en init de página
applyStandaloneMode({
sidebar: '.my-sidebar',
layout: '.my-layout',
hideElements: ['.overview-tab', '.menu']
});
// Detectar standalone mode
if (isStandaloneMode()) {
console.log('Running in standalone mode');
}
Características
- ✅ URL construction automática con parámetro
standalone=1 - ✅ Callback opcional al abrir (ej: cerrar tab interna)
- ✅ Wrappers específicos para Terminal y Observatory
- ✅ Standalone mode detection y aplicación
Flujo de Uso Completo
1. En la página de origen (ej: Terminal):
// En el botón "New Tab"
button.onclick = () => {
openTerminalInNewTab(deviceId, deviceName, () => {
// Cerrar la tab interna después de abrir la nueva
this.app.closeTab(tabId);
});
};
2. En la página de destino (ej: Terminal):
// En DOMContentLoaded o init()
applyStandaloneMode({
sidebar: '.terminal-sidebar',
layout: '.terminal-layout'
});
Páginas que Usan TabHelper
- ✅ Terminal (
/terminal/) - ✅ Observatory Devices (
/monitoring/?device_id=X) - ✅ Observatory Manual Targets (
/monitoring/?target_id=X) - ✅ Observatory Overview (
/monitoring/?overview=1)
Agregar a Nueva Página
Paso 1: Import en archivo principal
import { openInNewTab, applyStandaloneMode } from '../utils/TabHelper.js';
Paso 2: Añadir botón “New Tab”
<button onclick="MyApp.openInNewTab()">New Tab</button>
Paso 3: Implementar función
MyApp.openInNewTab() {
openInNewTab({
baseUrl: '/my-page/',
params: { id: this.currentId },
onClose: () => this.closeInternalView()
});
}
Paso 4: Aplicar standalone mode en init
document.addEventListener('DOMContentLoaded', () => {
applyStandaloneMode({
sidebar: '.my-sidebar',
layout: '.my-layout'
});
});
3. EChartsService.js
Ubicación: static/js/services/EChartsService.js
Propósito: Factory centralizado para crear, configurar y gestionar gráficas Apache ECharts.
Cuándo Usar
- ✅ Para crear gráficas en Observatory
- ✅ Para aplicar estilos consistentes (13 estilos predefinidos)
- ✅ Para gestionar animación, zoom, export
Cuándo NO Usar
- ❌ Para gráficas simples de Canvas/SVG custom
- ❌ Para otras librerías de gráficas
API
import { EChartsService } from '../services/EChartsService.js';
// Crear gráfica
const chart = EChartsService.createChart('my-chart-id', {
xAxis: { type: 'time' },
yAxis: { type: 'value' },
series: [{ type: 'line', data: [...] }]
});
// Actualizar datos
EChartsService.updateChart('my-chart-id', newData);
// Reset zoom
EChartsService.resetZoom('my-chart-id');
// Export PNG
EChartsService.exportChart('my-chart-id', 'filename');
// Aplicar estilo
EChartsService.applyChartStyle('my-chart-id', 'smooth');
// Destruir
EChartsService.destroy('my-chart-id');
Características
- ✅ 13 estilos predefinidos (smooth, sharp, area, scatter, etc.)
- ✅ Gestión automática de resize
- ✅ Registry interna para evitar memory leaks
- ✅ Export a PNG con watermark
4. ToastService.js
Ubicación: static/js/services/ToastService.js
Propósito: Sistema de notificaciones toast consistente.
Cuándo Usar
- ✅ Para feedback de acciones (success, error, info)
- ✅ Para notificaciones temporales
Cuándo NO Usar
- ❌ Para confirmaciones (usar
confirm()) - ❌ Para modales complejos
API
import { ToastService } from '../services/ToastService.js';
ToastService.show('Operation successful', 'success', 3000);
ToastService.show('Error occurred', 'error', 5000);
ToastService.show('Loading...', 'info');
Nota: Disponible globalmente como window.showAppToast().
5. StateManager.js
Ubicación: static/js/state_core/StateManager.js
Propósito: Undo/Redo global para editores (Racks, Blueprints).
Cuándo Usar
- ✅ Para implementar Ctrl+Z / Ctrl+Y en editores
- ✅ Para historial de cambios
Cuándo NO Usar
- ❌ Para estado temporal de UI (usar variables locales)
- ❌ Para formularios simples
API
import { StateManager } from '../state_core/StateManager.js';
const stateManager = new StateManager(maxHistorySize);
// Guardar snapshot
stateManager.saveState(currentState);
// Undo
const previousState = stateManager.undo();
// Redo
const nextState = stateManager.redo();
6. PersistenceManager.js
Ubicación: static/js/state_core/PersistenceManager.js
Propósito: Wrapper seguro para localStorage con namespace y validación.
Cuándo Usar
- ✅ Para guardar preferencias de usuario
- ✅ Para persistir estado entre sesiones
- ✅ Para cache de configuración
Cuándo NO Usar
- ❌ Para datos sensibles (usar backend)
- ❌ Para datos grandes (>5MB)
API
import { PersistenceManager } from '../state_core/PersistenceManager.js';
// Save
PersistenceManager.save('myKey', { data: 'value' });
// Load
const data = PersistenceManager.load('myKey', defaultValue);
// Remove
PersistenceManager.remove('myKey');
// Clear all
PersistenceManager.clear();
7. IpValidator.js
Ubicación: static/js/utils/IpValidator.js
Propósito: Validación de direcciones IP según RFC 1918 (redes privadas).
Cuándo Usar
- ✅ Para validar IPs en formularios
- ✅ Para detectar IPs privadas vs públicas
API
import { isPrivateIP } from '../utils/IpValidator.js';
if (isPrivateIP('192.168.1.1')) {
console.log('IP privada');
}
8. Logger.js
Ubicación: static/js/services/Logger.js
Propósito: Sistema de logging con niveles (debug, info, warn, error).
Cuándo Usar
- ✅ Para logging consistente en desarrollo
- ✅ Para debugging con niveles configurables
API
import { Logger } from '../services/Logger.js';
const logger = new Logger('MyModule');
logger.debug('Debug message');
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message');
9. DeviceFilterService.js
Ubicación: static/js/utils/DeviceFilterService.js
Propósito: Filtrado centralizado de listas de dispositivos (search, status, group, rack). Compartido entre Terminal y Observatory.
Cuándo Usar
- ✅ Para filtrar device lists en sidebars
- ✅ Cuando una página tenga una lista de
.device-itemcon data attributes
Cuándo NO Usar
- ❌ Para filtrado de racks/blueprints (usan otro patrón)
API — Page-level filtering (fixed IDs)
import { DeviceFilterService } from '../utils/DeviceFilterService.js';
// Filter items in a list container
DeviceFilterService.filterDevices('#device-list');
// Clear search input and re-filter
DeviceFilterService.clearSearch(() => myFilterFn());
// Toggle search clear button visibility
DeviceFilterService.toggleClearBtn();
API — Scoped filtering (for modals / embedded panels)
Uses data-filter attributes instead of fixed IDs — avoids DOM conflicts when the page already has #device-search etc.
// Filter .filter-item elements within a scoped container
DeviceFilterService.filterScoped(overlayElement, '#my-list');
// Clear scoped search and re-filter
DeviceFilterService.clearScoped(overlayElement, () => myFilterFn());
// Toggle scoped clear button visibility
DeviceFilterService.toggleClearScoped(overlayElement);
Scoped HTML contract:
<input data-filter="search"> <!-- Search input -->
<button data-filter="clear"> <!-- Clear button -->
<select data-filter-attr="risk"> <!-- Matches item data-risk -->
<select data-filter-attr="status"> <!-- Matches item data-status -->
<!-- Items: .filter-item with data-name, data-ip, + any data-* matching selects -->
Consumers
terminal/DeviceManager.js— Terminal Hub sidebar (page-level)pages/observatory.js— Network Observatory sidebar (page-level)pages/wireless.js— Wireless Monitor sidebar (page-level)pages/observatory/ObservatoryCNS.js— Acknowledge History modal (scoped)
Expected HTML IDs (page-level mode)
#device-search— Search input#device-search-clear— Clear button#device-status-filter— Status dropdown#device-group-filter— Group dropdown#device-rack-filter— Rack dropdown
Expected Data Attributes on .device-item (page-level)
data-name— Device name (searchable)data-ip— IP address (searchable)data-status— Status (online/offline/unknown)data-group-ids— Comma-separated group IDsdata-rack-id— Rack ID
Expected Data Attributes on .filter-item (scoped mode)
data-name— Primary searchable textdata-ip— Secondary searchable text- Any
data-*matchingselect[data-filter-attr]values
10. ModalHelper (modal_helper.js)
Ubicación: static/js/editor/modal_helper.js
CSS: static/css/components.css (clases .modal-overlay, .modal-content, .modal-content-md, .modal-content-lg)
Propósito: Sistema centralizado de apertura/cierre de modales con drag & drop universal. Todos los modales de la aplicación deben usar este helper.
Cuándo Usar
- ✅ SIEMPRE para abrir/cerrar modales (
.modal-overlay) - ✅ Para configurar close handlers (botón X, Cancel, Escape)
- ✅ Para cerrar todos los modales de golpe (
closeAllModals)
Cuándo NO Usar
- ❌ Para togglear visibilidad de elementos internos (paneles, progress bars, formularios dentro de modales)
- ❌ Para overlays custom que no usan
.modal-overlay(ej: guide-overlay, wizard-overlay)
API
import { openModal, closeModal, toggleModal, setupModalCloseHandlers, initModal, closeAllModals } from '../editor/modal_helper.js';
// Abrir modal (display: flex)
openModal('my-modal-id');
// Cerrar modal (display: none)
closeModal('my-modal-id');
// Toggle
toggleModal('my-modal-id');
// Setup completo con close handlers (botón X, Cancel, backdrop)
setupModalCloseHandlers('my-modal-id', {
closeOnBackdrop: false, // default: false (click fuera NO cierra)
onClose: () => console.log('Modal cerrado')
});
// Init = setup + retorna objeto de control
const modal = initModal('my-modal-id');
modal.open();
modal.close();
// Cerrar todos los modales abiertos
closeAllModals();
Acceso Global
Las funciones principales están expuestas en window para scripts que no son ES modules:
window.openModal('my-modal-id');
window.closeModal('my-modal-id');
window.toggleModal('my-modal-id');
Configuración: closeOnBackdrop
Por defecto: false — hacer click fuera del modal NO lo cierra. Los modales solo se cierran con:
- Botón de cierre (
.close-modal,.modal-close,.modal-close-btn) - Botón Cancel en footer (
.btn-secondary,[data-action="cancel"]) - Llamada explícita a
closeModal()
Consumers
| Módulo | Import |
|---|---|
dashboard.js | openModal, closeModal |
auth.js | Globals (window.openModal/closeModal) |
base.js | Globals (window.openModal/closeModal) |
network_status.js | Globals (window.openModal/closeModal) |
script_manager.js | openModal, closeModal |
editor/backups.js | openModal, closeModal |
editor/import_export.js | openModal, closeModal, closeAllModals |
editor/ui_panels.js | openModal, setupModalCloseHandlers |
editor/DeviceInfoModal.js | setupModalCloseHandlers + window.openModal |
editor/SshConfigModal.js | setupModalCloseHandlers |
editor/PortConfigDiff.js | openModal, closeModal |
editor/ssh_integration.js | Globals (window.openModal/closeModal) |
terminal/AgentLifecycle.js | openModal as _open, closeModal as _close |
terminal/ManualConnection.js | openModal as _open, closeModal as _close |
terminal/DeviceManager.js | openModal, closeModal |
network/auto_provision.js | openModal as _openModal, closeModal as _closeModal |
observatory/ObservatoryAlerts.js | openModal, closeModal |
blueprints/MapInteraction.js | openModal, closeModal |
Estructura HTML de un Modal
<div id="my-modal" class="modal-overlay">
<div class="modal-content modal-content-md">
<div class="modal-header">
<h2>Title</h2>
<button class="modal-close-btn" title="Close">×</button>
</div>
<div class="modal-body">
<!-- Content -->
</div>
<div class="modal-footer">
<button class="btn btn-neutral close-modal" title="Cancel">Cancel</button>
<button class="btn btn-action" title="Save changes">Save</button>
</div>
</div>
</div>
Drag & Drop
Todos los modales son arrastrables desde su header. El sistema detecta automáticamente el drag handle con 3 niveles de fallback:
- Clases conocidas:
.modal-header,.modal-header-simple,.port-diff-modal-header,.guide-header - Heading directo: Primer
<h2>,<h3>o<h4>hijo directo del.modal-content - Div wrapper: Primer
<div>hijo que contenga un heading (ej:.terminal-header,.flex-between-center)
Implementación:
- MutationObserver detecta automáticamente cuando cualquier modal se hace visible (por
style.display,classList.add('active'), HTMX, etc.) - Usa
transform: translate(x, y)(GPU-accelerated, zero reflows) - Al cerrar, la posición se resetea al centro
- No interfiere con botones/inputs/selects dentro del header
- Cursor
grab/grabbingcomo feedback visual
No requiere configuración: Funciona automáticamente en todos los modales existentes y futuros.
Backlight Glow (CSS)
Los modales tienen un efecto de iluminación trasera blanca (backlight) definido en components.css:
- 3 capas de
box-shadowblanca con blur/spread progresivos - Simula luz indirecta posterior sobre fondo oscuro
- Las rack cards del Dashboard también tienen un glow sutil (
dashboard.css)
Ejemplo de Uso Incorrecto
// ❌ NO HACER ESTO
const modal = document.getElementById('my-modal');
modal.style.display = 'flex';
// ...
modal.style.display = 'none';
// ✅ HACER ESTO
import { openModal, closeModal } from '../editor/modal_helper.js';
openModal('my-modal');
closeModal('my-modal');
11. Semantic Button System (components.css)
Ubicación: static/css/components.css (sección SEMANTIC BUTTON ALIASES)
Propósito: Sistema centralizado de clases de botón con colores semánticos. Evita duplicación de estilos de botón en CSS de páginas individuales.
Cuándo Usar
- ✅ SIEMPRE que crees un botón en HTML o JS dinámico
- ✅ Para estados toggle de botones (activo/inactivo)
Cuándo NO Usar
- ❌ NUNCA crear estilos de botón custom en archivos CSS de página
- ❌ NUNCA duplicar colores/bordes de estas clases bajo otro nombre
Clases Disponibles
| Clase | Color | Intención |
|---|---|---|
btn-action | Azul (#82B1FF) | Save, Create, Confirm, Apply |
btn-danger | Rojo (—danger) | Delete, Remove, Disconnect |
btn-warning | Naranja (—warning) | Edit, Modify, Reset, Caution |
btn-success | Verde (—success) | Login, Connect, Accept |
btn-export | Amarillo (#e3b341) | Export, Print, Download |
btn-info | Cyan (#58a6ff) | Info, Status, Expert Charts |
btn-neutral | Blanco | Navigate, View, Close, Cancel |
Tamaños
| Clase | Alto | Uso típico |
|---|---|---|
| (base) | ~38px | Modales, formularios |
btn-sm | 28px | Toolbars, paneles |
btn-xs | 22px | Badges interactivos |
Uso en HTML
<button class="btn btn-sm btn-action" title="Save changes">Save</button>
Uso en JS Dinámico
// Crear botón
const btn = document.createElement('button');
btn.className = 'btn btn-sm btn-action';
btn.textContent = 'Save';
btn.title = 'Save changes';
// Toggle estado
btn.classList.toggle('btn-action', isActive);
btn.classList.toggle('btn-warning', !isActive);
Regla de Oro
Si necesitas propiedades adicionales (width, margin, text-transform), añádelas en el CSS de la página pero SIN redefinir colores, bordes ni fondos. Estos vienen exclusivamente de components.css.
Auditoría (05-03-2026)
Se eliminaron ~158 líneas de CSS duplicado en 12 archivos. Clases eliminadas: .btn-obs, .btn-obs-blue, .btn-obs-orange, .btn-obs-red, .btn-edit, .btn-delete, .chart-guide-btn, .chart-newtab-btn.
12. InsightUtils.js
Ubicación: static/js/utils/InsightUtils.js
Propósito: Constantes y helpers compartidos por todas las vistas de AI Insights (CNS).
Exports
| Export | Tipo | Descripción |
|---|---|---|
RISK_COLORS | Object | { HIGH: '#ef4444', MEDIUM: '#f59e0b', LOW: '#3b82f6' } |
RISK_PRIORITY | Object | { HIGH: 3, MEDIUM: 2, LOW: 1 } (para ordenar por severidad) |
escHtml(str) | Function | Escapa HTML (<, >, &, ") para prevenir XSS |
timeAgo(date) | Function | Formato relativo (“5m ago”, “2h ago”, “3d ago”) |
Consumidores
InsightDetailModal.js,InsightActions.js,ObservatoryCNS.js,ObservatoryOverview.js,PropertiesPanelEvents.js
Cuándo Usar
- ✅ Al renderizar badges de riesgo, colores de insight, timestamps relativos
- ✅ Al escapar contenido de insight en HTML dinámico
13. InsightDetailModal.js
Ubicación: static/js/utils/InsightDetailModal.js
Propósito: Modal unificado para mostrar detalle de AI Insights. Usado por Observatory y Rack Editor.
Exports
| Export | Tipo | Descripción |
|---|---|---|
showInsightDetailModal(insight, actions) | Function | Abre modal con detalle completo del insight |
showInsightListModal(insights, actions) | Function | Lista de insights → click abre detalle |
appendConversation(overlay, entry) | Function | Añade entrada Q&A al historial de conversaciones del modal |
Parámetro actions
{
onApply: (insightId) => { ... }, // Ejecutar fix SSH
onAcknowledge: (insightId, overlay) => { ... }, // Acknowledgar
onExplain: (insightId, overlay) => { ... }, // Preguntar a la IA
onRevise: (insightId, overlay) => { ... }, // Revisar diagnóstico
}
Features del Modal (v4)
- Grid de metadatos: target, risk level, confidence, status, provider, OSI layer, dates
- Summary + Root Cause: Con badge “REVISED” violeta si fue revisado, y valores originales en cursiva
- Commands + Rollback: Con syntax highlighting
- Acknowledge: Input de notas + botón, o info de acknowledge previo
- Explain: Input + respuesta IA, auto-append al historial de conversaciones
- Conversation History: Historial completo Q&A cargado desde API, scroll interno
- Revise Diagnosis: Botón visible solo cuando hay conversaciones, envía contexto a IA para re-evaluar
Consumidores
observatory.js(Observatory → viewInsight),PropertiesPanelEvents.js(Rack Editor)
14. InsightActions.js
Ubicación: static/js/utils/InsightActions.js
Propósito: Handlers centralizados de acciones sobre AI Insights. Evita duplicación entre Observatory, CNS History y Rack Editor.
Exports
| Export | Tipo | Descripción |
|---|---|---|
handleExplain(insightId, overlay, options) | async Function | POST explain, muestra respuesta, persiste conversación, auto-append |
handleRevise(insightId, overlay, onRefresh) | async Function | POST revise, muestra resultado, callback refresh si revisado |
showAcknowledgePrompt(insightId) | Function | Mini-modal para acknowledge con notas. Retorna Promise<string|null> |
buildInsightActions(options) | Function | Factory que retorna { onApply, onAcknowledge, onExplain, onRevise } |
handleExplain options
{
inputRole: 'explain-input', // data-role del input
btnAction: 'explain', // data-action del botón
resultRole: 'explain-result', // data-role del contenedor resultado
answerRole: 'explain-answer', // data-role del texto respuesta
providerRole: 'explain-provider', // data-role del provider
}
buildInsightActions options
{
onRefresh: () => loadCNSData(), // Callback tras apply/acknowledge/revise exitoso
}
Consumidores
observatory.js(buildInsightActions),PropertiesPanelEvents.js(buildInsightActions),ObservatoryCNS.js(handleExplain para History)
15. Group Service (Backend Centralizado)
Ubicación: monitoring/services/group_service.py
Propósito: Servicio compartido para grupos de dispositivos — centraliza queries de membresía, agregación de métricas VictoriaMetrics, y asignación de perfiles a grupos.
Cuándo Usar
- ✅ Para añadir grupos a un nuevo device type (ej: switches, servidores)
- ✅ Para consultar métricas agregadas de un grupo de targets
- ✅ Para gestionar membresía de perfiles en grupos
Cuándo NO Usar
- ❌ Para lógica de summary específica de un tipo (SSIDs en wireless, battery en UPS) — eso va en el endpoint específico
- ❌ Para operaciones CRUD de grupos (crear/eliminar) — eso usa
/api/network/device-groupsdirectamente
API (Python)
from monitoring.services.group_service import (
get_group_profiles,
get_group_target_ids,
query_group_metric,
assign_profiles_to_group,
)
# Obtener perfiles de un grupo filtrados por tipo
profiles = get_group_profiles(group_id, org, ['ups'])
# Extraer target IDs para queries VictoriaMetrics
target_ids = get_group_target_ids(profiles)
# Consultar métrica agregada (sum o avg según tipo)
result = query_group_metric(target_ids, tenant_id, 'battery_charge', hours=4)
# Asignar perfiles a un grupo (add selected, remove unselected)
count = assign_profiles_to_group(org, group_id, profile_ids, ['ups'])
Consumidores
monitoring/api/ups.py—group-summary,group-metric,group-assignmonitoring/api/wireless.py—group-metric,group-assign
Frontend counterparts
UpsGroupDetail.js— Vista de detalle de grupo UPS (summary, charts, members)WirelessGroupDetail.js— Vista de detalle de grupo wireless (summary, SSIDs, members)
16. ChartGuideService.js
Ubicación: static/js/services/ChartGuideService.js
Propósito: Modal centralizado de guía de interpretación de gráficas. Recibe título, registro de métricas y datos de guía; renderiza un modal con secciones por métrica (descripción, tabla de umbrales, tips).
Cuándo Usar
- ✅ Para añadir una guía de interpretación a cualquier página con gráficas (Observatory, Wireless, UPS, etc.)
- ✅ Cuando necesites explicar umbrales y rangos de métricas al usuario
Cuándo NO Usar
- ❌ Para modales genéricos sin estructura de guía de métricas — usar
modal_helper.js
API
import { showChartGuide } from '../services/ChartGuideService.js';
// registry: { key: { label, color } }
// guideData: { key: { description, thresholds: [{level,range,color,detail}], tips: [string] } }
showChartGuide('My Guide Title', registry, guideData);
Datos de guía por página
- Wireless:
static/js/pages/wireless/ChartGuideData.js— 9 métricas WiFi - Observatory:
static/js/pages/observatory/ObservatoryChartGuideData.js— heartbeat, bandwidth, errors
Consumidores
WirelessChartGrid.js→ botón “Chart Guide” en toolbar de APobservatory.js→ botón “Chart Guide” en header de device tab
CSS
Reutiliza clases existentes: .chart-guide-overlay, .chart-guide-modal, .chart-guide-header, .chart-guide-body, .chart-guide-section, .expert-threshold-table, .expert-threshold-dot, .expert-tips
17. ScopedInsightTab.js
Ubicación: static/js/services/ScopedInsightTab.js
Propósito: Componente reutilizable de pestaña CNS (AI Insights) con scope por targetIds. Renderiza tabla de insights, stats bar, filtros, paginación, export CSV y auto-refresh — todo scoped a un conjunto de dispositivos.
Cuándo Usar
- Para añadir una pestaña CNS a cualquier página de dispositivos (Observatory, Wireless, UPS, futuros tipos)
Cuándo NO Usar
- Para mostrar un solo insight (usar
InsightDetailModal.js) - Para indicadores en sidebar (usar
InsightIndicatorService.js)
API
import { ScopedInsightTab } from '../services/ScopedInsightTab.js';
const cnsTab = new ScopedInsightTab({
containerId: 'my-cns-container', // DOM element ID where tab renders
targetIds: [1, 2, 3], // MonitoringTarget IDs to scope
actionNamespace: 'wireless', // Namespace for button data-action (avoids DOM conflicts)
});
// Render the full CNS tab (stats + filters + table + pagination)
cnsTab.render();
// Refresh data (called on tab activation or WS insight_update)
cnsTab.refresh();
// Cleanup when tab is destroyed
cnsTab.destroy();
Parameters
| Parameter | Type | Description |
|---|---|---|
containerId | string | ID of the container element (created by caller or auto-created) |
targetIds | number[] | Array of MonitoringTarget IDs — insights are filtered to these targets only |
actionNamespace | string | Prefix for data-action attributes (e.g., wireless-cns-details) to avoid conflicts when multiple CNS tabs exist |
Consumers
ObservatoryCNS.js— Observatory (all targets, namespaceobservatory)wireless.js— Wireless Monitor (AP targets, namespacewireless)ups.js— UPS Monitor (UPS targets, namespaceups)
Backend dependency
GET /api/sentinel/insights/stats/?target_ids=1,2,3— scoped statsGET /api/sentinel/insights/?target_ids=1,2,3— scoped insight list
18. InsightIndicatorService.js
Ubicación: static/js/services/InsightIndicatorService.js
Propósito: Muestra puntos pulsantes de riesgo (HIGH=rojo, MEDIUM=naranja, LOW=azul) junto a los nombres de dispositivos en sidebars. Soporta actualizaciones en tiempo real via WebSocket.
Cuándo Usar
- Para mostrar indicadores de insights activos en listas de dispositivos de cualquier página
Cuándo NO Usar
- Para mostrar detalles de insights (usar
InsightDetailModal.js) - Para la tabla completa de insights (usar
ScopedInsightTab.js)
API
import { InsightIndicatorService } from '../services/InsightIndicatorService.js';
const indicators = new InsightIndicatorService({
containerSelector: '.device-list', // CSS selector of the sidebar list
targetAttr: 'data-target-id', // Attribute on .device-item with target ID
});
// Load initial indicators for given target IDs
await indicators.load([1, 2, 3]);
// Update a single device indicator (e.g., on WS insight_update)
indicators.update(targetId, { risk_level: 'HIGH', count: 2 });
// Clear all indicators
indicators.clear();
CSS
.insight-indicator— pulsing dot (defined incomponents.css)- Color classes:
.risk-high,.risk-medium,.risk-low
Consumers
observatory.js— Observatory sidebar (rack + manual devices)wireless.js— Wireless sidebar (AP devices)ups.js— UPS sidebar (UPS devices)
19. ObservatoryITSM.js (Centralized ITSM Dashboard)
Ubicación: static/js/pages/observatory/ObservatoryITSM.js
Propósito: Dashboard ITSM completo que se puede instanciar en cualquier contexto. Genera su propio HTML skeleton y channel modal, los inyecta en el DOM, y renderiza todas las secciones ITSM (SLA, Notifications, Escalation, Maintenance, Analytics, Known Issues, Runbooks, Patterns, Incident Groups).
Cuándo Usar
- Para incrustar el dashboard ITSM en cualquier página. Actualmente se accede desde Observatory y desde los botones “ITSM Settings” en las pestanas CNS de Wireless y UPS.
Cuándo NO Usar
- Para funcionalidad CNS (insights, tabla, filtros) — usar
ScopedInsightTab.js
API
import { ObservatoryITSM } from '../pages/observatory/ObservatoryITSM.js';
const itsm = new ObservatoryITSM({
containerId: 'itsm-container', // DOM element ID for the dashboard
namespace: 'observatory', // Namespace for data-action attributes
targetIds: null, // Optional: scope analytics/groups/patterns by target IDs
});
// Initialize: generates HTML skeleton, appends channel modal to body, loads data
itsm.init();
// Refresh data (called on tab activation)
itsm.refresh();
Parameters
| Parameter | Type | Description |
|---|---|---|
containerId | string | ID of the container where ITSM dashboard renders |
namespace | string | Prefix for data-action attributes to avoid DOM conflicts |
targetIds | number[] or null | When set, Analytics/Incident Groups/Recurring Patterns are scoped to these targets. SLA, Channels, Escalation, Maintenance, Known Issues, Runbooks remain org-wide |
Scoping behavior
| Section | Scoped by targetIds | Reason |
|---|---|---|
| Analytics | Yes | MTTA/MTTR/compliance filtered to relevant devices |
| Incident Groups | Yes | Only groups containing scoped targets |
| Recurring Patterns | Yes | Only patterns for scoped targets |
| SLA Policies | No | Org-wide configuration |
| Notification Channels | No | Org-wide configuration |
| Escalation Policies | No | Org-wide configuration |
| Maintenance Windows | No | Org-wide configuration |
| Known Issues | No | Org-wide knowledge base |
| Runbooks | No | Org-wide procedures |
Consumers
observatory.js— ITSM tab (full org-wide view)- Wireless/UPS CNS tabs — “ITSM Settings” button navigates to Observatory ITSM tab
Checklist: ¿Necesito Crear un Nuevo Servicio?
Antes de crear un nuevo servicio, verifica:
- ¿Ya existe un servicio similar en
services/outils/? - ¿He consultado este documento?
- ¿He buscado en el código existente? (
grep -r "keyword" static/js/) - ¿Este servicio será usado en más de 2 lugares?
- ¿Es específico de un módulo o reutilizable?
Si es específico de un módulo: Crear en subcarpeta del módulo (ej: static/js/terminal/)
Si es reutilizable: Añadir a services/ o utils/ y DOCUMENTAR AQUÍ.
Añadir un Nuevo Servicio Centralizado
Paso 1: Crear archivo en services/ o utils/
// static/js/services/MyNewService.js
export class MyNewService {
static doSomething() { ... }
}
Paso 2: Añadir a index.js correspondiente
// static/js/services/index.js
export { MyNewService } from './MyNewService.js';
Paso 3: DOCUMENTAR EN ESTE ARCHIVO (CENTRALIZED_SERVICES.md)
- Añadir sección con API, cuándo usar, cuándo NO usar, ejemplos
Paso 4: Actualizar Documentation/INDEX.md
- Registrar nuevo servicio en sección Frontend
Paso 5: Commit con mensaje claro
git commit -m "feat: add MyNewService centralizado (propósito del servicio)"
Referencias
- Design Guidelines:
Documentation/frontend/DESIGN_GUIDELINES.md - Performance Guidelines:
Documentation/frontend/PERFORMANCE_GUIDELINES.md - Agent Rules:
Documentation/guides/AGENT_RULES.md - Project Context:
Documentation/architecture/PROJECT_CONTEXT.md
Mantenido por: Equipo CreaRack Revisión recomendada: Al añadir nuevos servicios o módulos JS
Véase también
- [[crearack-tech—frontend—design-guidelines]] — guidelines de diseño UI
- [[crearack-tech—frontend—design-system]] — design system de componentes
- [[crearack-tech—frontend—performance-guidelines]] — guidelines de performance frontend
- [[crearack-tech—agents—dev-core]] — agente técnico del módulo core
- [[ia-tech—roles—dev-frontend]] — agente IA frontend