Skip to main content

Tickets bloqueados (features/locked-tickets)

Vista de administración que lista todos los tickets bloqueados del tenant (un ticket queda bloqueado por el agente que lo "mantiene") y permite liberarlos a la fuerza. Es una salida de emergencia para el bloqueo de tickets, protegida por un permiso propio. El módulo de backend es Ticket; el uso normal del bloqueo está en el Chat.

Ruta​

LockedTicketsModule se carga en shared/routes/content.routes.ts bajo locked-tickets (PAGE_URLS.LOCKED_TICKETS) con canActivate: [AuthGuard, permissionGuard] y data: { permission: 'TICKET_UNLOCK_ANY' }. LockedTicketsRoutingModule repite permissionGuard con el mismo permiso. Entrada de menú: lockedTickets.titleSidebar, dentro del submenú de mensajería (sidebarMenu.messaging), con permission: 'TICKET_UNLOCK_ANY'. El comentario en nav.constant.ts y en la ruta aclara que se usa un permiso dedicado y no TICKET_READ ni TICKET_UPDATE.

Archivos​

ArchivoRol
locked-tickets.module.ts / locked-tickets-routing.module.tsDeclaran página y tabla; importan NgbTooltipModule, TranslateModule, SharedModule (sin formularios)
pages/locked-tickets-page/locked-tickets-page.component.*Compone shared-breadcrumb (lockedTickets.titleSidebar, ruta padre tickets.titleSidebar) y locked-tickets-table
components/locked-tickets-table/locked-tickets-table.component.*Tabla, paginación y acción de liberar
components/locked-tickets-table/locked-tickets-table.component.spec.tsSpec de la tabla

Permisos​

AcrónimoEfecto
TICKET_UNLOCK_ANYRuta, menú y botón "liberar" (*appShowWithPermission)

Servicios y endpoints​

No tiene servicio propio: reutiliza TicketService (core/services/api/ticket.service.ts), como explica el comentario del componente.

MétodoEndpointDetalle
getLockedTickets({ page, limit })GET /tickets/get-all con lockedOnly=trueDelega en getTickets; el backend añade el filtro lockedBy != null. Respuesta data.results y data.meta.total
forceUnlockTicket(id)PATCH /tickets/force-unlock/:idBody vacío; el backend exige TICKET_UNLOCK_ANY y difunde el cambio de bloqueo por socket

:::caution Permiso del listado En el backend, GET get-all exige TICKET_READ (ticket.controller.ts), mientras que la ruta del frontend solo pide TICKET_UNLOCK_ANY. Un usuario con TICKET_UNLOCK_ANY pero sin TICKET_READ entraría a la pantalla y el listado fallaría. :::

Modelos​

Usa Ticket y TicketsFilter de core/interfaces/api/ticket.interface.ts. Campos relevantes: lockedBy?: string | null (id del agente), lockedByUser?: TicketAssignedUser | null (poblado por una consulta lockedByUserStages() solo en lecturas de lista y detalle), contact e identifier. TicketsFilter.lockedOnly?: boolean activa el filtro. No define interfaces propias.

Listado​

LockedTicketsTableComponent guarda currentPage y currentLimit (10) y pide una página al servidor en fetchPage; actualiza count, page y limit de tableConfig. No tiene filtros. Columnas (ninguna ordenable): identificador (título móvil), contacto (subtítulo móvil), bloqueado por y acciones.

  • getContactName: firstname más lastName; si no hay, username; si no hay contacto, vacío (se muestra —).
  • getLockedByName: igual con lockedByUser; si queda vacío se muestra lockedTickets.fields.lockedByUserUnknown.
  • Botón de recarga (words.reloadTable) que vuelve a la página 1.

Flujo​

  1. Abrir locked-tickets: el guard comprueba TICKET_UNLOCK_ANY y se pide la primera página con lockedOnly.
  2. Liberar: releaseTicket abre ConfirmDialogService.confirm (título, mensaje y etiquetas lockedTickets.releaseConfirm.*, icono warning).
  3. Si se confirma, forceUnlockTicket(_id) y, en éxito, loadTickets() (vuelve a la página 1). Si se cancela, no se llama al backend.

Estado, sockets y utilidades​

Esta feature no usa NgRx ni sockets: el comentario explica que reutilizar el slice de tickets del chat corrompería su filtro activo y la lógica removeIfFilterMismatch. El backend sí difunde ticket_lock_changed al liberar; el chat-socket.service.ts lo procesa (ticketLockChangedFromSocket) para el chat, pero esta tabla no lo escucha, así que no se refresca sola. Reutiliza DEFAULT_NGX_DATATABLE_CONFIG, shared-tables-ngx-datatable, ConfirmDialogService y *appShowWithPermission (ver Compartido).

Pruebas​

locked-tickets-table.component.spec.ts (12 casos): carga inicial con página 1 y límite 10, población de tickets, isLoading, onPageChange, onLimitChange, getContactName, getLockedByName y releaseTicket (no llama a forceUnlockTicket si se cancela; llama y recarga si se confirma). No hay spec de la página ni del módulo de rutas.

Gotchas e inconsistencias​

  • Desajuste de permisos entre ruta (TICKET_UNLOCK_ANY) y listado (TICKET_READ), descrito arriba.
  • La lista no se actualiza por socket: otro administrador o el propio agente pueden liberar el ticket y la fila seguirá visible hasta recargar.
  • getLockedByName accede a firstname, lastName y username con (user as any) aunque TicketAssignedUser ya los declara opcionales; el cast es innecesario.
  • getLockedTickets pasa lockedOnly por HttpParams con fromObject, de modo que viaja como la cadena true.
  • La acción no ofrece motivo ni nota: libera el bloqueo sin más datos.