Skip to main content

Dashboard (features/dashboard)

Panel de métricas del tenant: cuatro tarjetas de totales, una serie temporal de mensajes y cuatro gráficos (tickets por tipo, contactos, agentes y etiquetas con más actividad), todo filtrable por rango de fechas. Es la ruta a la que AdminGuard redirige a quien no es administrador (la raíz de la aplicación redirige a whaticket/chat, no al dashboard). Los datos salen del módulo de backend dashboard, que no tiene página propia en esta documentación; su fuente son los modelos de Contact, User, Ticket, Message y Label.

Ruta​

DashboardModule se carga en shared/routes/content.routes.ts bajo dashboard con canActivate: [AuthGuard] y sin permissionGuard. DashboardRoutingModule tampoco declara guards ni data. La entrada de menú (dashboard.titleSidebar, icono charts, type: 'link') no tiene permission: es visible para cualquier usuario autenticado.

Archivos​

ArchivoRol
dashboard.module.ts / dashboard-routing.module.tsDeclaran la página; importan FormsModule, TranslateModule, HighchartsChartComponent (de highcharts-angular) y SharedModule
pages/dashboard-page/dashboard-page.component.tsLógica: presets de fecha, carga paralela y construcción de las opciones de los gráficos
pages/dashboard-page/dashboard-page.component.htmlCabecera con breadcrumb, filtro de fechas, esqueletos de carga, tarjetas y gráficos
pages/dashboard-page/dashboard-page.component.scssVacío (solo un comentario: usa Bootstrap y estilos globales)
services/dashboard.service.tsDashboardDataService, acceso HTTP a /dashboard/*

Componentes compartidos usados: shared-metric-card (inputs title, value, subtitle, icon, color, trend), shared-apex-chart (recibe options y height) y shared-feather-icons.

Servicios y endpoints​

DashboardDataService (providedIn: 'root') usa ${environment.apiUrl}/dashboard. Todos los métodos aceptan un DateFilter (from?, to?, cadenas YYYY-MM-DD) y lo envían como query. El controlador del backend (dashboard.controller.ts) solo aplica @Auth(): no declara RequirePermission.

MétodoEndpointQueryRespuesta (data.results)
getOverview(filter)GET /dashboard/overviewfrom, toDashboardOverview
getTopContacts(filter, limit = 10)GET /dashboard/top-contactsfrom, to, limitTopContact[]
getTopUsers(filter, limit = 10)GET /dashboard/top-usersfrom, to, limitTopUser[]
getTopLabels(filter, limit = 10)GET /dashboard/top-labelsfrom, to, limitTopLabel[]
getMessagesTimeline(filter)GET /dashboard/messages-timelinefrom, toMessageDay[]
getTicketsByType(filter)GET /dashboard/tickets-by-typefrom, toTicketByType[]

Las respuestas llegan como { data: { results } } (sin message). El servicio devuelve [] si results falta, salvo getOverview, que devuelve undefined. El backend ignora fechas inválidas y, para to, extiende el límite a las 23:59:59.999 del día.

Modelos​

core/interfaces/api/dashboard.interface.ts (el archivo también conserva interfaces heredadas de otra aplicación, como DashboardStatistics, FinancialSummary o topPlayersAgents, que esta pantalla no usa):

InterfazCampos
DateFilterfrom?, to?
DashboardOverviewtotalContacts, activeContacts, totalUsers, activeUsers, totalTickets, totalMessages
TopContact_id, name, phone?, ticketCount, isActive
TopUser_id, name, username, email, ticketCount
TopLabel_id, name, color, contactCount
MessageDaydate, count
TicketByTypetype, count

Pantalla y flujo​

DashboardPageComponent arranca con isLoading = true y en ngOnInit llama setPreset('30d').

  1. Presets: today, 7d, 30d, 3m (90 días) y all (sin límites). setPreset calcula filterFrom y filterTo (YYYY-MM-DD con toISOString) y llama _loadAll.
  2. Rango manual: dos input type="date" ligados con ngModel a filterFrom y filterTo (con min y max cruzados). onCustomDateChange marca el preset all como activo y recarga.
  3. Carga: _loadAll pone isLoading = true, stockChartReady = false y ejecuta un forkJoin de las seis consultas. En éxito guarda los datos, construye los gráficos y apaga isLoading; en error solo apaga isLoading (sin mensaje propio).
  4. Tarjetas: contactos (con activos como subtítulo), usuarios (con activos), tickets y mensajes.
  5. Serie temporal de mensajes: gráfico Highcharts Stock (highcharts-chart con constructorType stockChart), con selector de rango (7d, 15d, 1m, todo), navegador y barra de desplazamiento, línea con degradado y una serie flags que marca el pico (▲) y el mínimo positivo (▽) cuando hay más de dos puntos. Los textos del gráfico (tooltip, botones, "Mensajes", "Pico del período") están escritos en español en el código, no pasan por i18n, y el tooltip formatea la fecha con es-EC.
  6. Gráficos ApexCharts vía shared-apex-chart: donut de tickets por tipo (la etiqueta vacía se rotula Unknown), barras horizontales de contactos por tickets, columnas de agentes por tickets atendidos (usa name o username si el nombre está vacío) y barras horizontales de etiquetas por contactos, coloreadas con el color de cada etiqueta. Cada panel muestra dashboard.noData si no hay datos.

Estado, sockets y utilidades​

No usa NgRx ni sockets: el estado vive en el componente y no se refresca solo. Dependencias: highcharts (módulo highcharts/highstock, registrado con provideHighcharts en app.module.ts), highcharts-angular, apexcharts y ng-apexcharts.

Pruebas​

No hay specs dentro de features/dashboard (ni de la página ni de DashboardDataService). Solo shared/routes/content.routes.spec.ts incluye un caso que fija que la ruta dashboard queda sin data.permission ni permissionGuard.

Gotchas e inconsistencias​

  • Módulos de negocio menciona DashboardService; la clase real se llama DashboardDataService (existe además un DashboardService en el backend).
  • Sin control de permisos: cualquier usuario autenticado ve los totales del tenant, incluidos los de usuarios, y el backend tampoco restringe por permiso.
  • toISOString calcula la fecha en UTC: cerca de medianoche, el "hoy" del navegador puede caer en el día anterior o siguiente.
  • onCustomDateChange activa el botón all aunque el rango sea personalizado (no hay un estado "personalizado").
  • totalUsers y activeUsers no dependen del rango de fechas (el backend los cuenta sin filtro createdAt); contactos, tickets y mensajes sí.
  • getOverview no tiene el valor por defecto [] de los demás: una respuesta sin results deja overview en undefined y las tarjetas fallarían al leer sus campos.
  • forkJoin es todo o nada: si una de las seis consultas falla, no se muestra nada y la pantalla queda sin datos ni aviso propio.
  • Los colores de los gráficos están fijados en el código (por ejemplo #7366ff), no salen del tema.
  • Las constantes DASHBOARD_* de global.constant.ts (por ejemplo DASHBOARD_BETTING_OVERVIEW) y DASHBOARD_ROUTES son restos de otro producto y no se usan aquí.