@product7/product7-js 0.1.0
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 +1025 -0
- package/dist/README.md +1025 -0
- package/dist/product7-js.js +14658 -0
- package/dist/product7-js.js.map +1 -0
- package/dist/product7-js.min.js +2 -0
- package/dist/product7-js.min.js.map +1 -0
- package/package.json +114 -0
- package/src/api/mock-data/index.js +360 -0
- package/src/api/services/ChangelogService.js +28 -0
- package/src/api/services/FeedbackService.js +44 -0
- package/src/api/services/HelpService.js +50 -0
- package/src/api/services/MessengerService.js +279 -0
- package/src/api/services/SurveyService.js +127 -0
- package/src/api/utils/helpers.js +30 -0
- package/src/core/APIService.js +303 -0
- package/src/core/BaseAPIService.js +298 -0
- package/src/core/EventBus.js +54 -0
- package/src/core/Product7.js +812 -0
- package/src/core/WebSocketService.js +275 -0
- package/src/docs/api.md +226 -0
- package/src/docs/example.md +461 -0
- package/src/docs/framework-integrations.md +714 -0
- package/src/docs/installation.md +281 -0
- package/src/index.js +894 -0
- package/src/styles/base.js +50 -0
- package/src/styles/changelog.js +665 -0
- package/src/styles/components.js +553 -0
- package/src/styles/design-tokens.js +124 -0
- package/src/styles/feedback.js +325 -0
- package/src/styles/messenger-components.js +632 -0
- package/src/styles/messenger-core.js +233 -0
- package/src/styles/messenger-features.js +169 -0
- package/src/styles/messenger-views.js +877 -0
- package/src/styles/messenger.js +17 -0
- package/src/styles/messengerCustomStyles.js +114 -0
- package/src/styles/styles.js +26 -0
- package/src/styles/survey.js +894 -0
- package/src/utils/errors.js +142 -0
- package/src/utils/helpers.js +219 -0
- package/src/widgets/BaseWidget.js +548 -0
- package/src/widgets/ButtonWidget.js +104 -0
- package/src/widgets/ChangelogWidget.js +615 -0
- package/src/widgets/InlineWidget.js +148 -0
- package/src/widgets/MessengerWidget.js +979 -0
- package/src/widgets/SurveyWidget.js +1325 -0
- package/src/widgets/TabWidget.js +45 -0
- package/src/widgets/WidgetFactory.js +70 -0
- package/src/widgets/messenger/MessengerState.js +323 -0
- package/src/widgets/messenger/components/MessengerLauncher.js +124 -0
- package/src/widgets/messenger/components/MessengerPanel.js +111 -0
- package/src/widgets/messenger/components/NavigationTabs.js +130 -0
- package/src/widgets/messenger/views/ChangelogView.js +167 -0
- package/src/widgets/messenger/views/ChatView.js +592 -0
- package/src/widgets/messenger/views/ConversationsView.js +244 -0
- package/src/widgets/messenger/views/HelpView.js +239 -0
- package/src/widgets/messenger/views/HomeView.js +300 -0
- package/src/widgets/messenger/views/PreChatFormView.js +109 -0
- package/types/index.d.ts +341 -0
package/README.md
ADDED
|
@@ -0,0 +1,1025 @@
|
|
|
1
|
+
# Feedback Widget SDK
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/%40product7%2Fproduct7-js)
|
|
4
|
+
[](https://bundlephobia.com/package/@product7/product7-js)
|
|
5
|
+
|
|
6
|
+
A lightweight, customizable JavaScript SDK for collecting user feedback on any website. Built for the Product7 feedback platform.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Display modes** — Side panel or centered modal
|
|
13
|
+
- **Size options** — Small, medium, or large widget sizes
|
|
14
|
+
- **Full color customization** — Background, text, and primary colors
|
|
15
|
+
- **Custom triggers** — Use your own buttons instead of floating widget
|
|
16
|
+
- **Mock mode** — Development without backend connection
|
|
17
|
+
- **Multiple positions** — 7 position options including center
|
|
18
|
+
- **Responsive design** — Works on desktop, tablet, and mobile
|
|
19
|
+
- **Easy integration** — Simple JavaScript API, minimal setup
|
|
20
|
+
- **Lightweight** — ~12KB gzipped
|
|
21
|
+
- **TypeScript support** — Full type definitions included
|
|
22
|
+
- **Accessible** — WCAG 2.1 compliant
|
|
23
|
+
- **Secure** — CSP friendly, no `eval()` usage
|
|
24
|
+
- **Messenger widget** — Real-time chat with WebSocket support
|
|
25
|
+
- **Help center** — Integrated help articles and collections
|
|
26
|
+
- **Changelog** — Display product updates and announcements
|
|
27
|
+
- **Environment auto-detection** — Automatic staging/production switching
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### CDN Installation (Recommended)
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/@product7/product7-js/dist/product7-js.min.js"></script>
|
|
37
|
+
<script>
|
|
38
|
+
const feedback = new Product7({
|
|
39
|
+
workspace: 'your-workspace-name',
|
|
40
|
+
boardName: 'your-board-id',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
feedback.init().then(() => {
|
|
44
|
+
const widget = feedback.createWidget('button');
|
|
45
|
+
widget.mount();
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### NPM Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install @product7/product7-js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
import { Product7 } from '@product7/product7-js';
|
|
58
|
+
|
|
59
|
+
const feedback = new Product7({
|
|
60
|
+
workspace: 'your-workspace-name',
|
|
61
|
+
boardName: 'your-board-id',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await feedback.init();
|
|
65
|
+
const widget = feedback.createWidget('button');
|
|
66
|
+
widget.mount();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Auto-Initialization
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<script>
|
|
73
|
+
window.Product7Config = {
|
|
74
|
+
workspace: 'your-workspace',
|
|
75
|
+
boardName: 'your-board-id',
|
|
76
|
+
autoCreate: {
|
|
77
|
+
type: 'button',
|
|
78
|
+
position: 'bottom-right',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
</script>
|
|
82
|
+
<script src="https://cdn.jsdelivr.net/npm/@product7/product7-js/dist/product7-js.min.js"></script>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
- [Installation Guide](src/docs/installation.md)
|
|
90
|
+
- [Framework Integrations](src/docs/framework-integrations.md)
|
|
91
|
+
- [API Reference](src/docs/api.md)
|
|
92
|
+
- [Examples](src/docs/example.md)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Widget Types
|
|
97
|
+
|
|
98
|
+
**Button Widget with Side Panel**
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
const widget = feedback.createWidget('button', {
|
|
102
|
+
position: 'bottom-right',
|
|
103
|
+
displayMode: 'panel', // Slides in from the side
|
|
104
|
+
size: 'medium',
|
|
105
|
+
});
|
|
106
|
+
widget.mount();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Button Widget with Modal**
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const widget = feedback.createWidget('button', {
|
|
113
|
+
position: 'bottom-right',
|
|
114
|
+
displayMode: 'modal',
|
|
115
|
+
size: 'large',
|
|
116
|
+
backgroundColor: '#ffffff',
|
|
117
|
+
textColor: '#1F2937',
|
|
118
|
+
primaryColor: '#21244A',
|
|
119
|
+
});
|
|
120
|
+
widget.mount();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Inline Widget**
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const inline = feedback.createWidget('inline');
|
|
127
|
+
inline.mount('#feedback-container');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Messenger Widget**
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const messenger = feedback.createWidget('messenger', {
|
|
134
|
+
position: 'bottom-right',
|
|
135
|
+
theme: 'light',
|
|
136
|
+
teamName: 'Support',
|
|
137
|
+
enableHelp: true,
|
|
138
|
+
enableChangelog: true,
|
|
139
|
+
});
|
|
140
|
+
messenger.mount();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
### SDK Configuration
|
|
148
|
+
|
|
149
|
+
| Option | Type | Required | Default | Description |
|
|
150
|
+
| ----------- | ------- | -------- | ----------- | -------------------------------------- |
|
|
151
|
+
| `workspace` | string | ✅ | - | Your workspace subdomain |
|
|
152
|
+
| `boardName` | string | ❌ | 'general' | Target board for feedback |
|
|
153
|
+
| `metadata` | object | ❌ | null | User identification data |
|
|
154
|
+
| `mock` | boolean | ❌ | false | Enable mock mode for development |
|
|
155
|
+
| `debug` | boolean | ❌ | false | Enable debug logging |
|
|
156
|
+
| `env` | string | ❌ | auto-detect | Environment: 'production' or 'staging' |
|
|
157
|
+
| `apiUrl` | string | ❌ | null | Custom API URL (overrides env) |
|
|
158
|
+
|
|
159
|
+
### Widget Configuration
|
|
160
|
+
|
|
161
|
+
| Option | Type | Default | Description |
|
|
162
|
+
| ----------------- | ------ | -------------- | ---------------------------------------------- |
|
|
163
|
+
| `displayMode` | string | 'panel' | `'panel'` (side slide) or `'modal'` (centered) |
|
|
164
|
+
| `size` | string | 'medium' | `'small'`, `'medium'`, or `'large'` |
|
|
165
|
+
| `position` | string | 'bottom-right' | Button position (see below) |
|
|
166
|
+
| `backgroundColor` | string | '#ffffff' | Panel/modal background color |
|
|
167
|
+
| `textColor` | string | '#1F2937' | Text color |
|
|
168
|
+
| `primaryColor` | string | ''#21244A | Button and accent color |
|
|
169
|
+
|
|
170
|
+
### Position Options
|
|
171
|
+
|
|
172
|
+
- `bottom-right` (default)
|
|
173
|
+
- `bottom-left`
|
|
174
|
+
- `top-right`
|
|
175
|
+
- `top-left`
|
|
176
|
+
- `bottom-center`
|
|
177
|
+
- `top-center`
|
|
178
|
+
- `center`
|
|
179
|
+
|
|
180
|
+
### Size Variants
|
|
181
|
+
|
|
182
|
+
| Size | Modal Width | Panel Width |
|
|
183
|
+
| ------ | ----------- | ----------- |
|
|
184
|
+
| small | 360px | 320px |
|
|
185
|
+
| medium | 480px | 420px |
|
|
186
|
+
| large | 600px | 520px |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Color Customization
|
|
191
|
+
|
|
192
|
+
Instead of predefined themes, use direct color values for full flexibility:
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
const widget = feedback.createWidget('button', {
|
|
196
|
+
displayMode: 'modal',
|
|
197
|
+
backgroundColor: '#1a1a2e', // Dark background
|
|
198
|
+
textColor: '#ffffff', // White text
|
|
199
|
+
primaryColor: '#e94560', // Red accent
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This approach works better than predefined themes because:
|
|
204
|
+
|
|
205
|
+
- Supports any brand color
|
|
206
|
+
- Works with both light and dark designs
|
|
207
|
+
- No restrictions on color combinations
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Custom Triggers
|
|
212
|
+
|
|
213
|
+
Hide the default floating button and trigger from your own UI:
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
window.Product7.onReady((sdk) => {
|
|
217
|
+
const widget = sdk.createWidget('button', {
|
|
218
|
+
displayMode: 'modal',
|
|
219
|
+
size: 'medium',
|
|
220
|
+
});
|
|
221
|
+
widget.mount();
|
|
222
|
+
widget.hide(); // Hide the floating button
|
|
223
|
+
|
|
224
|
+
// Trigger from your own button
|
|
225
|
+
document.getElementById('my-feedback-btn').addEventListener('click', () => {
|
|
226
|
+
widget.openPanel();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Mock Mode
|
|
234
|
+
|
|
235
|
+
For development without a backend connection:
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
window.Product7Config = {
|
|
239
|
+
workspace: 'demo',
|
|
240
|
+
mock: true, // No backend required
|
|
241
|
+
metadata: {
|
|
242
|
+
user_id: 'test_user',
|
|
243
|
+
email: 'test@example.com',
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Mock mode simulates:
|
|
249
|
+
|
|
250
|
+
- Session initialization
|
|
251
|
+
- Feedback submission (with 500ms delay)
|
|
252
|
+
- Default configuration values
|
|
253
|
+
- Messenger conversations and messages
|
|
254
|
+
- Help articles and collections
|
|
255
|
+
- Changelog entries
|
|
256
|
+
- Agent availability status
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Environments
|
|
261
|
+
|
|
262
|
+
The SDK supports multiple environments for development and production use.
|
|
263
|
+
|
|
264
|
+
### Production (Default)
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
const feedback = new Product7({
|
|
268
|
+
workspace: 'your-workspace',
|
|
269
|
+
environment: 'production', // Default, can be omitted
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**API URLs:**
|
|
274
|
+
|
|
275
|
+
- `https://api.product7.io/api/v1`
|
|
276
|
+
- `https://{workspace}.api.product7.io/api/v1`
|
|
277
|
+
|
|
278
|
+
### Staging
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
const feedback = new Product7({
|
|
282
|
+
workspace: 'your-workspace',
|
|
283
|
+
environment: 'staging',
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**API URLs:**
|
|
288
|
+
|
|
289
|
+
- `https://staging.api.product7.io/api/v1`
|
|
290
|
+
- `https://{workspace}.staging.api.product7.io/api/v1`
|
|
291
|
+
|
|
292
|
+
### Custom API URL
|
|
293
|
+
|
|
294
|
+
For self-hosted or custom deployments:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
const feedback = new Product7({
|
|
298
|
+
workspace: 'your-workspace',
|
|
299
|
+
apiUrl: 'https://your-custom-api.com/api/v1',
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Configuration Priority
|
|
306
|
+
|
|
307
|
+
Configuration values merge in this order (later overrides earlier):
|
|
308
|
+
|
|
309
|
+
1. **Default values** — Built-in SDK defaults
|
|
310
|
+
2. **Backend config** — Returned from `/widget/init` API
|
|
311
|
+
3. **SDK config** — `window.Product7Config`
|
|
312
|
+
4. **Widget options** — Passed to `createWidget()`
|
|
313
|
+
|
|
314
|
+
This allows backend-managed defaults while permitting per-widget customization.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Events
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
feedback.eventBus.on('feedback:submitted', (data) => {
|
|
322
|
+
console.log('Feedback submitted:', data);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
feedback.eventBus.on('feedback:error', (error) => {
|
|
326
|
+
console.error('Submission failed:', error);
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Development
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
git clone https://github.com/product7/product7-js.git
|
|
336
|
+
cd product7-js
|
|
337
|
+
npm install
|
|
338
|
+
npm run dev
|
|
339
|
+
npm run build
|
|
340
|
+
npm test
|
|
341
|
+
npm run size
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Browser Support
|
|
347
|
+
|
|
348
|
+
- Chrome 60+
|
|
349
|
+
- Firefox 55+
|
|
350
|
+
- Safari 12+
|
|
351
|
+
- Edge 79+
|
|
352
|
+
- iOS Safari 12+
|
|
353
|
+
- Android Chrome 60+
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Bundle Size
|
|
358
|
+
|
|
359
|
+
- **Minified**: ~41KB
|
|
360
|
+
- **Minified + Gzipped**: ~12KB
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Contributing
|
|
365
|
+
|
|
366
|
+
We welcome contributions! See [Contributing Guide](CONTRIBUTING.md).
|
|
367
|
+
|
|
368
|
+
1. Fork the repository
|
|
369
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
370
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
371
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
372
|
+
5. Open a Pull Request
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## License
|
|
377
|
+
|
|
378
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Support
|
|
383
|
+
|
|
384
|
+
- Docs: [docs.product7.io/product7-js](https://docs.product7.io/product7-js)
|
|
385
|
+
- Issues: [GitHub Issues](https://github.com/product7/product7-js/issues)
|
|
386
|
+
- Discord: [Join our community](https://discord.gg/product7)
|
|
387
|
+
- Email: [support@product7.io](mailto:support@product7.io)
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Messenger Widget
|
|
392
|
+
|
|
393
|
+
The SDK includes a full-featured messenger widget for real-time customer conversations, help articles, and changelog updates.
|
|
394
|
+
|
|
395
|
+
### Features
|
|
396
|
+
|
|
397
|
+
- **Real-time messaging** — WebSocket-powered live chat with typing indicators
|
|
398
|
+
- **Conversation management** — View and manage multiple conversations
|
|
399
|
+
- **Help center integration** — Browse help articles and collections
|
|
400
|
+
- **Changelog updates** — Display product updates and announcements
|
|
401
|
+
- **Agent availability** — Show online status and response times
|
|
402
|
+
- **Customizable UI** — Theme, colors, and position options
|
|
403
|
+
|
|
404
|
+
### Quick Start
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
const feedback = new Product7({
|
|
408
|
+
workspace: 'your-workspace',
|
|
409
|
+
metadata: {
|
|
410
|
+
user_id: 'user_123',
|
|
411
|
+
email: 'user@example.com',
|
|
412
|
+
name: 'John Doe',
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
await feedback.init();
|
|
417
|
+
|
|
418
|
+
const messenger = feedback.createWidget('messenger', {
|
|
419
|
+
position: 'bottom-right',
|
|
420
|
+
theme: 'light',
|
|
421
|
+
teamName: 'Support Team',
|
|
422
|
+
welcomeMessage: 'How can we help you today?',
|
|
423
|
+
enableHelp: true,
|
|
424
|
+
enableChangelog: true,
|
|
425
|
+
primaryColor: '#155EEF',
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
messenger.mount();
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Messenger Configuration Options
|
|
432
|
+
|
|
433
|
+
| Option | Type | Default | Description |
|
|
434
|
+
| ------------------ | -------- | ------------------ | --------------------------------------- |
|
|
435
|
+
| `position` | string | 'bottom-right' | Widget position on screen |
|
|
436
|
+
| `theme` | string | 'light' | 'light' or 'dark' |
|
|
437
|
+
| `teamName` | string | 'Support' | Team name displayed in header |
|
|
438
|
+
| `teamAvatars` | array | [] | Array of team member avatar URLs |
|
|
439
|
+
| `welcomeMessage` | string | 'How can we help?' | Welcome message on home view |
|
|
440
|
+
| `enableHelp` | boolean | true | Show help articles section |
|
|
441
|
+
| `enableChangelog` | boolean | true | Show changelog section |
|
|
442
|
+
| `enableNews` | boolean | - | Alias for `enableChangelog` |
|
|
443
|
+
| `autoLoadData` | boolean | true | Auto-fetch conversations/help/changelog |
|
|
444
|
+
| `initialView` | string | 'home' | Initial view on mount |
|
|
445
|
+
| `previewData` | object | null | Seed deterministic local data |
|
|
446
|
+
| `logoUrl` | string | - | Custom logo URL |
|
|
447
|
+
| `primaryColor` | string | '#155EEF' | Primary accent color |
|
|
448
|
+
| `onSendMessage` | function | null | Callback when message is sent |
|
|
449
|
+
| `onArticleClick` | function | null | Callback when help article is clicked |
|
|
450
|
+
| `onChangelogClick` | function | null | Callback when changelog item is clicked |
|
|
451
|
+
|
|
452
|
+
### Messenger Views
|
|
453
|
+
|
|
454
|
+
The messenger widget includes multiple views:
|
|
455
|
+
|
|
456
|
+
| View | Description |
|
|
457
|
+
| ----------- | --------------------------------- |
|
|
458
|
+
| `home` | Welcome screen with quick actions |
|
|
459
|
+
| `messages` | List of all conversations |
|
|
460
|
+
| `chat` | Individual conversation chat view |
|
|
461
|
+
| `help` | Help articles and collections |
|
|
462
|
+
| `changelog` | Product updates and announcements |
|
|
463
|
+
|
|
464
|
+
### Programmatic Control
|
|
465
|
+
|
|
466
|
+
```javascript
|
|
467
|
+
// Open/close messenger
|
|
468
|
+
messenger.open();
|
|
469
|
+
messenger.close();
|
|
470
|
+
messenger.toggle();
|
|
471
|
+
|
|
472
|
+
// Navigate to specific view
|
|
473
|
+
messenger.navigateTo('messages');
|
|
474
|
+
messenger.navigateTo('help');
|
|
475
|
+
messenger.navigateTo('changelog');
|
|
476
|
+
|
|
477
|
+
// Get current state
|
|
478
|
+
const state = messenger.getState();
|
|
479
|
+
console.log(state.isOpen, state.currentView, state.unreadCount);
|
|
480
|
+
|
|
481
|
+
// Update unread count
|
|
482
|
+
messenger.setUnreadCount(3);
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Real-time Events
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
// Message sent
|
|
489
|
+
feedback.eventBus.on('messenger:messageSent', (data) => {
|
|
490
|
+
console.log('Message sent:', data.message);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Messenger opened/closed
|
|
494
|
+
feedback.eventBus.on('messenger:opened', () => {
|
|
495
|
+
console.log('Messenger opened');
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
feedback.eventBus.on('messenger:closed', () => {
|
|
499
|
+
console.log('Messenger closed');
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### WebSocket Connection
|
|
504
|
+
|
|
505
|
+
The messenger automatically establishes a WebSocket connection for real-time features:
|
|
506
|
+
|
|
507
|
+
- **Incoming messages** — New messages appear instantly
|
|
508
|
+
- **Typing indicators** — See when agents are typing
|
|
509
|
+
- **Presence updates** — Real-time agent availability
|
|
510
|
+
|
|
511
|
+
The WebSocket connection is managed automatically and reconnects on disconnection.
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## Environment Auto-Detection
|
|
516
|
+
|
|
517
|
+
The SDK automatically detects the environment based on the hostname:
|
|
518
|
+
|
|
519
|
+
| Hostname Pattern | Environment |
|
|
520
|
+
| ----------------------------------- | ----------- |
|
|
521
|
+
| `localhost`, `127.0.0.1`, `*.local` | staging |
|
|
522
|
+
| Contains `staging` | staging |
|
|
523
|
+
| All other hostnames | production |
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
// Auto-detection (recommended)
|
|
527
|
+
const feedback = new Product7({
|
|
528
|
+
workspace: 'your-workspace',
|
|
529
|
+
// env is auto-detected from window.location.hostname
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Manual override
|
|
533
|
+
const feedback = new Product7({
|
|
534
|
+
workspace: 'your-workspace',
|
|
535
|
+
env: 'staging', // Force staging environment
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Survey Widget
|
|
542
|
+
|
|
543
|
+
The SDK includes a powerful survey widget for collecting structured user feedback through NPS, CSAT, CES, and custom surveys. Surveys can be triggered manually, on specific events, or managed through the backend dashboard.
|
|
544
|
+
|
|
545
|
+
### Survey Types
|
|
546
|
+
|
|
547
|
+
| Type | Description | Scale |
|
|
548
|
+
| ---------- | --------------------- | ------------------------------------------------- |
|
|
549
|
+
| **NPS** | Net Promoter Score | 1-5 numeric scale (default; configurable to 0-10) |
|
|
550
|
+
| **CSAT** | Customer Satisfaction | 5-point emoji scale |
|
|
551
|
+
| **CES** | Customer Effort Score | 5-level difficulty scale |
|
|
552
|
+
| **Custom** | Multi-question forms | Flexible input types |
|
|
553
|
+
|
|
554
|
+
### Quick Start
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
const feedback = new Product7({
|
|
558
|
+
workspace: 'your-workspace',
|
|
559
|
+
boardName: 'your-board-id',
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
await feedback.init();
|
|
563
|
+
|
|
564
|
+
// Show NPS survey
|
|
565
|
+
feedback.showSurvey({
|
|
566
|
+
surveyType: 'nps',
|
|
567
|
+
description:
|
|
568
|
+
'To what extent do you agree or disagree that our tools support the work you do?',
|
|
569
|
+
ratingScale: 5,
|
|
570
|
+
showTitle: false,
|
|
571
|
+
showDescription: true,
|
|
572
|
+
showFeedbackInput: false,
|
|
573
|
+
showSubmitButton: false,
|
|
574
|
+
autoSubmitOnSelect: true,
|
|
575
|
+
onSubmit: (response) => {
|
|
576
|
+
console.log('Survey submitted:', response);
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Survey Configuration Options
|
|
582
|
+
|
|
583
|
+
```javascript
|
|
584
|
+
feedback.showSurvey({
|
|
585
|
+
surveyId: 'backend-survey-id', // Optional: links response to backend survey
|
|
586
|
+
surveyType: 'nps', // 'nps' | 'csat' | 'ces' | 'custom'
|
|
587
|
+
position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'center' | 'bottom'
|
|
588
|
+
theme: 'light', // 'light' | 'dark'
|
|
589
|
+
title: 'Your survey title',
|
|
590
|
+
description: 'Optional description',
|
|
591
|
+
ratingScale: 5, // NPS scale (default 5, set 11 for 0-10)
|
|
592
|
+
showTitle: false, // default false for single-step rating surveys
|
|
593
|
+
showDescription: true, // default true for single-step rating surveys
|
|
594
|
+
showFeedbackInput: false, // default false for single-step rating surveys
|
|
595
|
+
showSubmitButton: false, // default false for single-step rating surveys
|
|
596
|
+
autoSubmitOnSelect: true, // default true for single-step rating surveys
|
|
597
|
+
lowLabel: 'Not likely', // Low end label (NPS/CES)
|
|
598
|
+
highLabel: 'Very likely', // High end label (NPS/CES)
|
|
599
|
+
customQuestions: [], // For custom surveys
|
|
600
|
+
onSubmit: (response) => {},
|
|
601
|
+
onDismiss: () => {},
|
|
602
|
+
});
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
### Backend-Driven Surveys
|
|
608
|
+
|
|
609
|
+
For surveys configured in the Product7 dashboard, use `showSurveyById()` to fetch and display them:
|
|
610
|
+
|
|
611
|
+
```javascript
|
|
612
|
+
// Show a specific survey by its backend ID
|
|
613
|
+
await feedback.showSurveyById('survey_abc123', {
|
|
614
|
+
position: 'center', // Override position if needed
|
|
615
|
+
onSubmit: (response) => {
|
|
616
|
+
console.log('Survey completed:', response);
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Fetching Active Surveys
|
|
622
|
+
|
|
623
|
+
Retrieve surveys that match the current user's targeting criteria:
|
|
624
|
+
|
|
625
|
+
```javascript
|
|
626
|
+
// Get all active surveys for the current context
|
|
627
|
+
const surveys = await feedback.getActiveSurveys();
|
|
628
|
+
|
|
629
|
+
console.log('Available surveys:', surveys);
|
|
630
|
+
// [{ id: 'survey_123', type: 'nps', title: '...', ... }]
|
|
631
|
+
|
|
632
|
+
// Show the first matching survey
|
|
633
|
+
if (surveys.length > 0) {
|
|
634
|
+
await feedback.showSurveyById(surveys[0].id);
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
With custom targeting context:
|
|
639
|
+
|
|
640
|
+
```javascript
|
|
641
|
+
const surveys = await feedback.getActiveSurveys({
|
|
642
|
+
url: '/dashboard/settings', // Override current URL
|
|
643
|
+
userProperties: {
|
|
644
|
+
plan: 'enterprise',
|
|
645
|
+
feature_usage: 'high',
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
### Survey Types
|
|
653
|
+
|
|
654
|
+
#### NPS Survey (Net Promoter Score)
|
|
655
|
+
|
|
656
|
+
```javascript
|
|
657
|
+
feedback.showSurvey({
|
|
658
|
+
surveyType: 'nps',
|
|
659
|
+
description:
|
|
660
|
+
'To what extent do you agree or disagree that our tools support the work you do?',
|
|
661
|
+
ratingScale: 5,
|
|
662
|
+
showTitle: false,
|
|
663
|
+
showDescription: true,
|
|
664
|
+
showFeedbackInput: false,
|
|
665
|
+
showSubmitButton: false,
|
|
666
|
+
autoSubmitOnSelect: true,
|
|
667
|
+
lowLabel: 'Strongly Disagree',
|
|
668
|
+
highLabel: 'Strongly Agree',
|
|
669
|
+
position: 'bottom-right',
|
|
670
|
+
onSubmit: (response) => {
|
|
671
|
+
console.log('Score:', response.score); // 1-5 by default
|
|
672
|
+
console.log('Feedback:', response.feedback);
|
|
673
|
+
},
|
|
674
|
+
});
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### CSAT Survey (Customer Satisfaction)
|
|
678
|
+
|
|
679
|
+
```javascript
|
|
680
|
+
feedback.showSurvey({
|
|
681
|
+
surveyType: 'csat',
|
|
682
|
+
title: 'How satisfied are you with our service?',
|
|
683
|
+
position: 'center',
|
|
684
|
+
theme: 'dark',
|
|
685
|
+
onSubmit: (response) => {
|
|
686
|
+
console.log('Satisfaction:', response.score); // 1-5
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
#### CES Survey (Customer Effort Score)
|
|
692
|
+
|
|
693
|
+
```javascript
|
|
694
|
+
feedback.showSurvey({
|
|
695
|
+
surveyType: 'ces',
|
|
696
|
+
title: 'How easy was it to complete your task?',
|
|
697
|
+
position: 'bottom',
|
|
698
|
+
onSubmit: (response) => {
|
|
699
|
+
console.log('Effort score:', response.score); // 1-5
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
#### Custom Survey
|
|
705
|
+
|
|
706
|
+
```javascript
|
|
707
|
+
feedback.showSurvey({
|
|
708
|
+
surveyType: 'custom',
|
|
709
|
+
title: 'Quick Feedback',
|
|
710
|
+
customQuestions: [
|
|
711
|
+
{
|
|
712
|
+
id: 'feature',
|
|
713
|
+
type: 'select',
|
|
714
|
+
label: 'Which feature do you use most?',
|
|
715
|
+
options: [
|
|
716
|
+
{ value: 'dashboard', label: 'Dashboard' },
|
|
717
|
+
{ value: 'reports', label: 'Reports' },
|
|
718
|
+
{ value: 'settings', label: 'Settings' },
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
id: 'improvement',
|
|
723
|
+
type: 'text',
|
|
724
|
+
label: 'What could we improve?',
|
|
725
|
+
placeholder: 'Your suggestions...',
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
onSubmit: (response) => {
|
|
729
|
+
console.log('Answers:', response.customAnswers);
|
|
730
|
+
// { feature: 'dashboard', improvement: 'Better charts' }
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
### Event-Triggered Surveys
|
|
738
|
+
|
|
739
|
+
Trigger surveys based on user actions:
|
|
740
|
+
|
|
741
|
+
```javascript
|
|
742
|
+
// After completing a purchase
|
|
743
|
+
document.getElementById('checkout-btn').addEventListener('click', async () => {
|
|
744
|
+
await processPayment();
|
|
745
|
+
|
|
746
|
+
feedback.showSurvey({
|
|
747
|
+
surveyType: 'csat',
|
|
748
|
+
title: 'How was your checkout experience?',
|
|
749
|
+
position: 'center',
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// After closing support chat
|
|
754
|
+
chatWidget.on('close', () => {
|
|
755
|
+
feedback.showSurvey({
|
|
756
|
+
surveyType: 'ces',
|
|
757
|
+
title: 'How easy was it to get help?',
|
|
758
|
+
position: 'bottom-right',
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// On page exit intent
|
|
763
|
+
document.addEventListener('mouseleave', (e) => {
|
|
764
|
+
if (e.clientY < 0 && !sessionStorage.getItem('exit_survey_shown')) {
|
|
765
|
+
sessionStorage.setItem('exit_survey_shown', 'true');
|
|
766
|
+
feedback.showSurvey({
|
|
767
|
+
surveyType: 'nps',
|
|
768
|
+
title: 'Before you go...',
|
|
769
|
+
description: 'How likely are you to recommend us?',
|
|
770
|
+
position: 'center',
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
### Time-Based Surveys
|
|
777
|
+
|
|
778
|
+
```javascript
|
|
779
|
+
// Show after 60 seconds on page
|
|
780
|
+
setTimeout(() => {
|
|
781
|
+
feedback.showSurvey({
|
|
782
|
+
surveyType: 'nps',
|
|
783
|
+
title: 'Enjoying our product?',
|
|
784
|
+
position: 'bottom-right',
|
|
785
|
+
});
|
|
786
|
+
}, 60000);
|
|
787
|
+
|
|
788
|
+
// Show after N page views
|
|
789
|
+
const pageViews = parseInt(localStorage.getItem('page_views') || '0') + 1;
|
|
790
|
+
localStorage.setItem('page_views', pageViews);
|
|
791
|
+
|
|
792
|
+
if (pageViews === 5) {
|
|
793
|
+
feedback.showSurvey({
|
|
794
|
+
surveyType: 'csat',
|
|
795
|
+
title: 'How are you finding things so far?',
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
### React Integration
|
|
803
|
+
|
|
804
|
+
```jsx
|
|
805
|
+
import { useEffect, useRef } from 'react';
|
|
806
|
+
import { Product7 } from '@product7/product7-js';
|
|
807
|
+
|
|
808
|
+
function useSurvey() {
|
|
809
|
+
const sdkRef = useRef(null);
|
|
810
|
+
|
|
811
|
+
useEffect(() => {
|
|
812
|
+
const sdk = new Product7({
|
|
813
|
+
workspace: 'your-workspace',
|
|
814
|
+
metadata: {
|
|
815
|
+
user_id: currentUser.id,
|
|
816
|
+
email: currentUser.email,
|
|
817
|
+
},
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
sdk.init().then(() => {
|
|
821
|
+
sdkRef.current = sdk;
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
return () => sdk.destroy();
|
|
825
|
+
}, []);
|
|
826
|
+
|
|
827
|
+
const showNPS = (options = {}) => {
|
|
828
|
+
sdkRef.current?.showSurvey({
|
|
829
|
+
surveyType: 'nps',
|
|
830
|
+
...options,
|
|
831
|
+
});
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const showCSAT = (options = {}) => {
|
|
835
|
+
sdkRef.current?.showSurvey({
|
|
836
|
+
surveyType: 'csat',
|
|
837
|
+
...options,
|
|
838
|
+
});
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
return { showNPS, showCSAT };
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Usage in component
|
|
845
|
+
function CheckoutSuccess() {
|
|
846
|
+
const { showCSAT } = useSurvey();
|
|
847
|
+
|
|
848
|
+
useEffect(() => {
|
|
849
|
+
showCSAT({
|
|
850
|
+
title: 'How was your checkout experience?',
|
|
851
|
+
position: 'center',
|
|
852
|
+
});
|
|
853
|
+
}, []);
|
|
854
|
+
|
|
855
|
+
return <div>Thank you for your purchase!</div>;
|
|
856
|
+
}
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Vue.js Integration
|
|
860
|
+
|
|
861
|
+
```vue
|
|
862
|
+
<script setup>
|
|
863
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
864
|
+
import { Product7 } from '@product7/product7-js';
|
|
865
|
+
|
|
866
|
+
const sdk = ref(null);
|
|
867
|
+
|
|
868
|
+
onMounted(async () => {
|
|
869
|
+
sdk.value = new Product7({
|
|
870
|
+
workspace: 'your-workspace',
|
|
871
|
+
metadata: {
|
|
872
|
+
user_id: currentUser.value.id,
|
|
873
|
+
email: currentUser.value.email,
|
|
874
|
+
},
|
|
875
|
+
});
|
|
876
|
+
await sdk.value.init();
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
onUnmounted(() => {
|
|
880
|
+
sdk.value?.destroy();
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
const showSurvey = (type, options = {}) => {
|
|
884
|
+
sdk.value?.showSurvey({
|
|
885
|
+
surveyType: type,
|
|
886
|
+
...options,
|
|
887
|
+
});
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// Trigger on button click
|
|
891
|
+
const handleFeedbackClick = () => {
|
|
892
|
+
showSurvey('nps', {
|
|
893
|
+
title: 'How likely are you to recommend us?',
|
|
894
|
+
position: 'center',
|
|
895
|
+
});
|
|
896
|
+
};
|
|
897
|
+
</script>
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
### Survey Events
|
|
903
|
+
|
|
904
|
+
```javascript
|
|
905
|
+
// Survey displayed
|
|
906
|
+
feedback.eventBus.on('survey:shown', (data) => {
|
|
907
|
+
console.log('Survey displayed:', data.type);
|
|
908
|
+
analytics.track('Survey Shown', { type: data.type });
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
// Survey submitted
|
|
912
|
+
feedback.eventBus.on('survey:submitted', (data) => {
|
|
913
|
+
console.log('Survey submitted:', data.response);
|
|
914
|
+
analytics.track('Survey Completed', {
|
|
915
|
+
type: data.response.type,
|
|
916
|
+
score: data.response.score,
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
// Survey dismissed without completing
|
|
921
|
+
feedback.eventBus.on('survey:dismissed', (data) => {
|
|
922
|
+
console.log('Survey dismissed');
|
|
923
|
+
analytics.track('Survey Dismissed');
|
|
924
|
+
});
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
### Response Data Format
|
|
928
|
+
|
|
929
|
+
```javascript
|
|
930
|
+
// NPS/CSAT/CES response
|
|
931
|
+
{
|
|
932
|
+
type: 'nps',
|
|
933
|
+
score: 9,
|
|
934
|
+
feedback: 'Great product!',
|
|
935
|
+
timestamp: '2025-01-26T10:30:00.000Z'
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Custom survey response
|
|
939
|
+
{
|
|
940
|
+
type: 'custom',
|
|
941
|
+
score: null,
|
|
942
|
+
feedback: 'Additional comments here',
|
|
943
|
+
customAnswers: {
|
|
944
|
+
feature: 'dashboard',
|
|
945
|
+
improvement: 'Better mobile support'
|
|
946
|
+
},
|
|
947
|
+
timestamp: '2025-01-26T10:30:00.000Z'
|
|
948
|
+
}
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
### Position Options
|
|
954
|
+
|
|
955
|
+
| Position | Description |
|
|
956
|
+
| -------------- | ----------------------------- |
|
|
957
|
+
| `bottom-right` | Bottom right corner (default) |
|
|
958
|
+
| `bottom-left` | Bottom left corner |
|
|
959
|
+
| `center` | Centered modal with backdrop |
|
|
960
|
+
| `bottom` | Full-width bottom bar |
|
|
961
|
+
|
|
962
|
+
### Theme Options
|
|
963
|
+
|
|
964
|
+
| Theme | Description |
|
|
965
|
+
| ------- | ------------------------------------- |
|
|
966
|
+
| `light` | White background, dark text (default) |
|
|
967
|
+
| `dark` | Dark background, light text |
|
|
968
|
+
|
|
969
|
+
---
|
|
970
|
+
|
|
971
|
+
### Programmatic Control
|
|
972
|
+
|
|
973
|
+
```javascript
|
|
974
|
+
// Create survey widget for later use
|
|
975
|
+
const survey = feedback.createWidget('survey', {
|
|
976
|
+
surveyType: 'nps',
|
|
977
|
+
title: 'Rate us',
|
|
978
|
+
});
|
|
979
|
+
survey.mount();
|
|
980
|
+
|
|
981
|
+
// Show when ready
|
|
982
|
+
survey.show();
|
|
983
|
+
|
|
984
|
+
// Hide programmatically
|
|
985
|
+
survey.hide();
|
|
986
|
+
|
|
987
|
+
// Destroy when done
|
|
988
|
+
survey.destroy();
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
### Survey Rate Limiting
|
|
992
|
+
|
|
993
|
+
Avoid survey fatigue by tracking when surveys were last shown:
|
|
994
|
+
|
|
995
|
+
```javascript
|
|
996
|
+
const SURVEY_COOLDOWN = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
997
|
+
|
|
998
|
+
function canShowSurvey(surveyType) {
|
|
999
|
+
const lastShown = localStorage.getItem(`survey_${surveyType}_shown`);
|
|
1000
|
+
if (!lastShown) return true;
|
|
1001
|
+
return Date.now() - parseInt(lastShown) > SURVEY_COOLDOWN;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function showSurveyWithCooldown(options) {
|
|
1005
|
+
if (!canShowSurvey(options.surveyType)) {
|
|
1006
|
+
console.log('Survey on cooldown');
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
localStorage.setItem(`survey_${options.surveyType}_shown`, Date.now());
|
|
1011
|
+
return feedback.showSurvey(options);
|
|
1012
|
+
}
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
---
|
|
1016
|
+
|
|
1017
|
+
## Related
|
|
1018
|
+
|
|
1019
|
+
- [Product7 Platform](https://product7.io)
|
|
1020
|
+
- [Vue.js Example](https://github.com/product7/product7-js-vue-example)
|
|
1021
|
+
- [React Example](https://github.com/product7/product7-js-react-example)
|
|
1022
|
+
|
|
1023
|
+
---
|
|
1024
|
+
|
|
1025
|
+
Made by the [Product7 Team](https://product7.io)
|