Skip to main content

UI de conexiones

src/app/features/connections/

connections/
├── connections.module.ts, connections-routing.module.ts, connections.constants.ts
├── pages/connections-page/ # breadcrumb + <connections-connections-table>
└── components/
├── connections-filter/ # nombre + canal
├── connections-table/ # grilla de tarjetas (nombre histórico "table")
├── connection-channel-modal/ # selector de canal
├── connection-web-modal/ # alta / edición / snippet del widget web
├── connection-whatsapp-modal/ # QR + formulario de atención
└── connection-instagram-modal/ # configuración tras el OAuth (nuevo)

La ruta connections (CONNECTION_READ) carga ConnectionsPageComponent, que solo muestra shared-breadcrumb y connections-connections-table. Para el backend de estos endpoints ver Connection.

ConnectionService​

core/services/api/connection.service.ts. Base: environment.apiUrl + /connections.

MétodoPeticiónNota
findConnectionsPaginated(page, limit, filter?)GET /connections/get-allDevuelve { results, total }
findConnectionById(id)GET /connections/get-one/:idNuevo; toma data.results[0]
createConnection(data)POST /connections/createSolo web y whatsapp-qr (ver backend)
updateConnection(id, data)PATCH /connections/update/:idMuestra toast de éxito
regenerateToken(id)PATCH /connections/regenerate-token/:idSolo canal web
removeConnection(id)DELETE /connections/remove/:id
startWhatsappSession(id)POST /connections/whatsapp/:id/startDevuelve { sessionState, qr, phoneJid? }
getWhatsappSessionStatus(id)GET /connections/whatsapp/:id/status
logoutWhatsappSession(id)DELETE /connections/whatsapp/:id/logout
authorizeInstagram({ connectionId? })POST /meta/oauth/instagram/authorizeNuevo; devuelve data.url

Modelo (connection.interface.ts)​

type ConnectionChannelType =
'web' | 'whatsapp-qr' | 'whatsapp-cloud' | 'facebook' | 'instagram';
type ConnectionStatus = 'connected' | 'disconnected' | 'pending';

interface ConnectionMetaConfig { // nuevo
accountId: string;
accountName: string;
expiresAt?: string | null;
authErrorAt?: string | null;
}

Connection incluye webConfig, whatsappConfig y, desde esta rama, metaConfig. ConnectionUpdateForm admite name, greetingMessage, farewellMessage, departmentIds, reopenToleranceMinutes, returnToLastAgent y webConfig. El token de acceso de Meta nunca llega al frontend: metaConfig solo expone cuenta y fechas.

ConnectionsTableComponent​

components/connections-table. Muestra las conexiones como tarjetas (connection-card) en una grilla responsiva (col-12 col-md-6 col-xl-4), con:

  • Paginación incremental: PAGE_SIZE = 12, botón "Cargar más" y contador n / total.
  • Esqueleto de carga (skeletonCards) en la primera carga y estado vacío.
  • Encabezado con degradado derivado del color del canal (headerGradient), icono del canal, nombre y un identificador (identifierFor): teléfono (para whatsapp-qr, derivado de phoneJid), @usuario (para instagram, de metaConfig.accountName) o dominio / prefijo de token (para web).
  • Píldora de estado: para WhatsApp QR usa whatsappConfig.sessionState (init, qr, connecting, connected, disconnected, logged_out, que el socket actualiza en vivo); para el resto, connection.status.
  • Menú de acciones (ngbDropdown) y botón de acción principal, gobernados por permisos.

Acciones por canal​

CanalMenúBotón principal
web"Código de instalación" (snippet) UPDATE"Editar" UPDATE
whatsapp-qr"Editar" y "Reconectar" UPDATE; "Cerrar sesión" DELETE (solo si hay sesión, es decir, estado distinto de init y logged_out)"Reconectar" UPDATE
instagram"Editar" UPDATE; "Reautorizar" CREATESi status === 'connected': "Editar" UPDATE; en otro caso: "Reautorizar" CREATE
todos"Eliminar" DELETE (con ConfirmDialogComponent)

El botón "+" (CONNECTION_CREATE) abre ConnectionChannelModalComponent. Según el canal elegido: web → openWebModal(ADD), whatsapp-qr → openWhatsappModal(), instagram → connectInstagram().

Estado en tiempo real​

ngOnInit se suscribe a ChatSocketService.whatsappSessionStatus$ y actualiza connection.whatsappConfig.sessionState (y phoneJid si viene) de la tarjeta correspondiente. Ver Sockets.

ConnectionChannelModalComponent​

Lista CONNECTION_CHANNELS en tarjetas de dos columnas. Los canales con enabled: false se dibujan con la clase is-disabled y la insignia "Próximamente" (connections.channels.soon); onSelect ignora su clic. Cierra el modal devolviendo el type elegido. Ver Iconos de canal.

ConnectionWebModalComponent​

Tres pasos (ModalStep): create, snippet y edit.

  • Crear: se piden uno o más dominios permitidos (con protocolo, https://mi-sitio.com) y el interruptor requiresAuth (por defecto activo). Llama a createConnection({ channelType: 'web', webConfig }) y pasa al paso snippet con el token real.
  • Snippet (showSnippetOnly abre directo aquí): genera el código <script> con window.WhaticketWidgetConfig = { token, user: {...}, ... } y carga <origin>/widget-loader.js (donde origin es window.location.origin). Si requiresAuth es falso omite el bloque user (el widget genera un visitante anónimo user-<uuid>). Incluye botón de copiar.
  • Editar: dos pestañas, identification (nombre, saludo, despedida con variables) y attention (departamentos obligatorios, tolerancia de reapertura en minutos y "volver al último agente"), más los dominios y requiresAuth. Permite cambiar el token con confirmación (regenerateToken); el widget anterior deja de funcionar.

El widget que consume este snippet es un proyecto aparte (widget-loader.js); su documentación vive en la sección de Widget del sitio.

ConnectionWhatsappModalComponent​

Dos pasos (WhatsappModalStep = 'qr' | 'form'):

  1. QR: si no hay conexión previa, crea una (createConnection({ channelType: 'whatsapp-qr' })) y arranca la sesión (startWhatsappSession). Muestra el QR que llega por el evento de socket whatsapp_qr, y reacciona a whatsapp_session_status filtrando por connectionId. Al llegar connected avanza al formulario tras 1,2 s (STEP_ADVANCE_DELAY_MS).
  2. Formulario (startOnForm lo abre directamente): name, greetingMessage, farewellMessage, departmentIds (obligatorio), reopenToleranceMinutes (por defecto 15) y returnToLastAgent (por defecto true). Las variables insertables son {{greetings}}, {{username}}, {{fullName}}, {{phone}}, {{identifier}} y {{email}}.

Si el modal creó la conexión y se cierra sin haberla guardado y sin conectar el teléfono, ngOnDestroy la elimina (removeConnection) para no dejar conexiones huérfanas.

ConnectionInstagramModalComponent​

Ver OAuth de Instagram.

Filtro​

ConnectionsFilterComponent emite filterApply (con name y channelType) y filterReset. El selector de canal itera CONNECTION_CHANNELS, por lo que incluye también los canales deshabilitados.