@product7/feedback-sdk 1.0.6 → 1.0.8

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.
@@ -1,823 +1,459 @@
1
1
  # Examples
2
2
 
3
- Real-world examples of using the Feedback Widget SDK.
4
-
5
- ## 🚀 Quick Start Examples
6
-
7
- ### Basic Button Widget
8
-
9
- The simplest implementation - just a feedback button:
10
-
11
- ```html
12
- <!DOCTYPE html>
13
- <html>
14
- <head>
15
- <title>Basic Feedback Button</title>
16
- </head>
17
- <body>
18
- <h1>My Website</h1>
19
- <p>This is my awesome website content...</p>
20
-
21
- <script src="https://cdn.jsdelivr.net/npm/@product7/feedback-sdk@1/dist/feedback-sdk.min.js"></script>
22
- <script>
23
- const feedback = new FeedbackSDK({
24
- workspace: 'my-company',
25
- boardId: 'general-feedback',
26
- });
27
-
28
- feedback.init().then(() => {
29
- const widget = feedback.createWidget('button');
30
- widget.mount();
31
- });
32
- </script>
33
- </body>
34
- </html>
35
- ```
36
-
37
- ### Auto-Initialization
38
-
39
- Let the SDK set itself up automatically:
40
-
41
- ```html
42
- <script>
43
- // Configure before loading the SDK
44
- window.FeedbackSDKConfig = {
45
- workspace: 'my-company',
46
- boardId: 'general-feedback',
47
- theme: 'light',
48
- autoCreate: {
49
- type: 'button',
50
- position: 'bottom-right',
51
- },
52
- };
53
- </script>
54
- <script src="https://cdn.jsdelivr.net/npm/@product7/feedback-sdk@1/dist/feedback-sdk.min.js"></script>
55
- <!-- Widget appears automatically! -->
56
- ```
57
-
58
- ## 🎨 Widget Customization Examples
59
-
60
- ### Multiple Widgets
3
+ ## Basic Usage
61
4
 
62
- Create different widgets for different purposes:
5
+ ### Simple Setup
63
6
 
64
7
  ```javascript
65
- const feedback = new FeedbackSDK({
66
- workspace: 'my-company',
67
- debug: true,
68
- });
69
-
70
- await feedback.init();
8
+ import FeedbackSDK from '@product7/feedback-sdk';
71
9
 
72
- // Button for general feedback
73
- const generalButton = feedback.createWidget('button', {
74
- position: 'bottom-right',
75
- boardId: 'general-feedback',
10
+ const sdk = new FeedbackSDK({
11
+ workspace: 'my-workspace',
12
+ userContext: {
13
+ user_id: 'user_123',
14
+ email: 'user@example.com'
15
+ }
76
16
  });
77
- generalButton.mount();
78
17
 
79
- // Tab for bug reports
80
- const bugTab = feedback.createWidget('tab', {
81
- position: 'bottom-left',
82
- boardId: 'bug-reports',
83
- });
84
- bugTab.mount();
18
+ await sdk.init();
85
19
 
86
- // Inline form in footer
87
- const footerForm = feedback.createWidget('inline', {
88
- boardId: 'feature-requests',
89
- });
90
- footerForm.mount('#footer-feedback');
20
+ const widget = sdk.createWidget('button');
21
+ widget.mount();
91
22
  ```
92
23
 
93
- ### Custom Styling
24
+ ---
94
25
 
95
- Override default styles with CSS:
26
+ ## User Context
96
27
 
97
- ```html
98
- <style>
99
- /* Custom button color */
100
- .feedback-widget {
101
- --feedback-primary-color: #10b981;
102
- --feedback-primary-hover: #059669;
103
- }
28
+ ### Basic User Context
104
29
 
105
- /* Custom positioning */
106
- .feedback-widget-button.position-bottom-right {
107
- bottom: 100px;
108
- right: 30px;
30
+ ```javascript
31
+ const sdk = new FeedbackSDK({
32
+ workspace: 'my-workspace',
33
+ userContext: {
34
+ user_id: 'user_123',
35
+ email: 'user@example.com',
36
+ name: 'John Doe'
109
37
  }
38
+ });
39
+ ```
110
40
 
111
- /* Custom font */
112
- .feedback-widget {
113
- --feedback-font-family: 'Poppins', sans-serif;
114
- }
41
+ ### With Custom Fields
115
42
 
116
- /* Custom border radius */
117
- .feedback-trigger-btn {
118
- border-radius: 8px !important;
43
+ ```javascript
44
+ userContext: {
45
+ user_id: 'user_123',
46
+ email: 'user@example.com',
47
+ name: 'John Doe',
48
+ custom_fields: {
49
+ role: 'admin',
50
+ plan: 'pro',
51
+ signup_date: '2024-01-01'
119
52
  }
120
- </style>
121
-
122
- <script>
123
- const feedback = new FeedbackSDK({
124
- workspace: 'my-company',
125
- boardId: 'styled-feedback',
126
- });
127
-
128
- feedback.init().then(() => {
129
- const widget = feedback.createWidget('button');
130
- widget.mount();
131
- });
132
- </script>
53
+ }
133
54
  ```
134
55
 
135
- ### Dynamic Theme Switching
136
-
137
- Switch themes based on user preference:
56
+ ### With Company Data
138
57
 
139
58
  ```javascript
