npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@solucx/react-native-solucx-widget

v2.1.2

Published

The React Native SDK for Solucx Widget

Readme

@solucx/react-native-solucx-widget

React Native Expo TypeScript

Widget React Native para coleta de feedback e pesquisas de satisfacao, desenvolvido pela SoluCX.


Instalacao

npm install @solucx/react-native-solucx-widget

Inicio Rapido

Voce pode integrar o widget de duas formas:

  • Por funcao: monta um SoluCXWidgetHost no root e dispara a exibicao com SoluCXWidget.create(...).show().
  • Por componente: renderiza SoluCXWidget diretamente no JSX, no ponto exato em que o widget deve aparecer.

1. Uso por funcao: adicione o SoluCXWidgetHost no root do app

O SoluCXWidgetHost deve ser montado uma unica vez no componente raiz da aplicacao. Ele e responsavel por renderizar o widget quando solicitado via SoluCXWidget.create(...).show().

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

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

      {/* Obrigatorio: monte uma vez no root */}
      <SoluCXWidgetHost />
    </>
  );
}

2. Uso por funcao: dispare o widget em qualquer tela

Use SoluCXWidget.create() para construir e exibir o widget. As opcoes de supressao (dias de espera) sao opcionais — se nao forem passadas, o widget as busca automaticamente do painel de configuracao da jornada (configurado remotamente).

// 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: 'user_123',
      email: '[email protected]',
      name: 'Joao Silva',
    })
    .show();
}

Pronto! O widget vai buscar as configuracoes de supressao remotamente e decidir se deve exibir ou nao.

3. Uso por componente: renderize o widget diretamente no JSX

Quando voce quiser controlar a exibicao pelo proprio componente React, use SoluCXWidget. Nesse modo, nao e necessario montar SoluCXWidgetHost.

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

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

Exemplos Completos

Exemplo 1: Uso minimo (opcoes configuradas remotamente)

Este e o caso de uso mais comum. As regras de supressao (quantos dias esperar apos cada evento) sao configuradas no painel de configuracao da jornada e buscadas automaticamente pelo widget. Voce nao precisa passar nenhuma opcao.

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

// Disparar apos uma compra
function afterPurchase() {
  SoluCXWidget
    .create('SUA_CHAVE_SOLUCX')
    .setType('bottom')
    .setData({
      journey: 'pos_venda',
      customer_id: 'user_123',
      email: '[email protected]',
      name: 'Joao Silva',
      store_id: 'loja_01',
    })
    .show();
  // As opcoes de supressao serao buscadas automaticamente da API
  // com base na configuracao da jornada "pos_venda"
}

Exemplo 2: Com callbacks para monitorar eventos

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

