Skip to main content

Usuarios (features/users)

CRUD de usuarios (agentes y administradores) del tenant: listado paginado en servidor con filtros, alta, edición, vista de solo lectura y activación/desactivación. Contraparte del backend: User. Los roles que se asignan se gestionan en Control de acceso.

Ruta​

UsersModule se carga en shared/routes/content.routes.ts bajo users (PAGE_URLS.USERS) con canActivate: [AuthGuard, permissionGuard] y data: { permission: 'USER_READ' }. UsersRoutingModule repite el permissionGuard con el mismo permiso en la ruta vacía. Entrada de menú: sidebarMenu.users, con permission: 'USER_READ'.

Archivos​

ArchivoRol
users.module.ts / users-routing.module.tsDeclaran los cuatro componentes; importan ReactiveFormsModule, FormsModule, NgbModalModule, NgbTooltipModule, NgSelectModule, SharedModule
pages/users-page/users-page.component.*Solo compone shared-breadcrumb (users.titleSidebar, ruta padre tickets.titleSidebar) y users-users-table
components/users-table/users-table.component.*Tabla, paginación, filtros aplicados, conmutador de activo y apertura de modales; hospeda el filtro
components/users-filter/users-filter.component.*Panel de filtros (shared-accordions-toogle-panel)
components/user-modal-form/user-modal-form.component.*Modal de alta, edición y vista

Permisos​

AcrónimoEfecto
USER_READRuta y menú; también necesario en el backend para get-all y get-one
USER_CREATEBotón "agregar" (users.modal.add)
USER_UPDATEBotón "editar" por fila y conmutador de estado activo

El botón "ver" no tiene restricción adicional. No hay botón de eliminar en la UI, aunque el backend expone DELETE remove/:id con USER_DELETE (verificado en el controlador del backend).

Servicios y endpoints​

CrmUserService (core/services/api/crm-user.service.ts, providedIn: 'root') usa environment.apiUrl + API_URLS.USERS (/users) + API_ENDPOINTS.

MétodoEndpointDetalle
findUsersPaginated(page, limit, filter?)GET /users/get-allQuery page, limit más los filtros; devuelve { results, total } leyendo data.results y data.meta.total
createUser(data)POST /users/createMuestra toast de éxito con res.message (título users.title)
updateUser(id, data)PATCH /users/update/:idid es el _id del usuario; mismo toast
toggleActive(id)PATCH /users/toggle-active/:idBody vacío; devuelve el usuario actualizado

Además, CrmRoleService.getAll() (GET /roles/get-all) alimenta el selector de roles. Los endpoints de sesión (get-me, me/signature, me/avatar) viven en UserService y los usa Perfil y Auth.

:::note Rutas del backend User lista rutas REST simplificadas (GET /users/:id, POST /users). El frontend usa las rutas reales por CRUD_ROUTES (get-all, create, update/:id, toggle-active/:id); ver la nota de Endpoints. :::

Modelos​

core/interfaces/api/crm-user.interface.ts:

InterfazCampos
CrmUser_id, identifier, tenantId, username, email, roleId?: string | { _id, name, acronym }, isActive, firstname, secondFirstname?, lastName, secondLastName?, userType, createdAt?, updatedAt?
CrmUserFormusername, email, password?, roleId, firstname, secondFirstname?, lastName, secondLastName?, userType, isActive?
CrmUserFilterusername?, email?, firstname?, lastName?, userType?, isActive?: boolean
CrmUserPaginatedDataresults, meta { total, page, limit }

Constantes en core/helpers/global/crm-user.constant.ts: USER_TYPE (admin, agent), USER_TYPE_OPTIONS (etiquetas users.userTypes.admin = "Administrador" y users.userTypes.agent = "Operador") y CRM_USER_VALIDATIONS.

Listado y filtros​

