React Native SoluCX Widget

Paquete React Native para mostrar encuestas de satisfacción (NPS, CSAT, CES) de la plataforma SoluCX dentro de tu app.

Visión General

El widget carga la encuesta vía WebView y gestiona automáticamente las opciones de visualización (tipo, altura, frecuencia, muestreo y reglas de supresión). Solo necesitas informar los datos del cliente/transacción y el SDK se encarga del resto.

Modos de visualización: Bottom, Top, Modal e Inline.

Instalación

npm
pnpm
bun
yarn
npx expo install @solucx/react-native-solucx-widget

Inicio Rápido

Existen dos formas de integrar el widget. Elige la que mejor se adapte a tu proyecto:


Integración por Función (recomendado)

Esta es la forma más flexible. Montas un SoluCXWidgetHost una vez en el root del app y disparas el widget desde cualquier lugar usando SoluCXWidget.create(...).show().

Paso 1: Monta el SoluCXWidgetHost en el root

El SoluCXWidgetHost es el contenedor que renderiza el widget. Debe montarse una única vez, en el componente raíz.

// App.tsx
import { SoluCXWidgetHost } from '@solucx/react-native-solucx-widget';

export default function App() {
  return (
    <>
      <NavigationContainer>
        <Stack.Navigator>{/* tus pantallas */}</Stack.Navigator>
      </NavigationContainer>

      <SoluCXWidgetHost />
    </>
  );
}

Paso 2: Dispara el widget desde cualquier pantalla

Llama a SoluCXWidget.create() cuando quieras mostrar la encuesta. Informa solo la clave, el tipo de visualización y los datos del cliente — el SDK busca las demás configuraciones (altura, frecuencia, muestreo, etc.) automáticamente del panel de la jornada.

// CheckoutScreen.tsx
import { SoluCXWidget } from '@solucx/react-native-solucx-widget';

function onCheckoutComplete() {
  SoluCXWidget
    .create('TU_CLAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'pos_venta',
      customer_id: 'cliente_123',
      email: '[email protected]',
      name: 'Juan García',
    })
    .show();
}

Eso es todo. El SDK consulta la API, aplica las reglas de visualización configuradas en el panel y decide si el widget debe aparecer o no.

Paso 3 (opcional): Sobrescribe las opciones localmente

Si necesitas sobrescribir las configuraciones del panel para un caso específico, usa .setOptions(). Cuando se llama, el widget ignora la configuración remota y usa solo los valores proporcionados.

SoluCXWidget
  .create('TU_CLAVE_SOLUCX')
  .setType('modal')
  .setData({ journey: 'nps_trimestral', customer_id: 'cliente_789' })
  .setOptions({
    height: 600,
    samplingPercentage: 50,
    maxAttemptsAfterDismiss: 5,
    waitDaysAfterWidgetDismiss: 90,
  })
  .show();

Integración por Componente (alternativo)

Si prefieres controlar la visualización por el propio JSX, usa SoluCXWidgetView directamente. En este modo, no es necesario montar SoluCXWidgetHost — el componente es autosuficiente.

// CheckoutScreen.tsx
import { SoluCXWidgetView } from '@solucx/react-native-solucx-widget';

export function CheckoutScreen() {
  return (
    <SoluCXWidgetView
      soluCXKey="TU_CLAVE_SOLUCX"
      type="inline"
      data={{
        journey: 'pos_venta',
        customer_id: 'cliente_123',
        email: '[email protected]',
        name: 'Juan García',
      }}
      callbacks={{
        onOpened: (userId) => {
          console.log('Widget mostrado para:', userId);
        },
      }}
    />
  );
}

El componente se renderiza en la posición exacta donde se declara en JSX. Ideal para el modo inline.

Playground

Modos de Visualización

ModoComportamiento
bottomFijo en la parte inferior de la pantalla (por defecto)
topFijo en la parte superior de la pantalla
modalSuperposición centrada que bloquea la interacción con el fondo
inlineInsertado en el flujo del layout, en la posición donde el componente es declarado
// Bottom: barra fija en la parte inferior (por defecto)
SoluCXWidget.create('KEY').setType('bottom').setData(data).show();

// Top: barra fija en la parte superior
SoluCXWidget.create('KEY').setType('top').setData(data).show();

// Modal: superposición centrada que bloquea el fondo
SoluCXWidget.create('KEY').setType('modal').setData(data).show();

// Inline: integrado en el flujo del layout (respeta la posición en JSX)
SoluCXWidget.create('KEY').setType('inline').setData(data).show();

Ejemplo Completo con Callbacks

Este ejemplo muestra todas las etapas del ciclo de vida del widget — de la preparación al cierre:

import { SoluCXWidget } from '@solucx/react-native-solucx-widget';

