Etiquetas (features/labels)
CRUD de etiquetas de color que se asignan a contactos: listado paginado en servidor con filtros, alta, edición, vista de solo lectura y eliminación. Contraparte del backend: Label. Las etiquetas se asignan desde Contactos y desde el panel del Chat.
Ruta
LabelsModule se carga en shared/routes/content.routes.ts bajo labels (PAGE_URLS.LABELS) con canActivate: [AuthGuard, permissionGuard] y data: { permission: 'LABEL_READ' }. LabelsRoutingModule repite permissionGuard con el mismo permiso en la ruta vacía. Entrada de menú: labels.titleSidebar, con permission: 'LABEL_READ'.
Archivos
| Archivo | Rol |
|---|---|
labels.module.ts / labels-routing.module.ts | Declaran los cuatro componentes; importan ReactiveFormsModule, FormsModule, NgbModalModule, NgbTooltipModule, SharedModule (sin NgSelectModule) |
pages/labels-page/labels-page.component.* | Compone shared-breadcrumb y labels-labels-table |
components/labels-table/labels-table.component.* | Tabla, paginación, filtros aplicados, apertura del modal y eliminación; hospeda el filtro |
components/labels-filter/labels-filter.component.* | Filtro por nombre y estado |
components/label-modal-form/label-modal-form.component.* | Modal de alta, edición y vista |
*.spec.ts | Specs de las tres piezas con lógica (ver Pruebas) |
Permisos
| Acrónimo | Efecto |
|---|---|
LABEL_READ | Ruta y menú |
LABEL_CREATE | Botón "agregar" |
LABEL_UPDATE | Botón "editar" por fila |
LABEL_DELETE | Botón "eliminar" por fila (labels.modal.delete) |
El botón "ver" no tiene restricción adicional. Los mismos acrónimos protegen los endpoints en label.controller.ts.
Servicios y endpoints
LabelService (core/services/api/label.service.ts, providedIn: 'root') usa API_URLS.LABELS (/labels). Las mutaciones muestran toast de éxito con res.message (título labels.title).
| Método | Endpoint | Detalle |
|---|---|---|
findLabelsPaginated(page, limit, filter?) | GET /labels/get-all | Query page, limit más name / isActive; devuelve { results, total } |
createLabel(data) | POST /labels/create | |
updateLabel(id, data) | PATCH /labels/update/:id | id es el _id |
removeLabel(id) | DELETE /labels/remove/:id | Lo llama onDelete |
findLabelsPaginated lo consumen además el filtro y el modal de Contactos y las pantallas de chat (labels-panel, labels-quick-assign, chat-filter-panel, contact-reason-modal).
Modelos
core/interfaces/api/label.interface.ts:
| Interfaz | Campos |
|---|---|
Label | _id, name, color, description?, isActive, createdAt?, updatedAt? |
LabelForm | name, color, description?, isActive (también se usa como tipo del filtro, Partial<LabelForm>) |
LabelPaginatedData | results, meta { total, page, limit } |
Constantes: LABEL_VALIDATIONS en core/helpers/global/label.constant.ts (NAME 100, DESCRIPTION 500, COLOR 7) y LABELS_PAGE_LIMIT / DEFAULT_LABEL_COLOR (#6366f1) en core/helpers/ui/labels.constant.ts.
Listado y filtros
Mismo patrón que Contactos: currentPage, currentLimit (10) y currentFilter; fetchPage fija count, page y limit en tableConfig; ordenamiento local. Columnas: color (muestra del color, no ordenable), nombre (título móvil), descripción (subtítulo móvil), activo (insignia), creado y acciones.
LabelsFilterComponent guarda { name, isActive }; onApply recorta el nombre, omite vacíos, convierte isActive a booleano y calcula activeFiltersCount.
Formulario (LabelModalFormComponent)
| Campo | Validadores |
|---|---|
name | required, maxLength(100) |
color | required; input type="color", valor inicial #000000 |
description | maxLength(500) |
isActive | por defecto true |
En VIEW el formulario se deshabilita; onSubmit no hace nada. El payload es siempre { name, color, description, isActive }.
Flujo
- Abrir
labels: el guard compruebaLABEL_READy se pide la primera página. - Filtrar: se vuelve a pedir la página 1.
- Alta o edición: modal →
createLabel/updateLabel→closed('saved')→ recarga. - Eliminar:
onDeletellamaremoveLabel(_id)y recarga la página 1.
Estado, sockets y utilidades
No usa NgRx ni sockets. Reutiliza DEFAULT_NGX_DATATABLE_CONFIG, BUTTON_ACTIONS, FORMAT_FOR_DATES, shared-tables-ngx-datatable y shared-breadcrumb (ver Compartido).
Pruebas
Tres specs de Karma/Jasmine:
label-modal-form.component.spec.ts: acciones ADD, EDIT y VIEW (título, color por defecto#000000, validez del nombre, longitud máxima, llamada acreateLabel/updateLabel, no envío enVIEW,closeModal).labels-filter.component.spec.ts: estado inicial, recorte del nombre,isActive'true'/'false'/ vacío, conteo de filtros y reinicio.labels-table.component.spec.ts: carga inicial, cambio de filtro, de página y de límite,onDelete(llamaremoveLabely recarga) y apertura del modal con ADD / EDIT.
No hay spec de LabelService ni de la página.
Gotchas e inconsistencias
- Eliminar no pide confirmación: el botón llama
removeLabeldirectamente, a diferencia de Tickets bloqueados, que usaConfirmDialogService. - El color inicial del modal (
#000000) no coincide conDEFAULT_LABEL_COLOR(#6366f1) delabels.constant.ts; esa constante no la usa esta feature (ellabels-paneldel chat declara la suya propia). - El modal no controla
isSubmitting; un doble clic puede duplicar la petición. - Label describe un uso con NgRx (
loadLabels,selectAllLabels) y rutasGET /labels; en esta rama las etiquetas se manejan con servicio HTTP sin store y las rutas reales songet-all,create,update/:idyremove/:id. - El módulo no importa
NgSelectModule, por lo que el modal no puede usar selectores múltiples.