function showSurvey() {
  SoluCXWidget
    .create('SUA_CHAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'atendimento',
      customer_id: 'user_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) => {
        // Widget nao foi exibido por causa de uma regra de supressao
        console.log('Widget bloqueado:', reason);
        // Ex: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL"
      },
      onClosed: () => {
        console.log('Usuario 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();
}

Exemplo 3: Com opcoes locais (sobrescrevendo configuracao remota)

Se voce precisar sobrescrever as configuracoes da jornada para um caso especifico, passe as opcoes diretamente. Quando setOptions() e chamado, o widget nao busca configuracoes da API e usa os valores fornecidos.

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

function showUrgentSurvey() {
  SoluCXWidget
    .create('SUA_CHAVE_SOLUCX')
    .setType('modal')
    .setData({
      journey: 'nps_trimestral',
      customer_id: 'user_789',
      email: '[email protected]',
    })
    .setOptions({
      height: 600,
      maxAttemptsAfterDismiss: 5,        // Máximo de 5 tentativas para essa jornada
      waitDaysAfterWidgetDismiss: 90,    // Esperar 90 dias após o usuário fechar
      waitDaysAfterWidgetSubmit: 30,     // Esperar 30 dias após resposta completa
      waitDaysAfterWidgetDisplay: 7,     // Esperar 7 dias após qualquer exibição
    })
    .setCallbacks({
      onOpened: (userId) => console.log('Abriu:', userId),
      onCompleted: (userId) => console.log('Respondeu:', userId),
    })
    .show();
}

Exemplo 4: Diferentes tipos de widget

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

const data = {
  journey: 'pos_venda',
  customer_id: 'user_123',
  email: '[email protected]',
};

// Bottom: barra fixa na parte inferior (padrao)
SoluCXWidget.create('KEY').setType('bottom').setData(data).show();

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

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

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

Exemplo 5: Fechar o widget programaticamente

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

// Fechar o widget a qualquer momento
function handleTimeout() {
  SoluCXWidget.dismiss();
}

Exemplo 6: Consultar logs de supressao

Os metodos de log sao metodos de instancia — usam os dados do builder (instanceKey do create(), journey e userId do setData()) para identificar automaticamente qual registro acessar. Nao e necessario passar esses parametros novamente.

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

// Cria a instancia com os dados que identificam o registro
const widget = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

// Consultar os logs de supressao
const logs = await widget.getWidgetLogs();
console.log(logs);
// {
//   attempts: 3,                       // quantas vezes o widget foi exibido nesta experiencia
//   answeredTransactionIds: ['txn-1'], // transacoes que ja responderam e nao devem reabrir o widget
//   lastExperienceId: 'pos_venda',     // ultima jornada associada ao contador
//   lastDisplayAttempt: 1741000000000, // ultima tentativa de exibicao bloqueada
//   lastFirstAccess: 1740000000000,    // primeira vez que o widget foi exibido
//   lastDisplay: 1741000000000,        // ultima exibicao do widget
//   lastDismiss: 1741000000000,        // ultima vez que o usuario fechou
//   lastSubmit: 0,                     // ultimo envio completo
//   lastPartialSubmit: 0,              // ultimo envio parcial
// }

Exemplo 7: Sobrescrever datas de eventos (debug/teste)

Use overrideTimestamp para simular cenarios de teste sem esperar os dias reais.

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

const widget = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

// Informar primeiro acesso do usuário 
await widget.overrideTimestamp('lastFirstAccess', new Date('2026-01-01T00:00:00Z'));

// Ou usar timestamp em milissegundos diretamente
await widget.overrideTimestamp('lastFirstAccess', 1700000000000);

// Resetar um campo para "nunca aconteceu" (valor 0)
await widget.overrideTimestamp('lastSubmit', 0);

// Depois de sobrescrever, pode exibir o widget normalmente
widget.show();

Campos disponiveis para overrideTimestamp:

| Campo | Descricao | |---|---| | lastDisplayAttempt | Ultima tentativa de exibicao (widget foi bloqueado) | | lastFirstAccess | Primeira vez que o widget foi exibido para o usuario | | lastDisplay | Ultima exibicao do widget | | lastDismiss | Ultima vez que o usuario fechou o widget | | lastSubmit | Ultimo envio completo da pesquisa | | lastPartialSubmit | Ultimo envio parcial da pesquisa | | answeredTransactionIds | Lista de transaction_id que ja responderam e devem ser bloqueados localmente |

Exemplo 8: Limpar logs de supressao

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

const widget = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

// Limpar TODOS os logs (reseta contadores e timestamps)
await widget.clearWidgetLogs();

// Apos limpar, o widget vai se comportar como se nunca tivesse sido exibido
widget.show();

Exemplo 9: Forcar exibicao do widget (ignorar bloqueio)

Se o widget esta sendo bloqueado por uma regra de supressao e voce quer forca-lo a exibir (por exemplo, para testes), sobrescreva a data do evento que esta bloqueando:

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

const widget = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

// Verificar qual evento esta bloqueando
const logs = await widget.getWidgetLogs();
console.log('Ultimo dismiss:', new Date(logs.lastDismiss));
console.log('Tentativas:', logs.attempts);

// Resetar o dismiss para desbloquear
await widget.overrideTimestamp('lastDismiss', 0);

// Agora o widget vai exibir (se nao houver outra regra bloqueando)
widget.show();

Exemplo 10: Isolamento por jornada — dois widgets independentes

Cada combinacao de instanceKey + journey + userId tem seus proprios logs. Widgets de jornadas diferentes nao interferem entre si.

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

// Widget da jornada "pos_venda"
const widgetPosVenda = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

// Widget da jornada "atendimento"
const widgetAtendimento = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'atendimento', customer_id: 'user_123' });

