@product7/feedback-sdk 1.1.8 → 1.2.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 CHANGED
@@ -368,6 +368,462 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
368
368
 
369
369
  ---
370
370
 
371
+ ## Survey Widget
372
+
373
+ 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.
374
+
375
+ ### Survey Types
376
+
377
+ | Type | Description | Scale |
378
+ | ---------- | --------------------- | ------------------------ |
379
+ | **NPS** | Net Promoter Score | 0-10 numeric scale |
380
+ | **CSAT** | Customer Satisfaction | 5-point emoji scale |
381
+ | **CES** | Customer Effort Score | 5-level difficulty scale |
382
+ | **Custom** | Multi-question forms | Flexible input types |
383
+
384
+ ### Quick Start
385
+
386
+ ```javascript
387
+ const feedback = new FeedbackSDK({
388
+ workspace: 'your-workspace',
389
+ boardId: 'your-board-id',
390
+ });
391
+
392
+ await feedback.init();
393
+
394
+ // Show NPS survey
395
+ feedback.showSurvey({
396
+ surveyType: 'nps',
397
+ title: 'How likely are you to recommend us?',
398
+ onSubmit: (response) => {
399
+ console.log('Survey submitted:', response);
400
+ },
401
+ });
402
+ ```
403
+
404
+ ### Survey Configuration Options
405
+
406
+ ```javascript
407
+ feedback.showSurvey({
408
+ surveyId: 'backend-survey-id', // Optional: links response to backend survey
409
+ surveyType: 'nps', // 'nps' | 'csat' | 'ces' | 'custom'
410
+ position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'center' | 'bottom'
411
+ theme: 'light', // 'light' | 'dark'
412
+ title: 'Your survey title',
413
+ description: 'Optional description',
414
+ lowLabel: 'Not likely', // Low end label (NPS/CES)
415
+ highLabel: 'Very likely', // High end label (NPS/CES)
416
+ customQuestions: [], // For custom surveys
417
+ onSubmit: (response) => {},
418
+ onDismiss: () => {},
419
+ });
420
+ ```
421
+
422
+ ---
423
+
424
+ ### Backend-Driven Surveys
425
+
426
+ For surveys configured in the Product7 dashboard, use `showSurveyById()` to fetch and display them:
427
+
428
+ ```javascript
429
+ // Show a specific survey by its backend ID
430
+ await feedback.showSurveyById('survey_abc123', {
431
+ position: 'center', // Override position if needed
432
+ onSubmit: (response) => {
433
+ console.log('Survey completed:', response);
434
+ },
435
+ });
436
+ ```
437
+
438
+ ### Fetching Active Surveys
439
+
440
+ Retrieve surveys that match the current user's targeting criteria:
441
+
442
+ ```javascript
443
+ // Get all active surveys for the current context
444
+ const surveys = await feedback.getActiveSurveys();
445
+
446
+ console.log('Available surveys:', surveys);
447
+ // [{ id: 'survey_123', type: 'nps', title: '...', ... }]
448
+
449
+ // Show the first matching survey
450
+ if (surveys.length > 0) {
451
+ await feedback.showSurveyById(surveys[0].id);
452
+ }
453
+ ```
454
+
455
+ With custom targeting context:
456
+
457
+ ```javascript
458
+ const surveys = await feedback.getActiveSurveys({
459
+ url: '/dashboard/settings', // Override current URL
460
+ userProperties: {
461
+ plan: 'enterprise',
462
+ feature_usage: 'high',
463
+ },
464
+ });
465
+ ```
466
+
467
+ ---
468
+
469
+ ### Survey Types
470
+
471
+ #### NPS Survey (Net Promoter Score)
472
+
473
+ ```javascript
474
+ feedback.showSurvey({
475
+ surveyType: 'nps',
476
+ title: 'How likely are you to recommend us to a friend?',
477
+ lowLabel: 'Not at all likely',
478
+ highLabel: 'Extremely likely',
479
+ position: 'bottom-right',
480
+ onSubmit: (response) => {
481
+ console.log('Score:', response.score); // 0-10
482
+ console.log('Feedback:', response.feedback);
483
+ },
484
+ });
485
+ ```
486
+
487
+ #### CSAT Survey (Customer Satisfaction)
488
+
489
+ ```javascript
490
+ feedback.showSurvey({
491
+ surveyType: 'csat',
492
+ title: 'How satisfied are you with our service?',
493
+ position: 'center',
494
+ theme: 'dark',
495
+ onSubmit: (response) => {
496
+ console.log('Satisfaction:', response.score); // 1-5
497
+ },
498
+ });
499
+ ```
500
+
501
+ #### CES Survey (Customer Effort Score)
502
+
503
+ ```javascript
504
+ feedback.showSurvey({
505
+ surveyType: 'ces',
506
+ title: 'How easy was it to complete your task?',
507
+ position: 'bottom',
508
+ onSubmit: (response) => {
509
+ console.log('Effort score:', response.score); // 1-5
510
+ },
511
+ });
512
+ ```
513
+
514
+ #### Custom Survey
515
+
516
+ ```javascript
517
+ feedback.showSurvey({
518
+ surveyType: 'custom',
519
+ title: 'Quick Feedback',
520
+ customQuestions: [
521
+ {
522
+ id: 'feature',
523
+ type: 'select',
524
+ label: 'Which feature do you use most?',
525
+ options: [
526
+ { value: 'dashboard', label: 'Dashboard' },
527
+ { value: 'reports', label: 'Reports' },
528
+ { value: 'settings', label: 'Settings' },
529
+ ],
530
+ },
531
+ {
532
+ id: 'improvement',
533
+ type: 'text',
534
+ label: 'What could we improve?',
535
+ placeholder: 'Your suggestions...',
536
+ },
537
+ ],
538
+ onSubmit: (response) => {
539
+ console.log('Answers:', response.customAnswers);
540
+ // { feature: 'dashboard', improvement: 'Better charts' }
541
+ },
542
+ });
543
+ ```
544
+
545
+ ---
546
+
547
+ ### Event-Triggered Surveys
548
+
549
+ Trigger surveys based on user actions:
550
+
551
+ ```javascript
552
+ // After completing a purchase
553
+ document.getElementById('checkout-btn').addEventListener('click', async () => {
554
+ await processPayment();
555
+
556
+ feedback.showSurvey({
557
+ surveyType: 'csat',
558
+ title: 'How was your checkout experience?',
559
+ position: 'center',
560
+ });
561
+ });
562
+
563
+ // After closing support chat
564
+ chatWidget.on('close', () => {
565
+ feedback.showSurvey({
566
+ surveyType: 'ces',
567
+ title: 'How easy was it to get help?',
568
+ position: 'bottom-right',
569
+ });
570
+ });
571
+
572
+ // On page exit intent
573
+ document.addEventListener('mouseleave', (e) => {
574
+ if (e.clientY < 0 && !sessionStorage.getItem('exit_survey_shown')) {
575
+ sessionStorage.setItem('exit_survey_shown', 'true');
576
+ feedback.showSurvey({
577
+ surveyType: 'nps',
578
+ title: 'Before you go...',
579
+ description: 'How likely are you to recommend us?',
580
+ position: 'center',
581
+ });
582
+ }
583
+ });
584
+ ```
585
+
586
+ ### Time-Based Surveys
587
+
588
+ ```javascript
589
+ // Show after 60 seconds on page
590
+ setTimeout(() => {
591
+ feedback.showSurvey({
592
+ surveyType: 'nps',
593
+ title: 'Enjoying our product?',
594
+ position: 'bottom-right',
595
+ });
596
+ }, 60000);
597
+
598
+ // Show after N page views
599
+ const pageViews = parseInt(localStorage.getItem('page_views') || '0') + 1;
600
+ localStorage.setItem('page_views', pageViews);
601
+
602
+ if (pageViews === 5) {
603
+ feedback.showSurvey({
604
+ surveyType: 'csat',
605
+ title: 'How are you finding things so far?',
606
+ });
607
+ }
608
+ ```
609
+
610
+ ---
611
+
612
+ ### React Integration
613
+
614
+ ```jsx
615
+ import { useEffect, useRef } from 'react';
616
+ import { FeedbackSDK } from '@product7/feedback-sdk';
617
+
618
+ function useSurvey() {
619
+ const sdkRef = useRef(null);
620
+
621
+ useEffect(() => {
622
+ const sdk = new FeedbackSDK({
623
+ workspace: 'your-workspace',
624
+ userContext: {
625
+ user_id: currentUser.id,
626
+ email: currentUser.email,
627
+ },
628
+ });
629
+
630
+ sdk.init().then(() => {
631
+ sdkRef.current = sdk;
632
+ });
633
+
634
+ return () => sdk.destroy();
635
+ }, []);
636
+
637
+ const showNPS = (options = {}) => {
638
+ sdkRef.current?.showSurvey({
639
+ surveyType: 'nps',
640
+ ...options,
641
+ });
642
+ };
643
+
644
+ const showCSAT = (options = {}) => {
645
+ sdkRef.current?.showSurvey({
646
+ surveyType: 'csat',
647
+ ...options,
648
+ });
649
+ };
650
+
651
+ return { showNPS, showCSAT };
652
+ }
653
+
654
+ // Usage in component
655
+ function CheckoutSuccess() {
656
+ const { showCSAT } = useSurvey();
657
+
658
+ useEffect(() => {
659
+ showCSAT({
660
+ title: 'How was your checkout experience?',
661
+ position: 'center',
662
+ });
663
+ }, []);
664
+
665
+ return <div>Thank you for your purchase!</div>;
666
+ }
667
+ ```
668
+
669
+ ### Vue.js Integration
670
+
671
+ ```vue
672
+ <script setup>
673
+ import { onMounted, onUnmounted, ref } from 'vue';
674
+ import { FeedbackSDK } from '@product7/feedback-sdk';
675
+
676
+ const sdk = ref(null);
677
+
678
+ onMounted(async () => {
679
+ sdk.value = new FeedbackSDK({
680
+ workspace: 'your-workspace',
681
+ userContext: {
682
+ user_id: currentUser.value.id,
683
+ email: currentUser.value.email,
684
+ },
685
+ });
686
+ await sdk.value.init();
687
+ });
688
+
689
+ onUnmounted(() => {
690
+ sdk.value?.destroy();
691
+ });
692
+
693
+ const showSurvey = (type, options = {}) => {
694
+ sdk.value?.showSurvey({
695
+ surveyType: type,
696
+ ...options,
697
+ });
698
+ };
699
+
700
+ // Trigger on button click
701
+ const handleFeedbackClick = () => {
702
+ showSurvey('nps', {
703
+ title: 'How likely are you to recommend us?',
704
+ position: 'center',
705
+ });
706
+ };
707
+ </script>
708
+ ```
709
+
710
+ ---
711
+
712
+ ### Survey Events
713
+
714
+ ```javascript
715
+ // Survey displayed
716
+ feedback.eventBus.on('survey:shown', (data) => {
717
+ console.log('Survey displayed:', data.type);
718
+ analytics.track('Survey Shown', { type: data.type });
719
+ });
720
+
721
+ // Survey submitted
722
+ feedback.eventBus.on('survey:submitted', (data) => {
723
+ console.log('Survey submitted:', data.response);
724
+ analytics.track('Survey Completed', {
725
+ type: data.response.type,
726
+ score: data.response.score,
727
+ });
728
+ });
729
+
730
+ // Survey dismissed without completing
731
+ feedback.eventBus.on('survey:dismissed', (data) => {
732
+ console.log('Survey dismissed');
733
+ analytics.track('Survey Dismissed');
734
+ });
735
+ ```
736
+
737
+ ### Response Data Format
738
+
739
+ ```javascript
740
+ // NPS/CSAT/CES response
741
+ {
742
+ type: 'nps',
743
+ score: 9,
744
+ feedback: 'Great product!',
745
+ timestamp: '2025-01-26T10:30:00.000Z'
746
+ }
747
+
748
+ // Custom survey response
749
+ {
750
+ type: 'custom',
751
+ score: null,
752
+ feedback: 'Additional comments here',
753
+ customAnswers: {
754
+ feature: 'dashboard',
755
+ improvement: 'Better mobile support'
756
+ },
757
+ timestamp: '2025-01-26T10:30:00.000Z'
758
+ }
759
+ ```
760
+
761
+ ---
762
+
763
+ ### Position Options
764
+
765
+ | Position | Description |
766
+ | -------------- | ----------------------------- |
767
+ | `bottom-right` | Bottom right corner (default) |
768
+ | `bottom-left` | Bottom left corner |
769
+ | `center` | Centered modal with backdrop |
770
+ | `bottom` | Full-width bottom bar |
771
+
772
+ ### Theme Options
773
+
774
+ | Theme | Description |
775
+ | ------- | ------------------------------------- |
776
+ | `light` | White background, dark text (default) |
777
+ | `dark` | Dark background, light text |
778
+
779
+ ---
780
+
781
+ ### Programmatic Control
782
+
783
+ ```javascript
784
+ // Create survey widget for later use
785
+ const survey = feedback.createWidget('survey', {
786
+ surveyType: 'nps',
787
+ title: 'Rate us',
788
+ });
789
+ survey.mount();
790
+
791
+ // Show when ready
792
+ survey.show();
793
+
794
+ // Hide programmatically
795
+ survey.hide();
796
+
797
+ // Destroy when done
798
+ survey.destroy();
799
+ ```
800
+
801
+ ### Survey Rate Limiting
802
+
803
+ Avoid survey fatigue by tracking when surveys were last shown:
804
+
805
+ ```javascript
806
+ const SURVEY_COOLDOWN = 7 * 24 * 60 * 60 * 1000; // 7 days
807
+
808
+ function canShowSurvey(surveyType) {
809
+ const lastShown = localStorage.getItem(`survey_${surveyType}_shown`);
810
+ if (!lastShown) return true;
811
+ return Date.now() - parseInt(lastShown) > SURVEY_COOLDOWN;
812
+ }
813
+
814
+ function showSurveyWithCooldown(options) {
815
+ if (!canShowSurvey(options.surveyType)) {
816
+ console.log('Survey on cooldown');
817
+ return null;
818
+ }
819
+
820
+ localStorage.setItem(`survey_${options.surveyType}_shown`, Date.now());
821
+ return feedback.showSurvey(options);
822
+ }
823
+ ```
824
+
825
+ ---
826
+
371
827
  ## Related
372
828
 
373
829
  - [Product7 Platform](https://product7.io)