140
- const feedback = new FeedbackSDK({
141
- workspace: 'my-company',
142
- boardId: 'general-feedback',
143
- theme: 'light',
144
- });
145
-
146
- await feedback.init();
147
-
148
- const widget = feedback.createWidget('button');
149
- widget.mount();
59
+ userContext: {
60
+ user_id: 'user_123',
61
+ email: 'user@example.com',
62
+ name: 'John Doe',
63
+ company: {
64
+ id: 'company_456',
65
+ name: 'Acme Corp',
66
+ monthly_spend: 1000
67
+ }
68
+ }
69
+ ```
150
70
 
151
- // Theme toggle function
152
- function toggleTheme() {
153
- const isDark = document.body.classList.contains('dark-mode');
154
- const newTheme = isDark ? 'light' : 'dark';
71
+ ### Extract from Auth
155
72
 
156
- // Update SDK theme
157
- feedback.updateConfig({ theme: newTheme });
73
+ ```javascript
74
+ const authData = {
75
+ sub: 'user_123',
76
+ email: 'user@example.com',
77
+ name: 'John Doe',
78
+ role: 'admin'
79
+ };
158
80
 
159
- // Update your site theme
160
- document.body.classList.toggle('dark-mode');
161
- }
81
+ const userContext = FeedbackSDK.extractUserContextFromAuth(authData);
162
82
 
163
- // Bind to your theme toggle button
164
- document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
83
+ const sdk = new FeedbackSDK({
84
+ workspace: 'my-workspace',
85
+ userContext
86
+ });
165
87
  ```
166
88
 
167
- ## 📱 Responsive Examples
89
+ ---
168
90
 
169
- ### Mobile-First Design
91
+ ## Widget Customization
170
92
 
171
- Optimize for mobile devices:
93
+ ### Different Positions
172
94
 
173
95
  ```javascript