// Os logs sao independentes:
const logsPosVenda = await widgetPosVenda.getWidgetLogs();
const logsAtendimento = await widgetAtendimento.getWidgetLogs();
// logsPosVenda pode ter 5 tentativas enquanto logsAtendimento tem 0

// Limpar os logs de uma jornada NAO afeta a outra
await widgetPosVenda.clearWidgetLogs();
// Apenas os logs de pos_venda foram resetados

Exemplo 11: Widget sem userId (dispositivo anonimo)

Quando nao ha customer_id, email ou cpf, os logs sao compartilhados por todos os usuarios daquela jornada naquele dispositivo.

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

// Sem userId — logs sao por instanceKey:journey (compartilhado no dispositivo)
const widget = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda' });

const logs = await widget.getWidgetLogs();
// Chave de armazenamento: "SUA_CHAVE_SOLUCX:pos_venda"

// Com userId — logs sao por instanceKey:journey:userId (isolado por usuario)
const widgetComUser = SoluCXWidget
  .create('SUA_CHAVE_SOLUCX')
  .setData({ journey: 'pos_venda', customer_id: 'user_123' });

const logsComUser = await widgetComUser.getWidgetLogs();
// Chave de armazenamento: "SUA_CHAVE_SOLUCX:pos_venda:user_123"

Opcoes de Supressao (waitDays)

Importante: Voce nao precisa passar essas opcoes no codigo. Elas podem (e devem) ser configuradas remotamente no painel de configuracao da jornada. O widget busca automaticamente as configuracoes da API quando setOptions() nao e chamado.

As opcoes controlam quantos dias o widget deve esperar apos cada tipo de evento antes de exibir novamente:

| Opcao | Descricao | |---|---| | enabled | Habilita/desabilita o widget. Quando false, bloqueia imediatamente sem validar outras regras | | samplingPercentage | Porcentagem de usuarios que verao o widget (0-100). Valor 100 ou undefined = sem amostragem | | maxAttemptsAfterDismiss | Numero maximo de tentativas de exibicao por experiencia | | waitDaysAfterWidgetDisplayAttempt | Dias apos uma tentativa de exibicao bloqueada | | waitDaysAfterWidgetFirstAccess | Dias apos o primeiro acesso do usuario a jornada | | waitDaysAfterWidgetDisplay | Dias apos qualquer exibicao do widget | | waitDaysAfterWidgetDismiss | Dias apos o usuario fechar o widget | | waitDaysAfterWidgetSubmit | Dias apos o usuario responder a pesquisa | | waitDaysAfterWidgetPartialSubmit | Dias apos o usuario responder parcialmente |

Regras:

  • enabled: false bloqueia o widget imediatamente com BLOCKED_BY_DISABLED, sem verificar nenhuma outra regra
  • enabled: true ou undefined = widget habilitado (comportamento padrao)
  • samplingPercentage: sorteia se o usuario vera o widget. Ex: 50 = 50% de chance. Valor 100 ou undefined = todos veem. Valor 0 = ninguem ve
  • Valor 0 ou undefined nos campos de dias = sem restricao (o campo nao bloqueia)
  • Se qualquer regra bloquear, o widget nao exibe e chama onBlocked(reason)
  • Quando bloqueado, o reason indica qual regra bloqueou (ex: "BLOCKED_BY_WIDGET_DISMISS_INTERVAL")
  • maxAttemptsAfterDismiss conta quantas vezes o widget foi exibido para aquela experiencia (jornada). Se o ID da experiencia (journey ou form_id) mudar, o contador de tentativas e resetado automaticamente
  • Quando o usuario engaja na pesquisa (QUESTION_ANSWERED, envio completo ou envio parcial), o contador de tentativas tambem e resetado para nao bloquear exibicoes futuras por tentativas antigas
  • Quando existe transaction_id e essa transacao ja foi concluida ou parcialmente concluida antes, o widget bloqueia localmente e nao faz chamadas de opcoes nem de preflight

API Completa

SoluCXWidget (classe principal)

Metodos de construcao (builder pattern)

