Skip to main content

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​

ArchivoRol
labels.module.ts / labels-routing.module.tsDeclaran 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.tsSpecs de las tres piezas con lógica (ver Pruebas)

Permisos​

AcrónimoEfecto
LABEL_READRuta y menú
LABEL_CREATEBotón "agregar"
LABEL_UPDATEBotón "editar" por fila
LABEL_DELETEBotó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étodoEndpointDetalle
findLabelsPaginated(page, limit, filter?)GET /labels/get-allQuery page, limit más name / isActive; devuelve { results, total }
createLabel(data)POST /labels/create
updateLabel(id, data)PATCH /labels/update/:idid es el _id
removeLabel(id)DELETE /labels/remove/:idLo 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:

InterfazCampos
Label_id, name, color, description?, isActive, createdAt?, updatedAt?
LabelFormname, color, description?, isActive (también se usa como tipo del filtro, Partial<LabelForm>)
LabelPaginatedDataresults, 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)​

CampoValidadores
namerequired, maxLength(100)
colorrequired; input type="color", valor inicial #000000
descriptionmaxLength(500)
isActivepor defecto true

En VIEW el formulario se deshabilita; onSubmit no hace nada. El payload es siempre { name, color, description, isActive }.

Flujo​

  1. Abrir labels: el guard comprueba LABEL_READ y se pide la primera página.
  2. Filtrar: se vuelve a pedir la página 1.
  3. Alta o edición: modal → createLabel / updateLabel → closed('saved') → recarga.
  4. Eliminar: onDelete llama removeLabel(_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 a createLabel / updateLabel, no envío en VIEW, 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 (llama removeLabel y 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 removeLabel directamente, a diferencia de Tickets bloqueados, que usa ConfirmDialogService.
  • El color inicial del modal (#000000) no coincide con DEFAULT_LABEL_COLOR (#6366f1) de labels.constant.ts; esa constante no la usa esta feature (el labels-panel del 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 rutas GET /labels; en esta rama las etiquetas se manejan con servicio HTTP sin store y las rutas reales son get-all, create, update/:id y remove/:id.
  • El módulo no importa NgSelectModule, por lo que el modal no puede usar selectores múltiples.