@product7/feedback-sdk 1.1.4 → 1.1.6
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 +20 -16
- package/dist/README.md +20 -16
- package/dist/feedback-sdk.js +392 -410
- 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/APIService.js +14 -11
- package/src/core/FeedbackSDK.js +28 -11
- package/src/docs/api.md +46 -12
- package/src/docs/example.md +33 -31
- package/src/docs/installation.md +50 -41
- package/src/index.js +5 -2
- package/src/styles/styles.js +82 -226
- package/src/widgets/BaseWidget.js +37 -31
- package/src/widgets/ButtonWidget.js +10 -8
- package/src/widgets/SurveyWidget.js +221 -126
- package/types/index.d.ts +67 -62
|
@@ -40,7 +40,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
40
40
|
show() {
|
|
41
41
|
this._renderSurvey();
|
|
42
42
|
this.surveyState.isVisible = true;
|
|
43
|
-
this.sdk.eventBus.emit('survey:shown', {
|
|
43
|
+
this.sdk.eventBus.emit('survey:shown', {
|
|
44
|
+
widget: this,
|
|
45
|
+
type: this.surveyOptions.surveyType,
|
|
46
|
+
});
|
|
44
47
|
return this;
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -55,16 +58,19 @@ export class SurveyWidget extends BaseWidget {
|
|
|
55
58
|
|
|
56
59
|
const config = this._getSurveyConfig();
|
|
57
60
|
const positionStyles = this._getPositionStyles();
|
|
58
|
-
const themeStyles =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const themeStyles =
|
|
62
|
+
this.surveyOptions.theme === 'dark'
|
|
63
|
+
? 'background: #1a1a1a; color: #fff;'
|
|
64
|
+
: 'background: #fff; color: #1d1d1f;';
|
|
61
65
|
|
|
62
66
|
// Create backdrop for center position
|
|
63
67
|
if (this.surveyOptions.position === 'center') {
|
|
64
68
|
this.backdropElement = document.createElement('div');
|
|
65
69
|
this.backdropElement.className = 'feedback-survey-backdrop';
|
|
66
70
|
document.body.appendChild(this.backdropElement);
|
|
67
|
-
this.backdropElement.addEventListener('click', () =>
|
|
71
|
+
this.backdropElement.addEventListener('click', () =>
|
|
72
|
+
this._handleDismiss()
|
|
73
|
+
);
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
this.surveyElement = document.createElement('div');
|
|
@@ -88,67 +94,92 @@ export class SurveyWidget extends BaseWidget {
|
|
|
88
94
|
// Animate in
|
|
89
95
|
requestAnimationFrame(() => {
|
|
90
96
|
this.surveyElement.style.opacity = '1';
|
|
91
|
-
this.surveyElement.style.transform =
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
this.surveyElement.style.transform =
|
|
98
|
+
this.surveyOptions.position === 'center'
|
|
99
|
+
? 'translate(-50%, -50%) scale(1)'
|
|
100
|
+
: 'translateY(0)';
|
|
94
101
|
});
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
_getSurveyConfig() {
|
|
98
105
|
const configs = {
|
|
99
106
|
nps: {
|
|
100
|
-
title:
|
|
101
|
-
|
|
107
|
+
title:
|
|
108
|
+
this.surveyOptions.title || 'How likely are you to recommend us?',
|
|
109
|
+
description:
|
|
110
|
+
this.surveyOptions.description ||
|
|
111
|
+
'On a scale of 0-10, how likely are you to recommend our product to a friend or colleague?',
|
|
102
112
|
html: `
|
|
103
113
|
<div class="feedback-survey-nps" style="display: flex; justify-content: space-between; gap: 4px;">
|
|
104
|
-
${[...Array(11).keys()]
|
|
114
|
+
${[...Array(11).keys()]
|
|
115
|
+
.map(
|
|
116
|
+
(n) => `
|
|
105
117
|
<button class="feedback-survey-nps-btn" data-score="${n}" style="width: 28px; height: 36px; border: 1px solid ${this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7'}; border-radius: 6px; background: ${this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa'}; cursor: pointer; font-size: 12px; color: ${this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f'}; transition: all 0.15s;">${n}</button>
|
|
106
|
-
`
|
|
118
|
+
`
|
|
119
|
+
)
|
|
120
|
+
.join('')}
|
|
107
121
|
</div>
|
|
108
122
|
<div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: ${this.surveyOptions.theme === 'dark' ? '#888' : '#86868b'};">
|
|
109
123
|
<span>${this.surveyOptions.lowLabel || 'Not likely'}</span>
|
|
110
124
|
<span>${this.surveyOptions.highLabel || 'Very likely'}</span>
|
|
111
125
|
</div>
|
|
112
|
-
|
|
126
|
+
`,
|
|
113
127
|
},
|
|
114
128
|
csat: {
|
|
115
129
|
title: this.surveyOptions.title || 'How satisfied are you?',
|
|
116
|
-
description:
|
|
130
|
+
description:
|
|
131
|
+
this.surveyOptions.description ||
|
|
132
|
+
'How would you rate your overall satisfaction with our product?',
|
|
117
133
|
html: `
|
|
118
134
|
<div class="feedback-survey-csat" style="display: flex; justify-content: center; gap: 16px;">
|
|
119
|
-
${['😞', '😕', '😐', '🙂', '😄']
|
|
135
|
+
${['😞', '😕', '😐', '🙂', '😄']
|
|
136
|
+
.map(
|
|
137
|
+
(emoji, i) => `
|
|
120
138
|
<button class="feedback-survey-csat-btn" data-score="${i + 1}" style="background: none; border: none; cursor: pointer; font-size: 36px; transition: transform 0.15s; padding: 8px;">${emoji}</button>
|
|
121
|
-
`
|
|
139
|
+
`
|
|
140
|
+
)
|
|
141
|
+
.join('')}
|
|
122
142
|
</div>
|
|
123
143
|
<div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: ${this.surveyOptions.theme === 'dark' ? '#888' : '#86868b'};">
|
|
124
144
|
<span>${this.surveyOptions.lowLabel || 'Very dissatisfied'}</span>
|
|
125
145
|
<span>${this.surveyOptions.highLabel || 'Very satisfied'}</span>
|
|
126
146
|
</div>
|
|
127
|
-
|
|
147
|
+
`,
|
|
128
148
|
},
|
|
129
149
|
ces: {
|
|
130
150
|
title: this.surveyOptions.title || 'How easy was it?',
|
|
131
|
-
description:
|
|
151
|
+
description:
|
|
152
|
+
this.surveyOptions.description ||
|
|
153
|
+
'How easy was it to accomplish your task today?',
|
|
132
154
|
html: `
|
|
133
155
|
<div class="feedback-survey-ces" style="display: flex; justify-content: space-between; gap: 8px;">
|
|
134
|
-
${['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy']
|
|
156
|
+
${['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy']
|
|
157
|
+
.map(
|
|
158
|
+
(label, i) => `
|
|
135
159
|
<button class="feedback-survey-ces-btn" data-score="${i + 1}" style="flex: 1; padding: 12px 8px; border: 1px solid ${this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7'}; border-radius: 8px; background: ${this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa'}; cursor: pointer; font-size: 11px; color: ${this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f'}; transition: all 0.15s;">${label}</button>
|
|
136
|
-
`
|
|
160
|
+
`
|
|
161
|
+
)
|
|
162
|
+
.join('')}
|
|
137
163
|
</div>
|
|
138
|
-
|
|
164
|
+
`,
|
|
139
165
|
},
|
|
140
166
|
custom: {
|
|
141
167
|
title: this.surveyOptions.title || 'Quick Feedback',
|
|
142
|
-
description:
|
|
143
|
-
|
|
144
|
-
|
|
168
|
+
description:
|
|
169
|
+
this.surveyOptions.description ||
|
|
170
|
+
'Help us improve by answering a few questions.',
|
|
171
|
+
html: this._renderCustomQuestions(),
|
|
172
|
+
},
|
|
145
173
|
};
|
|
146
174
|
|
|
147
175
|
return configs[this.surveyOptions.surveyType] || configs.nps;
|
|
148
176
|
}
|
|
149
177
|
|
|
150
178
|
_renderCustomQuestions() {
|
|
151
|
-
if (
|
|
179
|
+
if (
|
|
180
|
+
!this.surveyOptions.customQuestions ||
|
|
181
|
+
this.surveyOptions.customQuestions.length === 0
|
|
182
|
+
) {
|
|
152
183
|
// Default custom questions
|
|
153
184
|
return `
|
|
154
185
|
<div style="margin-bottom: 16px;">
|
|
@@ -164,20 +195,28 @@ export class SurveyWidget extends BaseWidget {
|
|
|
164
195
|
<div>
|
|
165
196
|
<label style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500;">How often do you use it?</label>
|
|
166
197
|
<div class="feedback-survey-frequency" style="display: flex; gap: 8px;">
|
|
167
|
-
${['Daily', 'Weekly', 'Monthly', 'Rarely']
|
|
198
|
+
${['Daily', 'Weekly', 'Monthly', 'Rarely']
|
|
199
|
+
.map(
|
|
200
|
+
(freq) => `
|
|
168
201
|
<button class="feedback-survey-freq-btn" data-freq="${freq}" style="flex: 1; padding: 10px; border: 1px solid ${this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7'}; border-radius: 8px; background: ${this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa'}; cursor: pointer; font-size: 12px; color: ${this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f'}; transition: all 0.15s;">${freq}</button>
|
|
169
|
-
`
|
|
202
|
+
`
|
|
203
|
+
)
|
|
204
|
+
.join('')}
|
|
170
205
|
</div>
|
|
171
206
|
</div>
|
|
172
207
|
`;
|
|
173
208
|
}
|
|
174
209
|
|
|
175
|
-
return this.surveyOptions.customQuestions
|
|
210
|
+
return this.surveyOptions.customQuestions
|
|
211
|
+
.map(
|
|
212
|
+
(q, index) => `
|
|
176
213
|
<div style="margin-bottom: 16px;">
|
|
177
214
|
<label style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500;">${q.label}</label>
|
|
178
215
|
${this._renderQuestionInput(q, index)}
|
|
179
216
|
</div>
|
|
180
|
-
`
|
|
217
|
+
`
|
|
218
|
+
)
|
|
219
|
+
.join('');
|
|
181
220
|
}
|
|
182
221
|
|
|
183
222
|
_renderQuestionInput(question, index) {
|
|
@@ -186,7 +225,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
186
225
|
return `
|
|
187
226
|
<select class="feedback-survey-select" data-question="${question.id || index}" style="width: 100%; padding: 10px; border: 1px solid ${this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7'}; border-radius: 8px; font-size: 14px; background: ${this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#fff'}; color: ${this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f'};">
|
|
188
227
|
<option value="">${question.placeholder || 'Select an option'}</option>
|
|
189
|
-
${question.options.map(opt => `<option value="${opt.value}">${opt.label}</option>`).join('')}
|
|
228
|
+
${question.options.map((opt) => `<option value="${opt.value}">${opt.label}</option>`).join('')}
|
|
190
229
|
</select>
|
|
191
230
|
`;
|
|
192
231
|
case 'text':
|
|
@@ -200,10 +239,14 @@ export class SurveyWidget extends BaseWidget {
|
|
|
200
239
|
|
|
201
240
|
_getPositionStyles() {
|
|
202
241
|
const positions = {
|
|
203
|
-
'bottom-right':
|
|
204
|
-
|
|
205
|
-
'
|
|
206
|
-
|
|
242
|
+
'bottom-right':
|
|
243
|
+
'bottom: 24px; right: 24px; opacity: 0; transform: translateY(20px); transition: all 0.3s ease;',
|
|
244
|
+
'bottom-left':
|
|
245
|
+
'bottom: 24px; left: 24px; opacity: 0; transform: translateY(20px); transition: all 0.3s ease;',
|
|
246
|
+
center:
|
|
247
|
+
'top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95); opacity: 0; transition: all 0.3s ease;',
|
|
248
|
+
bottom:
|
|
249
|
+
'bottom: 0; left: 0; right: 0; border-radius: 16px 16px 0 0; max-width: none; opacity: 0; transform: translateY(20px); transition: all 0.3s ease;',
|
|
207
250
|
};
|
|
208
251
|
return positions[this.surveyOptions.position] || positions['bottom-right'];
|
|
209
252
|
}
|
|
@@ -216,11 +259,15 @@ export class SurveyWidget extends BaseWidget {
|
|
|
216
259
|
closeBtn.addEventListener('click', () => this._handleDismiss());
|
|
217
260
|
|
|
218
261
|
// Submit button
|
|
219
|
-
const submitBtn = this.surveyElement.querySelector(
|
|
262
|
+
const submitBtn = this.surveyElement.querySelector(
|
|
263
|
+
'.feedback-survey-submit'
|
|
264
|
+
);
|
|
220
265
|
submitBtn.addEventListener('click', () => this._handleSubmit());
|
|
221
266
|
|
|
222
267
|
// Feedback textarea
|
|
223
|
-
const textarea = this.surveyElement.querySelector(
|
|
268
|
+
const textarea = this.surveyElement.querySelector(
|
|
269
|
+
'.feedback-survey-textarea'
|
|
270
|
+
);
|
|
224
271
|
textarea.addEventListener('input', (e) => {
|
|
225
272
|
this.surveyState.feedback = e.target.value;
|
|
226
273
|
});
|
|
@@ -241,133 +288,177 @@ export class SurveyWidget extends BaseWidget {
|
|
|
241
288
|
const type = this.surveyOptions.surveyType;
|
|
242
289
|
|
|
243
290
|
if (type === 'nps') {
|
|
244
|
-
this.surveyElement
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
btn.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
291
|
+
this.surveyElement
|
|
292
|
+
.querySelectorAll('.feedback-survey-nps-btn')
|
|
293
|
+
.forEach((btn) => {
|
|
294
|
+
btn.addEventListener('click', () =>
|
|
295
|
+
this._selectNPS(parseInt(btn.dataset.score))
|
|
296
|
+
);
|
|
297
|
+
btn.addEventListener('mouseenter', () => {
|
|
298
|
+
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
299
|
+
btn.style.borderColor = '#007aff';
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
btn.addEventListener('mouseleave', () => {
|
|
303
|
+
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
304
|
+
btn.style.borderColor =
|
|
305
|
+
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
306
|
+
}
|
|
307
|
+
});
|
|
255
308
|
});
|
|
256
|
-
});
|
|
257
309
|
}
|
|
258
310
|
|
|
259
311
|
if (type === 'csat') {
|
|
260
|
-
this.surveyElement
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
btn.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
btn.style.transform = 'scale(1)';
|
|
268
|
-
}
|
|
312
|
+
this.surveyElement
|
|
313
|
+
.querySelectorAll('.feedback-survey-csat-btn')
|
|
314
|
+
.forEach((btn) => {
|
|
315
|
+
btn.addEventListener('click', () =>
|
|
316
|
+
this._selectCSAT(parseInt(btn.dataset.score))
|
|
317
|
+
);
|
|
318
|
+
btn.addEventListener('mouseenter', () => {
|
|
319
|
+
btn.style.transform = 'scale(1.1)';
|
|
320
|
+
});
|
|
321
|
+
btn.addEventListener('mouseleave', () => {
|
|
322
|
+
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
323
|
+
btn.style.transform = 'scale(1)';
|
|
324
|
+
}
|
|
325
|
+
});
|
|
269
326
|
});
|
|
270
|
-
});
|
|
271
327
|
}
|
|
272
328
|
|
|
273
329
|
if (type === 'ces') {
|
|
274
|
-
this.surveyElement
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
btn.
|
|
279
|
-
|
|
330
|
+
this.surveyElement
|
|
331
|
+
.querySelectorAll('.feedback-survey-ces-btn')
|
|
332
|
+
.forEach((btn) => {
|
|
333
|
+
btn.addEventListener('click', () =>
|
|
334
|
+
this._selectCES(parseInt(btn.dataset.score))
|
|
335
|
+
);
|
|
336
|
+
btn.addEventListener('mouseenter', () => {
|
|
337
|
+
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
338
|
+
btn.style.borderColor = '#007aff';
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
btn.addEventListener('mouseleave', () => {
|
|
342
|
+
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
343
|
+
btn.style.borderColor =
|
|
344
|
+
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
345
|
+
}
|
|
346
|
+
});
|
|
280
347
|
});
|
|
281
|
-
btn.addEventListener('mouseleave', () => {
|
|
282
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
283
|
-
btn.style.borderColor = this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
348
|
}
|
|
288
349
|
|
|
289
350
|
if (type === 'custom') {
|
|
290
351
|
// Frequency buttons
|
|
291
|
-
this.surveyElement
|
|
292
|
-
|
|
293
|
-
|
|
352
|
+
this.surveyElement
|
|
353
|
+
.querySelectorAll('.feedback-survey-freq-btn')
|
|
354
|
+
.forEach((btn) => {
|
|
355
|
+
btn.addEventListener('click', () =>
|
|
356
|
+
this._selectFrequency(btn.dataset.freq)
|
|
357
|
+
);
|
|
358
|
+
});
|
|
294
359
|
|
|
295
360
|
// Select inputs
|
|
296
|
-
this.surveyElement
|
|
297
|
-
|
|
298
|
-
|
|
361
|
+
this.surveyElement
|
|
362
|
+
.querySelectorAll('.feedback-survey-select')
|
|
363
|
+
.forEach((select) => {
|
|
364
|
+
select.addEventListener('change', (e) => {
|
|
365
|
+
this.surveyState.customAnswers[select.dataset.question] =
|
|
366
|
+
e.target.value;
|
|
367
|
+
});
|
|
299
368
|
});
|
|
300
|
-
});
|
|
301
369
|
|
|
302
370
|
// Text inputs
|
|
303
|
-
this.surveyElement
|
|
304
|
-
|
|
305
|
-
|
|
371
|
+
this.surveyElement
|
|
372
|
+
.querySelectorAll('.feedback-survey-input')
|
|
373
|
+
.forEach((input) => {
|
|
374
|
+
input.addEventListener('input', (e) => {
|
|
375
|
+
this.surveyState.customAnswers[input.dataset.question] =
|
|
376
|
+
e.target.value;
|
|
377
|
+
});
|
|
306
378
|
});
|
|
307
|
-
});
|
|
308
379
|
}
|
|
309
380
|
}
|
|
310
381
|
|
|
311
382
|
_selectNPS(score) {
|
|
312
383
|
this.surveyState.score = score;
|
|
313
|
-
this.surveyElement
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
btn.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
384
|
+
this.surveyElement
|
|
385
|
+
.querySelectorAll('.feedback-survey-nps-btn')
|
|
386
|
+
.forEach((btn) => {
|
|
387
|
+
const btnScore = parseInt(btn.dataset.score);
|
|
388
|
+
if (btnScore === score) {
|
|
389
|
+
btn.style.background = '#007aff';
|
|
390
|
+
btn.style.borderColor = '#007aff';
|
|
391
|
+
btn.style.color = '#fff';
|
|
392
|
+
} else {
|
|
393
|
+
btn.style.background =
|
|
394
|
+
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
395
|
+
btn.style.borderColor =
|
|
396
|
+
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
397
|
+
btn.style.color =
|
|
398
|
+
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
399
|
+
}
|
|
400
|
+
});
|
|
325
401
|
}
|
|
326
402
|
|
|
327
403
|
_selectCSAT(score) {
|
|
328
404
|
this.surveyState.score = score;
|
|
329
|
-
this.surveyElement
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
405
|
+
this.surveyElement
|
|
406
|
+
.querySelectorAll('.feedback-survey-csat-btn')
|
|
407
|
+
.forEach((btn) => {
|
|
408
|
+
const btnScore = parseInt(btn.dataset.score);
|
|
409
|
+
btn.style.transform = btnScore === score ? 'scale(1.2)' : 'scale(1)';
|
|
410
|
+
});
|
|
333
411
|
}
|
|
334
412
|
|
|
335
413
|
_selectCES(score) {
|
|
336
414
|
this.surveyState.score = score;
|
|
337
|
-
this.surveyElement
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
btn.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
415
|
+
this.surveyElement
|
|
416
|
+
.querySelectorAll('.feedback-survey-ces-btn')
|
|
417
|
+
.forEach((btn) => {
|
|
418
|
+
const btnScore = parseInt(btn.dataset.score);
|
|
419
|
+
if (btnScore === score) {
|
|
420
|
+
btn.style.background = '#007aff';
|
|
421
|
+
btn.style.borderColor = '#007aff';
|
|
422
|
+
btn.style.color = '#fff';
|
|
423
|
+
} else {
|
|
424
|
+
btn.style.background =
|
|
425
|
+
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
426
|
+
btn.style.borderColor =
|
|
427
|
+
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
428
|
+
btn.style.color =
|
|
429
|
+
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
430
|
+
}
|
|
431
|
+
});
|
|
349
432
|
}
|
|
350
433
|
|
|
351
434
|
_selectFrequency(freq) {
|
|
352
435
|
this.surveyState.customAnswers.frequency = freq;
|
|
353
|
-
this.surveyElement
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
btn.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
436
|
+
this.surveyElement
|
|
437
|
+
.querySelectorAll('.feedback-survey-freq-btn')
|
|
438
|
+
.forEach((btn) => {
|
|
439
|
+
if (btn.dataset.freq === freq) {
|
|
440
|
+
btn.style.background = '#007aff';
|
|
441
|
+
btn.style.borderColor = '#007aff';
|
|
442
|
+
btn.style.color = '#fff';
|
|
443
|
+
} else {
|
|
444
|
+
btn.style.background =
|
|
445
|
+
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
446
|
+
btn.style.borderColor =
|
|
447
|
+
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
448
|
+
btn.style.color =
|
|
449
|
+
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
450
|
+
}
|
|
451
|
+
});
|
|
364
452
|
}
|
|
365
453
|
|
|
366
454
|
async _handleSubmit() {
|
|
367
455
|
const type = this.surveyOptions.surveyType;
|
|
368
456
|
|
|
369
457
|
// Validate
|
|
370
|
-
if (
|
|
458
|
+
if (
|
|
459
|
+
(type === 'nps' || type === 'csat' || type === 'ces') &&
|
|
460
|
+
this.surveyState.score === null
|
|
461
|
+
) {
|
|
371
462
|
this._showError('Please select a rating');
|
|
372
463
|
return;
|
|
373
464
|
}
|
|
@@ -415,10 +506,13 @@ export class SurveyWidget extends BaseWidget {
|
|
|
415
506
|
|
|
416
507
|
const error = document.createElement('div');
|
|
417
508
|
error.className = 'feedback-survey-error';
|
|
418
|
-
error.style.cssText =
|
|
509
|
+
error.style.cssText =
|
|
510
|
+
'color: #ef4444; font-size: 13px; margin-top: 8px; text-align: center;';
|
|
419
511
|
error.textContent = message;
|
|
420
512
|
|
|
421
|
-
const submitBtn = this.surveyElement.querySelector(
|
|
513
|
+
const submitBtn = this.surveyElement.querySelector(
|
|
514
|
+
'.feedback-survey-submit'
|
|
515
|
+
);
|
|
422
516
|
submitBtn.parentNode.insertBefore(error, submitBtn);
|
|
423
517
|
|
|
424
518
|
setTimeout(() => error.remove(), 3000);
|
|
@@ -468,9 +562,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
468
562
|
|
|
469
563
|
if (surveyEl) {
|
|
470
564
|
surveyEl.style.opacity = '0';
|
|
471
|
-
surveyEl.style.transform =
|
|
472
|
-
|
|
473
|
-
|
|
565
|
+
surveyEl.style.transform =
|
|
566
|
+
this.surveyOptions.position === 'center'
|
|
567
|
+
? 'translate(-50%, -50%) scale(0.95)'
|
|
568
|
+
: 'translateY(20px)';
|
|
474
569
|
setTimeout(() => {
|
|
475
570
|
if (surveyEl && surveyEl.parentNode) {
|
|
476
571
|
surveyEl.parentNode.removeChild(surveyEl);
|
package/types/index.d.ts
CHANGED
|
@@ -1,68 +1,73 @@
|
|
|
1
|
-
|
|
2
1
|
declare module '@product7/feedback-sdk' {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
export interface FeedbackConfig {
|
|
3
|
+
workspace: string;
|
|
4
|
+
userContext?: UserContext;
|
|
5
|
+
debug?: boolean;
|
|
6
|
+
boardId?: string;
|
|
7
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
8
|
+
theme?: 'light' | 'dark';
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
autoShow?: boolean;
|
|
11
|
+
}
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
export interface UserContext {
|
|
14
|
+
user_id?: string;
|
|
15
|
+
email?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
custom_fields?: Record<string, any>;
|
|
18
|
+
company?: {
|
|
19
|
+
id?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
monthly_spend?: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
export interface ButtonWidget {
|
|
26
|
+
id: string;
|
|
27
|
+
type: 'button';
|
|
28
|
+
mount(container?: string | HTMLElement): this;
|
|
29
|
+
destroy(): void;
|
|
30
|
+
show(): this;
|
|
31
|
+
hide(): this;
|
|
32
|
+
openModal(): void;
|
|
33
|
+
closeModal(): void;
|
|
34
|
+
}
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
export class FeedbackSDK {
|
|
37
|
+
constructor(config: FeedbackConfig);
|
|
38
|
+
init(): Promise<{
|
|
39
|
+
initialized: boolean;
|
|
40
|
+
config?: any;
|
|
41
|
+
sessionToken?: string;
|
|
42
|
+
expiresIn?: number;
|
|
43
|
+
}>;
|
|
44
|
+
createWidget(
|
|
45
|
+
type: 'button',
|
|
46
|
+
options?: Partial<FeedbackConfig>
|
|
47
|
+
): ButtonWidget;
|
|
48
|
+
setUserContext(userContext: UserContext): void;
|
|
49
|
+
getUserContext(): UserContext | null;
|
|
50
|
+
destroy(): void;
|
|
51
|
+
readonly initialized: boolean;
|
|
52
|
+
}
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
interface FeedbackSDKExport {
|
|
55
|
+
FeedbackSDK: typeof FeedbackSDK;
|
|
56
|
+
ButtonWidget: any;
|
|
57
|
+
create: (config: FeedbackConfig) => FeedbackSDK;
|
|
58
|
+
initWithUser: (
|
|
59
|
+
config: Omit<FeedbackConfig, 'userContext'>,
|
|
60
|
+
userContext: UserContext
|
|
61
|
+
) => Promise<FeedbackSDK>;
|
|
62
|
+
getInstance: () => FeedbackSDK | null;
|
|
63
|
+
isReady: () => boolean;
|
|
64
|
+
setUserContext: (userContext: UserContext) => void;
|
|
65
|
+
onReady: (callback: (sdk: FeedbackSDK) => void) => void;
|
|
66
|
+
onError: (callback: (error: Error) => void) => void;
|
|
67
|
+
version: string;
|
|
68
|
+
instance: FeedbackSDK | null;
|
|
69
|
+
}
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
71
|
+
const FeedbackSDKDefault: FeedbackSDKExport;
|
|
72
|
+
export default FeedbackSDKDefault;
|
|
73
|
+
}
|