React Native SoluCX Widget

Pacote React Native para exibir pesquisas de satisfação (NPS, CSAT, CES) da plataforma SoluCX dentro do seu app.

Visão Geral

O widget carrega a pesquisa via WebView e gerencia automaticamente as opções de exibição (tipo, altura, frequência, amostragem e regras de supressão). Você só precisa informar os dados do cliente/transação e o SDK cuida do resto.

Modos de exibição: Bottom, Top, Modal e Inline.

Instalação

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

Início Rápido

Existem duas formas de integrar o widget. Escolha a que melhor se encaixa no seu projeto:


Integração por Função (recomendado)

Essa é a forma mais flexível. Você monta um SoluCXWidgetHost uma vez no root do app e dispara o widget de qualquer lugar usando SoluCXWidget.create(...).show().

Passo 1: Monte o SoluCXWidgetHost no root

O SoluCXWidgetHost é o contêiner que renderiza o widget. Ele deve ser montado uma única vez, no componente raiz.

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

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

      <SoluCXWidgetHost />
    </>
  );
}

Passo 2: Dispare o widget em qualquer tela

Chame SoluCXWidget.create() quando quiser exibir a pesquisa. Informe apenas a chave, o tipo de exibição e os dados do cliente — o SDK busca as demais configurações (altura, frequência, amostragem, etc.) automaticamente do painel da jornada.

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

function onCheckoutComplete() {
  SoluCXWidget
    .create('SUA_CHAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'pos_venda',
      customer_id: 'cliente_123',
      email: '[email protected]',
      name: 'João Silva',
    })
    .show();
}

Só isso. O SDK consulta a API, aplica as regras de exibição configuradas no painel e decide se o widget deve aparecer ou não.

Passo 3 (opcional): Sobrescreva as opções localmente

Se você precisar sobrescrever as configurações do painel para um caso específico, use .setOptions(). Quando chamado, o widget ignora a configuração remota e usa apenas os valores fornecidos.

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

Integração por Componente (alternativo)

Se preferir controlar a exibição pelo próprio JSX, use SoluCXWidgetView diretamente. Nesse modo, não é necessário montar SoluCXWidgetHost — o componente é autossuficiente.

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

export function CheckoutScreen() {
  return (
    <SoluCXWidgetView
      soluCXKey="SUA_CHAVE_SOLUCX"
      type="inline"
      data={{
        journey: 'pos_venda',
        customer_id: 'cliente_123',
        email: '[email protected]',
        name: 'João Silva',
      }}
      callbacks={{
        onOpened: (userId) => {
          console.log('Widget exibido para:', userId);
        },
      }}
    />
  );
}

O componente é renderizado na posição exata onde é declarado no JSX. Ideal para o modo inline.

Playground

Modos de Exibição

ModoComportamento
bottomFixo na parte inferior da tela (padrão)
topFixo no topo da tela
modalSobreposição centralizada que bloqueia a interação com o fundo
inlineInserido no fluxo do layout, na posição onde o componente é declarado
// Bottom: barra fixa na parte inferior (padrão)
SoluCXWidget.create('KEY').setType('bottom').setData(data).show();

// Top: barra fixa no topo
SoluCXWidget.create('KEY').setType('top').setData(data).show();

// Modal: sobreposição centralizada que bloqueia o fundo
SoluCXWidget.create('KEY').setType('modal').setData(data).show();

// Inline: integrado ao fluxo do layout (respeita a posição no JSX)
SoluCXWidget.create('KEY').setType('inline').setData(data).show();

Exemplo Completo com Callbacks

Este exemplo mostra todas as etapas do ciclo de vida do widget — da preparação ao fechamento:

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

