Skip to main content

Configuración (features/settings)

Pantalla de configuración del tenant, solo para administradores. Dos pestañas: General (idioma y zona horaria) y Atención (horario de atención, resolución automática y encuesta CSAT, en tres paneles de acordeón). Cada sección se guarda por separado contra su propio endpoint PATCH. El módulo de backend (TenantSettingsController, settings/) no tiene página propia en esta documentación; el servicio de configuración lo usa la ingesta de mensajes descrita en Message, y el tenant al que pertenece se describe en Tenant.

Ruta​

SettingsModule se carga en shared/routes/content.routes.ts bajo settings (PAGE_URLS.SETTINGS) con canActivate: [AuthGuard, permissionGuard] y data: { permission: 'SETTINGS_READ' }. SettingsRoutingModule declara en la ruta vacía canActivate: [AuthGuard, AdminGuard, permissionGuard] con el mismo permiso: además del permiso exige userType === 'admin' (si no, AdminGuard redirige a dashboard). Entrada de menú: sidebarMenu.settings, con permission: 'SETTINGS_READ' (el menú no comprueba el tipo de usuario).

Archivos​

ArchivoRol
settings.module.ts / settings-routing.module.tsDeclaran la página y los cuatro componentes; importan ReactiveFormsModule, FormsModule, NgbAccordionModule, SharedModule
pages/settings-page/settings-page.component.*Contenedor: carga la configuración, maneja pestañas y reemplaza cada sección al guardarse
components/general-settings/general-settings.component.*Formulario de idioma y zona horaria (settings-general)
components/business-hours-panel/business-hours-panel.component.*Horario de atención (settings-business-hours-panel)
components/auto-resolution-panel/auto-resolution-panel.component.* (+ .scss)Resolución automática (settings-auto-resolution-panel)
components/csat-panel/csat-panel.component.*Encuesta de satisfacción (settings-csat-panel)
settings-routing.module.spec.ts y specs de componentesVer Pruebas