| Metodo | Descricao | |---|---| | SoluCXWidget.create(soluCXKey) | Cria uma nova instancia do widget | | .setType(type) | Define o tipo: 'bottom', 'top', 'modal', 'inline' | | .setData(data) | Define os dados do usuario/transacao | | .setOptions(options) | Define opcoes locais (opcional - se nao chamar, busca da API) | | .setCallbacks(callbacks) | Define callbacks de eventos | | .show() | Exibe o widget |

Metodos estaticos

| Metodo | Descricao | |---|---| | SoluCXWidget.dismiss() | Fecha o widget programaticamente |

Metodos de instancia (log management)

Usam os dados do builder (instanceKey, journey, userId) para acessar os logs da combinacao correta.

| Metodo | Descricao | |---|---| | .getWidgetLogs() | Retorna os logs de supressao | | .overrideTimestamp(field, date) | Sobrescreve uma data de evento (debug/teste) | | .clearWidgetLogs() | Limpa todos os logs |

Isolamento de armazenamento: Os logs sao isolados por instanceKey:journey:userId. Um app com 2 widgets de jornadas diferentes tera dados de supressao independentes. O userId e opcional — quando presente, os logs sao por usuario.

SoluCXWidgetHost (componente React)

Componente que deve ser montado uma vez no root do app quando a integracao for feita por funcao com SoluCXWidget.create(...).show(). Nao e necessario ao usar SoluCXWidgetView diretamente.

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

// No root do app
<SoluCXWidgetHost />

SoluCXWidgetView (componente React)

Componente para uso direto no JSX. Pode ser usado no lugar do fluxo por funcao quando fizer mais sentido controlar a renderizacao pelo componente.

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

<SoluCXWidgetView
  soluCXKey="SUA_CHAVE_SOLUCX"
  type="inline"
  data={{ journey: 'pos_venda', customer_id: 'user_123' }}
/>

Props:

| Prop | Descricao | |---|---| | soluCXKey | Chave de instancia do widget | | type | Tipo do widget: 'bottom', 'top', 'modal', 'inline' | | data | Dados do usuario/transacao | | options | Opcoes locais de configuracao e supressao | | callbacks | Callbacks de ciclo de vida e eventos |

WidgetData

interface WidgetData {
  journey?: string;          // Nome da jornada (obrigatorio na pratica)
  email?: string;            // Email do usuario
  name?: string;             // Nome do usuario
  cpf?: string;              // CPF
  document?: string;         // Documento
  phone?: string;            // Telefone
  phone2?: string;           // Telefone secundario
  birth_date?: string;       // Data de nascimento (YYYY-MM-DD)
  transaction_id?: string;   // ID da transacao
  customer_id?: string;      // ID do cliente
  store_id?: string;         // ID da loja
  store_name?: string;       // Nome da loja
  employee_id?: string;      // ID do funcionario
  employee_name?: string;    // Nome do funcionario
  amount?: number;           // Valor da transacao
  score?: number;            // Score
  [key: `param_${string}`]: string | number | undefined;  // Parametros customizados
}

WidgetCallbacks

interface WidgetCallbacks {
  onPreOpen?: (userId: string) => void;        // Antes de abrir
  onOpened?: (userId: string) => void;         // Widget aberto
  onBlocked?: (reason: BlockReason) => void;   // Widget bloqueado por regra de supressao
  onClosed?: () => void;                       // Usuario fechou
  onCompleted?: (userId: string) => void;      // Pesquisa respondida
  onPartialCompleted?: (userId: string) => void; // Pesquisa parcialmente respondida
  onError?: (message: string) => void;         // Erro no widget
  onPageChanged?: (page: string) => void;      // Pagina mudou
  onQuestionAnswered?: () => void;             // Pergunta respondida
  onResize?: (height: string) => void;         // Widget redimensionou
}

WidgetOptions

