@solucx/react-native-solucx-widget 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.intern.md CHANGED
@@ -1,473 +1,513 @@
1
- # SoluCX Widget
2
-
3
- Um widget React Native modular para coleta de feedback e pesquisas, desenvolvido seguindo princípios de Clean Code e arquitetura escalável.
4
-
5
- ## 📋 Visão Geral
6
-
7
- O SoluCX Widget oferece quatro modos de renderização flexíveis para integração em aplicações React Native/Expo:
8
-
9
- - **Top**: Widget fixo no topo da tela
10
- - **Bottom**: Widget fixo na parte inferior
11
- - **Modal**: Widget em sobreposição centralizada
12
- - **Inline**: Widget integrado ao fluxo do layout
13
-
14
- ## 🏗️ Arquitetura
15
-
16
- ### Componentes Principais
17
-
18
- - [`SoluCXWidget.tsx`](src/SoluCXWidget.tsx) - Componente principal
19
- - [`useWidgetState.ts`](src/hooks/useWidgetState.ts) - Hook para gerenciamento de estado
20
- - [`WidgetEventService`](src/services/widgetEventService.ts) - Serviço de eventos
21
- - [`StorageService`](src/services/storage.ts) - Persistência local
22
-
23
- ### Estrutura de Arquivos
24
-
25
- ```
26
- src/
27
- ├── SoluCXWidget.tsx # Componente principal
28
- ├── components/ # Componentes especializados
29
- ├── ModalWidget.tsx # Widget modal
30
- │ ├── InlineWidget.tsx # Widget inline
31
- └── OverlayWidget.tsx # Widget overlay (top/bottom)
32
- ├── hooks/ # Hooks personalizados
33
- │ └── useWidgetState.ts # Estado do widget
34
- ├── services/ # Serviços
35
- │ ├── widgetEventService.ts # Gerenciamento de eventos
36
- │ └── storage.ts # Persistência de dados
37
- ├── interfaces/ # Tipos TypeScript
38
- │ ├── index.ts # Exports centralizados
39
- ├── WidgetData.ts # Dados do widget
40
- ├── WidgetOptions.ts # Opções de configuração
41
- │ ├── WidgetResponse.ts # Respostas e erros
42
- └── WidgetSamplerLog.ts # Log de tentativas
43
- ├── styles/ # Estilos
44
- └── widgetStyles.ts # Estilos por tipo
45
- ├── utils/ # Utilitários
46
- │ └── urlUtils.ts # Construção de URLs
47
- ├── constants/ # Constantes
48
- │ └── webViewConstants.ts # URLs e configurações
49
- └── __tests__/ # Testes
50
- ├── urlUtils.test.ts
51
- └── useWidgetState.test.ts
52
- ```
53
-
54
- ## 🚀 Instalação e Uso
55
-
56
- ### Instalação
57
-
58
- ```bash
59
- # Como dependência local (monorepo)
60
- npm install solucx_widget@file:./modules/solucx_widget
61
-
62
- # Dependências peer necessárias
63
- npm install react-native-webview @react-native-async-storage/async-storage
64
- ```
65
-
66
- ### Uso Básico
67
-
68
- ```tsx
69
- import React from 'react';
70
- import { SoluCXWidget } from 'solucx_widget';
71
-
72
- export default function MyComponent() {
73
- return (
74
- <SoluCXWidget
75
- soluCXKey="sua-chave-solucx"
76
- type="bottom" // 'top' | 'bottom' | 'modal' | 'inline'
77
- data={{
78
- journey: "nome_da_jornada",
79
- name: "Nome do Cliente",
80
- email: "cliente@email.com",
81
- phone: "11999999999",
82
- store_id: "1",
83
- employee_id: "1",
84
- amount: 100,
85
- param_REGIAO: "SUDESTE"
86
- }}
87
- options={{
88
- height: 400
89
- }}
90
- />
91
- );
92
- }
93
- ```
94
-
95
- ## 🎛️ API do Componente
96
-
97
- ### Props do SoluCXWidget
98
-
99
- ```typescript
100
- interface SoluCXWidgetProps {
101
- soluCXKey: string; // Chave de autenticação SoluCX
102
- type: WidgetType; // Modo de renderização
103
- data: WidgetData; // Dados do cliente/transação
104
- options: WidgetOptions; // Configurações do widget
105
- }
106
- ```
107
-
108
- ### Tipos de Widget
109
-
110
- ```typescript
111
- type WidgetType = "bottom" | "top" | "inline" | "modal";
112
- ```
113
-
114
- ### Dados do Widget
115
-
116
- ```typescript
117
- interface WidgetData {
118
- // Identificadores
119
- transaction_id?: string;
120
- customer_id?: string;
121
-
122
- // Dados do cliente
123
- name?: string;
124
- email?: string;
125
- phone?: string;
126
- birth_date?: string;
127
- document?: string;
128
-
129
- // Dados da transação
130
- store_id?: string;
131
- store_name?: string;
132
- employee_id?: string;
133
- employee_name?: string;
134
- amount?: number;
135
- score?: number;
136
-
137
- // Parâmetros customizados
138
- journey?: string;
139
- [key: string]: string | number | undefined;
140
- }
141
- ```
142
-
143
- ### Opções de Configuração
144
-
145
- ```typescript
146
- interface WidgetOptions {
147
- height?: number; // Altura
148
- retry?: { // Configuração de retry
149
- attempts?: number; // Número de tentativas
150
- interval?: number; // Intervalo entre tentativas
151
- };
152
- waitDelayAfterRating?: number; // Delay após avaliação
153
- }
154
- ```
155
-
156
- ## 🔄 Sistema de Eventos
157
-
158
- O widget comunica através de mensagens WebView bidirecionais:
159
-
160
- ### Eventos Suportados
161
-
162
- ```typescript
163
- type EventKey =
164
- | "FORM_OPENED" // Widget foi aberto
165
- | "FORM_CLOSE" // Usuário fechou o widget
166
- | "FORM_ERROR" // Erro no carregamento
167
- | "FORM_RESIZE" // Widget redimensionado
168
- | "FORM_PAGECHANGED" // Mudança de página
169
- | "QUESTION_ANSWERED" // Pergunta respondida
170
- | "FORM_COMPLETED" // Formulário concluído
171
- | "FORM_PARTIALCOMPLETED" // Completado parcialmente
172
- ```
173
-
174
- ### Tratamento de Eventos
175
-
176
- Os eventos são processados automaticamente pelo [`WidgetEventService`](src/services/widgetEventService.ts):
177
-
178
- ```typescript
179
- const eventService = new WidgetEventService(
180
- setIsWidgetVisible, // Função para controlar visibilidade
181
- resize, // Função para redimensionar
182
- open // Função para abrir widget
183
- );
184
- ```
185
-
186
- ## 💾 Persistência de Dados
187
-
188
- O widget utiliza [`AsyncStorage`](@react-native-async-storage/async-storage) para persistir:
189
-
190
- - **Histórico de tentativas**: Controla quantas vezes o widget foi exibido
191
- - **Última avaliação**: Data da última interação
192
- - **Controle de frequência**: Evita spam de widgets
193
-
194
- ### Estrutura dos Dados
195
-
196
- ```typescript
197
- interface WidgetSamplerLog {
198
- attempts: number; // Número de tentativas
199
- lastAttempt: number; // Timestamp da última tentativa
200
- lastRating: number; // Timestamp da última avaliação
201
- lastParcial: number; // Timestamp do último parcial
202
- }
203
- ```
204
-
205
- ## 🎨 Customização de Estilos
206
-
207
- ### Estilos por Tipo
208
-
209
- Cada tipo de widget possui estilos específicos definidos em [`widgetStyles.ts`](src/styles/widgetStyles.ts):
210
-
211
- ```typescript
212
- export const getWidgetStyles = (type: WidgetType) => {
213
- const styleMap = {
214
- 'bottom': { container: styles.wrapper, content: styles.bottom },
215
- 'top': { container: styles.wrapper, content: styles.top },
216
- 'inline': { container: styles.inlineWrapper, content: styles.inline },
217
- 'modal': { container: styles.wrapper, content: styles.inline }
218
- };
219
-
220
- return styleMap[type] || styleMap.bottom;
221
- };
222
- ```
223
-
224
- ### Características Visuais
225
-
226
- - **Bottom/Top**: Position absolute com z-index elevado
227
- - **Modal**: Overlay com background semitransparente
228
- - **Inline**: Integrado ao fluxo normal do layout
229
-
230
- ## 🔧 Utilitários
231
-
232
- ### Construção de URLs
233
-
234
- O [`urlUtils.ts`](src/utils/urlUtils.ts) gerencia a construção de URLs da pesquisa:
235
-
236
- ```typescript
237
- export function buildWidgetURL(key: string, data: WidgetData): string {
238
- const params = new URLSearchParams(data as Record<string, string>);
239
- const baseURL = `${BASE_URL}/${key}/?mode=widget`;
240
-
241
- if (data.transaction_id) {
242
- return `${baseURL}&${params.toString()}`;
243
- }
244
-
245
- return `${baseURL}&transaction_id=&${params.toString()}`;
246
- }
247
- ```
248
-
249
- ## 🧪 Testes
250
-
251
- ### Executando Testes
252
-
253
- ```bash
254
- # Todos os testes
255
- npm test
256
-
257
- # Teste específico
258
- npm test -- urlUtils.test.ts
259
- npm test -- useWidgetState.test.ts
260
- ```
261
-
262
- ### Cobertura de Testes
263
-
264
- - Construção de URLs
265
- - ✅ Gerenciamento de eventos
266
- - Estados do widget
267
- - ✅ Persistência de dados
268
-
269
- ## ⚙️ Configuração
270
-
271
- ### Constantes
272
-
273
- Configurações centralizadas em [`webViewConstants.ts`](src/constants/webViewConstants.ts):
274
-
275
- ```typescript
276
- export const BASE_URL = 'https://survey-link.solucx.com.br/link';
277
- export const STORAGE_KEY = '@solucxWidgetLog';
278
- export const MIN_HEIGHT = 200;
279
- export const FIXED_Z_INDEX = 9999;
280
- export const MODAL_Z_INDEX = 10000;
281
- ```
282
-
283
- ### JavaScript Injection
284
-
285
- Listener para comunicação WebView:
286
-
287
- ```typescript
288
- export const WEB_VIEW_MESSAGE_LISTENER = `
289
- window.addEventListener('message', function(event) {
290
- window.ReactNativeWebView.postMessage(event.data);
291
- });
292
- `;
293
- ```
294
-
295
- ## 🚨 Considerações Importantes
296
-
297
- ### 1. **Comportamento de Posicionamento**
298
-
299
- ⚠️ **Ponto Crítico**: A posição no JSX **não determina** onde widgets `top`, `bottom` e `modal` aparecem:
300
-
301
- ```tsx
302
- // ❌ Isso NÃO faz o widget aparecer no meio
303
- <Text>Conteúdo antes</Text>
304
- <SoluCXWidget type="bottom" {...props} /> {/* Sempre aparece embaixo */}
305
- <Text>Conteúdo depois</Text>
306
-
307
- // ✅ Apenas o tipo "inline" respeita a posição no código
308
- <Text>Conteúdo antes</Text>
309
- <SoluCXWidget type="inline" {...props} /> {/* Aparece aqui */}
310
- <Text>Conteúdo depois</Text>
311
- ```
312
-
313
- ### 2. **Performance**
314
-
315
- - Widget usa WebView interna (overhead de performance)
316
- - Carregamento lazy das pesquisas
317
- - Cache automático via AsyncStorage
318
- - JavaScript injection para comunicação
319
-
320
- ### 3. **Segurança**
321
-
322
- - **Chaves API**: Nunca hardcode em produção
323
- - **Dados sensíveis**: Não são logados automaticamente
324
- - **URLs**: Validadas antes do carregamento
325
- - **HTTPS**: Obrigatório para comunicação
326
-
327
- ### 4. **Limitações Técnicas**
328
-
329
- - Requer conexão com internet
330
- - Dependente do WebView do sistema
331
- - Eventos assíncronos (não bloqueantes)
332
- - Storage limitado do dispositivo
333
-
334
- ## 📝 Desenvolvimento
335
-
336
- ### Adicionando Novos Tipos
337
-
338
- 1. **Estenda o tipo WidgetType**:
339
- ```typescript
340
- // modules/solucx_widget/src/interfaces/index.ts
341
- export type WidgetType = "bottom" | "top" | "inline" | "modal" | "novo_tipo";
342
- ```
343
-
344
- 2. **Adicione estilos correspondentes**:
345
- ```typescript
346
- // modules/solucx_widget/src/styles/widgetStyles.ts
347
- const styleMap = {
348
- // ... estilos existentes
349
- 'novo_tipo': { container: styles.novoWrapper, content: styles.novoContent }
350
- };
351
- ```
352
-
353
- 3. **Implemente lógica no componente principal**:
354
- ```typescript
355
- // modules/solucx_widget/src/SoluCXWidget.tsx
356
- if (type === 'novo_tipo') {
357
- return <NovoTipoWidget>{/* ... */}</NovoTipoWidget>;
358
- }
359
- ```
360
-
361
- ### Adicionando Novos Eventos
362
-
363
- 1. **Estenda EventKey**:
364
- ```typescript
365
- // modules/solucx_widget/src/interfaces/index.ts
366
- export type EventKey =
367
- | "FORM_OPENED"
368
- | "NOVO_EVENTO" // Novo evento
369
- ```
370
-
371
- 2. **Implemente handler**:
372
- ```typescript
373
- // modules/solucx_widget/src/services/widgetEventService.ts
374
- private executeEvent(eventKey: EventKey, value: string): WidgetResponse {
375
- const eventHandlers = {
376
- // ... handlers existentes
377
- NOVO_EVENTO: (value: string) => this.handleNovoEvento(value),
378
- };
379
- }
380
- ```
381
-
382
- 3. **Adicione testes**:
383
- ```typescript
384
- // modules/solucx_widget/src/__tests__/widgetEventService.test.ts
385
- it('should handle NOVO_EVENTO correctly', () => {
386
- const result = service.handleMessage('NOVO_EVENTO-data', true);
387
- expect(result).toEqual({ status: 'success' });
388
- });
389
- ```
390
-
391
- ## 🔍 Troubleshooting
392
-
393
- ### Problemas Comuns
394
-
395
- #### 1. **Widget não aparece**
396
- ```bash
397
- # Verificações:
398
- Chave SoluCX válida?
399
- ✅ Conectividade com internet?
400
- ✅ Logs do WebView no console?
401
- Evento FORM_OPENED sendo disparado?
402
- ```
403
-
404
- #### 2. **Eventos não funcionam**
405
- ```bash
406
- # Verificações:
407
- ✅ JavaScript listener injetado?
408
- Formato das mensagens correto?
409
- WebView carregou completamente?
410
- postMessage funcionando?
411
- ```
412
-
413
- #### 3. **Layout quebrado**
414
- ```bash
415
- # Verificações:
416
- ✅ Dimensões adequadas para o dispositivo?
417
- Estilos corretos para o tipo?
418
- ✅ Z-index conflitando?
419
- ✅ SafeArea configurada?
420
- ```
421
-
422
- #### 4. **Storage não persiste**
423
- ```bash
424
- # Verificações:
425
- ✅ AsyncStorage instalado?
426
- ✅ Permissões de storage?
427
- Dados sendo serializados corretamente?
428
- ✅ Chaves de storage únicas?
429
- ```
430
-
431
- ### Debug Avançado
432
-
433
- ```typescript
434
- // Habilitar logs detalhados
435
- console.log("Widget event received:", processedKey, value);
436
-
437
- // Verificar dados persistidos
438
- const data = await storageService.read();
439
- console.log("Stored data:", data);
440
-
441
- // Verificar URL construída
442
- const url = buildWidgetURL(soluCXKey, data);
443
- console.log("Widget URL:", url);
444
- ```
445
-
446
- ## 📚 Dependências
447
-
448
- ### Peer Dependencies
449
-
450
- ```json
451
- {
452
- "react-native-webview": "^13.0.0",
453
- "@react-native-async-storage/async-storage": "^2.0.0",
454
- "react": "^19.0.0",
455
- "react-native": "^0.79.0"
456
- }
457
- ```
458
-
459
- ### Compatibilidade
460
-
461
- - **React Native**: 0.70+
462
- - **Expo**: 50+
463
- - **iOS**: 11+
464
- - **Android**: API 21+
465
- - **Web**: Suporte limitado (WebView)
466
-
467
- ## 📄 Licença
468
-
469
- Este projeto é privado e proprietário da SoluCX.
470
-
471
- ---
472
-
473
- **Desenvolvido com ❤️ pela equipe SoluCX**
1
+ # SoluCX Widget
2
+
3
+ Um widget React Native modular para coleta de feedback e pesquisas, desenvolvido seguindo princípios de Clean Code e arquitetura escalável.
4
+
5
+ ## 📋 Visão Geral
6
+
7
+ O SoluCX Widget oferece quatro modos de renderização flexíveis para integração em aplicações React Native/Expo:
8
+
9
+ - **Top**: Widget fixo no topo da tela
10
+ - **Bottom**: Widget fixo na parte inferior
11
+ - **Modal**: Widget em sobreposição centralizada
12
+ - **Inline**: Widget integrado ao fluxo do layout
13
+
14
+ ## 🏗️ Arquitetura
15
+
16
+ ### Componentes Principais
17
+
18
+ - [`SoluCXWidget.tsx`](src/SoluCXWidget.tsx) - Componente principal
19
+ - [`useWidgetState.ts`](src/hooks/useWidgetState.ts) - Hook para gerenciamento de estado
20
+ - [`useWidgetHeight.ts`](src/hooks/useWidgetHeight.ts) - Hook para gerenciamento de altura (dinâmica/fixa)
21
+ - [`WidgetEventService`](src/services/widgetEventService.ts) - Serviço de eventos
22
+ - [`StorageService`](src/services/storage.ts) - Persistência local
23
+
24
+ ### Estrutura de Arquivos
25
+
26
+ ```
27
+ src/
28
+ ├── SoluCXWidget.tsx # Componente principal
29
+ ├── components/ # Componentes especializados
30
+ │ ├── ModalWidget.tsx # Widget modal
31
+ ├── InlineWidget.tsx # Widget inline
32
+ │ └── OverlayWidget.tsx # Widget overlay (top/bottom)
33
+ ├── hooks/ # Hooks personalizados
34
+ ├── useWidgetState.ts # Estado do widget
35
+ │ ├── useWidgetHeight.ts # Gerenciamento de altura (dinâmica/fixa)
36
+ │ └── useHeightAnimation.ts # Animação de altura
37
+ ├── services/ # Serviços
38
+ │ ├── widgetEventService.ts # Gerenciamento de eventos
39
+ └── storage.ts # Persistência de dados
40
+ ├── interfaces/ # Tipos TypeScript
41
+ │ ├── index.ts # Exports centralizados
42
+ ├── WidgetData.ts # Dados do widget
43
+ ├── WidgetOptions.ts # Opções de configuração
44
+ ├── WidgetResponse.ts # Respostas e erros
45
+ │ └── WidgetSamplerLog.ts # Log de tentativas
46
+ ├── styles/ # Estilos
47
+ │ └── widgetStyles.ts # Estilos por tipo
48
+ ├── utils/ # Utilitários
49
+ └── urlUtils.ts # Construção de URLs
50
+ ├── constants/ # Constantes
51
+ └── webViewConstants.ts # URLs e configurações
52
+ └── __tests__/ # Testes
53
+ ├── urlUtils.test.ts
54
+ └── useWidgetState.test.ts
55
+ ```
56
+
57
+ ## 🚀 Instalação e Uso
58
+
59
+ ### Instalação
60
+
61
+ ```bash
62
+ # Como dependência local (monorepo)
63
+ npm install solucx_widget@file:./modules/solucx_widget
64
+
65
+ # Dependências peer necessárias
66
+ npm install react-native-webview @react-native-async-storage/async-storage
67
+ ```
68
+
69
+ ### Uso Básico
70
+
71
+ ```tsx
72
+ import React from 'react';
73
+ import { SoluCXWidget } from 'solucx_widget';
74
+
75
+ export default function MyComponent() {
76
+ return (
77
+ <SoluCXWidget
78
+ soluCXKey='sua-chave-solucx'
79
+ type='bottom' // 'top' | 'bottom' | 'modal' | 'inline'
80
+ data={{
81
+ journey: 'nome_da_jornada',
82
+ name: 'Nome do Cliente',
83
+ email: 'cliente@email.com',
84
+ phone: '11999999999',
85
+ store_id: '1',
86
+ employee_id: '1',
87
+ amount: 100,
88
+ param_REGIAO: 'SUDESTE'
89
+ }}
90
+ options={{
91
+ height: 400
92
+ }}
93
+ />
94
+ );
95
+ }
96
+ ```
97
+
98
+ ## 🎛️ API do Componente
99
+
100
+ ### Props do SoluCXWidget
101
+
102
+ ```typescript
103
+ interface SoluCXWidgetProps {
104
+ soluCXKey: string; // Chave de autenticação SoluCX
105
+ type: WidgetType; // Modo de renderização
106
+ data: WidgetData; // Dados do cliente/transação
107
+ options: WidgetOptions; // Configurações do widget
108
+ }
109
+ ```
110
+
111
+ ### Tipos de Widget
112
+
113
+ ```typescript
114
+ type WidgetType = 'bottom' | 'top' | 'inline' | 'modal';
115
+ ```
116
+
117
+ ### Dados do Widget
118
+
119
+ ```typescript
120
+ interface WidgetData {
121
+ // Identificadores
122
+ transaction_id?: string;
123
+ customer_id?: string;
124
+
125
+ // Dados do cliente
126
+ name?: string;
127
+ email?: string;
128
+ phone?: string;
129
+ birth_date?: string;
130
+ document?: string;
131
+
132
+ // Dados da transação
133
+ store_id?: string;
134
+ store_name?: string;
135
+ employee_id?: string;
136
+ employee_name?: string;
137
+ amount?: number;
138
+ score?: number;
139
+
140
+ // Parâmetros customizados
141
+ journey?: string;
142
+ [key: string]: string | number | undefined;
143
+ }
144
+ ```
145
+
146
+ ### Opções de Configuração
147
+
148
+ ```typescript
149
+ interface WidgetOptions {
150
+ height?: number; // Altura fixa em pontos (não pixels)
151
+ // Se não fornecido: altura dinâmica baseada em eventos de resize
152
+ // Se fornecido: altura fixa para todos os tipos de widget
153
+ retry?: {
154
+ // Configuração de retry
155
+ attempts?: number; // Número de tentativas
156
+ interval?: number; // Intervalo entre tentativas (ms)
157
+ };
158
+ waitDelayAfterRating?: number; // Delay após avaliação (ms)
159
+ }
160
+ ```
161
+
162
+ **⚙️ Gerenciamento de Altura:**
163
+
164
+ O widget possui dois modos de altura:
165
+
166
+ 1. **Altura Dinâmica (padrão)**: Quando `height` não é fornecido, o widget se ajusta automaticamente através de eventos `FORM_RESIZE` do conteúdo. Funciona para todos os tipos (`bottom`, `top`, `inline`, `modal`).
167
+
168
+ 2. **Altura Fixa**: Quando `height` é especificado, o valor é fixo e eventos de resize são ignorados. Funciona para todos os tipos de widget.
169
+
170
+ ```tsx
171
+ // Altura dinâmica - se adapta ao conteúdo
172
+ <SoluCXWidget
173
+ type="bottom"
174
+ options={{}} // height não especificado
175
+ {...props}
176
+ />
177
+
178
+ // Altura fixa de 400 pontos
179
+ <SoluCXWidget
180
+ type="modal"
181
+ options={{ height: 400 }} // altura fixa
182
+ {...props}
183
+ />
184
+ ```
185
+
186
+ **⚠️ Importante**: O valor de `height` é sempre em **pontos** (points), não pixels, seguindo o padrão do React e React Native. O sistema operacional converte automaticamente para pixels considerando a densidade da tela do dispositivo.
187
+
188
+ ## 🔄 Sistema de Eventos
189
+
190
+ O widget comunica através de mensagens WebView bidirecionais:
191
+
192
+ ### Eventos Suportados
193
+
194
+ ```typescript
195
+ type EventKey =
196
+ | 'FORM_OPENED' // Widget foi aberto
197
+ | 'FORM_CLOSE' // Usuário fechou o widget
198
+ | 'FORM_ERROR' // Erro no carregamento
199
+ | 'FORM_RESIZE' // Widget redimensionado
200
+ | 'FORM_PAGECHANGED' // Mudança de página
201
+ | 'QUESTION_ANSWERED' // Pergunta respondida
202
+ | 'FORM_COMPLETED' // Formulário concluído
203
+ | 'FORM_PARTIALCOMPLETED'; // Completado parcialmente
204
+ ```
205
+
206
+ ### Tratamento de Eventos
207
+
208
+ Os eventos são processados automaticamente pelo [`WidgetEventService`](src/services/widgetEventService.ts):
209
+
210
+ ```typescript
211
+ const eventService = new WidgetEventService(
212
+ setIsWidgetVisible, // Função para controlar visibilidade
213
+ resize, // Função para redimensionar
214
+ open // Função para abrir widget
215
+ );
216
+ ```
217
+
218
+ ## 💾 Persistência de Dados
219
+
220
+ O widget utiliza [`AsyncStorage`](@react-native-async-storage/async-storage) para persistir:
221
+
222
+ - **Histórico de tentativas**: Controla quantas vezes o widget foi exibido
223
+ - **Última avaliação**: Data da última interação
224
+ - **Controle de frequência**: Evita spam de widgets
225
+
226
+ ### Estrutura dos Dados
227
+
228
+ ```typescript
229
+ interface WidgetSamplerLog {
230
+ attempts: number; // Número de tentativas
231
+ lastAttempt: number; // Timestamp da última tentativa
232
+ lastRating: number; // Timestamp da última avaliação
233
+ lastParcial: number; // Timestamp do último parcial
234
+ }
235
+ ```
236
+
237
+ ## 🎨 Customização de Estilos
238
+
239
+ ### Estilos por Tipo
240
+
241
+ Cada tipo de widget possui estilos específicos definidos em [`widgetStyles.ts`](src/styles/widgetStyles.ts):
242
+
243
+ ```typescript
244
+ export const getWidgetStyles = (type: WidgetType) => {
245
+ const styleMap = {
246
+ bottom: { container: styles.wrapper, content: styles.bottom },
247
+ top: { container: styles.wrapper, content: styles.top },
248
+ inline: { container: styles.inlineWrapper, content: styles.inline },
249
+ modal: { container: styles.wrapper, content: styles.inline }
250
+ };
251
+
252
+ return styleMap[type] || styleMap.bottom;
253
+ };
254
+ ```
255
+
256
+ ### Características Visuais
257
+
258
+ - **Bottom/Top**: Position absolute com z-index elevado
259
+ - **Modal**: Overlay com background semitransparente
260
+ - **Inline**: Integrado ao fluxo normal do layout
261
+
262
+ ## 🔧 Utilitários
263
+
264
+ ### Construção de URLs
265
+
266
+ O [`urlUtils.ts`](src/utils/urlUtils.ts) gerencia a construção de URLs da pesquisa:
267
+
268
+ ```typescript
269
+ export function buildWidgetURL(key: string, data: WidgetData): string {
270
+ const params = new URLSearchParams(data as Record<string, string>);
271
+ const baseURL = `${BASE_URL}/${key}/?mode=widget`;
272
+
273
+ if (data.transaction_id) {
274
+ return `${baseURL}&${params.toString()}`;
275
+ }
276
+
277
+ return `${baseURL}&transaction_id=&${params.toString()}`;
278
+ }
279
+ ```
280
+
281
+ ## 🧪 Testes
282
+
283
+ ### Executando Testes
284
+
285
+ ```bash
286
+ # Todos os testes
287
+ npm test
288
+
289
+ # Teste específico
290
+ npm test -- urlUtils.test.ts
291
+ npm test -- useWidgetState.test.ts
292
+ ```
293
+
294
+ ### Cobertura de Testes
295
+
296
+ - ✅ Construção de URLs
297
+ - Gerenciamento de eventos
298
+ - ✅ Estados do widget
299
+ - Persistência de dados
300
+
301
+ ## ⚙️ Configuração
302
+
303
+ ### Constantes
304
+
305
+ Configurações centralizadas em [`webViewConstants.ts`](src/constants/webViewConstants.ts):
306
+
307
+ ```typescript
308
+ export const BASE_URL = 'https://survey-link.solucx.com.br/link';
309
+ export const STORAGE_KEY = '@solucxWidgetLog';
310
+ export const MIN_HEIGHT = 200;
311
+ export const FIXED_Z_INDEX = 9999;
312
+ export const MODAL_Z_INDEX = 10000;
313
+ ```
314
+
315
+ ### JavaScript Injection
316
+
317
+ Listener para comunicação WebView:
318
+
319
+ ```typescript
320
+ export const WEB_VIEW_MESSAGE_LISTENER = `
321
+ window.addEventListener('message', function(event) {
322
+ window.ReactNativeWebView.postMessage(event.data);
323
+ });
324
+ `;
325
+ ```
326
+
327
+ ## 🚨 Considerações Importantes
328
+
329
+ ### 1. **Comportamento de Posicionamento**
330
+
331
+ ⚠️ **Ponto Crítico**: A posição no JSX **não determina** onde widgets `top`, `bottom` e `modal` aparecem:
332
+
333
+ ```tsx
334
+ // Isso NÃO faz o widget aparecer no meio
335
+ <Text>Conteúdo antes</Text>
336
+ <SoluCXWidget type="bottom" {...props} /> {/* Sempre aparece embaixo */}
337
+ <Text>Conteúdo depois</Text>
338
+
339
+ // ✅ Apenas o tipo "inline" respeita a posição no código
340
+ <Text>Conteúdo antes</Text>
341
+ <SoluCXWidget type="inline" {...props} /> {/* Aparece aqui */}
342
+ <Text>Conteúdo depois</Text>
343
+ ```
344
+
345
+ ### 2. **Performance**
346
+
347
+ - Widget usa WebView interna (overhead de performance)
348
+ - Carregamento lazy das pesquisas
349
+ - Cache automático via AsyncStorage
350
+ - JavaScript injection para comunicação
351
+
352
+ ### 3. **Segurança**
353
+
354
+ - **Chaves API**: Nunca hardcode em produção
355
+ - **Dados sensíveis**: Não são logados automaticamente
356
+ - **URLs**: Validadas antes do carregamento
357
+ - **HTTPS**: Obrigatório para comunicação
358
+
359
+ ### 4. **Limitações Técnicas**
360
+
361
+ - Requer conexão com internet
362
+ - Dependente do WebView do sistema
363
+ - Eventos assíncronos (não bloqueantes)
364
+ - Storage limitado do dispositivo
365
+
366
+ ## 📝 Desenvolvimento
367
+
368
+ ### Adicionando Novos Tipos
369
+
370
+ 1. **Estenda o tipo WidgetType**:
371
+
372
+ ```typescript
373
+ // modules/solucx_widget/src/interfaces/index.ts
374
+ export type WidgetType = 'bottom' | 'top' | 'inline' | 'modal' | 'novo_tipo';
375
+ ```
376
+
377
+ 2. **Adicione estilos correspondentes**:
378
+
379
+ ```typescript
380
+ // modules/solucx_widget/src/styles/widgetStyles.ts
381
+ const styleMap = {
382
+ // ... estilos existentes
383
+ novo_tipo: { container: styles.novoWrapper, content: styles.novoContent }
384
+ };
385
+ ```
386
+
387
+ 3. **Implemente lógica no componente principal**:
388
+
389
+ ```typescript
390
+ // modules/solucx_widget/src/SoluCXWidget.tsx
391
+ if (type === 'novo_tipo') {
392
+ return <NovoTipoWidget>{/* ... */}</NovoTipoWidget>;
393
+ }
394
+ ```
395
+
396
+ ### Adicionando Novos Eventos
397
+
398
+ 1. **Estenda EventKey**:
399
+
400
+ ```typescript
401
+ // modules/solucx_widget/src/interfaces/index.ts
402
+ export type EventKey = 'FORM_OPENED' | 'NOVO_EVENTO'; // Novo evento
403
+ ```
404
+
405
+ 2. **Implemente handler**:
406
+
407
+ ```typescript
408
+ // modules/solucx_widget/src/services/widgetEventService.ts
409
+ private executeEvent(eventKey: EventKey, value: string): WidgetResponse {
410
+ const eventHandlers = {
411
+ // ... handlers existentes
412
+ NOVO_EVENTO: (value: string) => this.handleNovoEvento(value),
413
+ };
414
+ }
415
+ ```
416
+
417
+ 3. **Adicione testes**:
418
+
419
+ ```typescript
420
+ // modules/solucx_widget/src/__tests__/widgetEventService.test.ts
421
+ it('should handle NOVO_EVENTO correctly', () => {
422
+ const result = service.handleMessage('NOVO_EVENTO-data', true);
423
+ expect(result).toEqual({ status: 'success' });
424
+ });
425
+ ```
426
+
427
+ ## 🔍 Troubleshooting
428
+
429
+ ### Problemas Comuns
430
+
431
+ #### 1. **Widget não aparece**
432
+
433
+ ```bash
434
+ # Verificações:
435
+ Chave SoluCX válida?
436
+ ✅ Conectividade com internet?
437
+ Logs do WebView no console?
438
+ Evento FORM_OPENED sendo disparado?
439
+ ```
440
+
441
+ #### 2. **Eventos não funcionam**
442
+
443
+ ```bash
444
+ # Verificações:
445
+ ✅ JavaScript listener injetado?
446
+ Formato das mensagens correto?
447
+ ✅ WebView carregou completamente?
448
+ postMessage funcionando?
449
+ ```
450
+
451
+ #### 3. **Layout quebrado**
452
+
453
+ ```bash
454
+ # Verificações:
455
+ Dimensões adequadas para o dispositivo?
456
+ ✅ Estilos corretos para o tipo?
457
+ ✅ Z-index conflitando?
458
+ ✅ SafeArea configurada?
459
+ ```
460
+
461
+ #### 4. **Storage não persiste**
462
+
463
+ ```bash
464
+ # Verificações:
465
+ AsyncStorage instalado?
466
+ ✅ Permissões de storage?
467
+ Dados sendo serializados corretamente?
468
+ ✅ Chaves de storage únicas?
469
+ ```
470
+
471
+ ### Debug Avançado
472
+
473
+ ```typescript
474
+ // Habilitar logs detalhados
475
+ console.log('Widget event received:', processedKey, value);
476
+
477
+ // Verificar dados persistidos
478
+ const data = await storageService.read();
479
+ console.log('Stored data:', data);
480
+
481
+ // Verificar URL construída
482
+ const url = buildWidgetURL(soluCXKey, data);
483
+ console.log('Widget URL:', url);
484
+ ```
485
+
486
+ ## 📚 Dependências
487
+
488
+ ### Peer Dependencies
489
+
490
+ ```json
491
+ {
492
+ "react-native-webview": "^13.0.0",
493
+ "@react-native-async-storage/async-storage": "^2.0.0",
494
+ "react": "^19.0.0",
495
+ "react-native": "^0.79.0"
496
+ }
497
+ ```
498
+
499
+ ### Compatibilidade
500
+
501
+ - **React Native**: 0.70+
502
+ - **Expo**: 50+
503
+ - **iOS**: 11+
504
+ - **Android**: API 21+
505
+ - **Web**: Suporte limitado (WebView)
506
+
507
+ ## 📄 Licença
508
+
509
+ Este projeto é privado e proprietário da SoluCX.
510
+
511
+ ---
512
+
513
+ **Desenvolvido com ❤️ pela equipe SoluCX**