function showSurvey() {
  SoluCXWidget
    .create('TU_CLAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'atención',
      customer_id: 'cliente_456',
      email: '[email protected]',
      name: 'María Santos',
      employee_id: 'agente_01',
      employee_name: 'Carlos',
    })
    .setCallbacks({
      onPreOpen: (userId) => {
        console.log('Preparando widget para:', userId);
      },
      onOpened: (userId) => {
        console.log('Widget mostrado para:', userId);
        analytics.track('survey_shown', { userId });
      },
      onBlocked: (reason) => {
        // Reglas de visualización impidieron la visualización
        console.log('Widget bloqueado:', reason);
        // Ej: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL", "BLOCKED_BY_SAMPLING"
      },
      onClosed: () => {
        console.log('Usuario cerró el widget');
      },
      onCompleted: (userId) => {
        console.log('Encuesta completada por:', userId);
        analytics.track('survey_completed', { userId });
      },
      onPartialCompleted: (userId) => {
        console.log('Encuesta parcialmente completada por:', userId);
      },
      onError: (message) => {
        console.error('Error en el widget:', message);
      },
    })
    .show();
}

Cerrar el widget programáticamente

import { SoluCXWidget } from '@solucx/react-native-solucx-widget';

SoluCXWidget.dismiss();

API

SoluCXWidget (clase principal)

Métodos de construcción (builder pattern)

MétodoDescripción
SoluCXWidget.create(soluCXKey)Crea una nueva instancia del widget
.setType(type)Define el tipo: 'bottom', 'top', 'modal', 'inline'
.setData(data)Define los datos del usuario/transacción
.setOptions(options)Define opciones del widget locales (opcional — si no se llama, busca de la API)
.setCallbacks(callbacks)Define callbacks de eventos
.show()Muestra el widget

Métodos estáticos

MétodoDescripción
SoluCXWidget.dismiss()Cierra el widget programáticamente

Métodos de instancia (gestión de logs)

Usan los datos del builder (instanceKey, journey, userId) para acceder a los logs de la combinación correcta.

MétodoDescripción
.getWidgetLogs()Retorna los logs del widget
.overrideTimestamp(field, date)Sobrescribe una fecha de evento (debug/pruebas)
.clearWidgetLogs()Limpia todos los logs

WidgetData

Datos enviados para identificar al cliente y el contexto de la encuesta.

interface WidgetData {
  // Identificador de la jornada (obligatorio en la práctica)
  journey?: string;

  // Identificación del usuario
  customer_id?: string;
  name?: string;
  email?: string;
  phone?: string;
  phone2?: string;
  document?: string;
  birth_date?: string;     // Formato: YYYY-MM-DD

  // Contexto de la transacción
  transaction_id?: string;
  store_id?: string;
  store_name?: string;
  employee_id?: string;
  employee_name?: string;
  amount?: number;
  score?: number;

  // ID del formulario (solo para Flex Survey)
  form_id?: string;

  // Parámetros personalizados (usa el prefijo param_)
  [key: `param_${string}`]: string | number | undefined;
}

WidgetOptions

Configura las opciones de visualización del widget (tipo, altura, frecuencia, muestreo y reglas de supresión). Opcional — si setOptions() no se llama, el widget busca estas configuraciones automáticamente de la API (panel de configuración de la jornada).

interface WidgetOptions {
  enabled?: boolean;
  samplingPercentage?: number;
  type?: string;
  height?: number;
  maxAttemptsAfterDismiss?: number;
  waitDaysAfterWidgetDisplayAttempt?: number;
  waitDaysAfterWidgetFirstAccess?: number;
  waitDaysAfterWidgetDisplay?: number;
  waitDaysAfterWidgetDismiss?: number;
  waitDaysAfterWidgetSubmit?: number;
  waitDaysAfterWidgetPartialSubmit?: number;
}
PropiedadTipoPor defectoDescripción
enabledbooleantrueCuando false, bloquea inmediatamente sin validar otras reglas
samplingPercentagenumber100Porcentaje de usuarios que verán el widget (0-100). 100 = todos ven. 0 = nadie ve
typestring'bottom'Tipo de visualización: 'bottom', 'top', 'modal', 'inline'. Puede ser configurado remotamente por el panel de la jornada
heightnumber600Altura fija del widget en píxeles. 0 = automático. Puede ser configurado remotamente por el panel de la jornada
maxAttemptsAfterDismissnumber0Número máximo de visualizaciones por experiencia. 0 = sin límite. Se resetea cuando el ID de la experiencia cambia
waitDaysAfterWidgetDisplayAttemptnumber0Días a esperar después de un intento de visualización bloqueado
waitDaysAfterWidgetFirstAccessnumber0Días a esperar después del primer acceso del usuario a la jornada
waitDaysAfterWidgetDisplaynumber0Días a esperar después de cualquier visualización del widget
waitDaysAfterWidgetDismissnumber0Días a esperar después de que el usuario cierre el widget
waitDaysAfterWidgetSubmitnumber0Días a esperar después de envío completo de la encuesta
waitDaysAfterWidgetPartialSubmitnumber0Días a esperar después de envío parcial de la encuesta

WidgetCallbacks

Funciones llamadas en cada etapa del ciclo de vida del widget. Todas son opcionales.

interface WidgetCallbacks {
  onPreOpen?: (userId: string) => void;
  onOpened?: (userId: string) => void;
  onBlocked?: (blockReason: BlockReason | string | undefined) => void;
  onClosed?: () => void;
  onCompleted?: (userId: string) => void;
  onPartialCompleted?: (userId: string) => void;
  onError?: (message: string) => void;
  onPageChanged?: (page: string) => void;
  onQuestionAnswered?: () => void;
  onResize?: (height: string) => void;
}