function showSurvey() {
  SoluCXWidget
    .create('SUA_CHAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'atendimento',
      customer_id: 'cliente_456',
      email: '[email protected]',
      name: 'Maria Santos',
      employee_id: 'atendente_01',
      employee_name: 'Carlos',
    })
    .setCallbacks({
      onPreOpen: (userId) => {
        console.log('Preparando widget para:', userId);
      },
      onOpened: (userId) => {
        console.log('Widget exibido para:', userId);
        analytics.track('survey_shown', { userId });
      },
      onBlocked: (reason) => {
        // Regras de exibição impediram a exibição
        console.log('Widget bloqueado:', reason);
        // Ex: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL", "BLOCKED_BY_SAMPLING"
      },
      onClosed: () => {
        console.log('Usuário fechou o widget');
      },
      onCompleted: (userId) => {
        console.log('Pesquisa respondida por:', userId);
        analytics.track('survey_completed', { userId });
      },
      onPartialCompleted: (userId) => {
        console.log('Pesquisa parcialmente respondida por:', userId);
      },
      onError: (message) => {
        console.error('Erro no widget:', message);
      },
    })
    .show();
}

Fechar o widget programaticamente

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

SoluCXWidget.dismiss();

API

SoluCXWidget (classe principal)

Métodos de construção (builder pattern)

MétodoDescrição
SoluCXWidget.create(soluCXKey)Cria uma nova instância do widget
.setType(type)Define o tipo: 'bottom', 'top', 'modal', 'inline'
.setData(data)Define os dados do usuário/transação
.setOptions(options)Define opções do widget locais (opcional — se não chamar, busca da API)
.setCallbacks(callbacks)Define callbacks de eventos
.show()Exibe o widget

Métodos estáticos

MétodoDescrição
SoluCXWidget.dismiss()Fecha o widget programaticamente

Métodos de instância (gerenciamento de logs)

Usam os dados do builder (instanceKey, journey, userId) para acessar os logs da combinação correta.

MétodoDescrição
.getWidgetLogs()Retorna os logs do widget
.overrideTimestamp(field, date)Sobrescreve uma data de evento (debug/teste)
.clearWidgetLogs()Limpa todos os logs

WidgetData

Dados enviados para identificar o cliente e o contexto da pesquisa.

interface WidgetData {
  // Identificador da jornada (obrigatório na prática)
  journey?: string;

  // Identificação do usuário
  customer_id?: string;
  name?: string;
  email?: string;
  phone?: string;
  phone2?: string;
  document?: string;
  birth_date?: string;     // Formato: YYYY-MM-DD

  // Contexto da transação
  transaction_id?: string;
  store_id?: string;
  store_name?: string;
  employee_id?: string;
  employee_name?: string;
  amount?: number;
  score?: number;

  // ID do formulário (apenas para Flex Survey)
  form_id?: string;

  // Parâmetros customizados (use o prefixo param_)
  [key: `param_${string}`]: string | number | undefined;
}

WidgetOptions

Configura as opções de exibição do widget (tipo, altura, frequência, amostragem e regras de supressão). Opcional — se setOptions() não for chamado, o widget busca essas configurações automaticamente da API (painel de configuração da 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;
}
PropriedadeTipoPadrãoDescrição
enabledbooleantrueQuando false, bloqueia imediatamente sem validar outras regras
samplingPercentagenumber100Porcentagem de usuários que verão o widget (0-100). 100 = todos veem. 0 = ninguém vê
typestring'bottom'Tipo de exibição: 'bottom', 'top', 'modal', 'inline'. Pode ser configurado remotamente pelo painel da jornada
heightnumber600Altura fixa do widget em pixels. 0 = automático. Pode ser configurado remotamente pelo painel da jornada
maxAttemptsAfterDismissnumber0Número máximo de exibições por experiência. 0 = sem limite. Reseta quando o ID da experiência muda
waitDaysAfterWidgetDisplayAttemptnumber0Dias a esperar após tentativa de exibição bloqueada
waitDaysAfterWidgetFirstAccessnumber0Dias a esperar após o primeiro acesso do usuário à jornada
waitDaysAfterWidgetDisplaynumber0Dias a esperar após qualquer exibição do widget
waitDaysAfterWidgetDismissnumber0Dias a esperar após o usuário fechar o widget
waitDaysAfterWidgetSubmitnumber0Dias a esperar após envio completo da pesquisa
waitDaysAfterWidgetPartialSubmitnumber0Dias a esperar após envio parcial da pesquisa

