Etiquetas, metadatos y motivo de contacto
Componentes de la pantalla de Chat de tickets que gestionan lo que se sabe del contacto: las etiquetas (catálogo en Etiquetas, backend Label), los metadatos libres y el motivo de contacto del ticket. Las etiquetas y los metadatos pertenecen al contacto (no al ticket); ver Contactos y Contact.
Archivos
| Archivo | Rol | Estado |
|---|---|---|
components/chat/chat-labels-bar/chat-labels-bar.component.* | Barra de chips bajo la cabecera de la conversación | En uso |
components/chat/labels-quick-assign/labels-quick-assign.component.*, labels-quick-assign-overlay.service.ts, labels-quick-assign.types.ts | Selector rápido anclado a la barra | En uso |
components/chat/labels-panel/labels-panel.component.* | Panel completo de etiquetas y metadatos (acción "Motivos de contacto") | En uso |
components/chat/contact-reason-modal/contact-reason-modal.component.* | Modal para registrar el motivo de contacto del ticket | Declarado, sin llamador activo |
components/chat/contact-metadata-modal/contact-metadata-modal.component.* | Modal solo de metadatos | No declarado en ningún módulo ni usado |
components/chat/ticket-history-bar/ticket-history-bar.component.* | Barra horizontal de tickets previos | Declarado, nunca renderizado |
core/helpers/ui/labels.constant.ts | LABELS_PAGE_LIMIT = 10, DEFAULT_LABEL_COLOR = '#6366f1' |
Servicios y endpoints
| Llamada | Petición | Uso |
|---|---|---|
LabelService.findLabelsPaginated(page, limit, filter) | GET /labels/get-all (isActive: true, name si hay búsqueda) | Catálogo paginado en los tres componentes |
LabelService.createLabel | POST /labels/create | Crear desde el selector rápido |
LabelService.updateLabel | PATCH /labels/update/:id | Editar nombre y color en el panel |
LabelService.removeLabel | DELETE /labels/remove/:id | Eliminar del catálogo desde el panel |
ContactService.assignLabels(contactId, labelIds) | PATCH /contacts/:id/assign-labels con { labelIds } | Reemplaza el conjunto de etiquetas del contacto |
ContactService.updateContact(contactId, { metadata }) | PATCH /contacts/update/:id | Guarda los metadatos |
TicketService.createContactReason(ticketId, data) | POST /tickets/contact-reason/:id con { labels?, description? } | Solo desde el modal de motivo de contacto |
Barra de etiquetas (ChatLabelsBarComponent)
Muestra las etiquetas del contacto como chips (punto de color, nombre y, si existe, descripción). Con CONTACT_UPDATE (LABELS_ASSIGN_PERMISSION) añade el botón de gestión: si no hay etiquetas, un botón "agregar la primera"; si las hay, un +. Sin el permiso muestra los chips en solo lectura (o nada si no hay). Emite quickAssign con el elemento ancla. ChatConversationComponent.onLabelsQuickAssign arma un LabelsQuickAssignRequest (anchor, contactId del contacto, currentLabels) y lo emite hasta la lista, que lo atiende con LabelsQuickAssignOverlayService.
Asignación rápida (LabelsQuickAssignComponent)
Overlay anclado a la barra (se cierra con clic fuera o Escape) abierto por LabelsQuickAssignOverlayService.open(request), que devuelve un observable con el conjunto confirmado de etiquetas.
- Lista paginada (10 por página, scroll infinito, búsqueda con debounce de 300 ms) con casilla por etiqueta activa; enfoca el buscador al abrir.
- Alternar una etiqueta persiste el conjunto completo de inmediato (
assignLabels) y revierte el cambio local si falla. - Crear y asignar: si el texto de búsqueda no coincide exactamente con ninguna etiqueta, aparece "crear" (requiere
LABEL_CREATE); crea la etiqueta activa con el color por defecto y la asigna. - Tras cada guardado emite
savedcon las etiquetas del contacto; la lista las aplica de forma optimista en los tickets en memoria (applyLabelsOptimistically) y recarga.
Panel completo (LabelsPanelComponent)
Se abre como overlay centrado desde la acción de menú "Motivos de contacto" (contactReasons), con las etiquetas y los metadatos actuales del contacto del ticket.
- Etiquetas del contacto: bloque de seleccionadas (chips) y lista paginada solo con las no seleccionadas (10 por página, búsqueda con debounce de 300 ms); si una página entera ya está seleccionada, pide la siguiente automáticamente para no dejar el scroll vacío.
- Gestión del catálogo en línea: editar nombre y color de una etiqueta (
LABEL_UPDATE,PATCH /labels/update/:id) y eliminarla (LABEL_DELETE, confirmación yDELETE /labels/remove/:id; también la saca de la selección). - Metadatos: lista de pares nombre y descripción libres; agregar (
CONTACT_UPDATE, requiere nombre) y quitar. Son los que la lista muestra en "Información adicional". - Guardar (visible con
CONTACT_UPDATEoLABEL_UPDATE): ejecuta en paraleloassignLabelsyupdateContact({ metadata })conforkJoin; en éxito emiteupdated(la lista recarga) y cierra.
Modal de motivo de contacto (ContactReasonModalComponent)
Modal Ng estático (sin cierre por teclado ni clic fuera). Entradas: ticketId y requirement (none, optional o required, el campo contactReasonRequirement del departamento; ver Departamentos). Selector múltiple de etiquetas activas (20 por página, búsqueda con debounce) y descripción de hasta 500 caracteres. Con requirement === 'required' exige al menos una etiqueta y una descripción no vacía; en los otros casos siempre se puede guardar. Guarda con POST /tickets/contact-reason/:id y cierra devolviendo 'saved'.
El backend (TICKET_UPDATE) crea un registro con el departamento del ticket, las etiquetas y la descripción. El único llamador es ChatListComponent._handleResolveWithContactReason, que según el departamento del ticket abriría este modal antes de resolver (obligatorio, opcional o sin modal); ese método es privado y no se invoca desde ningún flujo de la rama, de modo que en la práctica el modal no se abre y el requisito del departamento no se aplica al resolver.
Metadatos y barra de historial sin uso
ContactMetadataModalComponent(entradascontactIdycurrentMetadata; salidascloseysaved) permite agregar y quitar pares y guardarlos conupdateContact; su función la cubre hoy el panel de etiquetas y no está declarado enChatModule.TicketHistoryBarComponent(entradastickets,activeTicketId,isLoadingMore,hasMore; salidasticketSelect,loadMore) es una barra horizontal con paginación por scroll lateral y el estado, el agente y un extracto del último mensaje de cada ticket. Está declarado enChatModulepero ninguna plantilla lo usa; el historial por contacto se ve hoy en la pestaña "history" del panel de datos.
Pruebas
No hay specs para chat-labels-bar, labels-quick-assign, labels-panel, contact-reason-modal, contact-metadata-modal ni ticket-history-bar. La visibilidad por permiso de las pestañas y el flujo de edición de contacto de la lista están cubiertos en chat-list.component.spec.ts (ver Lista de tickets).
Gotchas
- La acción "Motivos de contacto" del menú abre el panel de etiquetas y metadatos, no el modal de motivo de contacto ni llama a
POST /tickets/contact-reason/:id. - El panel de etiquetas edita y elimina etiquetas del catálogo del tenant, no solo las del contacto: la edición y el borrado afectan a la etiqueta compartida (ver Etiquetas).
- Las etiquetas del panel y del selector rápido se asignan reemplazando el conjunto completo del contacto (
assignLabelsrecibe todos los ids), no añadiendo una a una.