@product7/feedback-sdk 1.3.2 → 1.3.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/dist/feedback-sdk.js +2343 -3194
- 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/api/services/MessengerService.js +20 -0
- package/src/index.js +2 -3
- package/src/styles/base.js +27 -52
- package/src/styles/changelog.js +152 -269
- package/src/styles/components.js +489 -0
- package/src/styles/design-tokens.js +104 -0
- package/src/styles/feedback.js +59 -369
- package/src/styles/messenger-core.js +390 -0
- package/src/styles/messenger-features.js +347 -0
- package/src/styles/messenger-help.js +298 -0
- package/src/styles/messenger-themes.js +500 -0
- package/src/styles/messenger-views.js +618 -0
- package/src/styles/messenger.js +558 -0
- package/src/styles/styles.js +24 -2
- package/src/styles/surveys.js +354 -0
- package/src/widgets/BaseWidget.js +25 -58
- package/src/widgets/ButtonWidget.js +3 -18
- package/src/widgets/ChangelogWidget.js +7 -48
- package/src/widgets/InlineWidget.js +16 -13
- package/src/widgets/MessengerWidget.js +51 -54
- package/src/widgets/SurveyWidget.js +42 -146
- package/src/widgets/TabWidget.js +2 -22
- package/src/widgets/messenger/components/MessengerLauncher.js +10 -5
- package/src/widgets/messenger/components/MessengerPanel.js +5 -27
- package/src/widgets/messenger/components/NavigationTabs.js +5 -14
- package/src/widgets/messenger/views/ChangelogView.js +13 -14
- package/src/widgets/messenger/views/ChatView.js +43 -36
- package/src/widgets/messenger/views/ConversationsView.js +16 -21
- package/src/widgets/messenger/views/HelpView.js +10 -10
- package/src/widgets/messenger/views/HomeView.js +11 -16
- package/src/widgets/messenger/views/PreChatFormView.js +18 -30
- package/src/styles/messengerStyles.js +0 -2328
|
@@ -5,15 +5,14 @@ export class SurveyWidget extends BaseWidget {
|
|
|
5
5
|
super({ ...options, type: 'survey' });
|
|
6
6
|
|
|
7
7
|
this.surveyOptions = {
|
|
8
|
-
surveyId: options.surveyId || null,
|
|
9
|
-
surveyType: options.surveyType || 'nps',
|
|
10
|
-
position: options.position || 'bottom-right',
|
|
8
|
+
surveyId: options.surveyId || null,
|
|
9
|
+
surveyType: options.surveyType || 'nps',
|
|
10
|
+
position: options.position || 'bottom-right',
|
|
11
11
|
title: options.title || null,
|
|
12
12
|
description: options.description || null,
|
|
13
13
|
lowLabel: options.lowLabel || null,
|
|
14
14
|
highLabel: options.highLabel || null,
|
|
15
15
|
customQuestions: options.customQuestions || [],
|
|
16
|
-
theme: options.theme || 'light',
|
|
17
16
|
onSubmit: options.onSubmit || null,
|
|
18
17
|
onDismiss: options.onDismiss || null,
|
|
19
18
|
};
|
|
@@ -27,10 +26,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
_render() {
|
|
30
|
-
// Survey widget doesn't render a trigger button, it's shown programmatically
|
|
31
29
|
const container = document.createElement('div');
|
|
32
30
|
container.className = 'feedback-survey-container';
|
|
33
|
-
container.style.display = 'none';
|
|
34
31
|
return container;
|
|
35
32
|
}
|
|
36
33
|
|
|
@@ -54,17 +51,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
_renderSurvey() {
|
|
57
|
-
// Remove any existing survey
|
|
58
54
|
this._closeSurvey();
|
|
59
55
|
|
|
60
56
|
const config = this._getSurveyConfig();
|
|
61
|
-
const positionStyles = this._getPositionStyles();
|
|
62
|
-
const themeStyles =
|
|
63
|
-
this.surveyOptions.theme === 'dark'
|
|
64
|
-
? 'background: #1a1a1a; color: #fff;'
|
|
65
|
-
: 'background: #fff; color: #1d1d1f;';
|
|
66
57
|
|
|
67
|
-
// Create backdrop for center position
|
|
68
58
|
if (this.surveyOptions.position === 'center') {
|
|
69
59
|
this.backdropElement = document.createElement('div');
|
|
70
60
|
this.backdropElement.className = 'feedback-survey-backdrop';
|
|
@@ -75,24 +65,22 @@ export class SurveyWidget extends BaseWidget {
|
|
|
75
65
|
}
|
|
76
66
|
|
|
77
67
|
this.surveyElement = document.createElement('div');
|
|
78
|
-
this.surveyElement.className = `feedback-survey feedback-survey-${this.surveyOptions.position}
|
|
79
|
-
this.surveyElement.style.cssText = `position: fixed; ${positionStyles} z-index: 10000; ${themeStyles} border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); padding: 24px; min-width: 320px; max-width: 400px; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;`;
|
|
68
|
+
this.surveyElement.className = `feedback-survey feedback-survey-${this.surveyOptions.position}`;
|
|
80
69
|
|
|
81
70
|
this.surveyElement.innerHTML = `
|
|
82
|
-
<button class="feedback-survey-close"
|
|
83
|
-
<h3 class="feedback-survey-title"
|
|
84
|
-
<p class="feedback-survey-description"
|
|
71
|
+
<button class="feedback-survey-close">×</button>
|
|
72
|
+
<h3 class="feedback-survey-title">${config.title}</h3>
|
|
73
|
+
<p class="feedback-survey-description">${config.description}</p>
|
|
85
74
|
<div class="feedback-survey-content">${config.html}</div>
|
|
86
|
-
<div class="feedback-survey-feedback"
|
|
87
|
-
<textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)"
|
|
75
|
+
<div class="feedback-survey-feedback">
|
|
76
|
+
<textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)"></textarea>
|
|
88
77
|
</div>
|
|
89
|
-
<button class="feedback-survey-submit"
|
|
78
|
+
<button class="feedback-survey-submit">Submit</button>
|
|
90
79
|
`;
|
|
91
80
|
|
|
92
81
|
document.body.appendChild(this.surveyElement);
|
|
93
82
|
this._attachSurveyEvents();
|
|
94
83
|
|
|
95
|
-
// Animate in
|
|
96
84
|
requestAnimationFrame(() => {
|
|
97
85
|
this.surveyElement.style.opacity = '1';
|
|
98
86
|
this.surveyElement.style.transform =
|
|
@@ -111,16 +99,16 @@ export class SurveyWidget extends BaseWidget {
|
|
|
111
99
|
this.surveyOptions.description ||
|
|
112
100
|
'On a scale of 0-10, how likely are you to recommend our product to a friend or colleague?',
|
|
113
101
|
html: `
|
|
114
|
-
<div class="feedback-survey-nps"
|
|
102
|
+
<div class="feedback-survey-nps">
|
|
115
103
|
${[...Array(11).keys()]
|
|
116
104
|
.map(
|
|
117
105
|
(n) => `
|
|
118
|
-
<button class="feedback-survey-nps-btn" data-score="${n}"
|
|
106
|
+
<button class="feedback-survey-nps-btn" data-score="${n}">${n}</button>
|
|
119
107
|
`
|
|
120
108
|
)
|
|
121
109
|
.join('')}
|
|
122
110
|
</div>
|
|
123
|
-
<div
|
|
111
|
+
<div class="feedback-survey-labels">
|
|
124
112
|
<span>${this.surveyOptions.lowLabel || 'Not likely'}</span>
|
|
125
113
|
<span>${this.surveyOptions.highLabel || 'Very likely'}</span>
|
|
126
114
|
</div>
|
|
@@ -132,16 +120,16 @@ export class SurveyWidget extends BaseWidget {
|
|
|
132
120
|
this.surveyOptions.description ||
|
|
133
121
|
'How would you rate your overall satisfaction with our product?',
|
|
134
122
|
html: `
|
|
135
|
-
<div class="feedback-survey-csat"
|
|
123
|
+
<div class="feedback-survey-csat">
|
|
136
124
|
${['😞', '😕', '😐', '🙂', '😄']
|
|
137
125
|
.map(
|
|
138
126
|
(emoji, i) => `
|
|
139
|
-
<button class="feedback-survey-csat-btn" data-score="${i + 1}"
|
|
127
|
+
<button class="feedback-survey-csat-btn" data-score="${i + 1}">${emoji}</button>
|
|
140
128
|
`
|
|
141
129
|
)
|
|
142
130
|
.join('')}
|
|
143
131
|
</div>
|
|
144
|
-
<div
|
|
132
|
+
<div class="feedback-survey-labels">
|
|
145
133
|
<span>${this.surveyOptions.lowLabel || 'Very dissatisfied'}</span>
|
|
146
134
|
<span>${this.surveyOptions.highLabel || 'Very satisfied'}</span>
|
|
147
135
|
</div>
|
|
@@ -153,11 +141,11 @@ export class SurveyWidget extends BaseWidget {
|
|
|
153
141
|
this.surveyOptions.description ||
|
|
154
142
|
'How easy was it to accomplish your task today?',
|
|
155
143
|
html: `
|
|
156
|
-
<div class="feedback-survey-ces"
|
|
144
|
+
<div class="feedback-survey-ces">
|
|
157
145
|
${['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy']
|
|
158
146
|
.map(
|
|
159
147
|
(label, i) => `
|
|
160
|
-
<button class="feedback-survey-ces-btn" data-score="${i + 1}"
|
|
148
|
+
<button class="feedback-survey-ces-btn" data-score="${i + 1}">${label}</button>
|
|
161
149
|
`
|
|
162
150
|
)
|
|
163
151
|
.join('')}
|
|
@@ -181,11 +169,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
181
169
|
!this.surveyOptions.customQuestions ||
|
|
182
170
|
this.surveyOptions.customQuestions.length === 0
|
|
183
171
|
) {
|
|
184
|
-
// Default custom questions
|
|
185
172
|
return `
|
|
186
|
-
<div
|
|
187
|
-
<label
|
|
188
|
-
<select class="feedback-survey-select" data-question="feature"
|
|
173
|
+
<div class="sdk-form-group">
|
|
174
|
+
<label class="sdk-label">What feature do you use most?</label>
|
|
175
|
+
<select class="feedback-survey-select" data-question="feature">
|
|
189
176
|
<option value="">Select a feature</option>
|
|
190
177
|
<option value="feedback">Feedback Collection</option>
|
|
191
178
|
<option value="surveys">Surveys</option>
|
|
@@ -193,13 +180,13 @@ export class SurveyWidget extends BaseWidget {
|
|
|
193
180
|
<option value="integrations">Integrations</option>
|
|
194
181
|
</select>
|
|
195
182
|
</div>
|
|
196
|
-
<div>
|
|
197
|
-
<label
|
|
198
|
-
<div class="feedback-survey-frequency"
|
|
183
|
+
<div class="sdk-form-group">
|
|
184
|
+
<label class="sdk-label">How often do you use it?</label>
|
|
185
|
+
<div class="feedback-survey-frequency">
|
|
199
186
|
${['Daily', 'Weekly', 'Monthly', 'Rarely']
|
|
200
187
|
.map(
|
|
201
188
|
(freq) => `
|
|
202
|
-
<button class="feedback-survey-freq-btn" data-freq="${freq}"
|
|
189
|
+
<button class="feedback-survey-freq-btn" data-freq="${freq}">${freq}</button>
|
|
203
190
|
`
|
|
204
191
|
)
|
|
205
192
|
.join('')}
|
|
@@ -211,8 +198,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
211
198
|
return this.surveyOptions.customQuestions
|
|
212
199
|
.map(
|
|
213
200
|
(q, index) => `
|
|
214
|
-
<div
|
|
215
|
-
<label
|
|
201
|
+
<div class="sdk-form-group">
|
|
202
|
+
<label class="sdk-label">${q.label}</label>
|
|
216
203
|
${this._renderQuestionInput(q, index)}
|
|
217
204
|
</div>
|
|
218
205
|
`
|
|
@@ -224,48 +211,31 @@ export class SurveyWidget extends BaseWidget {
|
|
|
224
211
|
switch (question.type) {
|
|
225
212
|
case 'select':
|
|
226
213
|
return `
|
|
227
|
-
<select class="feedback-survey-select" data-question="${question.id || index}"
|
|
214
|
+
<select class="feedback-survey-select" data-question="${question.id || index}">
|
|
228
215
|
<option value="">${question.placeholder || 'Select an option'}</option>
|
|
229
216
|
${question.options.map((opt) => `<option value="${opt.value}">${opt.label}</option>`).join('')}
|
|
230
217
|
</select>
|
|
231
218
|
`;
|
|
232
219
|
case 'text':
|
|
233
220
|
return `
|
|
234
|
-
<input type="text" class="feedback-survey-input" data-question="${question.id || index}" placeholder="${question.placeholder || ''}"
|
|
221
|
+
<input type="text" class="feedback-survey-input" data-question="${question.id || index}" placeholder="${question.placeholder || ''}">
|
|
235
222
|
`;
|
|
236
223
|
default:
|
|
237
224
|
return '';
|
|
238
225
|
}
|
|
239
226
|
}
|
|
240
227
|
|
|
241
|
-
_getPositionStyles() {
|
|
242
|
-
const positions = {
|
|
243
|
-
'bottom-right':
|
|
244
|
-
'bottom: 24px; right: 24px; opacity: 0; transform: translateY(20px); transition: all 0.3s ease;',
|
|
245
|
-
'bottom-left':
|
|
246
|
-
'bottom: 24px; left: 24px; opacity: 0; transform: translateY(20px); transition: all 0.3s ease;',
|
|
247
|
-
center:
|
|
248
|
-
'top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95); opacity: 0; transition: all 0.3s ease;',
|
|
249
|
-
bottom:
|
|
250
|
-
'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;',
|
|
251
|
-
};
|
|
252
|
-
return positions[this.surveyOptions.position] || positions['bottom-right'];
|
|
253
|
-
}
|
|
254
|
-
|
|
255
228
|
_attachSurveyEvents() {
|
|
256
229
|
if (!this.surveyElement) return;
|
|
257
230
|
|
|
258
|
-
// Close button
|
|
259
231
|
const closeBtn = this.surveyElement.querySelector('.feedback-survey-close');
|
|
260
232
|
closeBtn.addEventListener('click', () => this._handleDismiss());
|
|
261
233
|
|
|
262
|
-
// Submit button
|
|
263
234
|
const submitBtn = this.surveyElement.querySelector(
|
|
264
235
|
'.feedback-survey-submit'
|
|
265
236
|
);
|
|
266
237
|
submitBtn.addEventListener('click', () => this._handleSubmit());
|
|
267
238
|
|
|
268
|
-
// Feedback textarea
|
|
269
239
|
const textarea = this.surveyElement.querySelector(
|
|
270
240
|
'.feedback-survey-textarea'
|
|
271
241
|
);
|
|
@@ -273,10 +243,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
273
243
|
this.surveyState.feedback = e.target.value;
|
|
274
244
|
});
|
|
275
245
|
|
|
276
|
-
// Survey type specific events
|
|
277
246
|
this._attachTypeSpecificEvents();
|
|
278
247
|
|
|
279
|
-
// Escape key
|
|
280
248
|
this._escapeHandler = (e) => {
|
|
281
249
|
if (e.key === 'Escape') {
|
|
282
250
|
this._handleDismiss();
|
|
@@ -295,17 +263,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
295
263
|
btn.addEventListener('click', () =>
|
|
296
264
|
this._selectNPS(parseInt(btn.dataset.score))
|
|
297
265
|
);
|
|
298
|
-
btn.addEventListener('mouseenter', () => {
|
|
299
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
300
|
-
btn.style.borderColor = '#007aff';
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
btn.addEventListener('mouseleave', () => {
|
|
304
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
305
|
-
btn.style.borderColor =
|
|
306
|
-
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
266
|
});
|
|
310
267
|
}
|
|
311
268
|
|
|
@@ -316,14 +273,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
316
273
|
btn.addEventListener('click', () =>
|
|
317
274
|
this._selectCSAT(parseInt(btn.dataset.score))
|
|
318
275
|
);
|
|
319
|
-
btn.addEventListener('mouseenter', () => {
|
|
320
|
-
btn.style.transform = 'scale(1.1)';
|
|
321
|
-
});
|
|
322
|
-
btn.addEventListener('mouseleave', () => {
|
|
323
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
324
|
-
btn.style.transform = 'scale(1)';
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
276
|
});
|
|
328
277
|
}
|
|
329
278
|
|
|
@@ -334,22 +283,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
334
283
|
btn.addEventListener('click', () =>
|
|
335
284
|
this._selectCES(parseInt(btn.dataset.score))
|
|
336
285
|
);
|
|
337
|
-
btn.addEventListener('mouseenter', () => {
|
|
338
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
339
|
-
btn.style.borderColor = '#007aff';
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
btn.addEventListener('mouseleave', () => {
|
|
343
|
-
if (this.surveyState.score !== parseInt(btn.dataset.score)) {
|
|
344
|
-
btn.style.borderColor =
|
|
345
|
-
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
286
|
});
|
|
349
287
|
}
|
|
350
288
|
|
|
351
289
|
if (type === 'custom') {
|
|
352
|
-
// Frequency buttons
|
|
353
290
|
this.surveyElement
|
|
354
291
|
.querySelectorAll('.feedback-survey-freq-btn')
|
|
355
292
|
.forEach((btn) => {
|
|
@@ -358,7 +295,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
358
295
|
);
|
|
359
296
|
});
|
|
360
297
|
|
|
361
|
-
// Select inputs
|
|
362
298
|
this.surveyElement
|
|
363
299
|
.querySelectorAll('.feedback-survey-select')
|
|
364
300
|
.forEach((select) => {
|
|
@@ -368,7 +304,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
368
304
|
});
|
|
369
305
|
});
|
|
370
306
|
|
|
371
|
-
// Text inputs
|
|
372
307
|
this.surveyElement
|
|
373
308
|
.querySelectorAll('.feedback-survey-input')
|
|
374
309
|
.forEach((input) => {
|
|
@@ -387,16 +322,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
387
322
|
.forEach((btn) => {
|
|
388
323
|
const btnScore = parseInt(btn.dataset.score);
|
|
389
324
|
if (btnScore === score) {
|
|
390
|
-
btn.
|
|
391
|
-
btn.style.borderColor = '#007aff';
|
|
392
|
-
btn.style.color = '#fff';
|
|
325
|
+
btn.classList.add('selected');
|
|
393
326
|
} else {
|
|
394
|
-
btn.
|
|
395
|
-
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
396
|
-
btn.style.borderColor =
|
|
397
|
-
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
398
|
-
btn.style.color =
|
|
399
|
-
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
327
|
+
btn.classList.remove('selected');
|
|
400
328
|
}
|
|
401
329
|
});
|
|
402
330
|
}
|
|
@@ -407,7 +335,11 @@ export class SurveyWidget extends BaseWidget {
|
|
|
407
335
|
.querySelectorAll('.feedback-survey-csat-btn')
|
|
408
336
|
.forEach((btn) => {
|
|
409
337
|
const btnScore = parseInt(btn.dataset.score);
|
|
410
|
-
|
|
338
|
+
if (btnScore === score) {
|
|
339
|
+
btn.classList.add('selected');
|
|
340
|
+
} else {
|
|
341
|
+
btn.classList.remove('selected');
|
|
342
|
+
}
|
|
411
343
|
});
|
|
412
344
|
}
|
|
413
345
|
|
|
@@ -418,16 +350,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
418
350
|
.forEach((btn) => {
|
|
419
351
|
const btnScore = parseInt(btn.dataset.score);
|
|
420
352
|
if (btnScore === score) {
|
|
421
|
-
btn.
|
|
422
|
-
btn.style.borderColor = '#007aff';
|
|
423
|
-
btn.style.color = '#fff';
|
|
353
|
+
btn.classList.add('selected');
|
|
424
354
|
} else {
|
|
425
|
-
btn.
|
|
426
|
-
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
427
|
-
btn.style.borderColor =
|
|
428
|
-
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
429
|
-
btn.style.color =
|
|
430
|
-
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
355
|
+
btn.classList.remove('selected');
|
|
431
356
|
}
|
|
432
357
|
});
|
|
433
358
|
}
|
|
@@ -438,16 +363,9 @@ export class SurveyWidget extends BaseWidget {
|
|
|
438
363
|
.querySelectorAll('.feedback-survey-freq-btn')
|
|
439
364
|
.forEach((btn) => {
|
|
440
365
|
if (btn.dataset.freq === freq) {
|
|
441
|
-
btn.
|
|
442
|
-
btn.style.borderColor = '#007aff';
|
|
443
|
-
btn.style.color = '#fff';
|
|
366
|
+
btn.classList.add('selected');
|
|
444
367
|
} else {
|
|
445
|
-
btn.
|
|
446
|
-
this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#f8f9fa';
|
|
447
|
-
btn.style.borderColor =
|
|
448
|
-
this.surveyOptions.theme === 'dark' ? '#444' : '#d2d2d7';
|
|
449
|
-
btn.style.color =
|
|
450
|
-
this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f';
|
|
368
|
+
btn.classList.remove('selected');
|
|
451
369
|
}
|
|
452
370
|
});
|
|
453
371
|
}
|
|
@@ -455,7 +373,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
455
373
|
async _handleSubmit() {
|
|
456
374
|
const type = this.surveyOptions.surveyType;
|
|
457
375
|
|
|
458
|
-
// Validate
|
|
459
376
|
if (
|
|
460
377
|
(type === 'nps' || type === 'csat' || type === 'ces') &&
|
|
461
378
|
this.surveyState.score === null
|
|
@@ -478,12 +395,10 @@ export class SurveyWidget extends BaseWidget {
|
|
|
478
395
|
timestamp: new Date().toISOString(),
|
|
479
396
|
};
|
|
480
397
|
|
|
481
|
-
// Call onSubmit callback if provided
|
|
482
398
|
if (this.surveyOptions.onSubmit) {
|
|
483
399
|
this.surveyOptions.onSubmit(response);
|
|
484
400
|
}
|
|
485
401
|
|
|
486
|
-
// Submit to API
|
|
487
402
|
try {
|
|
488
403
|
const surveyId =
|
|
489
404
|
this.surveyOptions.surveyId || `local_${type}_${Date.now()}`;
|
|
@@ -499,7 +414,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
499
414
|
}
|
|
500
415
|
|
|
501
416
|
async _handleDismiss() {
|
|
502
|
-
// Call dismiss API if survey has a backend ID
|
|
503
417
|
if (this.surveyOptions.surveyId) {
|
|
504
418
|
try {
|
|
505
419
|
await this.apiService.dismissSurvey(this.surveyOptions.surveyId);
|
|
@@ -516,14 +430,11 @@ export class SurveyWidget extends BaseWidget {
|
|
|
516
430
|
}
|
|
517
431
|
|
|
518
432
|
_showError(message) {
|
|
519
|
-
// Simple error display - could be enhanced
|
|
520
433
|
const existing = this.surveyElement.querySelector('.feedback-survey-error');
|
|
521
434
|
if (existing) existing.remove();
|
|
522
435
|
|
|
523
436
|
const error = document.createElement('div');
|
|
524
437
|
error.className = 'feedback-survey-error';
|
|
525
|
-
error.style.cssText =
|
|
526
|
-
'color: #ef4444; font-size: 13px; margin-top: 8px; text-align: center;';
|
|
527
438
|
error.textContent = message;
|
|
528
439
|
|
|
529
440
|
const submitBtn = this.surveyElement.querySelector(
|
|
@@ -537,22 +448,8 @@ export class SurveyWidget extends BaseWidget {
|
|
|
537
448
|
_showSuccessNotification() {
|
|
538
449
|
const notification = document.createElement('div');
|
|
539
450
|
notification.className = 'feedback-survey-success';
|
|
540
|
-
notification.style.cssText = `
|
|
541
|
-
position: fixed;
|
|
542
|
-
top: 24px;
|
|
543
|
-
right: 24px;
|
|
544
|
-
background: #10b981;
|
|
545
|
-
color: white;
|
|
546
|
-
padding: 16px 24px;
|
|
547
|
-
border-radius: 12px;
|
|
548
|
-
font-size: 14px;
|
|
549
|
-
font-weight: 500;
|
|
550
|
-
z-index: 10001;
|
|
551
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
552
|
-
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
553
|
-
`;
|
|
554
451
|
notification.innerHTML = `
|
|
555
|
-
<div
|
|
452
|
+
<div>
|
|
556
453
|
<span style="font-size: 18px;">✓</span>
|
|
557
454
|
<span>Thank you for your feedback!</span>
|
|
558
455
|
</div>
|
|
@@ -572,7 +469,6 @@ export class SurveyWidget extends BaseWidget {
|
|
|
572
469
|
this._escapeHandler = null;
|
|
573
470
|
}
|
|
574
471
|
|
|
575
|
-
// Capture references to avoid race conditions with setTimeout
|
|
576
472
|
const surveyEl = this.surveyElement;
|
|
577
473
|
const backdropEl = this.backdropElement;
|
|
578
474
|
|
|
@@ -612,4 +508,4 @@ export class SurveyWidget extends BaseWidget {
|
|
|
612
508
|
this._closeSurvey();
|
|
613
509
|
super.destroy();
|
|
614
510
|
}
|
|
615
|
-
}
|
|
511
|
+
}
|
package/src/widgets/TabWidget.js
CHANGED
|
@@ -7,7 +7,7 @@ export class TabWidget extends BaseWidget {
|
|
|
7
7
|
|
|
8
8
|
_render() {
|
|
9
9
|
const tab = document.createElement('div');
|
|
10
|
-
tab.className = `feedback-widget feedback-widget-tab
|
|
10
|
+
tab.className = `feedback-widget feedback-widget-tab position-${this.options.position}`;
|
|
11
11
|
tab.innerHTML = `
|
|
12
12
|
<div class="feedback-tab-trigger">
|
|
13
13
|
<span class="feedback-tab-text">Feedback</span>
|
|
@@ -24,26 +24,6 @@ export class TabWidget extends BaseWidget {
|
|
|
24
24
|
_attachEvents() {
|
|
25
25
|
const tab = this.element.querySelector('.feedback-tab-trigger');
|
|
26
26
|
tab.addEventListener('click', this.openModal);
|
|
27
|
-
|
|
28
|
-
tab.addEventListener('mouseenter', () => {
|
|
29
|
-
if (!this.state.isSubmitting) {
|
|
30
|
-
tab.style.transform = this._getHoverTransform();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
tab.addEventListener('mouseleave', () => {
|
|
35
|
-
tab.style.transform = 'none';
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
_getHoverTransform() {
|
|
40
|
-
const position = this.options.position;
|
|
41
|
-
if (position.includes('right')) {
|
|
42
|
-
return 'translateX(-5px)';
|
|
43
|
-
} else if (position.includes('left')) {
|
|
44
|
-
return 'translateX(5px)';
|
|
45
|
-
}
|
|
46
|
-
return 'none';
|
|
47
27
|
}
|
|
48
28
|
|
|
49
29
|
updateText(text) {
|
|
@@ -62,4 +42,4 @@ export class TabWidget extends BaseWidget {
|
|
|
62
42
|
);
|
|
63
43
|
}
|
|
64
44
|
}
|
|
65
|
-
}
|
|
45
|
+
}
|
|
@@ -3,7 +3,7 @@ export class MessengerLauncher {
|
|
|
3
3
|
this.state = state;
|
|
4
4
|
this.options = {
|
|
5
5
|
position: options.position || 'bottom-right',
|
|
6
|
-
primaryColor: options.primaryColor || '#
|
|
6
|
+
primaryColor: options.primaryColor || '#155EEF',
|
|
7
7
|
...options,
|
|
8
8
|
};
|
|
9
9
|
this.element = null;
|
|
@@ -36,20 +36,25 @@ export class MessengerLauncher {
|
|
|
36
36
|
: '';
|
|
37
37
|
|
|
38
38
|
this.element.innerHTML = `
|
|
39
|
-
<button class="messenger-launcher-btn" aria-label="Open messenger"
|
|
39
|
+
<button class="messenger-launcher-btn" aria-label="Open messenger">
|
|
40
40
|
<span class="messenger-launcher-icon messenger-launcher-icon-chat">
|
|
41
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="
|
|
41
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256">
|
|
42
42
|
<path d="M144,140a12,12,0,1,1-12-12A12,12,0,0,1,144,140Zm44-12a12,12,0,1,0,12,12A12,12,0,0,0,188,128Zm51.34,83.47a16,16,0,0,1-19.87,19.87l-24.71-7.27A80,80,0,0,1,86.43,183.42a79,79,0,0,1-25.19-7.35l-24.71,7.27a16,16,0,0,1-19.87-19.87l7.27-24.71A80,80,0,1,1,169.58,72.59a80,80,0,0,1,62.49,114.17ZM81.3,166.3a79.94,79.94,0,0,1,70.38-93.87A64,64,0,0,0,39.55,134.19a8,8,0,0,1,.63,6L32,168l27.76-8.17a8,8,0,0,1,6,.63A63.45,63.45,0,0,0,81.3,166.3Zm135.15,15.89a64,64,0,1,0-26.26,26.26,8,8,0,0,1,6-.63L224,216l-8.17-27.76A8,8,0,0,1,216.45,182.19Z"></path>
|
|
43
43
|
</svg>
|
|
44
44
|
</span>
|
|
45
45
|
<span class="messenger-launcher-icon messenger-launcher-icon-close" style="display: none;">
|
|
46
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="
|
|
46
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256">
|
|
47
47
|
<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
|
|
48
48
|
</svg>
|
|
49
49
|
</span>
|
|
50
50
|
${badgeHtml}
|
|
51
51
|
</button>
|
|
52
52
|
`;
|
|
53
|
+
|
|
54
|
+
// Set custom primary color if provided
|
|
55
|
+
if (this.options.primaryColor) {
|
|
56
|
+
this.element.querySelector('.messenger-launcher-btn').style.setProperty('background', this.options.primaryColor);
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
_attachEvents() {
|
|
@@ -116,4 +121,4 @@ export class MessengerLauncher {
|
|
|
116
121
|
this.element.parentNode.removeChild(this.element);
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
|
-
}
|
|
124
|
+
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MessengerPanel - Main panel container for messenger
|
|
3
|
-
*/
|
|
4
1
|
import { NavigationTabs } from './NavigationTabs.js';
|
|
5
2
|
|
|
6
3
|
export class MessengerPanel {
|
|
@@ -8,7 +5,6 @@ export class MessengerPanel {
|
|
|
8
5
|
this.state = state;
|
|
9
6
|
this.options = {
|
|
10
7
|
position: options.position || 'bottom-right',
|
|
11
|
-
theme: options.theme || 'dark',
|
|
12
8
|
...options,
|
|
13
9
|
};
|
|
14
10
|
this.element = null;
|
|
@@ -18,16 +14,13 @@ export class MessengerPanel {
|
|
|
18
14
|
this._unsubscribe = null;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
/**
|
|
22
|
-
* Register view components
|
|
23
|
-
*/
|
|
24
17
|
registerView(name, ViewClass) {
|
|
25
18
|
this.viewRegistry[name] = ViewClass;
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
render() {
|
|
29
22
|
this.element = document.createElement('div');
|
|
30
|
-
this.element.className = `messenger-panel messenger-panel-${this.options.position}
|
|
23
|
+
this.element.className = `messenger-panel messenger-panel-${this.options.position}`;
|
|
31
24
|
|
|
32
25
|
this.element.innerHTML = `
|
|
33
26
|
<div class="messenger-panel-content">
|
|
@@ -36,15 +29,12 @@ export class MessengerPanel {
|
|
|
36
29
|
</div>
|
|
37
30
|
`;
|
|
38
31
|
|
|
39
|
-
// Render navigation tabs
|
|
40
32
|
this.navigationTabs = new NavigationTabs(this.state, this.options);
|
|
41
33
|
const navContainer = this.element.querySelector('.messenger-panel-nav');
|
|
42
34
|
navContainer.appendChild(this.navigationTabs.render());
|
|
43
35
|
|
|
44
|
-
// Render initial view
|
|
45
36
|
this._renderCurrentView();
|
|
46
37
|
|
|
47
|
-
// Subscribe to state changes
|
|
48
38
|
this._unsubscribe = this.state.subscribe((type) => {
|
|
49
39
|
if (type === 'viewChange') {
|
|
50
40
|
this._renderCurrentView();
|
|
@@ -57,44 +47,35 @@ export class MessengerPanel {
|
|
|
57
47
|
_renderCurrentView() {
|
|
58
48
|
const viewsContainer = this.element.querySelector('.messenger-panel-views');
|
|
59
49
|
|
|
60
|
-
// Destroy current view if exists
|
|
61
50
|
if (this.currentViewComponent && this.currentViewComponent.destroy) {
|
|
62
51
|
this.currentViewComponent.destroy();
|
|
63
52
|
}
|
|
64
53
|
|
|
65
|
-
// Clear container
|
|
66
54
|
viewsContainer.innerHTML = '';
|
|
67
55
|
|
|
68
|
-
// Get view class
|
|
69
56
|
const ViewClass = this.viewRegistry[this.state.currentView];
|
|
70
57
|
if (ViewClass) {
|
|
71
58
|
this.currentViewComponent = new ViewClass(this.state, this.options);
|
|
72
59
|
viewsContainer.appendChild(this.currentViewComponent.render());
|
|
73
60
|
} else {
|
|
74
|
-
viewsContainer.innerHTML = `<div class="messenger-
|
|
61
|
+
viewsContainer.innerHTML = `<div class="messenger-empty-state">
|
|
62
|
+
<p>View not found: ${this.state.currentView}</p>
|
|
63
|
+
</div>`;
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
66
|
|
|
78
|
-
/**
|
|
79
|
-
* Show the panel with animation
|
|
80
|
-
*/
|
|
81
67
|
show() {
|
|
82
68
|
if (this.element) {
|
|
83
69
|
this.element.style.display = 'block';
|
|
84
|
-
// Trigger animation
|
|
85
70
|
requestAnimationFrame(() => {
|
|
86
71
|
this.element.classList.add('open');
|
|
87
72
|
});
|
|
88
73
|
}
|
|
89
74
|
}
|
|
90
75
|
|
|
91
|
-
/**
|
|
92
|
-
* Hide the panel with animation
|
|
93
|
-
*/
|
|
94
76
|
hide() {
|
|
95
77
|
if (this.element) {
|
|
96
78
|
this.element.classList.remove('open');
|
|
97
|
-
// Wait for animation to complete
|
|
98
79
|
setTimeout(() => {
|
|
99
80
|
if (this.element) {
|
|
100
81
|
this.element.style.display = 'none';
|
|
@@ -103,9 +84,6 @@ export class MessengerPanel {
|
|
|
103
84
|
}
|
|
104
85
|
}
|
|
105
86
|
|
|
106
|
-
/**
|
|
107
|
-
* Set panel header content (used by views)
|
|
108
|
-
*/
|
|
109
87
|
setHeader(headerContent) {
|
|
110
88
|
const header = this.element.querySelector('.messenger-panel-header');
|
|
111
89
|
if (header) {
|
|
@@ -127,4 +105,4 @@ export class MessengerPanel {
|
|
|
127
105
|
this.element.parentNode.removeChild(this.element);
|
|
128
106
|
}
|
|
129
107
|
}
|
|
130
|
-
}
|
|
108
|
+
}
|