@solucx/react-native-solucx-widget
v2.1.2
Published
The React Native SDK for Solucx Widget
Readme
@solucx/react-native-solucx-widget
Widget React Native para coleta de feedback e pesquisas de satisfacao, desenvolvido pela SoluCX.
Instalacao
npm install @solucx/react-native-solucx-widgetInicio 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.
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 resetadosExemplo 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: 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 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. OuserIde 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.