UsersTableComponent guarda currentPage, currentLimit (10 por defecto) y currentFilter; fetchPage pide al servidor y fija count, page y limit en tableConfig (el ordenamiento es local, externalSorting: false). onPageChange y onLimitChange (este vuelve a la página 1) vuelven a consultar. Recargar, aplicar o limpiar filtros reinician en la página 1.

Columnas: usuario (username, título móvil), nombre, apellido (subtítulo móvil), email (no ordenable), tipo (badge con users.userTypes.<tipo>), activo, creado y acciones. La celda "activo" es un app-switch visible solo con USER_UPDATE; onToggleActive guarda togglingId para deshabilitarlo durante la petición y, en éxito, copia isActive del usuario devuelto a la fila (no recarga la tabla).

UsersFilterComponent usa un objeto plano con ngModel (username, email, firstname, lastName, userType, isActive) y emite filterApply / filterReset. onApply recorta espacios, omite campos vacíos y convierte isActive de 'true'/'false' a booleano; calcula activeFiltersCount. Los botones aplicar/limpiar los dispara el panel compartido mediante los atributos data-filter-action, detectados por los selectores de core/helpers/ui/toogle-panel.constant.ts.

Formulario (UserModalFormComponent)​

Recibe action (ADD, EDIT, VIEW) y user. Carga los roles en ngOnInit. En VIEW el formulario queda deshabilitado, se oculta el campo contraseña y el pie.

CampoValidadores
usernamerequired, minLength(2), maxLength(50)
emailrequired, email, maxLength(150)
passworden alta: required, minLength(8); en edición opcional (pista users.fields.passwordHint); maxlength HTML 100
roleIdrequired; select nativo con todos los roles (CrmRole), opción vacía "—"
userTyperequired; por defecto admin
firstname, lastNamerequired, maxLength(80)
secondFirstname, secondLastNamemaxLength(80)
isActivepor defecto true (checkbox)

_buildPayload recorta contraseña y segundos nombres y los omite si están vacíos; en edición, si no se escribió contraseña se elimina del payload. roleId se normaliza con typeof roleId === 'string' ? roleId : roleId?._id. Los errores de campo muestran words.fieldRequired, words.invalidEmail o words.minLength (con min).

Flujo​

  1. Abrir users: el guard comprueba USER_READ y la tabla pide la primera página.
  2. Filtrar: el panel emite un CrmUserFilter y se vuelve a pedir la página 1.
  3. Alta: modal → createUser → toast con el mensaje del backend → closed('saved') → recarga.
  4. Edición: modal con datos poblados → updateUser(_id, payload) → recarga.
  5. Activar o desactivar: switch en la fila → toggleActive(_id) → la fila se actualiza.

Estado, sockets y utilidades​

No usa NgRx ni sockets. El estado vive en los componentes. Reutiliza DEFAULT_NGX_DATATABLE_CONFIG, BUTTON_ACTIONS, FORMAT_FOR_DATES, app-switch, shared-tables-ngx-datatable y shared-accordions-toogle-panel (ver Compartido).

Pruebas​

Esta feature no tiene specs. CrmUserService tampoco.

Gotchas e inconsistencias​

  • Dos servicios de usuario: CrmUserService (CRUD, /users/...) y UserService (get-me, firma, avatar). UserService.API_URL apunta a /user (singular) pero no se usa; sus llamadas reales van a /users/....
  • userType limitado a dos valores en la UI (admin, agent), mientras que TYPES_USER_PERMISSIONS (helpers/global/user-management.constant.ts) enumera cinco (ver Auth, interceptores y RBAC).
  • El modal no tiene control de isSubmitting: el botón guardar solo se deshabilita por invalid, de modo que un doble clic rápido puede enviar dos peticiones (los modales de access-control sí protegen esto).
  • updateUser y toggleActive reciben _id, mientras que roles, permisos y módulos usan identifier en sus URLs.
  • No hay eliminación de usuarios en la UI; se desactivan.
  • El rol y el userType son independientes: el primero decide permisos, el segundo solo el tipo (por ejemplo, la directiva appHideForUserTypes y AdminGuard).