@product7/feedback-sdk 1.5.2 → 1.5.4

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.
@@ -6729,6 +6729,7 @@
6729
6729
  lowLabel: options.lowLabel || null,
6730
6730
  highLabel: options.highLabel || null,
6731
6731
  customQuestions: options.customQuestions || [],
6732
+ pages: Array.isArray(options.pages) ? options.pages : [],
6732
6733
  respondentId: options.respondentId || null,
6733
6734
  email: options.email || null,
6734
6735
  onSubmit: options.onSubmit || null,
@@ -6739,6 +6740,8 @@
6739
6740
  score: null,
6740
6741
  feedback: '',
6741
6742
  customAnswers: {},
6743
+ pageAnswers: {},
6744
+ currentPageIndex: 0,
6742
6745
  isVisible: false,
6743
6746
  };
6744
6747
  }
@@ -6769,9 +6772,19 @@
6769
6772
  }
6770
6773
 
6771
6774
  _renderSurvey() {
6772
- this._closeSurvey();
6775
+ this._closeSurvey(false);
6773
6776
 
6774
6777
  const config = this._getSurveyConfig();
6778
+ const isMultiPage = this._isMultiPageSurvey();
6779
+ const isLastPage = this._isLastPage();
6780
+ const pageProgress = isMultiPage
6781
+ ? `Page ${this.surveyState.currentPageIndex + 1} of ${this.surveyOptions.pages.length}`
6782
+ : '';
6783
+ const submitLabel = isMultiPage && !isLastPage ? 'Next' : 'Submit';
6784
+ const backButton =
6785
+ isMultiPage && this.surveyState.currentPageIndex > 0
6786
+ ? '<button class="feedback-survey-back">Back</button>'
6787
+ : '';
6775
6788
 
6776
6789
  if (this.surveyOptions.position === 'center') {
6777
6790
  this.backdropElement = document.createElement('div');
@@ -6789,11 +6802,15 @@
6789
6802
  <button class="feedback-survey-close">&times;</button>
6790
6803
  <h3 class="feedback-survey-title">${config.title}</h3>
6791
6804
  <p class="feedback-survey-description">${config.description}</p>
6805
+ ${isMultiPage ? `<div class="feedback-survey-progress">${pageProgress}</div>` : ''}
6792
6806
  <div class="feedback-survey-content">${config.html}</div>
6793
6807
  <div class="feedback-survey-feedback">
6794
- <textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)"></textarea>
6808
+ <textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)">${this.surveyState.feedback || ''}</textarea>
6809
+ </div>
6810
+ <div class="feedback-survey-actions">
6811
+ ${backButton}
6812
+ <button class="feedback-survey-submit">${submitLabel}</button>
6795
6813
  </div>
