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

v3.1.4

Published

The React Native SDK for Solucx Widget

Downloads

1,565

Readme

@solucx/react-native-solucx-widget

React React Native TypeScript

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 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.

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 via setOptions() 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: 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 backend bloqueia a exibicao
  • O SDK consulta GET /widget/available para 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 applyClientSettings ser chamado (antes da resposta de GET /widget/available) sao descartados, nao ficam enfileirados
  • clientSettings.surveyEventsEndpoint define a URL completa do endpoint (a API ja retorna o path final). Se nao vier (endpoint ausente ou requisicao falhar sem retornar clientSettings), o SDK usa o endpoint padrao https://survey-events.solucx.com.br/survey-events/events
  • clientSettings.disableEvents e 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.value contem o payload quando existir)
  • Cada requisicao e um POST {surveyEventsEndpoint} (URL usada como recebida, sem sufixo adicionado pelo SDK) com headers x-solucx-api-key, x-solucx-sdk-name, x-solucx-sdk-version e 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 receber null. Em projetos com TypeScript estrito, callbacks que aceitam somente string podem exigir ajuste.
  • Não houve inclusão de novas dependências no pacote. react-native-webview e react-native-safe-area-context continuam sendo peerDependencies necessárias à integração.

Interfaces públicas

  • WidgetData agora aceita survey.
  • WidgetConfig.type e a prop type passaram 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.surveyEventsEndpoint e desabilitar eventos em clientSettings.disableEvents.
  • Eventos configurados como desabilitados e eventos emitidos antes de receber clientSettings não são enviados.

Comportamento

  • widgetOptions recebidas em GET /widget/available passam a configurar a exibição. Valores locais de type e height tê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 com true;.
  • Quando não há identificador de usuário, os headers x-solucx-device-id e x-client-id deixam de ser enviados.

Corrigido

  • height: 0 passa a usar altura automática, em vez de renderizar o widget com altura zero.
  • Corrigido o acesso a Platform.Version para 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.