@product7/feedback-sdk 1.4.3 → 1.4.5

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.
@@ -109,18 +109,14 @@ export class ChangelogWidget extends BaseWidget {
109
109
 
110
110
  this.modalElement
111
111
  .querySelector('.changelog-modal-container')
112
- .addEventListener('click', (e) => {
113
- e.stopPropagation();
114
- });
112
+ .addEventListener('click', (e) => e.stopPropagation());
115
113
 
116
114
  this.modalElement
117
115
  .querySelector('.changelog-modal-close')
118
116
  .addEventListener('click', () => this.closeModal());
119
117
 
120
118
  this._escapeHandler = (e) => {
121
- if (e.key === 'Escape') {
122
- this.closeModal();
123
- }
119
+ if (e.key === 'Escape') this.closeModal();
124
120
  };
125
121
  document.addEventListener('keydown', this._escapeHandler);
126
122
  }
@@ -198,9 +194,7 @@ export class ChangelogWidget extends BaseWidget {
198
194
  }
199
195
 
200
196
  setTimeout(() => {
201
- if (container.parentNode) {
202
- container.parentNode.removeChild(container);
203
- }
197
+ if (container.parentNode) container.parentNode.removeChild(container);
204
198
  }, 2500);
205
199
  }
206
200
 
@@ -263,18 +257,14 @@ export class ChangelogWidget extends BaseWidget {
263
257
 
264
258
  this.listModalElement
265
259
  .querySelector('.changelog-list-modal-container')
266
- .addEventListener('click', (e) => {
267
- e.stopPropagation();
268
- });
260
+ .addEventListener('click', (e) => e.stopPropagation());
269
261
 
270
262
  this.listModalElement
271
263
  .querySelector('.changelog-list-modal-close')
272
264
  .addEventListener('click', () => this.closeSidebar());
273
265
 
274
266
  this._listModalEscapeHandler = (e) => {
275
- if (e.key === 'Escape') {
276
- this.closeSidebar();
277
- }
267
+ if (e.key === 'Escape') this.closeSidebar();
278
268
  };
279
269
  document.addEventListener('keydown', this._listModalEscapeHandler);
280
270
  }
@@ -284,9 +274,7 @@ export class ChangelogWidget extends BaseWidget {
284
274
  '.changelog-list-modal-body'
285
275
  );
286
276
 
287
- if (this.isLoading) {
288
- return;
289
- }
277
+ if (this.isLoading) return;
290
278
 