Fuera de la feature: core/services/api/settings.service.ts, core/interfaces/api/settings.interface.ts, core/states/settings/* y shared/validators/notifyBeforeResolve.ts.

Permisos​

AcrónimoEfecto
SETTINGS_READRuta y menú (más AdminGuard)
SETTINGS_UPDATEBotón "guardar" de cada panel (*appShowWithPermission). "Cancelar" siempre se ve

Servicios y endpoints​

SettingsService (providedIn: 'root') usa API_URLS.SETTINGS (/settings). Rutas verificadas contra tenant-settings.controller.ts y SETTINGS_ROUTES:

MétodoEndpointPermiso en el backendUso
getSettings()GET /settings/get-allSETTINGS_READDevuelve data.results[0] (un único documento por tenant)
updateGeneral(payload)PATCH /settings/generalSETTINGS_UPDATEPestaña General
updateSchedule(payload)PATCH /settings/attention/scheduleSETTINGS_UPDATEHorario de atención
updateAutoResolution(payload)PATCH /settings/attention/auto-resolutionSETTINGS_UPDATEResolución automática
updateCsat(payload)PATCH /settings/attention/csatSETTINGS_UPDATECSAT

Cada PATCH devuelve el TenantSettings completo y el panel usa solo su sección. El backend tiene además PATCH /settings/attention/scheduled-messages, sin método en el servicio ni panel en esta rama.

Modelos​

core/interfaces/api/settings.interface.ts:

InterfazCampos
GeneralSettingslanguage, timezone
BusinessHoursSettingsenabled, sendAfterChatbot, useWabot, absenceMessage, days: BusinessHoursDay[]
BusinessHoursDayday: WeekDay, enabled, startTime, endTime (WEEK_DAYS, lunes a domingo)
AutoResolutionSettingsenabled, sendFarewellMessage, resolveInMinutes, notifyInMinutes, notificationMessage, resolveInactiveWabot
CsatSettingsenabled, sendOnAutoResolve, sendAsInteractiveList, requestMessage, thankYouMessage, requestComment, perRatingMessage: PerRatingMessage (rating1 a rating5)
ScheduledMessagesSettingsenabled, allowNotes, minLeadTimeMinutes, maxHorizonDays, maxPendingPerTicket, maxPendingPerAgent, onOwnerInactive, onTicketResolved, cancelledSystemMessage (declarada para uso futuro; la pantalla no la edita)
TenantSettings_id?, tenantId?, las cuatro secciones, scheduledMessages?
SettingsStatesettings, loading, error

Página (SettingsPageComponent)​

En ngOnInit llama getSettings() y apaga isLoading (también en error). activeTab es 'general' o 'attention' (SettingsTab); onTabChange lo cambia. La pestaña Atención usa un ngbAccordion con tres ítems (business-hours abierto por defecto, auto-resolution, csat); el contenido va en ng-template para que los paneles se creen al expandirse. Cada panel emite saved con su sección actualizada y la página la reemplaza en settings (onGeneralSaved, onBusinessHoursSaved, onAutoResolutionSaved, onCsatSaved).

Paneles y formularios​

Patrón común: un setter @Input() settings toma una copia (_savedSnapshot) y vuelca el valor al formulario; onSave evita doble envío (isSaving) y formularios inválidos, llama al servicio, y en éxito actualiza la copia y el formulario con lo devuelto, muestra el toast settings.toast.updated (título settings.title) y emite saved; onCancel restaura la copia guardada. Los mensajes usan app-rich-text-editor con RICH_TEXT_VALIDATIONS.MESSAGE.MAX_LENGTH (1000) y app-switch para los booleanos.

General (GeneralSettingsComponent): language (required, por defecto es; opciones AVAILABLE_LANGUAGES es y en) y timezone (required; select con moment.tz.names() de moment-timezone). Tras guardar despacha change({ language }) del slice de idioma, de modo que la interfaz cambia de idioma de inmediato (ver i18n y tema); si la petición falla no se despacha.

Horario de atención (BusinessHoursPanelComponent): enabled, sendAfterChatbot y useWabot (las dos últimas con la etiqueta "próximamente", words.comingSoon), absenceMessage y un FormArray de siete días con enabled, startTime y endTime (input type="time"). Si la configuración no trae días se generan siete por defecto, deshabilitados, de 09:00 a 18:00. No hay validadores.

Resolución automática (AutoResolutionPanelComponent):

CampoValidadores
enabled—
sendFarewellMessagepor defecto true
resolveInMinutesrequired, min(1), por defecto 5
notifyInMinutesmin(1), por defecto 1
notificationMessagemáximo 1000 (editor)
resolveInactiveWabot"próximamente"

El grupo tiene el validador notifyBeforeResolveValidator: el aviso (notifyInMinutes) debe ser estrictamente menor que resolveInMinutes (error notifyNotBeforeResolve, mensaje settings.attention.autoResolution.notifyLessThanResolve); no se evalúa con la resolución apagada. Con enabled en falso, sendFarewellMessage, resolveInMinutes, notifyInMinutes y notificationMessage se deshabilitan (_syncEnabledState), y el payload usa getRawValue() para incluirlos igualmente. Las mismas reglas aplican a la resolución por departamento, que sobreescribe a la del tenant (ver Departamentos).

CSAT (CsatPanelComponent): enabled, sendOnAutoResolve, sendAsInteractiveList (con "próximamente"), requestComment, requestMessage y thankYouMessage (editor, máximo 1000) y perRatingMessage (FormGroup con cinco input type="text", rating1 a rating5). Sin validadores.

Estado NgRx​

Existe un slice settings (core/states/settings/, registrado en app.state.ts junto con SettingsEffects): acciones loadSettings, loadSettingsSuccess y loadSettingsFailure; el efecto llama getSettings() y mapea el error a settings.toast.error; selectores selectTenantSettings, selectSettingsLoading y selectSettingsError. Ningún código dentro de src/app despacha loadSettings, y la página de configuración carga por HTTP directo, no por el store. El slice se creó para que el compositor leyera scheduledMessages.enabled, pero ese dato hoy sale del perfil de usuario (user.featureFlags.scheduledMessagesEnabled, selector selectScheduledMessagesEnabled en auth.selectors.ts) porque los agentes sin SETTINGS_READ no pueden consultar este endpoint. Ver Estado.

No usa sockets.

Flujo​

  1. Abrir settings: AuthGuard, AdminGuard y permissionGuard (SETTINGS_READ); se pide GET /settings/get-all.
  2. Editar un panel y pulsar guardar (visible con SETTINGS_UPDATE): PATCH de esa sección → toast → el panel y la página se actualizan.
  3. En General, además cambia el idioma de la interfaz.
  4. Cancelar restaura el último estado guardado del panel.

Pruebas​

Seis specs de Karma/Jasmine, todas dentro de la feature más la del slice:

  • settings-routing.module.spec.ts: la ruta lleva SETTINGS_READ y contiene permissionGuard, AuthGuard y AdminGuard.
  • settings-page.component.spec.ts: carga con getSettings, pestaña inicial general, cambio de pestaña, isLoading y reemplazo de la sección general al guardar.
  • general-settings.component.spec.ts: población del formulario, updateGeneral, emisión de saved, despacho de change solo en éxito, cancelar y isSaving ante error.
  • business-hours-panel.component.spec.ts: FormArray de siete días, campos de primer nivel, updateSchedule, saved, cancelar y error.
  • auto-resolution-panel.component.spec.ts: población, updateAutoResolution (sin control farewellMessage), saved, cancelar, validadores (obligatorio, mínimo 1, aviso anterior a la resolución, sin error cruzado si está apagada) y habilitar o deshabilitar dependientes.
  • csat-panel.component.spec.ts: población con perRatingMessage anidado, updateCsat, saved, cancelar y error.

El slice tiene settings.effects.spec.ts y settings.reducers.spec.ts en core/states/settings/. No hay spec de SettingsService.

Gotchas e inconsistencias​

  • Doble criterio de acceso: la entrada de menú solo declara permission: 'SETTINGS_READ', mientras que la ruta exige además userType administrador; un usuario con el permiso pero de otro tipo es redirigido por AdminGuard al dashboard.
  • El slice settings no se alimenta: loadSettings no se despacha en ningún sitio; selectTenantSettings queda siempre en null (ver arriba).
  • Hay opciones visibles marcadas "próximamente" (sendAfterChatbot, useWabot, resolveInactiveWabot, sendAsInteractiveList) que sí viajan en el payload.
  • Los paneles inician con los datos de la primera carga; si otro administrador cambia la configuración, esta pantalla no se entera hasta recargar.
  • El backend permite configurar scheduledMessages (PATCH attention/scheduled-messages), pero la UI no lo expone, por lo que solo se puede cambiar por API.
  • getSettings asume results[0]: si el tenant no tuviera documento de configuración, la página queda sin pestañas (se oculta todo cuando settings es nulo).