@solucx/react-native-solucx-widget
v3.1.4
Published
The React Native SDK for Solucx Widget
Downloads
1,565
Readme
@solucx/react-native-solucx-widget
Widget React Native para coleta de feedback e pesquisas de satisfacao, desenvolvido pela SoluCX.
Instalacao
O SDK requer react 18 ou superior e react-native 0.72 ou superior. react-native-webview 13.13 ou superior e react-native-safe-area-context 5.4 ou superior tambem devem estar instalados no projeto. Essas faixas sao compativeis com Expo SDK 53 e posteriores:
npm install @solucx/react-native-solucx-widget react@">=18.0.0" react-native-webview@"^13.13.0" react-native-safe-area-context@"^5.4.0"Inicio Rapido
Voce pode integrar o widget de duas formas:
- Por funcao: monta um
SoluCXWidgetHostno root e dispara a exibicao comSoluCXWidget.create(...).show(). - Por componente: renderiza
SoluCXWidgetdiretamente 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.
Identificadores de experiencia
Em data, use journey para identificar uma jornada e/ou survey para identificar uma pesquisa especifica. survey e opcional e nao substitui journey; os dois campos sao enviados separadamente para a API quando informados. form_id e um identificador distinto e nao deve ser usado para informar a pesquisa.
SoluCXWidget
.create('SUA_CHAVE_SOLUCX')
.setData({
customer_id: 'user_123',
survey: 'pesquisa_pos_venda',
})
.show();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. As opcoes locais têm prioridade sobre as opcoes retornadas pela API, mas a API ainda e consultada para validar a disponibilidade e sincronizar os logs.
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,
type: 'modal',
})
.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: Sobrescrever datas de eventos (debug/teste)
Use overrideTimestamp para simular cenarios de teste sem esperar os dias reais. Os valores sao enviados como overrides na proxima chamada de GET /widget/available.
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
widget.overrideTimestamp('lastFirstAccess', new Date('2026-01-01T00:00:00Z'));
// Ou usar timestamp em milissegundos diretamente
widget.overrideTimestamp('lastFirstAccess', 1700000000000);
// Resetar um campo para "nunca aconteceu" (valor 0)
widget.overrideTimestamp('lastSubmit', 0);
// Depois de sobrescrever, pode exibir o widget normalmente
widget.show();Campos disponiveis para overrideTimestamp:
| Campo | Descricao |
|---|---|
| lastDisplayAttempt | Última tentativa de exibição (widget foi bloqueado) |
| lastFirstAccess | Primeira vez que o widget foi exibido para o usuário |
| lastDisplay | Última exibição do widget |
| lastDismiss | Última vez que o usuário fechou o widget |
| lastSubmit | Último envio completo da pesquisa |
| lastPartialSubmit | Último envio parcial da pesquisa |
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. As regras de supressao sao avaliadas pelo backend na chamada
GET /widget/available. Opcoes passadas viasetOptions()tem prioridade sobre as retornadas pela API.
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: falsebloqueia o widget imediatamente comBLOCKED_BY_DISABLED, sem verificar nenhuma outra regraenabled: trueouundefined= widget habilitado (comportamento padrao)samplingPercentage: sorteia se o usuario vera o widget. Ex:50= 50% de chance. Valor100ouundefined= todos veem. Valor0= ninguem ve- Valor
0ouundefinednos campos de dias = sem restricao (o campo nao bloqueia) - Se qualquer regra bloquear, o widget nao exibe e chama
onBlocked(reason) - Quando bloqueado, o
reasonindica qual regra bloqueou (ex:"BLOCKED_BY_WIDGET_DISMISS_INTERVAL") maxAttemptsAfterDismissconta quantas vezes o widget foi exibido para aquela experiencia (jornada). Se o ID da experiencia (journeyouform_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_ide essa transacao ja foi concluida ou parcialmente concluida antes, o backend bloqueia a exibicao - O SDK consulta
GET /widget/availablepara que o backend valide as regras de supressao, confirme a disponibilidade e sincronize os logs canonicos antes de abrir o widget - Apenas timestamps definidos manualmente por
overrideTimestamp()sao enviados como overrides opcionais nessa chamada remota
Script inicial do WebView
Quando a resposta de GET /widget/available inclui clientSettings.injectWebViewScript, o SDK o passa para injectedJavaScriptBeforeContentLoaded. Portanto, o código é executado antes de o conteúdo do formulário iniciar o carregamento.
{
"clientSettings": {
"injectWebViewScript": "window.widgetConfiguration = { enabled: true }; true;"
}
}O script deve terminar com true;. Ele executa dentro do WebView e deve ser fornecido apenas por uma configuração de backend confiável. Valide o comportamento em Android e iOS quando o script for essencial ao carregamento do formulário.
Survey Events (telemetria de eventos)
O SDK envia automaticamente eventos de ciclo de vida do widget para um endpoint de survey-events. O backend pode informar a URL via clientSettings na resposta de GET /widget/available; quando nao informado, o SDK usa um endpoint padrao.
| Evento | Quando e disparado |
|---|---|
| widget-started | Apos GET /widget/available responder com sucesso |
| widget-closed | Quando o usuario fecha o widget |
| widget-error | Quando o bootstrap falha (erro de rede, resposta invalida, etc.) |
| widget-block | Quando o backend informa que o widget nao esta disponivel; o motivo segue em data.reason |
Comportamento:
- Eventos disparados antes de
applyClientSettingsser chamado (antes da resposta deGET /widget/available) sao descartados, nao ficam enfileirados clientSettings.surveyEventsEndpointdefine a URL completa do endpoint (a API ja retorna o path final). Se nao vier (endpoint ausente ou requisicao falhar sem retornarclientSettings), o SDK usa o endpoint padraohttps://survey-events.solucx.com.br/survey-events/eventsclientSettings.disableEventse uma lista de nomes de eventos que devem ser ignorados (ex:["widget-closed"]; nomes legados como["closed"]continuam aceitos)- Alem dos eventos de ciclo de vida acima, mensagens recebidas do WebView tambem sao encaminhadas como eventos (nome =
widget-mais a chave antes do-;data.valuecontem o payload quando existir) - Cada requisicao e um
POST {surveyEventsEndpoint}(URL usada como recebida, sem sufixo adicionado pelo SDK) com headersx-solucx-api-key,x-solucx-sdk-name,x-solucx-sdk-versione corpo{ timestamp, traceId, event, data } - Falhas de rede ao enviar o evento sao ignoradas silenciosamente (fire-and-forget); o envio de eventos nunca bloqueia ou interrompe o widget
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
| Metodo | Descricao |
|---|---|
| .overrideTimestamp(field, date) | Sobrescreve uma data de evento (debug/teste). Os valores sao enviados para a API em GET /widget/available como overrides dos logs remotos. |
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
survey?: string; // Identificador da pesquisa (enviado junto com journey quando informado)
form_id?: string; // ID da experiencia/formulario
attempt_id?: string; // ID da tentativa
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 | null) => void; // Antes de abrir
onOpened?: (userId: string | null) => void; // Widget aberto
onBlocked?: (reason: BlockReason | string | undefined) => void; // Widget bloqueado por regra de supressao
onClosed?: () => void; // Usuario fechou
onCompleted?: (userId: string | null) => void; // Pesquisa respondida
onPartialCompleted?: (userId: string | null) => 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).
type?: 'bottom' | 'top' | 'modal' | 'inline'; // Tipo de exibicao do widget
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;
/** @deprecated Use maxAttemptsAfterDismiss e waitDaysAfterWidgetDismiss */
retry?: { attempts?: number; interval?: number };
/** @deprecated Use waitDaysAfterWidgetSubmit e waitDaysAfterWidgetPartialSubmit */
waitDelayAfterRating?: 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_TRANSACTION_ALREADY_ANSWERED | transaction_id ja respondido anteriormente |
| 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():
|
+-- Envia `GET /widget/available` com os dados do usuario, opcoes locais (se houver)
| e overrides de timestamp definidos manualmente por `overrideTimestamp()`
|
+-- O backend avalia as regras de supressao:
| +-- Se bloquear --> sincroniza `logs` retornados, chama onBlocked(reason), widget NAO exibe
| +-- Se liberar --> sincroniza `logs` retornados e usa a `url` para abrir o widget
|
+-- Se EXIBE:
| +-- Mescla opcoes locais com `widgetOptions` retornado pelo backend
| +-- Renderiza WebView com o formulario
| +-- onOpened(userId)
|
+-- Eventos do usuario:
+-- Fecha widget --> onClosed()
+-- Responde pergunta --> onQuestionAnswered()
+-- Responde pesquisa --> onCompleted(userId)
+-- Resposta parcial --> onPartialCompleted(userId)Compatibilidade
| Versao | React Native | Expo | iOS | Android | |---|---|---|---|---| | 3.1.x | 0.72+ | 54+ | 11+ | API 21+ |
Licenca
Proprietario - SoluCX. Uso restrito a clientes licenciados.
Changelog
3.1.3 - 2026-07-28
Comparação: v2.2.1
Para quem atualiza
- Não há migração obrigatória para o uso básico do SDK.
- Callbacks que recebem
userId: eles agora podem recebernull. Em projetos com TypeScript estrito, callbacks que aceitam somentestringpodem exigir ajuste. - Não houve inclusão de novas dependências no pacote.
react-native-webviewereact-native-safe-area-contextcontinuam sendopeerDependenciesnecessárias à integração.
Interfaces públicas
WidgetDataagora aceitasurvey.WidgetConfig.typee a proptypepassaram a ser opcionais. Quando não houver definição local ou remota, o tipo usado serábottom..
Eventos enviados
O SDK envia telemetria para survey-events sem bloquear a exibição do widget caso o envio falhe.
| Evento | Quando é enviado | Dados adicionais |
| :---- | :---- | :---- |
| widget-started | Após resposta bem-sucedida de GET /widget/available | Dados usados para abrir o widget |
| widget-closed | Quando o usuário fecha o widget | Dados usados para abrir o widget |
| widget-error | Quando o bootstrap falha ou a resposta não contém URL | message com o erro |
| widget-block | Quando o backend informa indisponibilidade | reason com o motivo do bloqueio |
| widget-<mensagem> | Para cada mensagem recebida do WebView | value, quando a mensagem o incluir |
- O backend pode definir a URL em
clientSettings.surveyEventsEndpointe desabilitar eventos emclientSettings.disableEvents. - Eventos configurados como desabilitados e eventos emitidos antes de receber
clientSettingsnão são enviados.
Comportamento
widgetOptionsrecebidas emGET /widget/availablepassam a configurar a exibição. Valores locais detypeeheighttêm prioridade sobre os valores remotos.clientSettings.injectWebViewScripté executado no WebView antes de o formulário carregar. O script deve vir de uma configuração de backend confiável e terminar comtrue;.- Quando não há identificador de usuário, os headers
x-solucx-device-idex-client-iddeixam de ser enviados.
Corrigido
height: 0passa a usar altura automática, em vez de renderizar o widget com altura zero.- Corrigido o acesso a
Platform.Versionpara evitar falha ao coletar informações do dispositivo quando a versão do sistema não estiver definida. - Corrigido o endpoint padrão de survey-events e o rastreamento do fechamento do widget.