174
- const feedback = new FeedbackSDK({
175
- workspace: 'my-company',
176
- boardId: 'mobile-feedback',
96
+ // Bottom right (default)
97
+ const widget1 = sdk.createWidget('button', {
98
+ position: 'bottom-right'
177
99
  });
178
100
 
179
- await feedback.init();
180
-
181
- // Different widgets for different screen sizes
182
- if (window.innerWidth <= 768) {
183
- // Mobile: Use tab widget to save space
184
- const mobileWidget = feedback.createWidget('tab', {
185
- position: 'bottom-right',
186
- });
187
- mobileWidget.mount();
188
- } else {
189
- // Desktop: Use button widget
190
- const desktopWidget = feedback.createWidget('button', {
191
- position: 'bottom-right',
192
- });
193
- desktopWidget.mount();
194
- }
101
+ // Bottom left
102
+ const widget2 = sdk.createWidget('button', {
103
+ position: 'bottom-left'
104
+ });
195
105
 
196
- // Handle screen resize
197
- window.addEventListener('resize', () => {
198
- // Destroy current widgets and recreate based on new size
199
- feedback.widgets.forEach((widget) => widget.destroy());
106
+ // Top right
107
+ const widget3 = sdk.createWidget('button', {
108
+ position: 'top-right'
109
+ });
200
110
 
201
- if (window.innerWidth <= 768) {
202
- const mobileWidget = feedback.createWidget('tab');
203
- mobileWidget.mount();
204
- } else {
205
- const desktopWidget = feedback.createWidget('button');
206
- desktopWidget.mount();
207
- }
111
+ // Top left
112
+ const widget4 = sdk.createWidget('button', {
113
+ position: 'top-left'
208
114
  });
209
115
  ```
210
116
 
211
- ## 🎯 Event-Driven Examples
212
-
213
- ### Analytics Integration
214
-
215
- Track feedback events with your analytics:
117
+ ### Dark Theme
216
118
 
217
119
  ```javascript
218
- const feedback = new FeedbackSDK({
219
- workspace: 'my-company',
220
- boardId: 'analytics-feedback',
120
+ const widget = sdk.createWidget('button', {
121
+ theme: 'dark'
221
122
  });
123
+ widget.mount();
124
+ ```
222
125
 
223
- await feedback.init();
126
+ ### Custom Board
224
127
 
225
- // Track feedback interactions
226
- feedback.eventBus.on('widget:mounted', (data) => {
227
- // Track widget load
228
- gtag('event', 'feedback_widget_loaded', {
229
- widget_type: data.widget.type,
230
- widget_id: data.widget.id,
231
- });
128
+ ```javascript
129
+ const widget = sdk.createWidget('button', {
130
+ boardId: 'feature-requests'
232
131
  });
132
+ widget.mount();
133
+ ```
233
134
 
234
- feedback.eventBus.on('feedback:submitted', (data) => {
235
- // Track successful submission
236
- gtag('event', 'feedback_submitted', {
237
- widget_type: data.widget.type,
238
- feedback_length: data.feedback.content?.length || 0,
239
- });
135
+ ### Multiple Widgets
240
136
 
241
- // Show thank you message
242
- showToast('Thank you for your feedback! 🙏');
137
+ ```javascript
138
+ // Feedback button
139
+ const feedbackWidget = sdk.createWidget('button', {
140
+ position: 'bottom-right',
141
+ boardId: 'general'
243
142
  });
143
+ feedbackWidget.mount();
244
144
 
245
- feedback.eventBus.on('feedback:error', (error) => {
246
- // Track errors
247
- gtag('event', 'feedback_error', {
248
- error_type: error.error.name,
249
- error_message: error.error.message,
250
- });
251
-
252
- // Show error message
253
- showToast('Failed to send feedback. Please try again.', 'error');
145
+ // Bug report button
146
+ const bugWidget = sdk.createWidget('button', {
147
+ position: 'bottom-left',
148
+ boardId: 'bug-reports'
254
149
  });
150
+ bugWidget.updateText('Report Bug');
151
+ bugWidget.mount();
152
+ ```
255
153
 
256
- const widget = feedback.createWidget('button');
154
+ ### Update Widget
155
+
156
+ ```javascript
157
+ const widget = sdk.createWidget('button');
257
158
  widget.mount();
258
159
 
259
- function showToast(message, type = 'success') {
260
- // Your toast implementation
261
- console.log(`${type}: ${message}`);
262
- }
160
+ // Update text
161
+ widget.updateText('Send Feedback');
162
+
163
+ // Update position
164
+ widget.updatePosition('top-right');
165
+
166
+ // Show/hide
167
+ widget.hide();
168
+ widget.show();
263
169
  ```
264
170
 
265
- ### User Authentication Integration
171
+ ---
266
172
 
267
- Integrate with your user system:
173
+ ## Event Handling
268
174
 
269
- ```javascript
270
- // Get user info from your auth system
271
- const currentUser = getCurrentUser();
175
+ ### Track Submissions
272
176
 
273
- const feedback = new FeedbackSDK({
274
- workspace: 'my-company',
275
- boardId: 'user-feedback',
276
- apiKey: currentUser?.apiKey, // Use user's API key if available
177
+ ```javascript
178
+ sdk.on('feedback:submitted', ({ widget, feedback }) => {
179
+ console.log('Feedback submitted:', feedback.id);
180
+
181
+ // Track in analytics
182
+ analytics.track('Feedback Submitted', {
183
+ feedbackId: feedback.id,
184
+ board: feedback.board
185
+ });
277
186
  });
187
+ ```
278
188
 
279
- await feedback.init();
189
+ ### Handle Errors
280
190
 
281
- const widget = feedback.createWidget('inline');
282
- widget.mount('#feedback-section');
191
+ ```javascript
192
+ sdk.on('feedback:error', ({ widget, error }) => {
193
+ console.error('Submission failed:', error.message);
194
+
195
+ // Show custom error notification
196
+ toast.error('Failed to submit feedback. Please try again.');
197
+ });
198
+ ```
283
199
 
284
- // Pre-populate user email
285
- if (currentUser?.email) {
286
- feedback.eventBus.on('widget:mounted', () => {
287
- // Pre-fill email field
288
- const emailInput = document.querySelector('input[name="email"]');
289
- if (emailInput) {
290
- emailInput.value = currentUser.email;
291
- emailInput.disabled = true; // Prevent editing
292
- }
293
- });
294
- }
200
+ ### Widget Lifecycle
295
201
 
296
- // Add user context to feedback
297
- feedback.eventBus.on('feedback:submitted', (data) => {
298
- // Log user-specific feedback submission
299
- console.log(`Feedback from ${currentUser.email}:`, data.feedback);
202
+ ```javascript
203
+ sdk.on('widget:mounted', ({ widget }) => {
204
+ console.log('Widget mounted:', widget.id);
300
205
  });
301
- ```
302
206
 
303
- ## 🌐 Framework Integration Examples
304
-
305
- ### React Integration
306
-
307
- ```jsx
308
- import React, { useEffect, useRef, useState } from 'react';
309
- import { FeedbackSDK } from '@product7/feedback-sdk';
310
-
311
- const FeedbackWidget = ({ workspace, boardId, type = 'button' }) => {
312
- const [sdk, setSdk] = useState(null);
313
- const [widget, setWidget] = useState(null);
314
- const containerRef = useRef(null);
315
-
316
- useEffect(() => {
317
- // Initialize SDK
318
- const initSDK = async () => {
319
- const feedbackSDK = new FeedbackSDK({
320
- workspace,
321
- boardId,
322
- debug: process.env.NODE_ENV === 'development',
323
- });
324
-
325
- await feedbackSDK.init();
326
- setSdk(feedbackSDK);
327
-
328
- // Create and mount widget
329
- const feedbackWidget = feedbackSDK.createWidget(type);
330
- if (type === 'inline' && containerRef.current) {
331
- feedbackWidget.mount(containerRef.current);
332
- } else {
333
- feedbackWidget.mount();
334
- }
335
- setWidget(feedbackWidget);
336
- };
337
-
338
- initSDK();
339
-
340
- // Cleanup
341
- return () => {
342
- if (widget) widget.destroy();
343
- if (sdk) sdk.destroy();
344
- };
345
- }, [workspace, boardId, type]);
346
-
347
- // For inline widgets, provide a container
348
- if (type === 'inline') {
349
- return <div ref={containerRef} className="feedback-container" />;
350
- }
207
+ sdk.on('widget:destroyed', ({ widget }) => {
208
+ console.log('Widget destroyed:', widget.id);
209
+ });
210
+ ```
351
211
 
352
- // For button/tab widgets, no container needed
353
- return null;
354
- };
212
+ ### Multiple Events
355
213
 
356
- // Usage
357
- export const App = () => {
358
- return (
359
- <div>
360
- <h1>My React App</h1>
361
-
362
- {/* Button widget */}
363
- <FeedbackWidget
364
- workspace="my-company"
365
- boardId="react-feedback"
366
- type="button"
367
- />
368
-
369
- {/* Inline widget in footer */}
370
- <footer>
371
- <FeedbackWidget
372
- workspace="my-company"
373
- boardId="footer-feedback"
374
- type="inline"
375
- />
376
- </footer>
377
- </div>
378
- );
379
- };
214
+ ```javascript
215
+ sdk
216
+ .on('feedback:submitted', handleSubmit)
217
+ .on('feedback:error', handleError)
218
+ .on('config:updated', handleConfigUpdate);
380
219
  ```
381
220
 
382
- ### Vue.js Integration
383
-
384
- ```vue
385
- <template>
386
- <div>
387
- <h1>My Vue App</h1>
388
-
389
- <!-- Container for inline widget -->
390
- <div v-if="widgetType === 'inline'" ref="feedbackContainer"></div>
391
- </div>
392
- </template>
393
-
394
- <script>
395
- import { FeedbackSDK } from '@product7/feedback-sdk';
396
-
397
- export default {
398
- name: 'FeedbackWidget',
399
- props: {
400
- workspace: { type: String, required: true },
401
- boardId: { type: String, required: true },
402
- widgetType: { type: String, default: 'button' },
403
- },
404
- data() {
405
- return {
406
- sdk: null,
407
- widget: null,
408
- };
409
- },
410
- async mounted() {
411
- await this.initFeedback();
412
- },
413
- beforeUnmount() {
414
- this.cleanup();
415
- },
416
- methods: {
417
- async initFeedback() {
418
- this.sdk = new FeedbackSDK({
419
- workspace: this.workspace,
420
- boardId: this.boardId,
421
- debug: process.env.NODE_ENV === 'development',
422
- });
423
-
424
- await this.sdk.init();
425
-
426
- // Listen to events
427
- this.sdk.eventBus.on('feedback:submitted', this.onFeedbackSubmitted);
428
- this.sdk.eventBus.on('feedback:error', this.onFeedbackError);
429
-
430
- // Create widget
431
- this.widget = this.sdk.createWidget(this.widgetType);
432
-
433
- if (this.widgetType === 'inline') {
434
- this.widget.mount(this.$refs.feedbackContainer);
435
- } else {
436
- this.widget.mount();
437
- }
438
- },
439
- onFeedbackSubmitted(data) {
440
- this.$emit('feedback-submitted', data);
441
- this.$toast.success('Thank you for your feedback!');
442
- },
443
- onFeedbackError(error) {
444
- this.$emit('feedback-error', error);
445
- this.$toast.error('Failed to submit feedback');
446
- },
447
- cleanup() {
448
- if (this.widget) this.widget.destroy();
449
- if (this.sdk) this.sdk.destroy();
450
- },
451
- },
452
- };
453
- </script>
221
+ ### One-time Events
222
+
223
+ ```javascript
224
+ sdk.once('sdk:initialized', (data) => {
225
+ console.log('SDK ready!', data.config);
226
+ });
454
227
  ```
455
228
 
456
- ### Angular Integration
457
-
458
- ```typescript
459
- // feedback-widget.component.ts
460
- import {
461
- Component,
462
- Input,
463
- OnInit,
464
- OnDestroy,
465
- ElementRef,
466
- ViewChild,
467
- } from '@angular/core';
468
- import { FeedbackSDK } from '@product7/feedback-sdk';
469
-
470
- @Component({
471
- selector: 'app-feedback-widget',
472
- template: `
473
- <div #feedbackContainer *ngIf="type === 'inline'"></div>
474
- `,
475
- styleUrls: ['./feedback-widget.component.css'],
476
- })
477
- export class FeedbackWidgetComponent implements OnInit, OnDestroy {
478
- @Input() workspace: string = '';
479
- @Input() boardId: string = '';
480
- @Input() type: string = 'button';
481
-
482
- @ViewChild('feedbackContainer') containerRef?: ElementRef;
483
-
484
- private sdk?: FeedbackSDK;
485
- private widget?: any;
486
-
487
- async ngOnInit() {
488
- await this.initializeFeedback();
489
- }
229
+ ---
490
230
 
491
- ngOnDestroy() {
492
- this.cleanup();
493
- }
231
+ ## Authentication Flows
494
232
 
495
- private async initializeFeedback() {
496
- this.sdk = new FeedbackSDK({
497
- workspace: this.workspace,
498
- boardId: this.boardId,
499
- debug: !environment.production,
500
- });
233
+ ### On Login
501
234
 
502
- await this.sdk.init();
235
+ ```javascript
236
+ async function handleLogin(authResponse) {
237
+ const userContext = FeedbackSDK.extractUserContextFromAuth(authResponse);
238
+ await sdk.reinitialize(userContext);
239
+
240
+ // Create widget after login
241
+ const widget = sdk.createWidget('button');
242
+ widget.mount();
243
+ }
244
+ ```
503
245
 
504
- // Set up event listeners
505
- this.sdk.eventBus.on('feedback:submitted', (data) => {
506
- console.log('Feedback submitted:', data);
507
- });
246
+ ### On Logout
508
247
 
509
- // Create widget
510
- this.widget = this.sdk.createWidget(this.type);
248
+ ```javascript
249
+ function handleLogout() {
250
+ sdk.apiService.clearSession();
251
+ sdk.destroyAllWidgets();
252
+ }
253
+ ```
511
254
 
512
- if (this.type === 'inline' && this.containerRef) {
513
- this.widget.mount(this.containerRef.nativeElement);
514
- } else {
515
- this.widget.mount();
516
- }
517
- }
255
+ ### Check Session
518
256
 
519
- private cleanup() {
520
- if (this.widget) this.widget.destroy();
521
- if (this.sdk) this.sdk.destroy();
522
- }
257
+ ```javascript
258
+ if (!sdk.apiService.isSessionValid()) {
259
+ await sdk.init();
523
260
  }
524
261
  ```
525
262
 
526
- ## 🚀 Advanced Examples
263
+ ---
527
264
 
528
- ### A/B Testing
265
+ ## Configuration Updates
529
266
 
530
- Test different widget configurations:
267
+ ### Update Theme
531
268
 
532
269
  ```javascript
533
- // A/B test: Button vs Tab widget
534
- const variant = Math.random() < 0.5 ? 'button' : 'tab';
270
+ sdk.updateConfig({ theme: 'dark' });
271
+ ```
272
+
273
+ ### Update Multiple Options
535
274
 
536
- const feedback = new FeedbackSDK({
537
- workspace: 'my-company',
538
- boardId: 'ab-test-feedback',
275
+ ```javascript
276
+ sdk.updateConfig({
277
+ theme: 'dark',
278
+ position: 'top-right',
279
+ showBackdrop: false
539
280
  });
281
+ ```
540
282
 
541
- await feedback.init();
283
+ ### Update User Context
542
284
 
543
- // Track A/B test variant
544
- gtag('event', 'feedback_widget_variant', {
545
- variant: variant,
285
+ ```javascript
286
+ sdk.setUserContext({
287
+ user_id: 'user_456',
288
+ email: 'newuser@example.com',
289
+ name: 'Jane Smith'
546
290
  });
291
+ ```
547
292
 
548
- const widget = feedback.createWidget(variant, {
549
- position: variant === 'button' ? 'bottom-right' : 'bottom-left',
550
- });
293
+ ---
294
+
295
+ ## Programmatic Control
551
296
 
297
+ ### Open Panel Programmatically
298
+
299
+ ```javascript
300
+ const widget = sdk.createWidget('button');
552
301
  widget.mount();
553
302
 
554
- // Track which variant gets more engagement
555
- feedback.eventBus.on('feedback:submitted', (data) => {
556
- gtag('event', 'feedback_submitted_variant', {
557
- variant: variant,
558
- widget_type: data.widget.type,
559
- });
560
- });
303
+ // Open panel from code
304
+ setTimeout(() => {
305
+ widget.openPanel();
306
+ }, 3000);
561
307
  ```
562
308
 
563
- ### Conditional Loading
564
-
565
- Load feedback widget based on conditions:
309
+ ### Close Panel
566
310
 
567
311
  ```javascript
568
- async function loadFeedbackWidget() {
569
- // Only load for logged-in users
570
- if (!isUserLoggedIn()) {
571
- return;
572
- }
312
+ widget.closePanel();
313
+ ```
573
314
 
574
- // Only load on certain pages
575
- const allowedPages = ['/dashboard', '/profile', '/settings'];
576
- if (!allowedPages.includes(window.location.pathname)) {
577
- return;
578
- }
315
+ ### Trigger from Custom Button
579
316
 
580
- // Don't load if user has submitted feedback recently
581
- const lastFeedback = localStorage.getItem('lastFeedbackSubmission');
582
- if (
583
- lastFeedback &&
584
- Date.now() - parseInt(lastFeedback) < 7 * 24 * 60 * 60 * 1000
585
- ) {
586
- return; // 7 days cooldown
317
+ ```javascript
318
+ const customButton = document.getElementById('feedback-btn');
319
+ customButton.addEventListener('click', () => {
320
+ const widget = sdk.getWidget('widget_123');
321
+ if (widget) {
322
+ widget.openPanel();
587
323
  }
324
+ });
325
+ ```
588
326
 
589
- // Initialize SDK
590
- const feedback = new FeedbackSDK({
591
- workspace: 'my-company',
592
- boardId: 'conditional-feedback',
593
- });
327
+ ---
594
328
 
595
- await feedback.init();
329
+ ## Error Handling
596
330
 
597
- // Track feedback submission timestamp
598
- feedback.eventBus.on('feedback:submitted', () => {
599
- localStorage.setItem('lastFeedbackSubmission', Date.now().toString());
600
- });
331
+ ### Try-Catch Pattern
601
332
 
602
- const widget = feedback.createWidget('button');
333
+ ```javascript
334
+ try {
335
+ await sdk.init();
336
+ const widget = sdk.createWidget('button');
603
337
  widget.mount();
338
+ } catch (error) {
339
+ if (error instanceof ConfigError) {
340
+ console.error('Config error:', error.message);
341
+ } else if (error instanceof APIError) {
342
+ console.error('API error:', error.status);
343
+ } else {
344
+ console.error('Unexpected error:', error);
345
+ }
604
346
  }
347
+ ```
348
+
349
+ ### Handle API Errors
605
350
 
606
- // Load when page is ready
607
- document.addEventListener('DOMContentLoaded', loadFeedbackWidget);
351
+ ```javascript
352
+ try {
353
+ await sdk.apiService.submitFeedback(data);
354
+ } catch (error) {
355
+ if (error.isNetworkError()) {
356
+ alert('No internet connection');
357
+ } else if (error.isClientError()) {
358
+ alert('Invalid data. Please check your input.');
359
+ } else if (error.isServerError()) {
360
+ alert('Server error. Please try again later.');
361
+ }
362
+ }
608
363
  ```
609
364
 
610
- ### Custom Widget Creation
365
+ ---
366
+
367
+ ## Advanced Usage
611
368
 
612
- Create your own widget type:
369
+ ### Custom Widget Creation
613
370
 
614
371
  ```javascript
615
- // Custom popup widget
616
- class PopupWidget extends FeedbackSDK.BaseWidget {
372
+ import { BaseWidget, WidgetFactory } from '@product7/feedback-sdk';
373
+
374
+ class CustomWidget extends BaseWidget {
617
375
  constructor(options) {
618
- super({ ...options, type: 'popup' });
619
- this.showDelay = options.showDelay || 5000;
376
+ super({ ...options, type: 'custom' });
620
377
  }
621
378
 
622
379
  _render() {
623
- const popup = document.createElement('div');
624
- popup.className = `feedback-popup theme-${this.options.theme}`;
625
- popup.innerHTML = `
626
- <div class="popup-content">
627
- <button class="popup-close">&times;</button>
628
- <h3>Quick Feedback</h3>
629
- <p>How was your experience?</p>
630
- <div class="popup-buttons">
631
- <button class="popup-btn good">😊 Good</button>
632
- <button class="popup-btn okay">😐 Okay</button>
633
- <button class="popup-btn bad">😞 Bad</button>
634
- </div>
635
- </div>
636
- `;
637
-
638
- // Auto-hide after delay
639
- setTimeout(() => {
640
- if (this.mounted && !this.destroyed) {
641
- this.show();
642
- }
643
- }, this.showDelay);
644
-
645
- return popup;
380
+ const element = document.createElement('div');
381
+ element.innerHTML = '<button>Custom Feedback</button>';
382
+ return element;
646
383
  }
647
384
 
648
385
  _attachEvents() {
649
- const popup = this.element;
650
-
651
- // Close button
652
- popup.querySelector('.popup-close').addEventListener('click', () => {
653
- this.hide();
654
- });
655
-
656
- // Rating buttons
657
- popup.querySelectorAll('.popup-btn').forEach((btn) => {
658
- btn.addEventListener('click', (e) => {
659
- const rating = e.target.classList.contains('good')
660
- ? 5
661
- : e.target.classList.contains('okay')
662
- ? 3
663
- : 1;
664
-
665
- this.submitQuickFeedback(rating);
666
- this.hide();
667
- });
668
- });
669
- }
670
-
671
- async submitQuickFeedback(rating) {
672
- try {
673
- const payload = {
674
- title: `Quick Feedback - Rating: ${rating}`,
675
- content: `User gave a ${rating}/5 rating`,
676
- board_id: this.options.boardId,
677
- };
678
-
679
- await this.apiService.submitFeedback(payload);
680
- this.sdk.eventBus.emit('feedback:submitted', { widget: this, rating });
681
- } catch (error) {
682
- this.sdk.eventBus.emit('feedback:error', { widget: this, error });
683
- }
386
+ this.element.querySelector('button')
387
+ .addEventListener('click', this.openPanel);
684
388
  }
685
389
  }
686
390
 
687
- // Register the custom widget
688
- FeedbackSDK.WidgetFactory.register('popup', PopupWidget);
391
+ WidgetFactory.register('custom', CustomWidget);
689
392
 
690
- // Use the custom widget
691
- const feedback = new FeedbackSDK({
692
- workspace: 'my-company',
693
- boardId: 'popup-feedback',
694
- });
695
-
696
- await feedback.init();
697
-
698
- const popupWidget = feedback.createWidget('popup', {
699
- showDelay: 3000, // Show after 3 seconds
700
- });
701
-
702
- popupWidget.mount();
393
+ const widget = sdk.createWidget('custom');
394
+ widget.mount();
703
395
  ```
704
396
 
705
- ## 💡 Best Practices Examples
706
-
707
- ### Performance Optimization
708
-
709
- Lazy load the SDK when needed:
397
+ ### Conditional Widget Loading
710
398
 
711
399
  ```javascript
712
- // Lazy load function
713
- async function loadFeedbackSDK() {
714
- if (window.FeedbackSDK) {
715
- return window.FeedbackSDK;
716
- }
717
-
718
- return new Promise((resolve) => {
719
- const script = document.createElement('script');
720
- script.src =
721
- 'https://cdn.jsdelivr.net/npm/@product7/feedback-sdk@1/dist/feedback-sdk.min.js';
722
- script.onload = () => resolve(window.FeedbackSDK);
723
- document.head.appendChild(script);
400
+ // Only show feedback for authenticated users
401
+ if (user.isAuthenticated) {
402
+ const sdk = new FeedbackSDK({
403
+ workspace: 'my-workspace',
404
+ userContext: {
405
+ user_id: user.id,
406
+ email: user.email
407
+ }
724
408
  });
409
+
410
+ await sdk.init();
411
+ const widget = sdk.createWidget('button');
412
+ widget.mount();
725
413
  }
414
+ ```
726
415
 
727
- // Load SDK only when user interacts with feedback trigger
728
- document
729
- .getElementById('feedback-trigger')
730
- .addEventListener('click', async () => {
731
- const FeedbackSDK = await loadFeedbackSDK();
732
-
733
- const feedback = new FeedbackSDK({
734
- workspace: 'my-company',
735
- boardId: 'lazy-feedback',
736
- });
416
+ ### Environment-based Configuration
737
417
 
738
- await feedback.init();
739
- const widget = feedback.createWidget('button');
740
- widget.mount();
418
+ ```javascript
419
+ const config = {
420
+ workspace: 'my-workspace',
421
+ userContext: getUserContext(),
422
+ apiUrl: process.env.NODE_ENV === 'production'
423
+ ? 'https://api.product7.io/api/v1'
424
+ : 'https://staging.api.product7.io/api/v1',
425
+ debug: process.env.NODE_ENV === 'development'
426
+ };
741
427
 
742
- // Open modal immediately
743
- widget.openModal();
744
- });
428
+ const sdk = new FeedbackSDK(config);
745
429
  ```
746
430
 
747
- ### Error Handling
431
+ ---
432
+
433
+ ## Testing
748
434
 
749
- Robust error handling and fallbacks:
435
+ ### Mock SDK
750
436
 
751
437
  ```javascript
752
- async function initializeFeedback() {
753
- try {
754
- const feedback = new FeedbackSDK({
755
- workspace: 'my-company',
756
- boardId: 'error-handling-feedback',
757
- debug: true,
758
- });
759
-
760
- await feedback.init();
761
-
762
- // Set up comprehensive error handling
763
- feedback.eventBus.on('feedback:error', (error) => {
764
- console.error('Feedback error:', error);
765
-
766
- // Show user-friendly error message
767
- const errorMsg = getUserFriendlyErrorMessage(error.error);
768
- showNotification(errorMsg, 'error');
769
-
770
- // Track error for monitoring
771
- if (window.Sentry) {
772
- Sentry.captureException(error.error);
773
- }
774
- });
775
-
776
- const widget = feedback.createWidget('button');
777
- widget.mount();
778
-
779
- // Success tracking
780
- feedback.eventBus.on('feedback:submitted', () => {
781
- showNotification('Feedback submitted successfully!', 'success');
782
- });
783
- } catch (initError) {
784
- console.error('Failed to initialize feedback SDK:', initError);
785
-
786
- // Fallback: show simple mailto link
787
- showFallbackFeedback();
788
- }
789
- }
438
+ // In tests
439
+ jest.mock('@product7/feedback-sdk');
790
440
 
791
- function getUserFriendlyErrorMessage(error) {
792
- if (error.name === 'APIError') {
793
- if (error.status === 0) {
794
- return 'Network error. Please check your connection.';
795
- } else if (error.status >= 500) {
796
- return 'Server error. Please try again later.';
797
- }
798
- }
799
- return 'Failed to submit feedback. Please try again.';
800
- }
441
+ test('should initialize SDK', async () => {
442
+ const sdk = new FeedbackSDK({
443
+ workspace: 'test',
444
+ userContext: { user_id: '123' }
445
+ });
446
+ await sdk.init();
447
+ expect(sdk.initialized).toBe(true);
448
+ });
449
+ ```
801
450
 
802
- function showFallbackFeedback() {
803
- const fallback = document.createElement('div');
804
- fallback.innerHTML = `
805
- <div style="position: fixed; bottom: 20px; right: 20px; z-index: 9999;">
806
- <a href="mailto:feedback@my-company.com?subject=Website Feedback"
807
- style="background: #155EEF; color: white; padding: 12px 20px;
808
- border-radius: 25px; text-decoration: none;">
809
- Send Feedback
810
- </a>
811
- </div>
812
- `;
813
- document.body.appendChild(fallback);
814
- }
451
+ ### Check Widget Exists
815
452
 
816
- function showNotification(message, type) {
817
- // Your notification system
818
- console.log(`${type}: ${message}`);
819
- }
453
+ ```javascript
454
+ const widget = sdk.createWidget('button');
455
+ widget.mount();
820
456
 
821
- // Initialize with error handling
822
- initializeFeedback();
823
- ```
457
+ const found = sdk.getWidget(widget.id);
458
+ expect(found).toBeDefined();
459
+ ```