Panel de tickets
El Panel de tickets es la vista de analytics del sistema de tickets de soporte. Agrega métricas de actividad, cumplimiento de SLA, antigüedad del backlog y distribución por tipo, calculadas en tiempo real desde chat_tickets (sin tabla precomputada en v1).
Las reglas de negocio (objetivos de SLA, mapeos de tipo, umbral de dormido) viven en apps/backend/src/chat/tickets/config/sla.config.ts como única fuente de verdad. Los campos derivados (slaStatus, ticketType, daysSinceCreated) se calculan al leer cada ticket — el frontend no reimplementa las reglas.
KPIs
GET /chat/tickets/kpis — admite filtros por ventana de fechas (dateFrom/dateTo), equipo (team / resolution_team) y prioridad. Los tickets marcados como invalid no cuentan.
| Métrica | Campo | Descripción |
|---|
| Tickets abiertos | totalOpen | Tickets en estado abierto ahora mismo (cualquier prioridad) |
| Resueltos en rango | totalResolvedInRange | Tickets resueltos/cerrados dentro de [dateFrom, dateTo] |
| Críticos sin resolver | criticalUnresolvedNow | Tickets abiertos con prioridad urgent o high |
| Backlog por cohortes | backlogOverDays | Abiertos con antigüedad > 1, > 3 y > 7 días |
| % backlog | backlogPercentage | Proporción (0-1) de abiertos con más de 3 días sobre totalOpen |
| FCR % | fcrPercentage | First Contact Resolution: resueltos con ≤ 1 comentario público y sin reaperturas |
| Tasa de reaperturas | reopenRate | Proporción (0-1) de resueltos en rango que se reabrieron al menos una vez |
| Tiempo medio de respuesta | avgResponseTimeMinutes | Minutos medios de creación → first_response_at, por prioridad |
| Tiempo medio de resolución | avgResolutionTimeMinutes | Minutos medios de creación → resolved_at, por prioridad |
| Cumplimiento SLA respuesta | slaResponseCompliance | Proporción (0-1) que cumple el SLA de 1ª respuesta, por prioridad |
| Cumplimiento SLA resolución | slaResolutionCompliance | Proporción (0-1) que cumple el SLA de resolución, por prioridad |
El FCR cruza chat_tickets con chat_ticket_comments: una consulta agrupada cuenta los comentarios públicos por ticket resuelto. Pasa el FCR quien tiene commentCount ≤ 1 y reopenedCount === 0.
Estados considerados abiertos: created, assigned, in_progress. Estados resueltos: resolved, closed.
El estado improvement_proposal era un estado falso ya migrado a la dimensión propia ticket_kind (ver Campos). Sigue en el CHECK de status durante la transición two-phase, pero las propuestas ya cuentan como abiertas por su lifecycle real (created/assigned). Excluir las propuestas de los KPIs (si CX lo pide) es un follow-up de producto.
SLAs
Objetivos en minutos (wall-clock 24/7), definidos en sla.config.ts:
| Prioridad | 1ª respuesta | Resolución |
|---|
urgent | 30 min | 6 h (360 min) |
high | 30 min | 24 h (1440 min) |
normal | 2 h (120 min) | 7 días (10080 min) |
low | 24 h (1440 min) | 30 días (43200 min) |
computeSlaStatus(ticket) devuelve el estado de la etapa actual del SLA:
- Tickets resueltos → siempre
ok.
- Antes de la 1ª respuesta → se compara el tiempo transcurrido contra el SLA de respuesta.
- Tras la 1ª respuesta → se compara contra el SLA de resolución.
| Estado | Condición |
|---|
ok | Transcurrido < 80 % del objetivo |
warning | Transcurrido ≥ 80 % del objetivo |
breached | Transcurrido ≥ 100 % del objetivo |
Por ahora el cálculo usa tiempo de reloj 24/7. La flag SLA_USE_BUSINESS_HOURS está reservada para una futura utilidad de horario laboral (zona horaria + festivos españoles + fines de semana) que podrá enchufarse sin tocar los servicios.
Tipos de ticket
resolveTicketType(ticket) deriva uno de cuatro tipos: Casa, Experiencia, Administrativo o Dormido. Regla híbrida, en orden:
- Override Dormido — si el ticket está abierto y sin actividad (
updatedAt / lastCommentAt) durante ≥ 30 días (DORMIDO_THRESHOLD_DAYS), el tipo es dormido, ignorando el resto.
- Equipo primario —
resolution_team → TICKET_TYPE_BY_TEAM.
- Categoría (fallback) —
category → TICKET_TYPE_BY_CATEGORY, cuando el equipo no resuelve un tipo.
- Default —
administrativo (TICKET_TYPE_DEFAULT).
Mapeo por equipo (TICKET_TYPE_BY_TEAM):
| Equipo | Tipo |
|---|
propiedades_y_mantenimiento, equipo_de_limpieza, manitas, promotora | Casa |
proveedores_externos (histórico) | Casa |
experiencia_del_cliente (histórico) | Experiencia |
seguro, diseño (histórico) | Administrativo |
Mapeo por categoría (TICKET_TYPE_BY_CATEGORY): facturacion / contratos / documentacion → Administrativo; reservas / check_in / check_out → Experiencia.
Aging
GET /chat/tickets/aging — cuenta los tickets abiertos agrupados por antigüedad desde su creación. Admite filtro por team. Cohortes (AGING_COHORTS), con límite inferior inclusivo y superior exclusivo:
| Cohorte | Rango |
|---|
lt1d | < 1 día |
1to3d | 1-3 días |
3to7d | 3-7 días |
gt7d | > 7 días |
Stats
GET /chat/tickets/stats?groupBy=... — agrega los tickets por una dimensión, aplicando la misma superficie de filtros que el listado (estado, prioridad, agente, propiedad, fechas y columnas nativas de Zendesk). Cada bucket devuelve count, openCount, resolvedCount y sumas/medias de coste (totalEstimatedCost, totalActualCost, avgEstimatedCost).
Valores de groupBy: status, priority, property, resolution_team, zone, category, destination, payer, repair_status, owner_approval, finance_approval, ticket_type. Para property se adjunta el nombre como label; ticket_type es una dimensión derivada (no columna) y se calcula con resolveTicketType.
Paneles del dashboard
La página (TicketsDashboardPage.tsx) abre con una rejilla de 3 columnas de 6 KPI cards: Tickets abiertos, Críticos sin resolver (urgentes + altas abiertas), Resueltos último 30 días, FCR % (objetivo 70-80 %), Reaperturas % y Backlog > 3 días (alerta si supera el umbral del 10 %). Debajo, los paneles van en una rejilla uniforme de 3 columnas (xl:grid-cols-3) para que las cards no se agranden:
| Panel | Contenido |
|---|
SlaComplianceCard | Cumplimiento de SLA de respuesta y resolución por prioridad |
BacklogAgingChart | Distribución del backlog por cohortes de antigüedad |
TicketsByTeamChart | Tickets por equipo de resolución (donut) |
TicketsByCategoryChart | Tickets por categoría (barras horizontales) |
TicketsByPayerChart | Tickets por pagador (donut) |
ResponseTimeBarChart / ResolutionTimeBarChart | Tiempos medios de respuesta / resolución por prioridad |
WeeklyTicketsChart | Volumen semanal de tickets (últimos 90 días) |
TicketsByPropertyTable | Métricas por casa — desglose por prioridad (ancho completo) |
DormantTicketsPanel | Tickets dormidos (abiertos sin actividad ≥ 30 días, ancho completo) |
Se retiraron dos paneles que perdieron valor con el modelo nuevo de equipos: TicketTypeDistributionChart (con los equipos actuales casi todo cae en “Casa”) y TicketsByPropertyChart (el gráfico era redundante con la tabla TicketsByPropertyTable, que se mantiene).
Filtros de la lista
El listado de tickets (GET /chat/tickets) admite, además de los filtros por columna (estado, prioridad, agente, propiedad, fechas y campos nativos de Zendesk), filtros sobre los campos derivados:
| Filtro | Efecto |
|---|
ticketType | Filtra por tipo derivado (casa / experiencia / administrativo / dormido) |
slaStatus | Filtra por estado de SLA (ok / warning / breached) |
ageCohort | Filtra por cohorte de antigüedad (lt1d / 1to3d / 3to7d / gt7d) |
isDormant | Filtra por la condición de dormido (true/false) |
onlyInvalid | Muestra solo los tickets marcados como inválidos (soft-delete) |
Los filtros derivados dependen de funciones puras (resolveTicketType, computeSlaStatus, isDormant) que no se pueden traducir a SQL sin duplicar las reglas. En v1 el listado trae el conjunto filtrado por columnas, deriva en memoria y pagina localmente — barato para el working set de CX (miles, no decenas de miles).