interface WidgetOptions {
  enabled?: boolean;  // Habilita/desabilita o widget (default: true). Quando false, bloqueia sem validar.
  samplingPercentage?: number;  // Porcentagem de usuarios que verao o widget (0-100). Default: 100 (todos).
  height?: number;  // Altura fixa em pontos (se nao informado, altura dinamica)
  maxAttemptsAfterDismiss?: number;  // Maximo de tentativas por experiencia (0 ou undefined = sem limite)
  // Regras de supressao (opcionais - configuradas remotamente pelo painel da jornada):
  waitDaysAfterWidgetDisplayAttempt?: number;
  waitDaysAfterWidgetFirstAccess?: number;
  waitDaysAfterWidgetDisplay?: number;
  waitDaysAfterWidgetDismiss?: number;
  waitDaysAfterWidgetSubmit?: number;
  waitDaysAfterWidgetPartialSubmit?: number;
}

BlockReason

Valores possiveis retornados no callback onBlocked:

| Valor | Significado | |---|---| | BLOCKED_BY_DISABLED | Widget desabilitado (enabled: false) | | BLOCKED_BY_SAMPLING | Usuario nao foi selecionado pela amostragem (samplingPercentage) | | BLOCKED_BY_MAX_ATTEMPTS | Numero maximo de tentativas atingido para esta experiencia | | BLOCKED_BY_WIDGET_DISPLAY_ATTEMPT_INTERVAL | Dentro do intervalo apos tentativa de exibicao | | BLOCKED_BY_WIDGET_FIRST_ACCESS_INTERVAL | Dentro do intervalo apos primeira visualizacao | | BLOCKED_BY_WIDGET_DISPLAY_INTERVAL | Dentro do intervalo apos exibicao | | BLOCKED_BY_WIDGET_DISMISS_INTERVAL | Dentro do intervalo apos fechamento | | BLOCKED_BY_WIDGET_SUBMIT_INTERVAL | Dentro do intervalo apos envio completo | | BLOCKED_BY_WIDGET_PARTIAL_SUBMIT_INTERVAL | Dentro do intervalo apos envio parcial |


Fluxo Interno

App chama SoluCXWidget.create('KEY').setData({...}).show()
  |
  v
SoluCXWidgetHost recebe a configuracao
  |
  v
Widget monta e executa bootstrap():
  |
  +-- Se setOptions() FOI chamado --> usa opcoes locais
  +-- Se setOptions() NAO foi chamado --> busca opcoes da API (painel da jornada)
  |
  +-- shouldDisplayWidget() verifica enabled + maxAttemptsAfterDismiss + 6 regras de supressao
  +-- (validacao LOCAL, antes de chamar a API, para reduzir carga no servidor):
  |     +-- Se transaction_id ja respondido --> onBlocked('BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED'), widget NAO exibe
  |     +-- Se enabled === false --> onBlocked('BLOCKED_BY_DISABLED'), widget NAO exibe
  |     +-- Sorteia baseado em samplingPercentage --> se nao selecionado, onBlocked('BLOCKED_BY_SAMPLING')
  |     +-- Se experiencia mudou --> reseta contador de tentativas
  |     +-- Se tentativas >= maxAttemptsAfterDismiss --> onBlocked('BLOCKED_BY_MAX_ATTEMPTS')
  |     +-- Para cada regra: timestamp do evento + dias de espera > agora?
  |     +-- Se QUALQUER regra bloqueia --> onBlocked(reason), widget NAO exibe
  |     +-- Se NENHUMA bloqueia --> continua
  |
  +-- Busca URL do formulario na API (preflight)
  |
  +-- Se EXIBE:
  |     +-- Incrementa contador de tentativas
  |     +-- Salva lastDisplay (e lastFirstAccess na primeira vez)
  |     +-- Renderiza WebView com o formulario
  |     +-- onOpened(userId)
  |
  +-- Eventos do usuario:
        +-- Fecha widget --> salva lastDismiss, onClosed()
      +-- Responde pergunta --> zera contador de tentativas, onQuestionAnswered()
    +-- Responde pesquisa --> zera contador de tentativas, salva transaction_id respondido, salva lastSubmit, onCompleted(userId)
    +-- Resposta parcial --> zera contador de tentativas, salva transaction_id respondido, salva lastPartialSubmit, onPartialCompleted(userId)

Compatibilidade

| Versao | React Native | Expo | iOS | Android | |---|---|---|---|---| | 1.0.x | 0.70+ | 50+ | 11+ | API 21+ |

Licenca

Proprietario - SoluCX. Uso restrito a clientes licenciados.