@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/feedback-sdk.js",
6
6
  "module": "src/index.js",
@@ -94,7 +94,9 @@ export class FeedbackSDK {
94
94
 
95
95
  try {
96
96
  const result = await this.apiService.getActiveSurveys(context);
97
- const surveys = result.data || [];
97
+ const surveys = (result.data || []).map((survey) =>
98
+ this._normalizeSurveyConfig(survey)
99
+ );
98
100
  if (context.includeIneligible) {
99
101
  return surveys;
100
102
  }
@@ -140,12 +142,13 @@ export class FeedbackSDK {
140
142
 
141
143
  return this.showSurvey({
142
144
  surveyId: surveyConfig.id,
143
- surveyType: surveyConfig.type,
145
+ surveyType: surveyConfig.surveyType || surveyConfig.type,
144
146
  title: surveyConfig.title,
145
147
  description: surveyConfig.description,
146
- lowLabel: surveyConfig.low_label,
147
- highLabel: surveyConfig.high_label,
148
- customQuestions: surveyConfig.questions,
148
+ lowLabel: surveyConfig.lowLabel || surveyConfig.low_label,
149
+ highLabel: surveyConfig.highLabel || surveyConfig.high_label,
150
+ customQuestions: surveyConfig.customQuestions || surveyConfig.questions,
151
+ pages: surveyConfig.pages,
149
152
  ...displayOptions,
150
153
  });
151
154
  }
@@ -166,20 +169,23 @@ export class FeedbackSDK {
166
169
  return null;
167
170
  }
168
171
 
172
+ const normalizedOptions = this._normalizeSurveyConfig(options);
173
+
169
174
  const surveyWidget = this.createWidget('survey', {
170
- surveyId: options.surveyId,
171
- surveyType: options.surveyType || options.type || 'nps',
172
- position: options.position || 'bottom-right',
173
- theme: options.theme || this.config.theme || 'light',
174
- title: options.title,
175
- description: options.description,
176
- lowLabel: options.lowLabel,
177
- highLabel: options.highLabel,
178
- customQuestions: options.customQuestions,
179
- respondentId: options.respondentId,
180
- email: options.email,
181
- onSubmit: options.onSubmit,
182
- onDismiss: options.onDismiss,
175
+ surveyId: normalizedOptions.surveyId,
176
+ surveyType: normalizedOptions.surveyType || normalizedOptions.type || 'nps',
177
+ position: normalizedOptions.position || 'bottom-right',
178
+ theme: normalizedOptions.theme || this.config.theme || 'light',
179
+ title: normalizedOptions.title,
180
+ description: normalizedOptions.description,
181
+ lowLabel: normalizedOptions.lowLabel,
182
+ highLabel: normalizedOptions.highLabel,
183
+ customQuestions: normalizedOptions.customQuestions,
184
+ pages: normalizedOptions.pages,
185
+ respondentId: normalizedOptions.respondentId,
186
+ email: normalizedOptions.email,
187
+ onSubmit: normalizedOptions.onSubmit,
188
+ onDismiss: normalizedOptions.onDismiss,
183
189
  });
184
190
 
185
191
  surveyWidget.mount();
@@ -194,6 +200,14 @@ export class FeedbackSDK {
194
200
  return shouldShow;
195
201
  }
196
202
 
203
+ const eligibilityShouldShow = this._getSurveyField(
204
+ survey.eligibility || {},
205
+ ['shouldShow', 'should_show']
206
+ );
207
+ if (typeof eligibilityShouldShow === 'boolean') {
208
+ return eligibilityShouldShow;
209
+ }
210
+
197
211
  const eligible = this._getSurveyField(survey, [
198
212
  'eligible',
199
213
  'isEligible',
@@ -208,6 +222,14 @@ export class FeedbackSDK {
208
222
  return !isAnswered;
209
223
  }
210
224
 
225
+ const eligibilityAnswered = this._getSurveyField(survey.eligibility || {}, [
226
+ 'isAnswered',
227
+ 'is_answered',
228
+ ]);
229
+ if (typeof eligibilityAnswered === 'boolean') {
230
+ return !eligibilityAnswered;
231
+ }
232
+
211
233
  return true;
212
234
  }
213
235
 
@@ -221,6 +243,15 @@ export class FeedbackSDK {
221
243
  return explicitReason;
222
244
  }
223
245
 
246
+ const eligibilityReason = this._getSurveyField(survey.eligibility || {}, [
247
+ 'reason',
248
+ 'suppressionReason',
249
+ 'suppression_reason',
250
+ ]);
251
+ if (eligibilityReason) {
252
+ return eligibilityReason;
253
+ }
254
+
224
255
  const isAnswered = this._getSurveyField(survey, ['isAnswered', 'is_answered']);
225
256
  if (isAnswered === true) {
226
257
  return 'already_answered';
@@ -229,6 +260,96 @@ export class FeedbackSDK {
229
260
  return 'ineligible';
230
261
  }
231
262
 
263
+ _normalizeSurveyConfig(survey = {}) {
264
+ const firstPage = Array.isArray(survey.pages) && survey.pages.length > 0
265
+ ? survey.pages[0]
266
+ : null;
267
+ const ratingConfig = firstPage
268
+ ? firstPage.rating_config || firstPage.ratingConfig || {}
269
+ : {};
270
+
271
+ const inferredType =
272
+ survey.type ||
273
+ this._inferSurveyTypeFromPage(firstPage) ||
274
+ 'nps';
275
+
276
+ return {
277
+ ...survey,
278
+ surveyId: survey.surveyId || survey.id || null,
279
+ surveyType: survey.surveyType || inferredType,
280
+ type: survey.type || inferredType,
281
+ should_show:
282
+ survey.should_show ??
283
+ (survey.eligibility ? survey.eligibility.should_show : undefined),
284
+ reason:
285
+ survey.reason ||
286
+ (survey.eligibility ? survey.eligibility.reason : undefined),
287
+ title:
288
+ survey.title ||
289
+ survey.name ||
290
+ (firstPage ? firstPage.title : null),
291
+ description:
292
+ survey.description ||
293
+ (firstPage ? firstPage.description : null),
294
+ lowLabel:
295
+ survey.lowLabel ||
296
+ survey.low_label ||
297
+ ratingConfig.low_label ||
298
+ null,
299
+ highLabel:
300
+ survey.highLabel ||
301
+ survey.high_label ||
302
+ ratingConfig.high_label ||
303
+ null,
304
+ customQuestions: survey.customQuestions || survey.questions || [],
305
+ pages: this._normalizeSurveyPages(survey.pages || []),
306
+ };
307
+ }
308
+
309
+ _normalizeSurveyPages(pages = []) {
310
+ if (!Array.isArray(pages)) {
311
+ return [];
312
+ }
313
+
314
+ return pages
315
+ .map((page, index) => ({
316
+ id: page.id || `page_${index}`,
317
+ type: page.type || 'rating',
318
+ title: page.title || '',
319
+ description: page.description || '',
320
+ required: page.required !== false,
321
+ position: page.position ?? index,
322
+ ratingConfig: page.ratingConfig || page.rating_config || null,
323
+ multipleChoiceConfig:
324
+ page.multipleChoiceConfig || page.multiple_choice_config || null,
325
+ linkConfig: page.linkConfig || page.link_config || null,
326
+ afterThisPage: page.afterThisPage || page.after_this_page || null,
327
+ }))
328
+ .sort((a, b) => a.position - b.position);
329
+ }
330
+
331
+ _inferSurveyTypeFromPage(page) {
332
+ if (!page) return null;
333
+ const ratingConfig = page.rating_config || page.ratingConfig || {};
334
+ const scale = ratingConfig.scale;
335
+ const surveyType = ratingConfig.survey_type;
336
+ const title = (page.title || '').toLowerCase();
337
+
338
+ if (scale === 11 || surveyType === 'nps') {
339
+ return 'nps';
340
+ }
341
+
342
+ if (
343
+ surveyType === 'emoji' ||
344
+ title.includes('effort') ||
345
+ title.includes('easy')
346
+ ) {
347
+ return 'ces';
348
+ }
349
+
350
+ return 'csat';
351
+ }
352
+
232
353
  _getSurveyField(survey, fields) {
233
354
  for (const field of fields) {
234
355
  if (survey[field] !== undefined && survey[field] !== null) {
@@ -311,13 +311,73 @@ export const surveyStyles = `
311
311
  box-shadow: 0 0 0 3px var(--color-primary-light);
312
312
  }
313
313
 
314
+ .feedback-survey-progress {
315
+ font-size: var(--font-size-xs);
316
+ color: var(--color-text-tertiary);
317
+ margin-bottom: var(--spacing-3);
318
+ }
319
+
320
+ .feedback-survey-actions {
321
+ display: flex;
322
+ gap: var(--spacing-2);
323
+ margin-top: var(--spacing-3);
324
+ }
325
+
326
+ .feedback-survey-back {
327
+ padding: var(--spacing-3) var(--spacing-4);
328
+ border: 1px solid var(--color-border);
329
+ border-radius: var(--radius-md);
330
+ background: var(--color-surface);
331
+ color: var(--color-text-primary);
332
+ font-size: var(--font-size-base);
333
+ font-weight: var(--font-weight-medium);
334
+ cursor: pointer;
335
+ font-family: inherit;
336
+ }
337
+
338
+ .feedback-survey-page-choice-btn {
339
+ width: 100%;
340
+ padding: var(--spacing-3);
341
+ border: 1px solid var(--color-border);
342
+ border-radius: var(--radius-md);
343
+ background: var(--color-surface);
344
+ color: var(--color-text-primary);
345
+ text-align: left;
346
+ font-size: var(--font-size-base);
347
+ cursor: pointer;
348
+ margin-bottom: var(--spacing-2);
349
+ font-family: inherit;
350
+ }
351
+
352
+ .feedback-survey-page-choice-btn.selected {
353
+ background: var(--color-primary);
354
+ border-color: var(--color-primary);
355
+ color: var(--color-white);
356
+ }
357
+
358
+ .feedback-survey-page-textarea {
359
+ width: 100%;
360
+ min-height: 96px;
361
+ padding: var(--spacing-3);
362
+ border: 1px solid var(--color-border);
363
+ border-radius: var(--radius-md);
364
+ font-size: var(--font-size-base);
365
+ font-family: inherit;
366
+ box-sizing: border-box;
367
+ outline: none;
368
+ }
369
+
370
+ .feedback-survey-page-textarea:focus {
371
+ border-color: var(--color-primary);
372
+ box-shadow: 0 0 0 3px var(--color-primary-light);
373
+ }
374
+
314
375
  /* ========================================
315
376
  SUBMIT & FEEDBACK
316
377
  ======================================== */
317
378
 
318
379
  .feedback-survey-submit {
319
380
  width: 100%;
320
- margin-top: var(--spacing-3);
321
381
  padding: var(--spacing-3);
322
382
  background: var(--color-primary);
323
383
  color: var(--color-white);