WidgetCallbacks

Funções chamadas em cada etapa do ciclo de vida do widget. Todas são opcionais.

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

Ao chamar .show(), o SDK executa os seguintes passos:

  1. Verifica as opções locais (enabled, amostragem, tentativas, intervalos)
  2. Se bloqueado: chama onBlocked com o motivo e o widget não aparece
  3. Se liberado: solicita a URL da pesquisa à API SoluCX (preflight)
  4. Chama onPreOpen → exibe o widget → chama onOpened
  5. Durante a pesquisa, o usuário interage com o formulário
  6. Quando o usuário responde a nota principal (NPS/CSAT/CES): chama onPartialCompleted
  7. Quando o usuário conclui toda a pesquisa: chama onCompleted
  8. Ao fechar (pelo usuário ou automaticamente): chama onClosed
  9. Se ocorrer erro em qualquer etapa: chama onError

Descrição dos callbacks

CallbackParâmetroQuando é chamado
onPreOpenuserId: stringImediatamente antes do widget ser exibido, após a validação das regras de exibição passar
onOpeneduserId: stringQuando o widget é exibido na tela
onBlockedreason: BlockReasonQuando as regras de exibição impedem o widget de ser exibido. Veja BlockReason
onClosed-Quando o widget é fechado, seja pelo usuário ou automaticamente
onCompleteduserId: stringQuando o usuário conclui toda a pesquisa (todas as etapas)
onPartialCompleteduserId: stringQuando o usuário responde a nota principal (NPS/CSAT/CES)
onErrormessage: stringQuando ocorre um erro na pesquisa (pesquisa expirada, já avaliada, falha de rede)
onPageChangedpage: stringQuando o usuário navega entre páginas (Flex Survey)
onQuestionAnswered-Quando o usuário responde uma pergunta (Flex Survey)
onResizeheight: stringQuando a altura do widget muda

BlockReason

Motivos pelos quais as regras de exibição impedem o widget de ser exibido. Esses bloqueios são locais (no dispositivo) e não representam erros da 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ãoDescrição
BLOCKED_BY_DISABLEDWidget desabilitado (enabled: false)
BLOCKED_BY_SAMPLINGUsuário não foi selecionado pela amostragem (samplingPercentage)
BLOCKED_BY_TRANSACTION_ALREADY_ANSWEREDTransação já foi respondida anteriormente
BLOCKED_BY_MAX_ATTEMPTSNúmero máximo de tentativas atingido para esta experiência
BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVALDentro do intervalo após tentativa de exibição
BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVALDentro do intervalo após primeiro acesso à jornada
BLOCKED_BY_WIDGET_DISPLAY_INTERVALDentro do intervalo após exibição
BLOCKED_BY_WIDGET_DISMISS_INTERVALDentro do intervalo após fechamento
BLOCKED_BY_WIDGET_SUBMIT_INTERVALDentro do intervalo após envio completo
BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVALDentro do intervalo após envio parcial

Múltiplos Widgets

Cada combinação de instanceKey + journey + userId opera com logs independentes:

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

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

// Cada um tem seus próprios contadores, timestamps e logs

Troubleshooting

Widget não aparece

  • Verifique se a chave SoluCX (soluCXKey) é válida
  • Verifique se há conectividade com a internet
  • Use o callback onBlocked para verificar se as regras de exibição estão impedindo a exibição
  • Use o callback onError para verificar se há erros na pesquisa

Callbacks não são chamados

  • Confirme que o objeto callbacks está sendo passado via .setCallbacks() ou como prop do SoluCXWidgetView
  • Verifique o console para mensagens de erro do WebView

Layout quebrado

  • Defina uma altura fixa via setOptions({ height: 600 }) se o redimensionamento automático não funcionar bem no seu layout
  • Para o modo inline, certifique-se de que o componente pai tem espaço suficiente

Compatibilidade

VersãoReact NativeExpoiOSAndroid
2.x0.70+50+11+API 21+

Licença

Este pacote é proprietário da SoluCX. O uso é restrito a clientes licenciados da plataforma SoluCX.