291
279
  if (this.changelogs.length === 0) {
292
280
  body.innerHTML = `
@@ -309,8 +297,7 @@ export class ChangelogWidget extends BaseWidget {
309
297
 
310
298
  body.querySelectorAll('.changelog-list-item').forEach((item, index) => {
311
299
  item.addEventListener('click', () => {
312
- const changelog = this.changelogs[index];
313
- this._handleViewUpdate(changelog);
300
+ this._handleViewUpdate(this.changelogs[index]);
314
301
  });
315
302
  });
316
303
  }
@@ -321,6 +308,10 @@ export class ChangelogWidget extends BaseWidget {
321
308
  const date = changelog.published_at
322
309
  ? this._formatDate(changelog.published_at)
323
310
  : '';
311
+ const description = this._truncateDescription(
312
+ changelog.excerpt || changelog.description,
313
+ 120
314
+ );
324
315
 
325
316
  return `
326
317
  <div class="changelog-list-item" data-index="${index}">
@@ -352,13 +343,7 @@ export class ChangelogWidget extends BaseWidget {
352
343
  : ''
353
344
  }
354
345
  <h3 class="changelog-list-item-title">${changelog.title}</h3>
355
- ${
356
- changelog.excerpt || changelog.description
357
- ? `
358
- <p class="changelog-list-item-description">${changelog.excerpt || changelog.description}</p>
359
- `
360
- : ''
361
- }
346
+ ${description ? `<p class="changelog-list-item-description">${description}</p>` : ''}
362
347
  </div>
363
348
  </div>
364
349
  </div>
@@ -385,9 +370,7 @@ export class ChangelogWidget extends BaseWidget {
385
370
  _renderCurrentChangelog() {
386
371
  const content = this.modalElement.querySelector('.changelog-modal-content');
387
372
 
388
- if (this.isLoading) {
389
- return;
390
- }
373
+ if (this.isLoading) return;
391
374
 
392
375
  if (this.changelogs.length === 0) {
393
376
  content.innerHTML = `
@@ -406,6 +389,10 @@ export class ChangelogWidget extends BaseWidget {
406
389
  const hasImage = changelog.cover_image || changelog.image;
407
390
  const imageUrl = changelog.cover_image || changelog.image;
408
391
  const hasMultiple = this.changelogs.length > 1;
392
+ const description = this._truncateDescription(
393
+ changelog.excerpt || changelog.description,
394
+ 160
395
+ );
409
396
 
410
397
  content.innerHTML = `
411
398
  <div class="changelog-popup-item">
@@ -420,13 +407,7 @@ export class ChangelogWidget extends BaseWidget {
420
407
  }
421
408
  <div class="changelog-popup-body">
422
409
  <h2 class="changelog-popup-title">${changelog.title}</h2>
423
- ${
424
- changelog.excerpt || changelog.description
425
- ? `
426
- <p class="changelog-popup-description">${changelog.excerpt || changelog.description}</p>
427
- `
428
- : ''
429
- }
410
+ ${description ? `<p class="changelog-popup-description">${description}</p>` : ''}
430
411
  <button class="changelog-popup-btn" type="button">
431
412
  ${this.options.viewButtonText || 'View Update'}
432
413
  </button>
@@ -473,8 +454,7 @@ export class ChangelogWidget extends BaseWidget {
473
454
  if (hasMultiple) {
474
455
  content.querySelectorAll('.changelog-dot').forEach((dot) => {
475
456
  dot.addEventListener('click', (e) => {
476
- const index = parseInt(e.target.dataset.index, 10);
477
- this.currentIndex = index;
457
+ this.currentIndex = parseInt(e.target.dataset.index, 10);
478
458
  this._renderCurrentChangelog();
479
459
  });
480
460
  });
@@ -484,10 +464,19 @@ export class ChangelogWidget extends BaseWidget {
484
464
  _handleViewUpdate(changelog) {
485
465
  this.sdk.eventBus.emit('changelog:view', { changelog });
486
466
 
487
- if (changelog.url || changelog.slug) {
488
- const url =
489
- changelog.url ||
490
- `${this.options.changelogBaseUrl || ''}/${changelog.slug}`;
467
+ const changelogBase = (this.options.changelogBaseUrl || '').replace(
468
+ /\/$/,
469
+ ''
470
+ );
471
+ const url =
472
+ changelog.url ||
473
+ (changelog.slug && changelogBase
474
+ ? `${changelogBase}/${changelog.slug}`
475
+ : null) ||
476
+ changelogBase ||
477
+ null;
478
+
479
+ if (url) {
491
480
  if (this.options.openInNewTab !== false) {
492
481
  window.open(url, '_blank', 'noopener,noreferrer');
493
482
  } else {
@@ -500,10 +489,26 @@ export class ChangelogWidget extends BaseWidget {
500
489
  }
501
490
  }
502
491
 
492
+ _stripHtml(html) {
493
+ const tmp = document.createElement('div');
494
+ tmp.innerHTML = html;
495
+ return (tmp.textContent || tmp.innerText || '').trim();
496
+ }
497
+
498
+ _truncateDescription(html, maxLength = 120) {
499
+ if (!html) return '';
500
+ const plain = this._stripHtml(html);
501
+ if (plain.length <= maxLength) return plain;
502
+ return plain.substring(0, maxLength).trimEnd() + '...';
503
+ }
504
+
503
505
  _formatDate(dateString) {
504
506
  const date = new Date(dateString);
505
- const options = { year: 'numeric', month: 'short', day: 'numeric' };
506
- return date.toLocaleDateString('en-US', options);
507
+ return date.toLocaleDateString('en-US', {
508
+ year: 'numeric',
509
+ month: 'short',
510
+ day: 'numeric',
511
+ });
507
512
  }
508
513
 
509
514
  _getContrastColor(hexColor) {
@@ -517,16 +522,12 @@ export class ChangelogWidget extends BaseWidget {
517
522
 
518
523
  hideBadge() {
519
524
  const badge = this.element?.querySelector('.changelog-badge');
520
- if (badge) {
521
- badge.style.display = 'none';
522
- }
525
+ if (badge) badge.style.display = 'none';
523
526
  }
524
527
 
525
528
  showBadge() {
526
529
  const badge = this.element?.querySelector('.changelog-badge');
527
- if (badge) {
528
- badge.style.display = 'block';
529
- }
530
+ if (badge) badge.style.display = 'block';
530
531
  }
531
532
 
532
533
  nextChangelog() {
@@ -31,7 +31,12 @@ export class MessengerWidget extends BaseWidget {
31
31
  enableHelp: options.enableHelp !== false,
32
32
  enableChangelog: options.enableChangelog !== false,
33
33
  featuredContent: options.featuredContent || null,
34
+ feedbackUrl: options.feedbackUrl || null,
35
+ changelogUrl: options.changelogUrl || null,
36
+ helpUrl: options.helpUrl || null,
37
+ roadmapUrl: options.roadmapUrl || null,
34
38
  onSendMessage: options.onSendMessage || null,
39
+ onFeedbackClick: options.onFeedbackClick || null,
35
40
  onArticleClick: options.onArticleClick || null,
36
41
  onChangelogClick: options.onChangelogClick || null,
37
42
  };
@@ -43,6 +48,12 @@ export class MessengerWidget extends BaseWidget {
43
48
  enableHelp: this.messengerOptions.enableHelp,
44
49
  enableChangelog: this.messengerOptions.enableChangelog,
45
50
  userContext: this.sdk?.apiService?.getUserContext() || null,
51
+ urls: {
52
+ feedback: this.messengerOptions.feedbackUrl,
53
+ changelog: this.messengerOptions.changelogUrl,
54
+ help: this.messengerOptions.helpUrl,
55
+ roadmap: this.messengerOptions.roadmapUrl,
56
+ },
46
57
  });
47
58
 
48
59
  this.launcher = null;
@@ -89,6 +100,7 @@ export class MessengerWidget extends BaseWidget {
89
100
  onSelectConversation: this._handleSelectConversation.bind(this),
90
101
  onStartNewConversation: this._handleNewConversationClick.bind(this),
91
102
  onIdentifyContact: this._handleIdentifyContact.bind(this),
103
+ onFeedbackClick: this.messengerOptions.onFeedbackClick,
92
104
  onArticleClick: this.messengerOptions.onArticleClick,
93
105
  onChangelogClick: this.messengerOptions.onChangelogClick,
94
106
  });
@@ -544,12 +556,20 @@ export class MessengerWidget extends BaseWidget {
544
556
  const response = await this.apiService.getHelpCollections();
545
557
  if (response.success && response.data) {
546
558
  const collections = response.data.collections || response.data;
559
+ const helpBase = (this.messengerOptions.helpUrl || '').replace(
560
+ /\/$/,
561
+ ''
562
+ );
563
+
547
564
  return collections.map((collection) => ({
548
565
  id: collection.id,
549
566
  title: collection.title,
550
567
  description: collection.description || '',
551
568
  articleCount: collection.article_count || 0,
552
- url: collection.url_slug ? `/help-docs/collections/${collection.url_slug}` : null,
569
+ url:
570
+ collection.url_slug && helpBase
571
+ ? `${helpBase}/collections/${collection.url_slug}`
572
+ : helpBase || null,
553
573
  }));
554
574
  }
555
575
  return [];
@@ -654,7 +674,7 @@ export class MessengerWidget extends BaseWidget {
654
674
  try {
655
675
  await this.apiService.sendTypingIndicator(conversationId, isTyping);
656
676
  } catch (error) {
657
- // Silent fail
677
+ // silent fail
658
678
  }
659
679
  }
660
680
 
@@ -762,6 +782,9 @@ export class MessengerWidget extends BaseWidget {
762
782
 
763
783
  if (response.success && response.data) {
764
784
  const changelogs = Array.isArray(response.data) ? response.data : [];
785
+ const changelogBase = (
786
+ this.messengerOptions.changelogUrl || ''
787
+ ).replace(/\/$/, '');
765
788
 
766
789
  const mappedItems = changelogs.map((item) => ({
767
790
  id: item.id,
@@ -770,7 +793,10 @@ export class MessengerWidget extends BaseWidget {
770
793
  tags: item.labels ? item.labels.map((label) => label.name) : [],
771
794
  coverImage: item.cover_image || null,
772
795
  publishedAt: item.published_at,
773
- url: item.slug ? `/changelog/${item.slug}` : null,
796
+ url:
797
+ item.slug && changelogBase
798
+ ? `${changelogBase}/${item.slug}`
799
+ : changelogBase || null,
774
800
  }));
775
801
 
776
802
  return {
@@ -34,6 +34,13 @@ export class MessengerState {
34
34
  this.isLoading = false;
35
35
  this.isLoadingMessages = false;
36
36
 
37
+ this.urls = options.urls || {
38
+ feedback: null,
39
+ changelog: null,
40
+ help: null,
41
+ roadmap: null,
42
+ };
43
+
37
44
  this._listeners = new Set();
38
45
  }
39
46
 
@@ -23,14 +23,14 @@ export class ChangelogView {
23
23
 
24
24
  _updateContent() {
25
25
  this.element.innerHTML = `
26
- <div class="messenger-changelog-header">
27
- <h2>Latest Updates</h2>
28
- </div>
29
-
30
- <div class="messenger-changelog-body">
31
- <div class="messenger-changelog-list"></div>
32
- </div>
33
- `;
26
+ <div class="messenger-changelog-header">
27
+ <h2>Latest Updates</h2>
28
+ </div>
29
+
30
+ <div class="messenger-changelog-body">
31
+ <div class="messenger-changelog-list"></div>
32
+ </div>
33
+ `;
34
34
 
35
35
  this._updateChangelogList();
36
36
  this._attachEvents();
@@ -23,30 +23,29 @@ export class HelpView {
23
23
 
24
24
  _updateContent() {
25
25
  const searchQuery = this.state.helpSearchQuery || '';
26
- const collections = this.state.helpArticles || [];
27
26
 
28
27
  this.element.innerHTML = `
29
- <div class="messenger-help-header">
30
- <div class="messenger-help-header-top">
31
- <h2>Help</h2>
32
- <button class="sdk-close-btn" aria-label="Close">
33
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256">
34
- <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>
35
- </svg>
36
- </button>
37
- </div>
38
- <input
39
- type="text"
40
- class="messenger-help-search-input"
41
- placeholder="Search for help..."
42
- value="${searchQuery}"
43
- />
44
- </div>
45
-
46
- <div class="messenger-help-body">
47
- <div class="messenger-help-collections"></div>
48
- </div>
49
- `;
28
+ <div class="messenger-help-header">
29
+ <div class="messenger-help-header-top">
30
+ <h2>Help</h2>
31
+ <button class="sdk-close-btn" aria-label="Close">
32
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256">
33
+ <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>
34
+ </svg>
35
+ </button>
36
+ </div>
37
+ <input
38
+ type="text"
39
+ class="messenger-help-search-input"
40
+ placeholder="Search for help..."
41
+ value="${searchQuery}"
42
+ />
43
+ </div>
44
+
45
+ <div class="messenger-help-body">
46
+ <div class="messenger-help-collections"></div>
47
+ </div>
48
+ `;
50
49
 
51
50
  this._updateCollectionsList();
52
51
  this._attachEvents();
@@ -129,11 +129,6 @@ export class HomeView {
129
129
  `;
130
130
 
131
131
  if (openConversation) {
132
- const preview = openConversation.lastMessage
133
- ? openConversation.lastMessage.length > 40
134
- ? openConversation.lastMessage.substring(0, 40) + '...'
135
- : openConversation.lastMessage
136
- : 'Continue your conversation';
137
132
  return `
138
133
  <button class="messenger-home-message-btn messenger-home-continue-btn" data-conversation-id="${openConversation.id}">
139
134
  <div class="messenger-home-continue-info">
@@ -141,18 +136,19 @@ export class HomeView {
141
136
  </div>
142
137
  ${sendIcon}
143
138
  </button>
144
- <button class="messenger-home-message-btn" data-action="feedback">
139
+ <button class="messenger-home-message-btn messenger-feedback-btn" data-action="feedback">
145
140
  <span class="messenger-home-continue-label">Leave us feedback</span>
146
141
  ${caretIcon}
147
142
  </button>
148
143
  `;
149
144
  }
145
+
150
146
  return `
151
147
  <button class="messenger-home-message-btn">
152
148
  <span>Start a conversation</span>
153
149
  ${sendIcon}
154
150
  </button>
155
- <button class="messenger-home-message-btn" data-action="feedback">
151
+ <button class="messenger-home-message-btn messenger-feedback-btn" data-action="feedback">
156
152
  <span>Leave us feedback</span>
157
153
  ${caretIcon}
158
154
  </button>
@@ -234,7 +230,9 @@ export class HomeView {
234
230
  this.state.setOpen(false);
235
231
  });
236
232
 
237
- const msgBtn = this.element.querySelector('.messenger-home-message-btn');
233
+ const msgBtn = this.element.querySelector(
234
+ '.messenger-home-message-btn:not(.messenger-feedback-btn)'
235
+ );
238
236
  if (msgBtn) {
239
237
  msgBtn.addEventListener('click', () => {
240
238
  const convId = msgBtn.dataset.conversationId;
@@ -251,11 +249,35 @@ export class HomeView {
251
249
  });
252
250
  }
253
251
 
252
+ const feedbackBtn = this.element.querySelector('.messenger-feedback-btn');
253
+ if (feedbackBtn) {
254
+ feedbackBtn.addEventListener('click', () => {
255
+ const url = this.state.urls?.feedback;
256
+
257
+ if (this.options.onFeedbackClick) {
258
+ this.options.onFeedbackClick();
259
+ } else if (url) {
260
+ window.open(url, '_blank');
261
+ } else {
262
+ console.warn(
263
+ '[Messenger] No feedback destination configured. Set `feedbackUrl` in MessengerWidget options.'
264
+ );
265
+ }
266
+ });
267
+ }
268
+
254
269
  this.element
255
- .querySelectorAll('.messenger-home-changelog-item')
256
- .forEach((item) => {
257
- item.addEventListener('click', () => {
258
- this.state.setView('changelog');
270
+ .querySelectorAll('.messenger-home-changelog-card')
271
+ .forEach((card) => {
272
+ card.addEventListener('click', () => {
273
+ const item = this.state.homeChangelogItems.find(
274
+ (i) => i.id === card.dataset.changelogId
275
+ );
276
+ if (item?.url) {
277
+ window.open(item.url, '_blank');
278
+ } else {
279
+ this.state.setView('changelog');
280
+ }
259
281
  });
260
282
  });
261
283