6796
- <button class="feedback-survey-submit">Submit</button>
6797
6814
  `;
6798
6815
 
6799
6816
  document.body.appendChild(this.surveyElement);
@@ -6809,6 +6826,10 @@
6809
6826
  }
6810
6827
 
6811
6828
  _getSurveyConfig() {
6829
+ if (this._isMultiPageSurvey()) {
6830
+ return this._getCurrentPageConfig();
6831
+ }
6832
+
6812
6833
  const configs = {
6813
6834
  nps: {
6814
6835
  title:
@@ -6882,6 +6903,166 @@
6882
6903
  return configs[this.surveyOptions.surveyType] || configs.nps;
6883
6904
  }
6884
6905
 
6906
+ _isMultiPageSurvey() {
6907
+ return (
6908
+ Array.isArray(this.surveyOptions.pages) &&
6909
+ this.surveyOptions.pages.length > 0
6910
+ );
6911
+ }
6912
+
6913
+ _getCurrentPage() {
6914
+ if (!this._isMultiPageSurvey()) return null;
6915
+ return this.surveyOptions.pages[this.surveyState.currentPageIndex] || null;
6916
+ }
6917
+
6918
+ _isLastPage() {
6919
+ if (!this._isMultiPageSurvey()) return true;
6920
+ return this.surveyState.currentPageIndex >= this.surveyOptions.pages.length - 1;
6921
+ }
6922
+
6923
+ _getCurrentPageConfig() {
6924
+ const page = this._getCurrentPage();
6925
+ if (!page) {
6926
+ return this._getFallbackSurveyConfig();
6927
+ }
6928
+
6929
+ return {
6930
+ title: page.title || this.surveyOptions.title || 'Quick Feedback',
6931
+ description:
6932
+ page.description ||
6933
+ this.surveyOptions.description ||
6934
+ 'Help us improve by answering this question.',
6935
+ html: this._renderSurveyPage(page),
6936
+ };
6937
+ }
6938
+
6939
+ _getFallbackSurveyConfig() {
6940
+ return {
6941
+ title: this.surveyOptions.title || 'Quick Feedback',
6942
+ description:
6943
+ this.surveyOptions.description ||
6944
+ 'Help us improve by answering a few questions.',
6945
+ html: this._renderCustomQuestions(),
6946
+ };
6947
+ }
6948
+
6949
+ _renderSurveyPage(page) {
6950
+ switch (page.type) {
6951
+ case 'rating':
6952
+ return this._renderRatingPage(page);
6953
+ case 'multiple_choice':
6954
+ return this._renderMultipleChoicePage(page);
6955
+ case 'text':
6956
+ return this._renderTextPage(page);
6957
+ default:
6958
+ return this._renderTextPage(page);
6959
+ }
6960
+ }
6961
+
6962
+ _renderRatingPage(page) {
6963
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
6964
+ const config = page.ratingConfig || page.rating_config || {};
6965
+ const scale = Number(config.scale) || 5;
6966
+ const ratingType = config.survey_type || this.surveyOptions.surveyType || 'csat';
6967
+ const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
6968
+ const currentRating = pageAnswer.rating;
6969
+
6970
+ const labels = `
6971
+ <div class="feedback-survey-labels">
6972
+ <span>${config.low_label || this.surveyOptions.lowLabel || ''}</span>
6973
+ <span>${config.high_label || this.surveyOptions.highLabel || ''}</span>
6974
+ </div>
6975
+ `;
6976
+
6977
+ if (scale === 11 || ratingType === 'nps') {
6978
+ return `
6979
+ <div class="feedback-survey-nps" data-page-id="${pageId}">
6980
+ ${[...Array(11).keys()]
6981
+ .map((n) => {
6982
+ const selected = currentRating === n ? ' selected' : '';
6983
+ return `<button class="feedback-survey-page-rating-btn feedback-survey-nps-btn${selected}" data-page-id="${pageId}" data-score="${n}">${n}</button>`;
6984
+ })
6985
+ .join('')}
6986
+ </div>
6987
+ ${labels}
6988
+ `;
6989
+ }
6990
+
6991
+ if (ratingType === 'emoji' && scale === 5) {
6992
+ const emojis = [
6993
+ '\uD83D\uDE1E',
6994
+ '\uD83D\uDE15',
6995
+ '\uD83D\uDE10',
6996
+ '\uD83D\uDE42',
6997
+ '\uD83D\uDE04',
6998
+ ];
6999
+ return `
7000
+ <div class="feedback-survey-csat" data-page-id="${pageId}">
7001
+ ${emojis
7002
+ .map((emoji, i) => {
7003
+ const score = i + 1;
7004
+ const selected = currentRating === score ? ' selected' : '';
7005
+ return `<button class="feedback-survey-page-rating-btn feedback-survey-csat-btn${selected}" data-page-id="${pageId}" data-score="${score}">${emoji}</button>`;
7006
+ })
7007
+ .join('')}
7008
+ </div>
7009
+ ${labels}
7010
+ `;
7011
+ }
7012
+
7013
+ return `
7014
+ <div class="feedback-survey-ces" data-page-id="${pageId}">
7015
+ ${[...Array(scale).keys()]
7016
+ .map((i) => {
7017
+ const score = i + 1;
7018
+ const selected = currentRating === score ? ' selected' : '';
7019
+ return `<button class="feedback-survey-page-rating-btn feedback-survey-ces-btn${selected}" data-page-id="${pageId}" data-score="${score}">${score}</button>`;
7020
+ })
7021
+ .join('')}
7022
+ </div>
7023
+ ${labels}
7024
+ `;
7025
+ }
7026
+
7027
+ _renderMultipleChoicePage(page) {
7028
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
7029
+ const config = page.multipleChoiceConfig || page.multiple_choice_config || {};
7030
+ const options = Array.isArray(config.options) ? config.options : [];
7031
+ const allowMultiple = config.allow_multiple === true || config.multiple === true;
7032
+ const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
7033
+ const selectedValues = Array.isArray(pageAnswer.values)
7034
+ ? pageAnswer.values
7035
+ : pageAnswer.value
7036
+ ? [pageAnswer.value]
7037
+ : [];
7038
+
7039
+ return `
7040
+ <div class="feedback-survey-multiple-choice" data-page-id="${pageId}" data-multiple="${allowMultiple}">
7041
+ ${options
7042
+ .map((option, index) => {
7043
+ const value =
7044
+ option.value || option.id || option.key || `option_${index}`;
7045
+ const label = option.label || option.text || String(value);
7046
+ const selected = selectedValues.includes(value) ? ' selected' : '';
7047
+ return `<button class="feedback-survey-page-choice-btn${selected}" data-page-id="${pageId}" data-value="${value}">${label}</button>`;
7048
+ })
7049
+ .join('')}
7050
+ </div>
7051
+ `;
7052
+ }
7053
+
7054
+ _renderTextPage(page) {
7055
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
7056
+ const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
7057
+ const value = pageAnswer.text || '';
7058
+
7059
+ return `
7060
+ <div class="feedback-survey-text-page" data-page-id="${pageId}">
7061
+ <textarea class="feedback-survey-page-textarea" data-page-id="${pageId}" placeholder="Type your answer...">${value}</textarea>
7062
+ </div>
7063
+ `;
7064
+ }
7065
+
6885
7066
  _renderCustomQuestions() {
6886
7067
  if (
6887
7068
  !this.surveyOptions.customQuestions ||
@@ -6954,6 +7135,11 @@
6954
7135
  );
6955
7136
  submitBtn.addEventListener('click', () => this._handleSubmit());
6956
7137
 
7138
+ const backBtn = this.surveyElement.querySelector('.feedback-survey-back');
7139
+ if (backBtn) {
7140
+ backBtn.addEventListener('click', () => this._handleBack());
7141
+ }
7142
+
6957
7143
  const textarea = this.surveyElement.querySelector(
6958
7144
  '.feedback-survey-textarea'
6959
7145
  );
@@ -6972,6 +7158,11 @@
6972
7158
  }
6973
7159
 
6974
7160
  _attachTypeSpecificEvents() {
7161
+ if (this._isMultiPageSurvey()) {
7162
+ this._attachCurrentPageEvents();
7163
+ return;
7164
+ }
7165
+
6975
7166
  const type = this.surveyOptions.surveyType;
6976
7167
 
6977
7168
  if (type === 'nps') {
@@ -7033,6 +7224,87 @@
7033
7224
  }
7034
7225
  }
7035
7226
 
7227
+ _attachCurrentPageEvents() {
7228
+ const page = this._getCurrentPage();
7229
+ if (!page || !this.surveyElement) return;
7230
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
7231
+
7232
+ if (page.type === 'rating') {
7233
+ this.surveyElement
7234
+ .querySelectorAll('.feedback-survey-page-rating-btn')
7235
+ .forEach((btn) => {
7236
+ btn.addEventListener('click', () => {
7237
+ const score = parseInt(btn.dataset.score);
7238
+ if (Number.isNaN(score)) return;
7239
+ this._setPageAnswer(pageId, { rating: score });
7240
+
7241
+ this.surveyElement
7242
+ .querySelectorAll('.feedback-survey-page-rating-btn')
7243
+ .forEach((item) => item.classList.remove('selected'));
7244
+ btn.classList.add('selected');
7245
+ });
7246
+ });
7247
+ }
7248
+
7249
+ if (page.type === 'multiple_choice') {
7250
+ const container = this.surveyElement.querySelector(
7251
+ '.feedback-survey-multiple-choice'
7252
+ );
7253
+ const allowMultiple = container
7254
+ ? container.dataset.multiple === 'true'
7255
+ : false;
7256
+
7257
+ this.surveyElement
7258
+ .querySelectorAll('.feedback-survey-page-choice-btn')
7259
+ .forEach((btn) => {
7260
+ btn.addEventListener('click', () => {
7261
+ const value = btn.dataset.value;
7262
+ if (!value) return;
7263
+
7264
+ const existing = this.surveyState.pageAnswers[pageId] || {};
7265
+ const existingValues = Array.isArray(existing.values)
7266
+ ? existing.values
7267
+ : existing.value
7268
+ ? [existing.value]
7269
+ : [];
7270
+
7271
+ let nextValues = [];
7272
+ if (allowMultiple) {
7273
+ const hasValue = existingValues.includes(value);
7274
+ nextValues = hasValue
7275
+ ? existingValues.filter((v) => v !== value)
7276
+ : [...existingValues, value];
7277
+ } else {
7278
+ nextValues = [value];
7279
+ }
7280
+
7281
+ this._setPageAnswer(pageId, {
7282
+ value: nextValues[0] || null,
7283
+ values: nextValues,
7284
+ });
7285
+
7286
+ if (!allowMultiple) {
7287
+ this.surveyElement
7288
+ .querySelectorAll('.feedback-survey-page-choice-btn')
7289
+ .forEach((item) => item.classList.remove('selected'));
7290
+ }
7291
+ btn.classList.toggle('selected', nextValues.includes(value));
7292
+ });
7293
+ });
7294
+ }
7295
+
7296
+ if (page.type === 'text') {
7297
+ const textarea = this.surveyElement.querySelector(
7298
+ '.feedback-survey-page-textarea'
7299
+ );
7300
+ if (textarea) {
7301
+ textarea.addEventListener('input', (e) => {
7302
+ this._setPageAnswer(pageId, { text: e.target.value });
7303
+ });
7304
+ }
7305
+ }
7306
+ }
7307
+
7036
7308
  _selectNPS(score) {
7037
7309
  this.surveyState.score = score;
7038
7310
  this.surveyElement
@@ -7091,7 +7363,21 @@
7091
7363
  async _handleSubmit() {
7092
7364
  const type = this.surveyOptions.surveyType;
7093
7365
 
7366
+ if (this._isMultiPageSurvey()) {
7367
+ if (!this._validateCurrentPage()) {
7368
+ return;
7369
+ }
7370
+
7371
+ const nextPageIndex = this._getNextPageIndex();
7372
+ if (nextPageIndex !== -1 && nextPageIndex !== this.surveyState.currentPageIndex) {
7373
+ this.surveyState.currentPageIndex = nextPageIndex;
7374
+ this._renderSurvey();
7375
+ return;
7376
+ }
7377
+ }
7378
+
7094
7379
  if (
7380
+ !this._isMultiPageSurvey() &&
7095
7381
  (type === 'nps' || type === 'csat' || type === 'ces') &&
7096
7382
  this.surveyState.score === null
7097
7383
  ) {
@@ -7100,20 +7386,28 @@
7100
7386
  }
7101
7387
 
7102
7388
  const respondent = this._getRespondentContext();
7389
+ const normalizedPageAnswers = this._normalizePageAnswersForSubmit();
7390
+ const mergedAnswers = {
7391
+ ...this.surveyState.customAnswers,
7392
+ ...(Object.keys(normalizedPageAnswers).length > 0 && {
7393
+ page_answers: normalizedPageAnswers,
7394
+ }),
7395
+ };
7103
7396
 
7104
7397
  const responseData = {
7105
- rating: this.surveyState.score,
7398
+ rating: this._getSubmissionRating(),
7106
7399
  feedback: this.surveyState.feedback,
7107
- answers: this.surveyState.customAnswers,
7400
+ answers: mergedAnswers,
7108
7401
  ...(respondent.respondentId && { respondentId: respondent.respondentId }),
7109
7402
  ...(respondent.email && { email: respondent.email }),
7110
7403
  };
7111
7404
 
7112
7405
  const response = {
7113
7406
  type: type,
7114
- score: this.surveyState.score,
7407
+ score: this._getSubmissionRating(),
7115
7408
  feedback: this.surveyState.feedback,
7116
- customAnswers: this.surveyState.customAnswers,
7409
+ customAnswers: mergedAnswers,
7410
+ pageAnswers: normalizedPageAnswers,
7117
7411
  timestamp: new Date().toISOString(),
7118
7412
  };
7119
7413
 
@@ -7135,6 +7429,138 @@
7135
7429
  this._showSuccessNotification();
7136
7430
  }
7137
7431
 
7432
+ _handleBack() {
7433
+ if (!this._isMultiPageSurvey()) return;
7434
+ if (this.surveyState.currentPageIndex <= 0) return;
7435
+ this.surveyState.currentPageIndex -= 1;
7436
+ this._renderSurvey();
7437
+ }
7438
+
7439
+ _setPageAnswer(pageId, data) {
7440
+ if (!pageId) return;
7441
+ this.surveyState.pageAnswers[pageId] = {
7442
+ ...(this.surveyState.pageAnswers[pageId] || {}),
7443
+ ...data,
7444
+ };
7445
+ }
7446
+
7447
+ _validateCurrentPage() {
7448
+ const page = this._getCurrentPage();
7449
+ if (!page || page.required === false) return true;
7450
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
7451
+
7452
+ const answer = this.surveyState.pageAnswers[pageId] || {};
7453
+
7454
+ if (page.type === 'rating') {
7455
+ if (typeof answer.rating !== 'number') {
7456
+ this._showError('Please select a rating');
7457
+ return false;
7458
+ }
7459
+ }
7460
+
7461
+ if (page.type === 'multiple_choice') {
7462
+ const hasSingle = Boolean(answer.value);
7463
+ const hasMany = Array.isArray(answer.values) && answer.values.length > 0;
7464
+ if (!hasSingle && !hasMany) {
7465
+ this._showError('Please select an option');
7466
+ return false;
7467
+ }
7468
+ }
7469
+
7470
+ if (page.type === 'text') {
7471
+ if (!answer.text || !String(answer.text).trim()) {
7472
+ this._showError('Please enter an answer');
7473
+ return false;
7474
+ }
7475
+ }
7476
+
7477
+ return true;
7478
+ }
7479
+
7480
+ _getNextPageIndex() {
7481
+ if (!this._isMultiPageSurvey()) {
7482
+ return -1;
7483
+ }
7484
+
7485
+ const page = this._getCurrentPage();
7486
+ const total = this.surveyOptions.pages.length;
7487
+ const currentIndex = this.surveyState.currentPageIndex;
7488
+ const fallbackNext = currentIndex + 1 < total ? currentIndex + 1 : -1;
7489
+ const navigation = page ? page.afterThisPage || page.after_this_page : null;
7490
+ if (!navigation) {
7491
+ return fallbackNext;
7492
+ }
7493
+
7494
+ const nextValue = navigation.default;
7495
+ if (!nextValue) {
7496
+ return fallbackNext;
7497
+ }
7498
+
7499
+ if (nextValue === 'end_survey') {
7500
+ return -1;
7501
+ }
7502
+ if (nextValue === 'next_page' || nextValue === 'next') {
7503
+ return fallbackNext;
7504
+ }
7505
+
7506
+ if (typeof nextValue === 'number') {
7507
+ return nextValue >= 0 && nextValue < total ? nextValue : fallbackNext;
7508
+ }
7509
+
7510
+ if (typeof nextValue === 'string') {
7511
+ const normalizedId = nextValue.replace(/^page:/, '');
7512
+ const pageIndex = this.surveyOptions.pages.findIndex(
7513
+ (item) => item.id === normalizedId
7514
+ );
7515
+ return pageIndex >= 0 ? pageIndex : fallbackNext;
7516
+ }
7517
+
7518
+ return fallbackNext;
7519
+ }
7520
+
7521
+ _getSubmissionRating() {
7522
+ if (typeof this.surveyState.score === 'number') {
7523
+ return this.surveyState.score;
7524
+ }
7525
+
7526
+ if (!this._isMultiPageSurvey()) {
7527
+ return null;
7528
+ }
7529
+
7530
+ for (const page of this.surveyOptions.pages) {
7531
+ const pageId = page.id || `page_${this.surveyOptions.pages.indexOf(page)}`;
7532
+ const answer = this.surveyState.pageAnswers[pageId];
7533
+ if (answer && typeof answer.rating === 'number') {
7534
+ return answer.rating;
7535
+ }
7536
+ }
7537
+
7538
+ return null;
7539
+ }
7540
+
7541
+ _normalizePageAnswersForSubmit() {
7542
+ const output = {};
7543
+ for (const [pageId, answer] of Object.entries(this.surveyState.pageAnswers)) {
7544
+ if (answer == null) continue;
7545
+ if (Array.isArray(answer.values) && answer.values.length > 0) {
7546
+ output[pageId] = answer.values;
7547
+ continue;
7548
+ }
7549
+ if (answer.value != null && answer.value !== '') {
7550
+ output[pageId] = answer.value;
7551
+ continue;
7552
+ }
7553
+ if (typeof answer.rating === 'number') {
7554
+ output[pageId] = answer.rating;
7555
+ continue;
7556
+ }
7557
+ if (typeof answer.text === 'string' && answer.text.trim()) {
7558
+ output[pageId] = answer.text.trim();
7559
+ }
7560
+ }
7561
+ return output;
7562
+ }
7563
+
7138
7564
  _getRespondentContext() {
7139
7565
  const sdkUserContext =
7140
7566
  typeof this.sdk.getUserContext === 'function'
@@ -7213,7 +7639,7 @@
7213
7639
  }, 3000);
7214
7640
  }
7215
7641
 
7216
- _closeSurvey() {
7642
+ _closeSurvey(resetState = true) {
7217
7643
  if (this._escapeHandler) {
7218
7644
  document.removeEventListener('keydown', this._escapeHandler);
7219
7645
  this._escapeHandler = null;
@@ -7246,12 +7672,16 @@
7246
7672
  this.backdropElement = null;
7247
7673
  }
7248
7674
 
7249
- this.surveyState = {
7250
- score: null,
7251
- feedback: '',
7252
- customAnswers: {},
7253
- isVisible: false,
7254
- };
7675
+ if (resetState) {
7676
+ this.surveyState = {
7677
+ score: null,
7678
+ feedback: '',
7679
+ customAnswers: {},
7680
+ pageAnswers: {},
7681
+ currentPageIndex: 0,
7682
+ isVisible: false,
7683
+ };
7684
+ }
7255
7685
  }
7256
7686
 
7257
7687
  destroy() {
@@ -7457,7 +7887,9 @@
7457
7887
 
7458
7888
  try {
7459
7889
  const result = await this.apiService.getActiveSurveys(context);
7460
- const surveys = result.data || [];
7890
+ const surveys = (result.data || []).map((survey) =>
7891
+ this._normalizeSurveyConfig(survey)
7892
+ );
7461
7893
  if (context.includeIneligible) {
7462
7894
  return surveys;
7463
7895
  }
@@ -7503,12 +7935,13 @@
7503
7935
 
7504
7936
  return this.showSurvey({
7505
7937
  surveyId: surveyConfig.id,
7506
- surveyType: surveyConfig.type,
7938
+ surveyType: surveyConfig.surveyType || surveyConfig.type,
7507
7939
  title: surveyConfig.title,
7508
7940
  description: surveyConfig.description,
7509
- lowLabel: surveyConfig.low_label,
7510
- highLabel: surveyConfig.high_label,
7511
- customQuestions: surveyConfig.questions,
7941
+ lowLabel: surveyConfig.lowLabel || surveyConfig.low_label,
7942
+ highLabel: surveyConfig.highLabel || surveyConfig.high_label,
7943
+ customQuestions: surveyConfig.customQuestions || surveyConfig.questions,
7944
+ pages: surveyConfig.pages,
7512
7945
  ...displayOptions,
7513
7946
  });
7514
7947
  }
@@ -7529,20 +7962,23 @@
7529
7962
  return null;
7530
7963
  }
7531
7964
 
7965
+ const normalizedOptions = this._normalizeSurveyConfig(options);
7966
+
7532
7967
  const surveyWidget = this.createWidget('survey', {
7533
- surveyId: options.surveyId,
7534
- surveyType: options.surveyType || options.type || 'nps',
7535
- position: options.position || 'bottom-right',
7536
- theme: options.theme || this.config.theme || 'light',
7537
- title: options.title,
7538
- description: options.description,
7539
- lowLabel: options.lowLabel,
7540
- highLabel: options.highLabel,
7541
- customQuestions: options.customQuestions,
7542
- respondentId: options.respondentId,
7543
- email: options.email,
7544
- onSubmit: options.onSubmit,
7545
- onDismiss: options.onDismiss,
7968
+ surveyId: normalizedOptions.surveyId,
7969
+ surveyType: normalizedOptions.surveyType || normalizedOptions.type || 'nps',
7970
+ position: normalizedOptions.position || 'bottom-right',
7971
+ theme: normalizedOptions.theme || this.config.theme || 'light',
7972
+ title: normalizedOptions.title,
7973
+ description: normalizedOptions.description,
7974
+ lowLabel: normalizedOptions.lowLabel,
7975
+ highLabel: normalizedOptions.highLabel,
7976
+ customQuestions: normalizedOptions.customQuestions,
7977
+ pages: normalizedOptions.pages,
7978
+ respondentId: normalizedOptions.respondentId,
7979
+ email: normalizedOptions.email,
7980
+ onSubmit: normalizedOptions.onSubmit,
7981
+ onDismiss: normalizedOptions.onDismiss,
7546
7982
  });
7547
7983
 
7548
7984
  surveyWidget.mount();
@@ -7557,6 +7993,14 @@
7557
7993
  return shouldShow;
7558
7994
  }
7559
7995
 
7996
+ const eligibilityShouldShow = this._getSurveyField(
7997
+ survey.eligibility || {},
7998
+ ['shouldShow', 'should_show']
7999
+ );
8000
+ if (typeof eligibilityShouldShow === 'boolean') {
8001
+ return eligibilityShouldShow;
8002
+ }
8003
+
7560
8004
  const eligible = this._getSurveyField(survey, [
7561
8005
  'eligible',
7562
8006
  'isEligible',
@@ -7571,6 +8015,14 @@
7571
8015
  return !isAnswered;
7572
8016
  }
7573
8017
 
8018
+ const eligibilityAnswered = this._getSurveyField(survey.eligibility || {}, [
8019
+ 'isAnswered',
8020
+ 'is_answered',
8021
+ ]);
8022
+ if (typeof eligibilityAnswered === 'boolean') {
8023
+ return !eligibilityAnswered;
8024
+ }
8025
+
7574
8026
  return true;
7575
8027
  }
7576
8028
 
@@ -7584,6 +8036,15 @@
7584
8036
  return explicitReason;
7585
8037
  }
7586
8038
 
8039
+ const eligibilityReason = this._getSurveyField(survey.eligibility || {}, [
8040
+ 'reason',
8041
+ 'suppressionReason',
8042
+ 'suppression_reason',
8043
+ ]);
8044
+ if (eligibilityReason) {
8045
+ return eligibilityReason;
8046
+ }
8047
+
7587
8048
  const isAnswered = this._getSurveyField(survey, ['isAnswered', 'is_answered']);
7588
8049
  if (isAnswered === true) {
7589
8050
  return 'already_answered';
@@ -7592,6 +8053,96 @@
7592
8053
  return 'ineligible';
7593
8054
  }
7594
8055
 
8056
+ _normalizeSurveyConfig(survey = {}) {
8057
+ const firstPage = Array.isArray(survey.pages) && survey.pages.length > 0
8058
+ ? survey.pages[0]
8059
+ : null;
8060
+ const ratingConfig = firstPage
8061
+ ? firstPage.rating_config || firstPage.ratingConfig || {}
8062
+ : {};
8063
+
8064
+ const inferredType =
8065
+ survey.type ||
8066
+ this._inferSurveyTypeFromPage(firstPage) ||
8067
+ 'nps';
8068
+
8069
+ return {
8070
+ ...survey,
8071
+ surveyId: survey.surveyId || survey.id || null,
8072
+ surveyType: survey.surveyType || inferredType,
8073
+ type: survey.type || inferredType,
8074
+ should_show:
8075
+ survey.should_show ??
8076
+ (survey.eligibility ? survey.eligibility.should_show : undefined),
8077
+ reason:
8078
+ survey.reason ||
8079
+ (survey.eligibility ? survey.eligibility.reason : undefined),
8080
+ title:
8081
+ survey.title ||
8082
+ survey.name ||
8083
+ (firstPage ? firstPage.title : null),
8084
+ description:
8085
+ survey.description ||
8086
+ (firstPage ? firstPage.description : null),
8087
+ lowLabel:
8088
+ survey.lowLabel ||
8089
+ survey.low_label ||
8090
+ ratingConfig.low_label ||
8091
+ null,
8092
+ highLabel:
8093
+ survey.highLabel ||
8094
+ survey.high_label ||
8095
+ ratingConfig.high_label ||
8096
+ null,
8097
+ customQuestions: survey.customQuestions || survey.questions || [],
8098
+ pages: this._normalizeSurveyPages(survey.pages || []),
8099
+ };
8100
+ }
8101
+
8102
+ _normalizeSurveyPages(pages = []) {
8103
+ if (!Array.isArray(pages)) {
8104
+ return [];
8105
+ }
8106
+
8107
+ return pages
8108
+ .map((page, index) => ({
8109
+ id: page.id || `page_${index}`,
8110
+ type: page.type || 'rating',
8111
+ title: page.title || '',
8112
+ description: page.description || '',
8113
+ required: page.required !== false,
8114
+ position: page.position ?? index,
8115
+ ratingConfig: page.ratingConfig || page.rating_config || null,
8116
+ multipleChoiceConfig:
8117
+ page.multipleChoiceConfig || page.multiple_choice_config || null,
8118
+ linkConfig: page.linkConfig || page.link_config || null,
8119
+ afterThisPage: page.afterThisPage || page.after_this_page || null,
8120
+ }))
8121
+ .sort((a, b) => a.position - b.position);
8122
+ }
8123
+
8124
+ _inferSurveyTypeFromPage(page) {
8125
+ if (!page) return null;
8126
+ const ratingConfig = page.rating_config || page.ratingConfig || {};
8127
+ const scale = ratingConfig.scale;
8128
+ const surveyType = ratingConfig.survey_type;
8129
+ const title = (page.title || '').toLowerCase();
8130
+
8131
+ if (scale === 11 || surveyType === 'nps') {
8132
+ return 'nps';
8133
+ }
8134
+
8135
+ if (
8136
+ surveyType === 'emoji' ||
8137
+ title.includes('effort') ||
8138
+ title.includes('easy')
8139
+ ) {
8140
+ return 'ces';
8141
+ }
8142
+
8143
+ return 'csat';
8144
+ }
8145
+
7595
8146
  _getSurveyField(survey, fields) {
7596
8147
  for (const field of fields) {
7597
8148
  if (survey[field] !== undefined && survey[field] !== null) {
@@ -11211,13 +11762,73 @@
11211
11762
  box-shadow: 0 0 0 3px var(--color-primary-light);
11212
11763
  }
11213
11764
 
11765
+ .feedback-survey-progress {
11766
+ font-size: var(--font-size-xs);
11767
+ color: var(--color-text-tertiary);
11768
+ margin-bottom: var(--spacing-3);
11769
+ }
11770
+
11771
+ .feedback-survey-actions {
11772
+ display: flex;
11773
+ gap: var(--spacing-2);
11774
+ margin-top: var(--spacing-3);
11775
+ }
11776
+
11777
+ .feedback-survey-back {
11778
+ padding: var(--spacing-3) var(--spacing-4);
11779
+ border: 1px solid var(--color-border);
11780
+ border-radius: var(--radius-md);
11781
+ background: var(--color-surface);
11782
+ color: var(--color-text-primary);
11783
+ font-size: var(--font-size-base);
11784
+ font-weight: var(--font-weight-medium);
11785
+ cursor: pointer;
11786
+ font-family: inherit;
11787
+ }
11788
+
11789
+ .feedback-survey-page-choice-btn {
11790
+ width: 100%;
11791
+ padding: var(--spacing-3);
11792
+ border: 1px solid var(--color-border);
11793
+ border-radius: var(--radius-md);
11794
+ background: var(--color-surface);
11795
+ color: var(--color-text-primary);
11796
+ text-align: left;
11797
+ font-size: var(--font-size-base);
11798
+ cursor: pointer;
11799
+ margin-bottom: var(--spacing-2);
11800
+ font-family: inherit;
11801
+ }
11802
+
11803
+ .feedback-survey-page-choice-btn.selected {
11804
+ background: var(--color-primary);
11805
+ border-color: var(--color-primary);
11806
+ color: var(--color-white);
11807
+ }
11808
+
11809
+ .feedback-survey-page-textarea {
11810
+ width: 100%;
11811
+ min-height: 96px;
11812
+ padding: var(--spacing-3);
11813
+ border: 1px solid var(--color-border);
11814
+ border-radius: var(--radius-md);
11815
+ font-size: var(--font-size-base);
11816
+ font-family: inherit;
11817
+ box-sizing: border-box;
11818
+ outline: none;
11819
+ }
11820
+
11821
+ .feedback-survey-page-textarea:focus {
11822
+ border-color: var(--color-primary);
11823
+ box-shadow: 0 0 0 3px var(--color-primary-light);
11824
+ }
11825
+
11214
11826
  /* ========================================
11215
11827
  SUBMIT & FEEDBACK
11216
11828
  ======================================== */
11217
11829
 
11218
11830
  .feedback-survey-submit {
11219
11831
  width: 100%;
11220
- margin-top: var(--spacing-3);
11221
11832
  padding: var(--spacing-3);
11222
11833
  background: var(--color-primary);
11223
11834
  color: var(--color-white);