@product7/feedback-sdk 1.5.4 → 1.5.7
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/dist/feedback-sdk.js +688 -230
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/core/FeedbackSDK.js +60 -30
- package/src/styles/changelog.js +32 -25
- package/src/styles/components.js +20 -12
- package/src/styles/feedback.js +1 -1
- package/src/styles/messenger-components.js +32 -18
- package/src/styles/survey.js +124 -26
- package/src/widgets/BaseWidget.js +11 -5
- package/src/widgets/ChangelogWidget.js +8 -3
- package/src/widgets/SurveyWidget.js +274 -72
- package/src/widgets/messenger/components/NavigationTabs.js +1 -6
- package/src/widgets/messenger/views/ChatView.js +6 -4
- package/src/widgets/messenger/views/ConversationsView.js +7 -5
- package/src/widgets/messenger/views/HelpView.js +112 -23
|
@@ -12,6 +12,25 @@ export class SurveyWidget extends BaseWidget {
|
|
|
12
12
|
description: options.description || null,
|
|
13
13
|
lowLabel: options.lowLabel || null,
|
|
14
14
|
highLabel: options.highLabel || null,
|
|
15
|
+
ratingScale: options.ratingScale || options.scale || null,
|
|
16
|
+
showFeedbackInput:
|
|
17
|
+
typeof options.showFeedbackInput === 'boolean'
|
|
18
|
+
? options.showFeedbackInput
|
|
19
|
+
: null,
|
|
20
|
+
showSubmitButton:
|
|
21
|
+
typeof options.showSubmitButton === 'boolean'
|
|
22
|
+
? options.showSubmitButton
|
|
23
|
+
: null,
|
|
24
|
+
autoSubmitOnSelect:
|
|
25
|
+
typeof options.autoSubmitOnSelect === 'boolean'
|
|
26
|
+
? options.autoSubmitOnSelect
|
|
27
|
+
: null,
|
|
28
|
+
showTitle:
|
|
29
|
+
typeof options.showTitle === 'boolean' ? options.showTitle : null,
|
|
30
|
+
showDescription:
|
|
31
|
+
typeof options.showDescription === 'boolean'
|
|
32
|
+
? options.showDescription
|
|
33
|
+
: null,
|
|
15
34
|
customQuestions: options.customQuestions || [],
|
|
16
35
|
pages: Array.isArray(options.pages) ? options.pages : [],
|
|
17
36
|
respondentId: options.respondentId || null,
|
|
@@ -26,6 +45,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
26
45
|
customAnswers: {},
|
|
27
46
|
pageAnswers: {},
|
|
28
47
|
currentPageIndex: 0,
|
|
48
|
+
isSubmitting: false,
|
|
29
49
|
isVisible: false,
|
|
30
50
|
};
|
|
31
51
|
}
|
|
@@ -61,6 +81,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
61
81
|
const config = this._getSurveyConfig();
|
|
62
82
|
const isMultiPage = this._isMultiPageSurvey();
|
|
63
83
|
const isLastPage = this._isLastPage();
|
|
84
|
+
const showFeedbackInput = this._shouldShowFeedbackInput();
|
|
85
|
+
const showActions = this._shouldShowActions();
|
|
86
|
+
const showTitle = this._shouldShowTitle(config);
|
|
87
|
+
const showDescription = this._shouldShowDescription(config);
|
|
64
88
|
const pageProgress = isMultiPage
|
|
65
89
|
? `Page ${this.surveyState.currentPageIndex + 1} of ${this.surveyOptions.pages.length}`
|
|
66
90
|
: '';
|
|
@@ -80,21 +104,33 @@ export class SurveyWidget extends BaseWidget {
|
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
this.surveyElement = document.createElement('div');
|
|
83
|
-
this.surveyElement.className = `feedback-survey feedback-survey-${this.surveyOptions.position}
|
|
107
|
+
this.surveyElement.className = `feedback-survey feedback-survey-${this.surveyOptions.position}${
|
|
108
|
+
showDescription && !showTitle
|
|
109
|
+
? ' feedback-survey-description-primary'
|
|
110
|
+
: ''
|
|
111
|
+
}`;
|
|
84
112
|
|
|
85
113
|
this.surveyElement.innerHTML = `
|
|
86
|
-
<button class="feedback-survey-close"
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
<button class="feedback-survey-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="200" y1="56" x2="56" y2="200" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="200" y1="200" x2="56" y2="56" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg></button>
|
|
115
|
+
${showTitle ? `<h3 class="feedback-survey-title">${config.title}</h3>` : ''}
|
|
116
|
+
${showDescription ? `<p class="feedback-survey-description">${config.description}</p>` : ''}
|
|
89
117
|
${isMultiPage ? `<div class="feedback-survey-progress">${pageProgress}</div>` : ''}
|
|
90
118
|
<div class="feedback-survey-content">${config.html}</div>
|
|
91
|
-
|
|
119
|
+
${
|
|
120
|
+
showFeedbackInput
|
|
121
|
+
? `<div class="feedback-survey-feedback">
|
|
92
122
|
<textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)">${this.surveyState.feedback || ''}</textarea>
|
|
93
|
-
</div
|
|
94
|
-
|
|
123
|
+
</div>`
|
|
124
|
+
: ''
|
|
125
|
+
}
|
|
126
|
+
${
|
|
127
|
+
showActions
|
|
128
|
+
? `<div class="feedback-survey-actions">
|
|
95
129
|
${backButton}
|
|
96
130
|
<button class="feedback-survey-submit">${submitLabel}</button>
|
|
97
|
-
</div
|
|
131
|
+
</div>`
|
|
132
|
+
: ''
|
|
133
|
+
}
|
|
98
134
|
`;
|
|
99
135
|
|
|
100
136
|
document.body.appendChild(this.surveyElement);
|
|
@@ -114,37 +150,62 @@ export class SurveyWidget extends BaseWidget {
|
|
|
114
150
|
return this._getCurrentPageConfig();
|
|
115
151
|
}
|
|
116
152
|
|
|
153
|
+
const npsScale = this._getNPSScale();
|
|
154
|
+
const npsUsesSegmentedScale = npsScale.values.length <= 7;
|
|
155
|
+
const npsContainerClass = npsUsesSegmentedScale
|
|
156
|
+
? 'feedback-survey-ces feedback-survey-rating-scale'
|
|
157
|
+
: 'feedback-survey-nps';
|
|
158
|
+
const npsButtonClass = npsUsesSegmentedScale
|
|
159
|
+
? 'feedback-survey-nps-btn feedback-survey-ces-btn feedback-survey-rating-scale-btn'
|
|
160
|
+
: 'feedback-survey-nps-btn';
|
|
161
|
+
const npsLowLabel =
|
|
162
|
+
this.surveyOptions.lowLabel ||
|
|
163
|
+
(npsScale.start === 0 ? 'Not likely' : 'Strongly Disagree');
|
|
164
|
+
const npsHighLabel =
|
|
165
|
+
this.surveyOptions.highLabel ||
|
|
166
|
+
(npsScale.start === 0 ? 'Very likely' : 'Strongly Agree');
|
|
167
|
+
const npsPrompt =
|
|
168
|
+
this.surveyOptions.description ||
|
|
169
|
+
this.surveyOptions.title ||
|
|
170
|
+
'How likely are you to recommend us?';
|
|
171
|
+
const csatPrompt =
|
|
172
|
+
this.surveyOptions.description ||
|
|
173
|
+
this.surveyOptions.title ||
|
|
174
|
+
'How satisfied are you?';
|
|
175
|
+
const cesPrompt =
|
|
176
|
+
this.surveyOptions.description ||
|
|
177
|
+
this.surveyOptions.title ||
|
|
178
|
+
'How easy was it?';
|
|
179
|
+
|
|
117
180
|
const configs = {
|
|
118
181
|
nps: {
|
|
119
|
-
title:
|
|
120
|
-
|
|
121
|
-
description:
|
|
122
|
-
this.surveyOptions.description ||
|
|
123
|
-
'On a scale of 0-10, how likely are you to recommend our product to a friend or colleague?',
|
|
182
|
+
title: this.surveyOptions.title || '',
|
|
183
|
+
description: npsPrompt,
|
|
124
184
|
html: `
|
|
125
|
-
<div class="
|
|
126
|
-
${
|
|
185
|
+
<div class="${npsContainerClass}">
|
|
186
|
+
${npsScale.values
|
|
127
187
|
.map(
|
|
128
188
|
(n) => `
|
|
129
|
-
<button class="
|
|
189
|
+
<button class="${npsButtonClass}" data-score="${n}">${n}</button>
|
|
130
190
|
`
|
|
131
191
|
)
|
|
132
192
|
.join('')}
|
|
133
193
|
</div>
|
|
134
|
-
|
|
135
|
-
<span>${this.surveyOptions.lowLabel || 'Not likely'}</span>
|
|
136
|
-
<span>${this.surveyOptions.highLabel || 'Very likely'}</span>
|
|
137
|
-
</div>
|
|
194
|
+
${this._renderScaleLabels(npsLowLabel, npsHighLabel)}
|
|
138
195
|
`,
|
|
139
196
|
},
|
|
140
197
|
csat: {
|
|
141
|
-
title: this.surveyOptions.title || '
|
|
142
|
-
description:
|
|
143
|
-
this.surveyOptions.description ||
|
|
144
|
-
'How would you rate your overall satisfaction with our product?',
|
|
198
|
+
title: this.surveyOptions.title || '',
|
|
199
|
+
description: csatPrompt,
|
|
145
200
|
html: `
|
|
146
201
|
<div class="feedback-survey-csat">
|
|
147
|
-
${[
|
|
202
|
+
${[
|
|
203
|
+
'\uD83D\uDE1E',
|
|
204
|
+
'\uD83D\uDE15',
|
|
205
|
+
'\uD83D\uDE10',
|
|
206
|
+
'\uD83D\uDE42',
|
|
207
|
+
'\uD83D\uDE04',
|
|
208
|
+
]
|
|
148
209
|
.map(
|
|
149
210
|
(emoji, i) => `
|
|
150
211
|
<button class="feedback-survey-csat-btn" data-score="${i + 1}">${emoji}</button>
|
|
@@ -152,17 +213,15 @@ export class SurveyWidget extends BaseWidget {
|
|
|
152
213
|
)
|
|
153
214
|
.join('')}
|
|
154
215
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
216
|
+
${this._renderScaleLabels(
|
|
217
|
+
this.surveyOptions.lowLabel || 'Very dissatisfied',
|
|
218
|
+
this.surveyOptions.highLabel || 'Very satisfied'
|
|
219
|
+
)}
|
|
159
220
|
`,
|
|
160
221
|
},
|
|
161
222
|
ces: {
|
|
162
|
-
title: this.surveyOptions.title || '
|
|
163
|
-
description:
|
|
164
|
-
this.surveyOptions.description ||
|
|
165
|
-
'How easy was it to accomplish your task today?',
|
|
223
|
+
title: this.surveyOptions.title || '',
|
|
224
|
+
description: cesPrompt,
|
|
166
225
|
html: `
|
|
167
226
|
<div class="feedback-survey-ces">
|
|
168
227
|
${['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy']
|
|
@@ -187,6 +246,91 @@ export class SurveyWidget extends BaseWidget {
|
|
|
187
246
|
return configs[this.surveyOptions.surveyType] || configs.nps;
|
|
188
247
|
}
|
|
189
248
|
|
|
249
|
+
_getNPSScale() {
|
|
250
|
+
const rawScale = Number(this.surveyOptions.ratingScale);
|
|
251
|
+
const scale = Number.isFinite(rawScale) && rawScale >= 2 ? rawScale : 5;
|
|
252
|
+
const start = scale === 11 ? 0 : 1;
|
|
253
|
+
return {
|
|
254
|
+
scale,
|
|
255
|
+
start,
|
|
256
|
+
values: Array.from({ length: scale }, (_, index) => start + index),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
_renderScaleLabels(lowLabel, highLabel) {
|
|
261
|
+
const low = lowLabel || '';
|
|
262
|
+
const high = highLabel || '';
|
|
263
|
+
if (!low && !high) {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
return `
|
|
267
|
+
<div class="feedback-survey-labels">
|
|
268
|
+
<span>${low}</span>
|
|
269
|
+
<span>${high}</span>
|
|
270
|
+
</div>
|
|
271
|
+
`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
_isRatingSurveyType(type = this.surveyOptions.surveyType) {
|
|
275
|
+
return type === 'nps' || type === 'csat' || type === 'ces';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_shouldShowTitle(config) {
|
|
279
|
+
if (!config || !config.title) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (typeof this.surveyOptions.showTitle === 'boolean') {
|
|
283
|
+
return this.surveyOptions.showTitle;
|
|
284
|
+
}
|
|
285
|
+
if (!this._isMultiPageSurvey() && this._isRatingSurveyType()) {
|
|
286
|
+
return !config.description;
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
_shouldShowDescription(config) {
|
|
292
|
+
if (typeof this.surveyOptions.showDescription === 'boolean') {
|
|
293
|
+
return this.surveyOptions.showDescription;
|
|
294
|
+
}
|
|
295
|
+
if (!this._isMultiPageSurvey() && this._isRatingSurveyType()) {
|
|
296
|
+
return Boolean(config && (config.description || config.title));
|
|
297
|
+
}
|
|
298
|
+
return Boolean(config && config.description);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
_shouldShowFeedbackInput() {
|
|
302
|
+
if (this._isMultiPageSurvey()) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
if (typeof this.surveyOptions.showFeedbackInput === 'boolean') {
|
|
306
|
+
return this.surveyOptions.showFeedbackInput;
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
_shouldAutoSubmitOnSelect() {
|
|
312
|
+
if (typeof this.surveyOptions.autoSubmitOnSelect === 'boolean') {
|
|
313
|
+
return this.surveyOptions.autoSubmitOnSelect;
|
|
314
|
+
}
|
|
315
|
+
if (this._isMultiPageSurvey()) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
if (this.surveyOptions.showSubmitButton === true) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
return this._isRatingSurveyType() && !this._shouldShowFeedbackInput();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
_shouldShowActions() {
|
|
325
|
+
if (this._isMultiPageSurvey()) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
if (typeof this.surveyOptions.showSubmitButton === 'boolean') {
|
|
329
|
+
return this.surveyOptions.showSubmitButton;
|
|
330
|
+
}
|
|
331
|
+
return !this._shouldAutoSubmitOnSelect();
|
|
332
|
+
}
|
|
333
|
+
|
|
190
334
|
_isMultiPageSurvey() {
|
|
191
335
|
return (
|
|
192
336
|
Array.isArray(this.surveyOptions.pages) &&
|
|
@@ -201,7 +345,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
201
345
|
|
|
202
346
|
_isLastPage() {
|
|
203
347
|
if (!this._isMultiPageSurvey()) return true;
|
|
204
|
-
return
|
|
348
|
+
return (
|
|
349
|
+
this.surveyState.currentPageIndex >= this.surveyOptions.pages.length - 1
|
|
350
|
+
);
|
|
205
351
|
}
|
|
206
352
|
|
|
207
353
|
_getCurrentPageConfig() {
|
|
@@ -212,10 +358,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
212
358
|
|
|
213
359
|
return {
|
|
214
360
|
title: page.title || this.surveyOptions.title || 'Quick Feedback',
|
|
215
|
-
description:
|
|
216
|
-
page.description ||
|
|
217
|
-
this.surveyOptions.description ||
|
|
218
|
-
'Help us improve by answering this question.',
|
|
361
|
+
description: page.description || this.surveyOptions.description || '',
|
|
219
362
|
html: this._renderSurveyPage(page),
|
|
220
363
|
};
|
|
221
364
|
}
|
|
@@ -223,9 +366,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
223
366
|
_getFallbackSurveyConfig() {
|
|
224
367
|
return {
|
|
225
368
|
title: this.surveyOptions.title || 'Quick Feedback',
|
|
226
|
-
description:
|
|
227
|
-
this.surveyOptions.description ||
|
|
228
|
-
'Help us improve by answering a few questions.',
|
|
369
|
+
description: this.surveyOptions.description || '',
|
|
229
370
|
html: this._renderCustomQuestions(),
|
|
230
371
|
};
|
|
231
372
|
}
|
|
@@ -247,24 +388,48 @@ export class SurveyWidget extends BaseWidget {
|
|
|
247
388
|
const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
|
|
248
389
|
const config = page.ratingConfig || page.rating_config || {};
|
|
249
390
|
const scale = Number(config.scale) || 5;
|
|
250
|
-
const ratingType =
|
|
391
|
+
const ratingType =
|
|
392
|
+
config.survey_type || this.surveyOptions.surveyType || 'csat';
|
|
251
393
|
const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
|
|
252
394
|
const currentRating = pageAnswer.rating;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
395
|
+
const defaultLowLabel =
|
|
396
|
+
ratingType === 'nps'
|
|
397
|
+
? scale === 11
|
|
398
|
+
? 'Not likely'
|
|
399
|
+
: 'Strongly Disagree'
|
|
400
|
+
: '';
|
|
401
|
+
const defaultHighLabel =
|
|
402
|
+
ratingType === 'nps'
|
|
403
|
+
? scale === 11
|
|
404
|
+
? 'Very likely'
|
|
405
|
+
: 'Strongly Agree'
|
|
406
|
+
: '';
|
|
407
|
+
const lowLabel =
|
|
408
|
+
config.low_label || this.surveyOptions.lowLabel || defaultLowLabel;
|
|
409
|
+
const highLabel =
|
|
410
|
+
config.high_label || this.surveyOptions.highLabel || defaultHighLabel;
|
|
411
|
+
const labels = this._renderScaleLabels(lowLabel, highLabel);
|
|
412
|
+
|
|
413
|
+
if (ratingType === 'nps') {
|
|
414
|
+
const npsScale = Number.isFinite(scale) && scale >= 2 ? scale : 5;
|
|
415
|
+
const start = npsScale === 11 ? 0 : 1;
|
|
416
|
+
const values = Array.from(
|
|
417
|
+
{ length: npsScale },
|
|
418
|
+
(_, index) => start + index
|
|
419
|
+
);
|
|
420
|
+
const usesSegmentedScale = values.length <= 7;
|
|
421
|
+
const containerClass = usesSegmentedScale
|
|
422
|
+
? 'feedback-survey-ces feedback-survey-rating-scale'
|
|
423
|
+
: 'feedback-survey-nps';
|
|
424
|
+
const buttonClass = usesSegmentedScale
|
|
425
|
+
? 'feedback-survey-page-rating-btn feedback-survey-nps-btn feedback-survey-ces-btn feedback-survey-rating-scale-btn'
|
|
426
|
+
: 'feedback-survey-page-rating-btn feedback-survey-nps-btn';
|
|
262
427
|
return `
|
|
263
|
-
<div class="
|
|
264
|
-
${
|
|
428
|
+
<div class="${containerClass}" data-page-id="${pageId}">
|
|
429
|
+
${values
|
|
265
430
|
.map((n) => {
|
|
266
431
|
const selected = currentRating === n ? ' selected' : '';
|
|
267
|
-
return `<button class="
|
|
432
|
+
return `<button class="${buttonClass}${selected}" data-page-id="${pageId}" data-score="${n}">${n}</button>`;
|
|
268
433
|
})
|
|
269
434
|
.join('')}
|
|
270
435
|
</div>
|
|
@@ -295,12 +460,12 @@ export class SurveyWidget extends BaseWidget {
|
|
|
295
460
|
}
|
|
296
461
|
|
|
297
462
|
return `
|
|
298
|
-
<div class="feedback-survey-ces" data-page-id="${pageId}">
|
|
463
|
+
<div class="feedback-survey-ces feedback-survey-rating-scale" data-page-id="${pageId}">
|
|
299
464
|
${[...Array(scale).keys()]
|
|
300
465
|
.map((i) => {
|
|
301
466
|
const score = i + 1;
|
|
302
467
|
const selected = currentRating === score ? ' selected' : '';
|
|
303
|
-
return `<button class="feedback-survey-page-rating-btn feedback-survey-ces-btn${selected}" data-page-id="${pageId}" data-score="${score}">${score}</button>`;
|
|
468
|
+
return `<button class="feedback-survey-page-rating-btn feedback-survey-ces-btn feedback-survey-rating-scale-btn${selected}" data-page-id="${pageId}" data-score="${score}">${score}</button>`;
|
|
304
469
|
})
|
|
305
470
|
.join('')}
|
|
306
471
|
</div>
|
|
@@ -310,9 +475,11 @@ export class SurveyWidget extends BaseWidget {
|
|
|
310
475
|
|
|
311
476
|
_renderMultipleChoicePage(page) {
|
|
312
477
|
const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
|
|
313
|
-
const config =
|
|
478
|
+
const config =
|
|
479
|
+
page.multipleChoiceConfig || page.multiple_choice_config || {};
|
|
314
480
|
const options = Array.isArray(config.options) ? config.options : [];
|
|
315
|
-
const allowMultiple =
|
|
481
|
+
const allowMultiple =
|
|
482
|
+
config.allow_multiple === true || config.multiple === true;
|
|
316
483
|
const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
|
|
317
484
|
const selectedValues = Array.isArray(pageAnswer.values)
|
|
318
485
|
? pageAnswer.values
|
|
@@ -417,7 +584,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
417
584
|
const submitBtn = this.surveyElement.querySelector(
|
|
418
585
|
'.feedback-survey-submit'
|
|
419
586
|
);
|
|
420
|
-
|
|
587
|
+
if (submitBtn) {
|
|
588
|
+
submitBtn.addEventListener('click', () => this._handleSubmit());
|
|
589
|
+
}
|
|
421
590
|
|
|
422
591
|
const backBtn = this.surveyElement.querySelector('.feedback-survey-back');
|
|
423
592
|
if (backBtn) {
|
|
@@ -427,9 +596,11 @@ export class SurveyWidget extends BaseWidget {
|
|
|
427
596
|
const textarea = this.surveyElement.querySelector(
|
|
428
597
|
'.feedback-survey-textarea'
|
|
429
598
|
);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
599
|
+
if (textarea) {
|
|
600
|
+
textarea.addEventListener('input', (e) => {
|
|
601
|
+
this.surveyState.feedback = e.target.value;
|
|
602
|
+
});
|
|
603
|
+
}
|
|
433
604
|
|
|
434
605
|
this._attachTypeSpecificEvents();
|
|
435
606
|
|
|
@@ -601,6 +772,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
601
772
|
btn.classList.remove('selected');
|
|
602
773
|
}
|
|
603
774
|
});
|
|
775
|
+
this._maybeAutoSubmit();
|
|
604
776
|
}
|
|
605
777
|
|
|
606
778
|
_selectCSAT(score) {
|
|
@@ -615,6 +787,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
615
787
|
btn.classList.remove('selected');
|
|
616
788
|
}
|
|
617
789
|
});
|
|
790
|
+
this._maybeAutoSubmit();
|
|
618
791
|
}
|
|
619
792
|
|
|
620
793
|
_selectCES(score) {
|
|
@@ -629,6 +802,14 @@ export class SurveyWidget extends BaseWidget {
|
|
|
629
802
|
btn.classList.remove('selected');
|
|
630
803
|
}
|
|
631
804
|
});
|
|
805
|
+
this._maybeAutoSubmit();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
_maybeAutoSubmit() {
|
|
809
|
+
if (!this._shouldAutoSubmitOnSelect()) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
this._handleSubmit();
|
|
632
813
|
}
|
|
633
814
|
|
|
634
815
|
_selectFrequency(freq) {
|
|
@@ -646,6 +827,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
646
827
|
|
|
647
828
|
async _handleSubmit() {
|
|
648
829
|
const type = this.surveyOptions.surveyType;
|
|
830
|
+
if (this.surveyState.isSubmitting) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
649
833
|
|
|
650
834
|
if (this._isMultiPageSurvey()) {
|
|
651
835
|
if (!this._validateCurrentPage()) {
|
|
@@ -653,7 +837,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
653
837
|
}
|
|
654
838
|
|
|
655
839
|
const nextPageIndex = this._getNextPageIndex();
|
|
656
|
-
if (
|
|
840
|
+
if (
|
|
841
|
+
nextPageIndex !== -1 &&
|
|
842
|
+
nextPageIndex !== this.surveyState.currentPageIndex
|
|
843
|
+
) {
|
|
657
844
|
this.surveyState.currentPageIndex = nextPageIndex;
|
|
658
845
|
this._renderSurvey();
|
|
659
846
|
return;
|
|
@@ -669,6 +856,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
669
856
|
return;
|
|
670
857
|
}
|
|
671
858
|
|
|
859
|
+
this.surveyState.isSubmitting = true;
|
|
860
|
+
|
|
672
861
|
const respondent = this._getRespondentContext();
|
|
673
862
|
const normalizedPageAnswers = this._normalizePageAnswersForSubmit();
|
|
674
863
|
const mergedAnswers = {
|
|
@@ -695,16 +884,18 @@ export class SurveyWidget extends BaseWidget {
|
|
|
695
884
|
timestamp: new Date().toISOString(),
|
|
696
885
|
};
|
|
697
886
|
|
|
698
|
-
if (this.surveyOptions.onSubmit) {
|
|
699
|
-
this.surveyOptions.onSubmit(response);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
887
|
try {
|
|
888
|
+
if (this.surveyOptions.onSubmit) {
|
|
889
|
+
this.surveyOptions.onSubmit(response);
|
|
890
|
+
}
|
|
891
|
+
|
|
703
892
|
const surveyId =
|
|
704
893
|
this.surveyOptions.surveyId || `local_${type}_${Date.now()}`;
|
|
705
894
|
await this.apiService.submitSurveyResponse(surveyId, responseData);
|
|
706
895
|
} catch (error) {
|
|
707
896
|
console.error('[SurveyWidget] Failed to submit survey:', error);
|
|
897
|
+
} finally {
|
|
898
|
+
this.surveyState.isSubmitting = false;
|
|
708
899
|
}
|
|
709
900
|
|
|
710
901
|
this.sdk.eventBus.emit('survey:submitted', { widget: this, response });
|
|
@@ -812,7 +1003,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
812
1003
|
}
|
|
813
1004
|
|
|
814
1005
|
for (const page of this.surveyOptions.pages) {
|
|
815
|
-
const pageId =
|
|
1006
|
+
const pageId =
|
|
1007
|
+
page.id || `page_${this.surveyOptions.pages.indexOf(page)}`;
|
|
816
1008
|
const answer = this.surveyState.pageAnswers[pageId];
|
|
817
1009
|
if (answer && typeof answer.rating === 'number') {
|
|
818
1010
|
return answer.rating;
|
|
@@ -824,7 +1016,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
824
1016
|
|
|
825
1017
|
_normalizePageAnswersForSubmit() {
|
|
826
1018
|
const output = {};
|
|
827
|
-
for (const [pageId, answer] of Object.entries(
|
|
1019
|
+
for (const [pageId, answer] of Object.entries(
|
|
1020
|
+
this.surveyState.pageAnswers
|
|
1021
|
+
)) {
|
|
828
1022
|
if (answer == null) continue;
|
|
829
1023
|
if (Array.isArray(answer.values) && answer.values.length > 0) {
|
|
830
1024
|
output[pageId] = answer.values;
|
|
@@ -851,8 +1045,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
851
1045
|
? this.sdk.getUserContext() || {}
|
|
852
1046
|
: {};
|
|
853
1047
|
const apiUserContext =
|
|
854
|
-
this.apiService &&
|
|
855
|
-
typeof this.apiService.getUserContext === 'function'
|
|
1048
|
+
this.apiService && typeof this.apiService.getUserContext === 'function'
|
|
856
1049
|
? this.apiService.getUserContext() || {}
|
|
857
1050
|
: {};
|
|
858
1051
|
const localUserContext = this.options.userContext || {};
|
|
@@ -900,7 +1093,15 @@ export class SurveyWidget extends BaseWidget {
|
|
|
900
1093
|
const submitBtn = this.surveyElement.querySelector(
|
|
901
1094
|
'.feedback-survey-submit'
|
|
902
1095
|
);
|
|
903
|
-
|
|
1096
|
+
const actions = this.surveyElement.querySelector(
|
|
1097
|
+
'.feedback-survey-actions'
|
|
1098
|
+
);
|
|
1099
|
+
const anchor = submitBtn || actions;
|
|
1100
|
+
if (anchor && anchor.parentNode) {
|
|
1101
|
+
anchor.parentNode.insertBefore(error, anchor);
|
|
1102
|
+
} else {
|
|
1103
|
+
this.surveyElement.appendChild(error);
|
|
1104
|
+
}
|
|
904
1105
|
|
|
905
1106
|
setTimeout(() => error.remove(), 3000);
|
|
906
1107
|
}
|
|
@@ -963,6 +1164,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
963
1164
|
customAnswers: {},
|
|
964
1165
|
pageAnswers: {},
|
|
965
1166
|
currentPageIndex: 0,
|
|
1167
|
+
isSubmitting: false,
|
|
966
1168
|
isVisible: false,
|
|
967
1169
|
};
|
|
968
1170
|
}
|
|
@@ -124,12 +124,7 @@ export class NavigationTabs {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
_getMessagesIcon() {
|
|
127
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="
|
|
128
|
-
<circle cx="128" cy="128" r="12"/>
|
|
129
|
-
<circle cx="84" cy="128" r="12"/>
|
|
130
|
-
<circle cx="172" cy="128" r="12"/>
|
|
131
|
-
<path d="M79.93,211.11a96,96,0,1,0-35-35h0L32.42,213.46a8,8,0,0,0,10.12,10.12l37.39-12.47Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
132
|
-
</svg>`;
|
|
127
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M71.58,144,32,176V48a8,8,0,0,1,8-8H168a8,8,0,0,1,8,8v88a8,8,0,0,1-8,8Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M80,144v40a8,8,0,0,0,8,8h96.42L224,224V96a8,8,0,0,0-8-8H176" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>`;
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
_getHelpIcon() {
|
|
@@ -116,10 +116,12 @@ export class ChatView {
|
|
|
116
116
|
<div class="messenger-compose-attachments-preview"></div>
|
|
117
117
|
|
|
118
118
|
<div class="messenger-chat-compose">
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
<button class="sdk-btn-icon messenger-compose-attach" aria-label="Attach file">
|
|
120
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="20" height="20">
|
|
121
|
+
<rect width="256" height="256" fill="none"/>
|
|
122
|
+
<path d="M160,80,76.69,164.69a16,16,0,0,0,22.63,22.62L198.63,86.63a32,32,0,0,0-45.26-45.26L54.06,142.06a48,48,0,0,0,67.88,67.88L204,128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
123
|
+
</svg>
|
|
124
|
+
</button> <div class="messenger-compose-input-wrapper">
|
|
123
125
|
<textarea class="messenger-compose-input" placeholder="${placeholder}" rows="1"></textarea>
|
|
124
126
|
</div>
|
|
125
127
|
<button class="messenger-compose-send" aria-label="Send" disabled>
|
|
@@ -69,11 +69,13 @@ export class ConversationsView {
|
|
|
69
69
|
this.element.innerHTML = `
|
|
70
70
|
<div class="messenger-conversations-header">
|
|
71
71
|
<h2>Messages</h2>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
<button class="sdk-close-btn" aria-label="Close">
|
|
73
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="18" height="18">
|
|
74
|
+
<rect width="256" height="256" fill="none"/>
|
|
75
|
+
<line x1="200" y1="56" x2="56" y2="200" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
76
|
+
<line x1="200" y1="200" x2="56" y2="56" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
77
79
|
</div>
|
|
78
80
|
|
|
79
81
|
<div class="messenger-conversations-body">
|