@encatch/web-sdk 0.0.13
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.md +756 -0
- package/dist-sdk/plugin/sdk/core-wrapper-CAKT3nVS.js +24258 -0
- package/dist-sdk/plugin/sdk/module-CGKWqAgY.js +495 -0
- package/dist-sdk/plugin/sdk/module.js +5 -0
- package/dist-sdk/plugin/sdk/preview-sdk.html +1182 -0
- package/dist-sdk/plugin/sdk/vite.svg +15 -0
- package/dist-sdk/plugin/sdk/web-form-engine-core.css +1 -0
- package/index.d.ts +207 -0
- package/package.json +72 -0
- package/src/@types/encatch-type.ts +111 -0
- package/src/encatch-instance.ts +161 -0
- package/src/feedback-api-types.ts +18 -0
- package/src/hooks/useDevice.ts +27 -0
- package/src/hooks/useFeedbackInterval.ts +71 -0
- package/src/hooks/useFeedbackTriggers.ts +342 -0
- package/src/hooks/useFetchElligibleFeedbackConfiguration.ts +330 -0
- package/src/hooks/useFetchFeedbackConfigurationDetails.ts +82 -0
- package/src/hooks/usePageChangeTracker.ts +88 -0
- package/src/hooks/usePrepopulatedAnswers.ts +123 -0
- package/src/hooks/useRefineTextForm.ts +48 -0
- package/src/hooks/useSubmitFeedbackForm.ts +125 -0
- package/src/hooks/useUserSession.ts +53 -0
- package/src/module.tsx +427 -0
- package/src/store/formResponses.ts +211 -0
- package/src/utils/browser-details.ts +35 -0
- package/src/utils/duration-utils.ts +143 -0
- package/src/utils/feedback-frequency-storage.ts +214 -0
- package/src/utils/feedback-storage.ts +166 -0
package/README.md
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
# Encatch Web SDK
|
|
2
|
+
|
|
3
|
+
A powerful, lightweight JavaScript SDK for collecting user feedback and running surveys on web applications. Built with Preact and TypeScript, the Encatch Web SDK provides intelligent trigger-based feedback collection with advanced targeting and frequency control.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Intelligent Triggers**: Automatically display feedback based on user behavior
|
|
8
|
+
- Page load triggers
|
|
9
|
+
- Custom event triggers
|
|
10
|
+
- Scroll-based triggers
|
|
11
|
+
- Time-delayed triggers
|
|
12
|
+
- **User Session Management**: Track and manage user sessions with device fingerprinting
|
|
13
|
+
- **Frequency Control**: Configure feedback display frequency and scheduling
|
|
14
|
+
- **Multi-language Support**: Display feedback in multiple languages
|
|
15
|
+
- **Theme Support**: Light and dark mode with custom CSS
|
|
16
|
+
- **Prepopulated Answers**: Pre-fill feedback forms with known data
|
|
17
|
+
- **Event Tracking**: Track user interactions and custom events
|
|
18
|
+
- **TypeScript Support**: Fully typed API for better developer experience
|
|
19
|
+
- **Lightweight**: Minimal bundle size with optimized performance
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Via Script Tag (Recommended)
|
|
24
|
+
|
|
25
|
+
Add the following script to your HTML file before the closing `</head>` tag:
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<script>
|
|
29
|
+
!function(){if(!window.encatch){var e={_i:[],apiKey:"",config:{host:"https://your-host.com",autoStartEnabled:!1,autoStartSessionDisabled:!0,themeMode:"light",language:"en",customCssLink:""},initialized:!1,chunkUrlLoader:function(e){return window.encatch.config.host+e},init:function(n,t){if(!("initialized"in window.encatch&&window.encatch.initialized)){if(window.encatch.apiKey=n,t?.host&&(window.encatch.config.host=t.host),t?.identity&&(window.encatch.config.identity=t.identity),void 0!==t?.autoStartSessionDisabled&&(window.encatch.config.autoStartSessionDisabled=t.autoStartSessionDisabled),void 0!==t?.processAfterIdentitySet?window.encatch.config.processAfterIdentitySet=t.processAfterIdentitySet:window.encatch.config.processAfterIdentitySet=!1,void 0!==t?.autoStartEnabled&&(window.encatch.config.autoStartEnabled=t.autoStartEnabled),t?.themeMode?window.encatch.config.themeMode=t.themeMode:window.encatch.config.themeMode="light",t?.language?window.encatch.config.language=t.language:window.encatch.config.language="en",t?.customCssLink&&(window.encatch.config.customCssLink=t.customCssLink),t?.setUser&&(window.encatch.config.setUser=t.setUser),window.encatch.initialized=!0,!import.meta.env.VITE_PATH_PREFIX||import.meta.env.VITE_PATH_PREFIX.startsWith("/")||(import.meta.env.VITE_PATH_PREFIX="/"+import.meta.env.VITE_PATH_PREFIX),import.meta.env.VITE_PATH_PREFIX=import.meta.env.VITE_PATH_PREFIX.replace(/\/+$/,""),scriptSrc=`${window.encatch.config.host}${import.meta.env.VITE_PATH_PREFIX||""}/encatch-web-sdk.js`,(script=document.createElement("script")).src=scriptSrc,script.type="module",script.async=!0,firstHeadElement=document.head.firstChild)firstHeadElement?document.head.insertBefore(script,firstHeadElement):document.head.appendChild(script);var o=document.createElement("div");o.id="enisght-root",document.body.appendChild(o)}}};window.encatch=e,["trackEvent","stop","start","setUser","setThemeMode","setLanguage","openFeedbackById","openFeedbackByName","verifyFeedbackIds","forceFetchEligibleFeedbacks","capturePageScrollEvent"].forEach((function(n){e[n]=function(){for(var t=arguments.length,o=new Array(t),i=0;i<t;i++)o[i]=arguments[i];window.encatch._i.push([n].concat(o))}}))}}();
|
|
30
|
+
</script>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then initialize the SDK:
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<script>
|
|
37
|
+
encatch.init('YOUR_API_KEY', {
|
|
38
|
+
host: 'https://your-host.com',
|
|
39
|
+
autoStartEnabled: true
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Via NPM (For Module Bundlers)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install @encatch/web-sdk
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import '@encatch/web-sdk';
|
|
52
|
+
|
|
53
|
+
encatch.init('YOUR_API_KEY', {
|
|
54
|
+
host: 'https://your-host.com',
|
|
55
|
+
autoStartEnabled: true
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
### Basic Setup
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// Initialize the SDK
|
|
65
|
+
encatch.init('YOUR_API_KEY', {
|
|
66
|
+
host: 'https://your-host.com',
|
|
67
|
+
autoStartEnabled: true,
|
|
68
|
+
themeMode: 'light',
|
|
69
|
+
language: 'en'
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Set user identity (optional)
|
|
73
|
+
encatch.setUser('user-123', {
|
|
74
|
+
$set: {
|
|
75
|
+
email: 'user@example.com',
|
|
76
|
+
name: 'John Doe',
|
|
77
|
+
plan: 'premium'
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Track custom events
|
|
82
|
+
encatch.trackEvent('button_clicked', {
|
|
83
|
+
path: '/dashboard',
|
|
84
|
+
button_id: 'upgrade-button'
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Manual Trigger
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// Open feedback by ID
|
|
92
|
+
encatch.openFeedbackById('feedback-config-id-123');
|
|
93
|
+
|
|
94
|
+
// Open feedback by name
|
|
95
|
+
encatch.openFeedbackByName('Customer Satisfaction Survey');
|
|
96
|
+
|
|
97
|
+
// With theme and language override
|
|
98
|
+
encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Configuration Options
|
|
102
|
+
|
|
103
|
+
### Initialization Options
|
|
104
|
+
|
|
105
|
+
| Option | Type | Default | Description |
|
|
106
|
+
|--------|------|---------|-------------|
|
|
107
|
+
| `host` | `string` | - | **Required**. The host URL for the Encatch API |
|
|
108
|
+
| `autoStartEnabled` | `boolean` | `false` | Automatically start fetching eligible feedbacks |
|
|
109
|
+
| `autoStartSessionDisabled` | `boolean` | `true` | Disable automatic session management |
|
|
110
|
+
| `processAfterIdentitySet` | `boolean` | `false` | Wait for user identity before processing |
|
|
111
|
+
| `themeMode` | `'light' \| 'dark'` | `'light'` | Default theme mode |
|
|
112
|
+
| `language` | `string` | `'en'` | Default language code |
|
|
113
|
+
| `customCssLink` | `string` | - | URL to custom CSS file |
|
|
114
|
+
| `setUser` | `UserInfo` | - | Initial user identity |
|
|
115
|
+
| `customProperties` | `Record<string, string>` | - | Custom properties for targeting |
|
|
116
|
+
| `onSessionDisabled` | `(action, message) => void` | - | Callback when session functions are disabled |
|
|
117
|
+
|
|
118
|
+
### Example: Complete Configuration
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
encatch.init('YOUR_API_KEY', {
|
|
122
|
+
host: 'https://your-host.com',
|
|
123
|
+
autoStartEnabled: true,
|
|
124
|
+
autoStartSessionDisabled: false,
|
|
125
|
+
themeMode: 'light',
|
|
126
|
+
language: 'en',
|
|
127
|
+
customCssLink: 'https://your-cdn.com/custom-theme.css',
|
|
128
|
+
setUser: {
|
|
129
|
+
userId: 'user-123',
|
|
130
|
+
traits: {
|
|
131
|
+
$set: {
|
|
132
|
+
email: 'user@example.com',
|
|
133
|
+
plan: 'premium'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
onSessionDisabled: (action, message) => {
|
|
138
|
+
console.warn(`Session action blocked: ${action} - ${message}`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## API Reference
|
|
144
|
+
|
|
145
|
+
### `init(apiKey, options)`
|
|
146
|
+
|
|
147
|
+
Initialize the Encatch SDK.
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
encatch.init('YOUR_API_KEY', {
|
|
151
|
+
host: 'https://your-host.com',
|
|
152
|
+
autoStartEnabled: true
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `start(userId?, traits?)`
|
|
157
|
+
|
|
158
|
+
Start the SDK and optionally set user identity. Useful when `autoStartEnabled: false`.
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
// Start without user
|
|
162
|
+
encatch.start();
|
|
163
|
+
|
|
164
|
+
// Start with user identity
|
|
165
|
+
encatch.start('user-123', {
|
|
166
|
+
$set: {
|
|
167
|
+
email: 'user@example.com',
|
|
168
|
+
name: 'John Doe'
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `stop()`
|
|
174
|
+
|
|
175
|
+
Stop the SDK. Behavior depends on initialization:
|
|
176
|
+
|
|
177
|
+
- If initialized with `autoStartEnabled: false`: Clears all data, feedbacks, and frequency tracking
|
|
178
|
+
- If initialized with `autoStartEnabled: true`: Makes user anonymous but retains feedbacks
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
encatch.stop();
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `setUser(userId?, traits?)`
|
|
185
|
+
|
|
186
|
+
Set or update user identity.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
// Set user identity
|
|
190
|
+
encatch.setUser('user-123', {
|
|
191
|
+
$set: { email: 'user@example.com', plan: 'premium' },
|
|
192
|
+
$set_once: { signup_date: '2024-01-01' },
|
|
193
|
+
$counter: { logins: 1 },
|
|
194
|
+
$unset: ['temp_property']
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Make user anonymous
|
|
198
|
+
encatch.setUser();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### User Traits Operators
|
|
202
|
+
|
|
203
|
+
- `$set`: Set property value (overwrites existing)
|
|
204
|
+
- `$set_once`: Set only if property doesn't exist
|
|
205
|
+
- `$counter`: Increment counter by value
|
|
206
|
+
- `$unset`: Remove properties (array of property names)
|
|
207
|
+
|
|
208
|
+
### `trackEvent(eventName, properties?)`
|
|
209
|
+
|
|
210
|
+
Track custom events for trigger matching.
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
encatch.trackEvent('purchase_completed', {
|
|
214
|
+
path: '/checkout/success',
|
|
215
|
+
product_id: 'prod-123',
|
|
216
|
+
amount: 99.99
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `openFeedbackById(feedbackConfigId, theme?, language?, event?, customProperties?, prepopulatedAnswers?)`
|
|
221
|
+
|
|
222
|
+
Manually open a feedback form by its configuration ID.
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// Basic usage
|
|
226
|
+
encatch.openFeedbackById('feedback-config-id-123');
|
|
227
|
+
|
|
228
|
+
// With theme and language override
|
|
229
|
+
encatch.openFeedbackById('feedback-config-id-123', 'dark', 'es');
|
|
230
|
+
|
|
231
|
+
// With prepopulated answers
|
|
232
|
+
encatch.openFeedbackById('feedback-config-id-123', 'light', 'en', undefined, undefined, [
|
|
233
|
+
{
|
|
234
|
+
sectionIndex: 0,
|
|
235
|
+
questionIndex: 0,
|
|
236
|
+
value: { text: 'Pre-filled answer' }
|
|
237
|
+
}
|
|
238
|
+
]);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `openFeedbackByName(feedbackName, theme?, language?, event?, customProperties?, prepopulatedAnswers?)`
|
|
242
|
+
|
|
243
|
+
Manually open a feedback form by its name.
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
encatch.openFeedbackByName('Customer Satisfaction Survey');
|
|
247
|
+
|
|
248
|
+
// With options
|
|
249
|
+
encatch.openFeedbackByName('NPS Survey', 'dark', 'es');
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### `setThemeMode(theme)`
|
|
253
|
+
|
|
254
|
+
Change the theme mode dynamically.
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
encatch.setThemeMode('dark');
|
|
258
|
+
encatch.setThemeMode('light');
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `setLanguage(language)`
|
|
262
|
+
|
|
263
|
+
Change the language dynamically.
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
encatch.setLanguage('es'); // Spanish
|
|
267
|
+
encatch.setLanguage('fr'); // French
|
|
268
|
+
encatch.setLanguage('en'); // English
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### `forceFetchEligibleFeedbacks()`
|
|
272
|
+
|
|
273
|
+
Force refresh the list of eligible feedbacks.
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
await encatch.forceFetchEligibleFeedbacks();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### `verifyFeedbackIds(feedbackIds)`
|
|
280
|
+
|
|
281
|
+
Verify which feedback IDs are currently eligible.
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
const validIds = encatch.verifyFeedbackIds([
|
|
285
|
+
'feedback-id-1',
|
|
286
|
+
'feedback-id-2',
|
|
287
|
+
'feedback-id-3'
|
|
288
|
+
]);
|
|
289
|
+
console.log('Valid feedback IDs:', validIds);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `capturePageScrollEvent(scrollPercent)`
|
|
293
|
+
|
|
294
|
+
Manually trigger scroll-based feedback.
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
// Track scroll percentage
|
|
298
|
+
window.addEventListener('scroll', () => {
|
|
299
|
+
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
|
|
300
|
+
encatch.capturePageScrollEvent(`${scrollPercent}%`);
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Event Tracking
|
|
305
|
+
|
|
306
|
+
### Automatic Page Tracking
|
|
307
|
+
|
|
308
|
+
The SDK automatically tracks page changes in single-page applications:
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
// Page changes are tracked automatically
|
|
312
|
+
// Triggers "pageLoad" event for configured triggers
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Custom Events
|
|
316
|
+
|
|
317
|
+
Track custom events for business logic:
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
// E-commerce example
|
|
321
|
+
encatch.trackEvent('product_viewed', {
|
|
322
|
+
path: '/products/123',
|
|
323
|
+
category: 'electronics',
|
|
324
|
+
price: 299.99
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
encatch.trackEvent('add_to_cart', {
|
|
328
|
+
path: '/products/123',
|
|
329
|
+
product_id: 'prod-123'
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
encatch.trackEvent('checkout_started', {
|
|
333
|
+
path: '/checkout',
|
|
334
|
+
cart_value: 599.98
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Scroll Tracking
|
|
339
|
+
|
|
340
|
+
```javascript
|
|
341
|
+
// Automatic scroll tracking (if configured in feedback triggers)
|
|
342
|
+
// Or manual tracking:
|
|
343
|
+
let scrollTriggered = false;
|
|
344
|
+
|
|
345
|
+
window.addEventListener('scroll', () => {
|
|
346
|
+
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
|
|
347
|
+
|
|
348
|
+
if (scrollPercent > 50 && !scrollTriggered) {
|
|
349
|
+
encatch.capturePageScrollEvent('50%');
|
|
350
|
+
scrollTriggered = true;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Advanced Usage
|
|
356
|
+
|
|
357
|
+
### Prepopulated Answers
|
|
358
|
+
|
|
359
|
+
Pre-fill feedback forms with known data:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
encatch.openFeedbackById('feedback-config-id', 'light', 'en', undefined, undefined, [
|
|
363
|
+
{
|
|
364
|
+
sectionIndex: 0, // First section
|
|
365
|
+
questionIndex: 0, // First question
|
|
366
|
+
value: { text: 'Pre-filled text answer' }
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
sectionIndex: 0,
|
|
370
|
+
questionIndex: 1,
|
|
371
|
+
value: { rating: 5 }
|
|
372
|
+
}
|
|
373
|
+
]);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Custom Properties for Targeting
|
|
377
|
+
|
|
378
|
+
Pass custom properties for advanced targeting:
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
encatch.openFeedbackById(
|
|
382
|
+
'feedback-config-id',
|
|
383
|
+
'light',
|
|
384
|
+
'en',
|
|
385
|
+
undefined,
|
|
386
|
+
{
|
|
387
|
+
user_segment: 'premium',
|
|
388
|
+
feature_flag: 'beta_features',
|
|
389
|
+
experiment_group: 'variant_a'
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Session Management
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
// Disable automatic session management
|
|
398
|
+
encatch.init('YOUR_API_KEY', {
|
|
399
|
+
host: 'https://your-host.com',
|
|
400
|
+
autoStartSessionDisabled: false
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Manual session control (when autoStartSessionDisabled: false)
|
|
404
|
+
encatch.start(); // Start a new session
|
|
405
|
+
encatch.stop(); // End current session
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Dynamic Theme Switching
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
// Listen to system theme changes
|
|
412
|
+
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
413
|
+
|
|
414
|
+
darkModeQuery.addEventListener('change', (e) => {
|
|
415
|
+
encatch.setThemeMode(e.matches ? 'dark' : 'light');
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Or use a theme toggle
|
|
419
|
+
function toggleTheme() {
|
|
420
|
+
const currentTheme = getCurrentTheme(); // Your theme logic
|
|
421
|
+
encatch.setThemeMode(currentTheme === 'dark' ? 'light' : 'dark');
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Custom CSS Styling
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
// Option 1: Provide custom CSS URL during initialization
|
|
429
|
+
encatch.init('YOUR_API_KEY', {
|
|
430
|
+
host: 'https://your-host.com',
|
|
431
|
+
customCssLink: 'https://your-cdn.com/custom-encatch-theme.css'
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Option 2: Configure custom CSS per feedback in the Encatch dashboard
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Conditional Feedback Display
|
|
438
|
+
|
|
439
|
+
```javascript
|
|
440
|
+
// Only show feedback to premium users
|
|
441
|
+
if (user.plan === 'premium') {
|
|
442
|
+
encatch.openFeedbackById('premium-user-feedback');
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Show different feedback based on user journey
|
|
446
|
+
if (isNewUser) {
|
|
447
|
+
encatch.openFeedbackByName('Onboarding Feedback');
|
|
448
|
+
} else {
|
|
449
|
+
encatch.openFeedbackByName('Feature Usage Survey');
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Form Event Listeners
|
|
454
|
+
|
|
455
|
+
The SDK provides two approaches for listening to feedback form events: **config-based** and **runtime-based**.
|
|
456
|
+
|
|
457
|
+
### Available Form Events
|
|
458
|
+
|
|
459
|
+
| Event | Payload | Description |
|
|
460
|
+
|-------|---------|-------------|
|
|
461
|
+
| `form:submit` | `{ feedbackIdentifier: string, feedbackConfigurationId: string, completionTimeInSeconds: number, response: Object }` | Emitted when the user completes and submits the feedback form |
|
|
462
|
+
| `form:view` | `{ feedbackIdentifier: string, feedbackConfigurationId: string }` | Emitted when the feedback form is displayed to the user |
|
|
463
|
+
| `form:close` | `{ feedbackIdentifier: string, feedbackConfigurationId: string }` | Emitted when the user closes the feedback form (with or without submitting) |
|
|
464
|
+
| `form:questionAnswered` | `{ questionId: string, answer: Object }` | Emitted when the user answers a question |
|
|
465
|
+
| `form:sectionChange` | `{ sectionIndex: number }` | Emitted when the user navigates to a different section |
|
|
466
|
+
| `form:error` | `{ error: string }` | Emitted when an error occurs during form interaction |
|
|
467
|
+
|
|
468
|
+
### Approach 1: Config-Based (Recommended)
|
|
469
|
+
|
|
470
|
+
Define event handlers during SDK initialization using the `onFormEvent` callback:
|
|
471
|
+
|
|
472
|
+
```javascript
|
|
473
|
+
encatch.init('YOUR_API_KEY', {
|
|
474
|
+
host: 'https://your-host.com',
|
|
475
|
+
autoStartEnabled: true,
|
|
476
|
+
onFormEvent: (formEvent) => {
|
|
477
|
+
// Listen to form submission
|
|
478
|
+
formEvent.onSubmit((data) => {
|
|
479
|
+
console.log('Form submitted:', data);
|
|
480
|
+
console.log('Feedback ID:', data.feedbackIdentifier);
|
|
481
|
+
console.log('Config ID:', data.feedbackConfigurationId);
|
|
482
|
+
console.log('Completion time:', data.completionTimeInSeconds);
|
|
483
|
+
|
|
484
|
+
// Send to analytics
|
|
485
|
+
analytics.track('Feedback Submitted', {
|
|
486
|
+
feedbackId: data.feedbackIdentifier,
|
|
487
|
+
configId: data.feedbackConfigurationId
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Listen to form view
|
|
492
|
+
formEvent.onView((data) => {
|
|
493
|
+
console.log('Form viewed:', data);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// Listen to form close
|
|
497
|
+
formEvent.onClose((data) => {
|
|
498
|
+
console.log('Form closed:', data);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Listen to question answered
|
|
502
|
+
formEvent.onQuestionAnswered((data) => {
|
|
503
|
+
console.log('Question answered:', data.questionId, data.answer);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Listen to section change
|
|
507
|
+
formEvent.onSectionChange((data) => {
|
|
508
|
+
console.log('Section changed to:', data.sectionIndex);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Listen to errors
|
|
512
|
+
formEvent.onError((data) => {
|
|
513
|
+
console.error('Form error:', data.error);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Approach 2: Runtime-Based
|
|
520
|
+
|
|
521
|
+
Subscribe to events at runtime using the `encatch.on()` method:
|
|
522
|
+
|
|
523
|
+
```javascript
|
|
524
|
+
// Subscribe to form submission
|
|
525
|
+
const unsubscribeSubmit = encatch.on('form:submit', (data) => {
|
|
526
|
+
console.log('Form submitted:', data);
|
|
527
|
+
alert(`Feedback submitted! ID: ${data.feedbackIdentifier}`);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// Subscribe to form view
|
|
531
|
+
const unsubscribeView = encatch.on('form:view', (data) => {
|
|
532
|
+
console.log('Form viewed:', data);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// Subscribe to form close
|
|
536
|
+
const unsubscribeClose = encatch.on('form:close', (data) => {
|
|
537
|
+
console.log('Form closed:', data);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Subscribe to question answered
|
|
541
|
+
const unsubscribeQuestion = encatch.on('form:questionAnswered', (data) => {
|
|
542
|
+
console.log('Question answered:', data);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Subscribe to section change
|
|
546
|
+
const unsubscribeSection = encatch.on('form:sectionChange', (data) => {
|
|
547
|
+
console.log('Section changed:', data);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Subscribe to errors
|
|
551
|
+
const unsubscribeError = encatch.on('form:error', (data) => {
|
|
552
|
+
console.error('Form error:', data);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Unsubscribe when no longer needed
|
|
556
|
+
unsubscribeSubmit();
|
|
557
|
+
unsubscribeView();
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Integration Examples
|
|
561
|
+
|
|
562
|
+
#### Google Analytics Integration
|
|
563
|
+
|
|
564
|
+
```javascript
|
|
565
|
+
encatch.init('YOUR_API_KEY', {
|
|
566
|
+
host: 'https://your-host.com',
|
|
567
|
+
autoStartEnabled: true,
|
|
568
|
+
onFormEvent: (formEvent) => {
|
|
569
|
+
formEvent.onView((data) => {
|
|
570
|
+
gtag('event', 'feedback_viewed', {
|
|
571
|
+
feedback_id: data.feedbackIdentifier,
|
|
572
|
+
config_id: data.feedbackConfigurationId
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
formEvent.onSubmit((data) => {
|
|
577
|
+
gtag('event', 'feedback_submitted', {
|
|
578
|
+
feedback_id: data.feedbackIdentifier,
|
|
579
|
+
config_id: data.feedbackConfigurationId,
|
|
580
|
+
completion_time: data.completionTimeInSeconds
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
formEvent.onClose((data) => {
|
|
585
|
+
gtag('event', 'feedback_closed', {
|
|
586
|
+
feedback_id: data.feedbackIdentifier
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
#### Segment Analytics Integration
|
|
594
|
+
|
|
595
|
+
```javascript
|
|
596
|
+
encatch.init('YOUR_API_KEY', {
|
|
597
|
+
host: 'https://your-host.com',
|
|
598
|
+
autoStartEnabled: true,
|
|
599
|
+
onFormEvent: (formEvent) => {
|
|
600
|
+
formEvent.onSubmit((data) => {
|
|
601
|
+
analytics.track('Feedback Completed', {
|
|
602
|
+
feedbackId: data.feedbackIdentifier,
|
|
603
|
+
configId: data.feedbackConfigurationId,
|
|
604
|
+
duration: data.completionTimeInSeconds
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
formEvent.onQuestionAnswered((data) => {
|
|
609
|
+
analytics.track('Question Answered', {
|
|
610
|
+
questionId: data.questionId,
|
|
611
|
+
answerType: data.answer.answer_type
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
#### Custom Data Layer (GTM)
|
|
619
|
+
|
|
620
|
+
```javascript
|
|
621
|
+
encatch.init('YOUR_API_KEY', {
|
|
622
|
+
host: 'https://your-host.com',
|
|
623
|
+
autoStartEnabled: true,
|
|
624
|
+
onFormEvent: (formEvent) => {
|
|
625
|
+
formEvent.onSubmit((data) => {
|
|
626
|
+
window.dataLayer = window.dataLayer || [];
|
|
627
|
+
window.dataLayer.push({
|
|
628
|
+
event: 'feedback_submitted',
|
|
629
|
+
feedbackId: data.feedbackIdentifier,
|
|
630
|
+
configId: data.feedbackConfigurationId,
|
|
631
|
+
completionTime: data.completionTimeInSeconds
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
#### CRM Integration (e.g., Salesforce, HubSpot)
|
|
639
|
+
|
|
640
|
+
```javascript
|
|
641
|
+
encatch.init('YOUR_API_KEY', {
|
|
642
|
+
host: 'https://your-host.com',
|
|
643
|
+
autoStartEnabled: true,
|
|
644
|
+
onFormEvent: (formEvent) => {
|
|
645
|
+
formEvent.onSubmit(async (data) => {
|
|
646
|
+
// Send to your CRM
|
|
647
|
+
await fetch('https://your-crm.com/api/feedback', {
|
|
648
|
+
method: 'POST',
|
|
649
|
+
headers: { 'Content-Type': 'application/json' },
|
|
650
|
+
body: JSON.stringify({
|
|
651
|
+
feedbackId: data.feedbackIdentifier,
|
|
652
|
+
userId: window.currentUserId,
|
|
653
|
+
response: data.response,
|
|
654
|
+
timestamp: new Date().toISOString()
|
|
655
|
+
})
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## TypeScript Support
|
|
663
|
+
|
|
664
|
+
The SDK is fully typed. TypeScript definitions are included:
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
import '@encatch/web-sdk';
|
|
668
|
+
|
|
669
|
+
// All methods are typed
|
|
670
|
+
encatch.init('YOUR_API_KEY', {
|
|
671
|
+
host: 'https://your-host.com',
|
|
672
|
+
autoStartEnabled: true,
|
|
673
|
+
themeMode: 'light', // Type: 'light' | 'dark'
|
|
674
|
+
language: 'en'
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// User traits are typed
|
|
678
|
+
encatch.setUser('user-123', {
|
|
679
|
+
$set: { email: 'user@example.com' },
|
|
680
|
+
$set_once: { signup_date: '2024-01-01' },
|
|
681
|
+
$counter: { logins: 1 },
|
|
682
|
+
$unset: ['temp_field']
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// Event properties are typed
|
|
686
|
+
encatch.trackEvent('custom_event', {
|
|
687
|
+
path: '/page',
|
|
688
|
+
event: 'click'
|
|
689
|
+
});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
## Development
|
|
693
|
+
|
|
694
|
+
### Prerequisites
|
|
695
|
+
|
|
696
|
+
- Node.js 18+
|
|
697
|
+
- pnpm
|
|
698
|
+
|
|
699
|
+
### Setup
|
|
700
|
+
|
|
701
|
+
```bash
|
|
702
|
+
# Install dependencies
|
|
703
|
+
pnpm install
|
|
704
|
+
|
|
705
|
+
# Start development server
|
|
706
|
+
pnpm dev
|
|
707
|
+
|
|
708
|
+
# Build for production
|
|
709
|
+
pnpm build
|
|
710
|
+
|
|
711
|
+
# Preview production build
|
|
712
|
+
pnpm preview
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### Project Structure
|
|
716
|
+
|
|
717
|
+
```
|
|
718
|
+
packages/web-sdk/
|
|
719
|
+
├── src/
|
|
720
|
+
│ ├── @types/ # TypeScript type definitions
|
|
721
|
+
│ ├── hooks/ # Preact hooks for functionality
|
|
722
|
+
│ ├── utils/ # Utility functions
|
|
723
|
+
│ ├── index.tsx # Main SDK initialization
|
|
724
|
+
│ ├── core-wrapper.tsx # Core SDK logic
|
|
725
|
+
│ └── surveySDK.ts # Event SDK
|
|
726
|
+
├── dist-sdk/ # Build output
|
|
727
|
+
├── package.json
|
|
728
|
+
├── vite.config.ts # Vite configuration
|
|
729
|
+
└── README.md
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Build Output
|
|
733
|
+
|
|
734
|
+
The build produces:
|
|
735
|
+
|
|
736
|
+
- `encatch-web-sdk.js` - Main SDK bundle
|
|
737
|
+
- `encatch-web-sdk.css` - Default styles
|
|
738
|
+
- `preview-sdk.js` - Preview/demo version
|
|
739
|
+
- `embedded-sdk.js` - Embedded version
|
|
740
|
+
|
|
741
|
+
## Browser Support
|
|
742
|
+
|
|
743
|
+
- Chrome 62+
|
|
744
|
+
- Firefox 59+
|
|
745
|
+
- Safari 12+
|
|
746
|
+
- iOS Safari 10.3+
|
|
747
|
+
- Opera 50+
|
|
748
|
+
- Edge (Chromium-based)
|
|
749
|
+
|
|
750
|
+
## License
|
|
751
|
+
|
|
752
|
+
Private - Copyright (c) Encatch
|
|
753
|
+
|
|
754
|
+
## Support
|
|
755
|
+
|
|
756
|
+
For issues, questions, or feature requests, please contact your Encatch account manager or visit the [Encatch Dashboard](https://app.encatch.com).
|