@product7/feedback-sdk 1.3.3 → 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.
Files changed (37) hide show
  1. package/dist/feedback-sdk.js +2321 -3173
  2. package/dist/feedback-sdk.js.map +1 -1
  3. package/dist/feedback-sdk.min.js +1 -1
  4. package/dist/feedback-sdk.min.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/api/services/MessengerService.js +20 -0
  7. package/src/index.js +2 -3
  8. package/src/styles/base.js +27 -52
  9. package/src/styles/changelog.js +152 -269
  10. package/src/styles/components.js +489 -0
  11. package/src/styles/design-tokens.js +104 -0
  12. package/src/styles/feedback.js +59 -369
  13. package/src/styles/messenger-core.js +390 -0
  14. package/src/styles/messenger-features.js +347 -0
  15. package/src/styles/messenger-help.js +298 -0
  16. package/src/styles/messenger-themes.js +500 -0
  17. package/src/styles/messenger-views.js +618 -0
  18. package/src/styles/messenger.js +558 -0
  19. package/src/styles/styles.js +24 -2
  20. package/src/styles/surveys.js +354 -0
  21. package/src/widgets/BaseWidget.js +25 -58
  22. package/src/widgets/ButtonWidget.js +3 -18
  23. package/src/widgets/ChangelogWidget.js +7 -48
  24. package/src/widgets/InlineWidget.js +16 -13
  25. package/src/widgets/MessengerWidget.js +0 -4
  26. package/src/widgets/SurveyWidget.js +42 -146
  27. package/src/widgets/TabWidget.js +2 -22
  28. package/src/widgets/messenger/components/MessengerLauncher.js +10 -5
  29. package/src/widgets/messenger/components/MessengerPanel.js +5 -27
  30. package/src/widgets/messenger/components/NavigationTabs.js +5 -14
  31. package/src/widgets/messenger/views/ChangelogView.js +13 -14
  32. package/src/widgets/messenger/views/ChatView.js +43 -36
  33. package/src/widgets/messenger/views/ConversationsView.js +16 -21
  34. package/src/widgets/messenger/views/HelpView.js +10 -10
  35. package/src/widgets/messenger/views/HomeView.js +11 -16
  36. package/src/widgets/messenger/views/PreChatFormView.js +18 -30
  37. 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, // Backend survey ID for API calls
9
- surveyType: options.surveyType || 'nps', // 'nps', 'csat', 'ces', 'custom'
10
- position: options.position || 'bottom-right', // 'bottom-right', 'bottom-left', 'center', 'bottom'
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} theme-${this.surveyOptions.theme}`;
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" style="position: absolute; top: 12px; right: 12px; background: none; border: none; font-size: 20px; cursor: pointer; color: ${this.surveyOptions.theme === 'dark' ? '#888' : '#86868b'}; line-height: 1;">&times;</button>
83
- <h3 class="feedback-survey-title" style="margin: 0 0 8px 0; font-size: 18px; font-weight: 600; padding-right: 24px;">${config.title}</h3>
84
- <p class="feedback-survey-description" style="color: ${this.surveyOptions.theme === 'dark' ? '#aaa' : '#86868b'}; margin: 0 0 20px 0; font-size: 14px;">${config.description}</p>
71
+ <button class="feedback-survey-close">&times;</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" style="margin-top: 16px;">
87
- <textarea class="feedback-survey-textarea" placeholder="Any additional feedback? (optional)" style="width: 100%; padding: 12px; border: 1px solid ${this.surveyOptions.theme === 'dark' ? '#333' : '#d2d2d7'}; border-radius: 8px; font-size: 14px; resize: none; height: 80px; background: ${this.surveyOptions.theme === 'dark' ? '#2a2a2a' : '#fff'}; color: ${this.surveyOptions.theme === 'dark' ? '#fff' : '#1d1d1f'}; font-family: inherit; box-sizing: border-box;"></textarea>
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" style="width: 100%; margin-top: 12px; padding: 12px; background: #007aff; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; font-family: inherit;">Submit</button>
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" style="display: flex; justify-content: space-between; gap: 4px;">
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}" 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
+ <button class="feedback-survey-nps-btn" data-score="${n}">${n}</button>
119
107
  `
120
108
  )
121
109
  .join('')}
122
110
  </div>
123
- <div style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: ${this.surveyOptions.theme === 'dark' ? '#888' : '#86868b'};">
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" style="display: flex; justify-content: center; gap: 16px;">
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}" style="background: none; border: none; cursor: pointer; font-size: 36px; transition: transform 0.15s; padding: 8px;">${emoji}</button>
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 style="display: flex; justify-content: space-between; margin-top: 8px; font-size: 11px; color: ${this.surveyOptions.theme === 'dark' ? '#888' : '#86868b'};">
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" style="display: flex; justify-content: space-between; gap: 8px;">
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}" 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>
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 style="margin-bottom: 16px;">
187
- <label style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500;">What feature do you use most?</label>
188
- <select class="feedback-survey-select" data-question="feature" 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'};">
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 style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500;">How often do you use it?</label>
198
- <div class="feedback-survey-frequency" style="display: flex; gap: 8px;">
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}" 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>
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 style="margin-bottom: 16px;">
215
- <label style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500;">${q.label}</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}" 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'};">
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 || ''}" 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'}; box-sizing: border-box;">
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.style.background = '#007aff';
391
- btn.style.borderColor = '#007aff';
392
- btn.style.color = '#fff';
325
+ btn.classList.add('selected');
393
326
  } else {
394
- btn.style.background =
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
- btn.style.transform = btnScore === score ? 'scale(1.2)' : 'scale(1)';
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.style.background = '#007aff';
422
- btn.style.borderColor = '#007aff';
423
- btn.style.color = '#fff';
353
+ btn.classList.add('selected');
424
354
  } else {
425
- btn.style.background =
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.style.background = '#007aff';
442
- btn.style.borderColor = '#007aff';
443
- btn.style.color = '#fff';
366
+ btn.classList.add('selected');
444
367
  } else {
445
- btn.style.background =
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 style="display: flex; align-items: center; gap: 8px;">
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
+ }
@@ -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 theme-${this.options.theme} position-${this.options.position}`;
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 || '#155eff',
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" style="background: ${this.options.primaryColor};">
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="#ffffff" viewBox="0 0 256 256">
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="#ffffff" viewBox="0 0 256 256">
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} theme-${this.options.theme}`;
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-view-placeholder">View not found: ${this.state.currentView}</div>`;
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
+ }