Ciclo de vida

Al llamar .show(), el SDK ejecuta los siguientes pasos:

  1. Verifica las opciones locales (enabled, muestreo, intentos, intervalos)
  2. Si bloqueado: llama a onBlocked con el motivo y el widget no aparece
  3. Si liberado: solicita la URL de la encuesta a la API SoluCX (preflight)
  4. Llama a onPreOpen → muestra el widget → llama a onOpened
  5. Durante la encuesta, el usuario interactúa con el formulario
  6. Cuando el usuario responde la nota principal (NPS/CSAT/CES): llama a onPartialCompleted
  7. Cuando el usuario completa toda la encuesta: llama a onCompleted
  8. Al cerrar (por el usuario o automáticamente): llama a onClosed
  9. Si ocurre un error en cualquier etapa: llama a onError

Descripción de los callbacks

CallbackParámetroCuándo se llama
onPreOpenuserId: stringInmediatamente antes de que el widget sea mostrado, después de pasar la validación de reglas de visualización
onOpeneduserId: stringCuando el widget se muestra en pantalla
onBlockedreason: BlockReasonCuando las reglas de visualización impiden que el widget se muestre. Ver BlockReason
onClosed-Cuando el widget se cierra, ya sea por el usuario o automáticamente
onCompleteduserId: stringCuando el usuario completa toda la encuesta (todas las etapas)
onPartialCompleteduserId: stringCuando el usuario responde la nota principal (NPS/CSAT/CES)
onErrormessage: stringCuando ocurre un error en la encuesta (encuesta expirada, ya evaluada, fallo de red)
onPageChangedpage: stringCuando el usuario navega entre páginas (Flex Survey)
onQuestionAnswered-Cuando el usuario responde una pregunta (Flex Survey)
onResizeheight: stringCuando la altura del widget cambia

BlockReason

Motivos por los cuales las reglas de visualización impiden que el widget se muestre. Estos bloqueos son locales (en el dispositivo) y no representan errores de la API.

type BlockReason =
    | "BLOCKED_BY_DISABLED"
    | "BLOCKED_BY_SAMPLING"
    | "BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED"
    | "BLOCKED_BY_MAX_ATTEMPTS"
    | "BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVAL"
    | "BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVAL"
    | "BLOCKED_BY_WIDGET_DISPLAY_INTERVAL"
    | "BLOCKED_BY_WIDGET_DISMISS_INTERVAL"
    | "BLOCKED_BY_WIDGET_SUBMIT_INTERVAL"
    | "BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVAL";
RazónDescripción
BLOCKED_BY_DISABLEDWidget deshabilitado (enabled: false)
BLOCKED_BY_SAMPLINGUsuario no fue seleccionado por el muestreo (samplingPercentage)
BLOCKED_BY_TRANSACTION_ALREADY_ANSWEREDTransacción ya fue respondida anteriormente
BLOCKED_BY_MAX_ATTEMPTSNúmero máximo de intentos alcanzado para esta experiencia
BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVALDentro del intervalo después de intento de visualización
BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVALDentro del intervalo después del primer acceso a la jornada
BLOCKED_BY_WIDGET_DISPLAY_INTERVALDentro del intervalo después de visualización
BLOCKED_BY_WIDGET_DISMISS_INTERVALDentro del intervalo después de cierre
BLOCKED_BY_WIDGET_SUBMIT_INTERVALDentro del intervalo después de envío completo
BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVALDentro del intervalo después de envío parcial

Múltiples Widgets

Cada combinación de instanceKey + journey + userId opera con logs independientes:

// Widget de la jornada "pos_venta" para user_123
SoluCXWidget.create('KEY').setData({ journey: 'pos_venta', customer_id: 'cliente_123' }).show();

// Widget de la jornada "atencion" para cliente_123
SoluCXWidget.create('KEY').setData({ journey: 'atencion', customer_id: 'cliente_123' }).show();

// Cada uno tiene sus propios contadores, timestamps y logs

Solución de Problemas

El widget no aparece

  • Verifica que la clave SoluCX (soluCXKey) sea válida
  • Verifica la conectividad a internet
  • Usa el callback onBlocked para verificar si las reglas de visualización están impidiendo la visualización
  • Usa el callback onError para verificar si hay errores en la encuesta

Los callbacks no se llaman

  • Confirma que el objeto callbacks se está pasando vía .setCallbacks() o como prop del SoluCXWidgetView
  • Verifica la consola para mensajes de error del WebView

Layout roto

  • Define una altura fija vía setOptions({ height: 600 }) si el redimensionamiento automático no funciona bien en tu layout
  • Para el modo inline, asegúrate de que el componente padre tiene espacio suficiente

Compatibilidad

VersiónReact NativeExpoiOSAndroid
2.x0.70+50+11+API 21+

Licencia

Este paquete es propiedad de SoluCX. El uso está restringido a clientes licenciados de la plataforma SoluCX.