@product7/feedback-sdk 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/feedback-sdk.js",
6
6
  "module": "src/index.js",
@@ -79,7 +79,9 @@ const MOCK_CONVERSATIONS = [
79
79
  id: 'conv_2',
80
80
  subject: 'Feature request',
81
81
  status: 'open',
82
- last_message_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString(),
82
+ last_message_at: new Date(
83
+ Date.now() - 6 * 24 * 60 * 60 * 1000
84
+ ).toISOString(),
83
85
  created_at: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(),
84
86
  unread: 0,
85
87
  assigned_user: {
@@ -121,11 +123,14 @@ const MOCK_MESSAGES = {
121
123
  id: 'msg_4',
122
124
  content: 'I would love to see a dark mode feature!',
123
125
  sender_type: 'customer',
124
- created_at: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000 - 30 * 60 * 1000).toISOString(),
126
+ created_at: new Date(
127
+ Date.now() - 6 * 24 * 60 * 60 * 1000 - 30 * 60 * 1000
128
+ ).toISOString(),
125
129
  },
126
130
  {
127
131
  id: 'msg_5',
128
- content: "Great suggestion! That feature will be available next week. I'll let you know when it's ready.",
132
+ content:
133
+ "Great suggestion! That feature will be available next week. I'll let you know when it's ready.",
129
134
  sender_type: 'agent',
130
135
  sender_name: 'Tom',
131
136
  sender_avatar: null,
@@ -155,7 +160,8 @@ const MOCK_HELP_COLLECTIONS = [
155
160
  {
156
161
  id: 'collection_3',
157
162
  title: 'AI Agent',
158
- description: 'Resolving customer questions instantly and accurately—from live chat to email.',
163
+ description:
164
+ 'Resolving customer questions instantly and accurately—from live chat to email.',
159
165
  articleCount: 82,
160
166
  icon: 'ph-robot',
161
167
  url: '#',
@@ -163,7 +169,8 @@ const MOCK_HELP_COLLECTIONS = [
163
169
  {
164
170
  id: 'collection_4',
165
171
  title: 'Channels',
166
- description: 'Enabling the channels you use to communicate with customers, all from the Inbox.',
172
+ description:
173
+ 'Enabling the channels you use to communicate with customers, all from the Inbox.',
167
174
  articleCount: 45,
168
175
  icon: 'ph-chat-circle',
169
176
  url: '#',
@@ -710,10 +717,13 @@ export class APIService {
710
717
  };
711
718
  }
712
719
 
713
- return this._makeRequest(`/widget/messenger/conversations/${conversationId}`, {
714
- method: 'GET',
715
- headers: { Authorization: `Bearer ${this.sessionToken}` },
716
- });
720
+ return this._makeRequest(
721
+ `/widget/messenger/conversations/${conversationId}`,
722
+ {
723
+ method: 'GET',
724
+ headers: { Authorization: `Bearer ${this.sessionToken}` },
725
+ }
726
+ );
717
727
  }
718
728
 
719
729
  /**
@@ -820,14 +830,17 @@ export class APIService {
820
830
  return { status: true, data: newMessage };
821
831
  }
822
832
 
823
- return this._makeRequest(`/widget/messenger/conversations/${conversationId}/messages`, {
824
- method: 'POST',
825
- headers: {
826
- 'Content-Type': 'application/json',
827
- Authorization: `Bearer ${this.sessionToken}`,
828
- },
829
- body: JSON.stringify({ content: data.content }),
830
- });
833
+ return this._makeRequest(
834
+ `/widget/messenger/conversations/${conversationId}/messages`,
835
+ {
836
+ method: 'POST',
837
+ headers: {
838
+ 'Content-Type': 'application/json',
839
+ Authorization: `Bearer ${this.sessionToken}`,
840
+ },
841
+ body: JSON.stringify({ content: data.content }),
842
+ }
843
+ );
831
844
  }
832
845
 
833
846
  /**
@@ -845,14 +858,17 @@ export class APIService {
845
858
  return { status: true };
846
859
  }
847
860
 
848
- return this._makeRequest(`/widget/messenger/conversations/${conversationId}/typing`, {
849
- method: 'POST',
850
- headers: {
851
- 'Content-Type': 'application/json',
852
- Authorization: `Bearer ${this.sessionToken}`,
853
- },
854
- body: JSON.stringify({ is_typing: isTyping }),
855
- });
861
+ return this._makeRequest(
862
+ `/widget/messenger/conversations/${conversationId}/typing`,
863
+ {
864
+ method: 'POST',
865
+ headers: {
866
+ 'Content-Type': 'application/json',
867
+ Authorization: `Bearer ${this.sessionToken}`,
868
+ },
869
+ body: JSON.stringify({ is_typing: isTyping }),
870
+ }
871
+ );
856
872
  }
857
873
 
858
874
  /**
@@ -869,10 +885,13 @@ export class APIService {
869
885
  return { status: true };
870
886
  }
871
887
 
872
- return this._makeRequest(`/widget/messenger/conversations/${conversationId}/read`, {
873
- method: 'POST',
874
- headers: { Authorization: `Bearer ${this.sessionToken}` },
875
- });
888
+ return this._makeRequest(
889
+ `/widget/messenger/conversations/${conversationId}/read`,
890
+ {
891
+ method: 'POST',
892
+ headers: { Authorization: `Bearer ${this.sessionToken}` },
893
+ }
894
+ );
876
895
  }
877
896
 
878
897
  /**
@@ -885,7 +904,10 @@ export class APIService {
885
904
  }
886
905
 
887
906
  if (this.mock) {
888
- const count = MOCK_CONVERSATIONS.reduce((sum, c) => sum + (c.unread || 0), 0);
907
+ const count = MOCK_CONVERSATIONS.reduce(
908
+ (sum, c) => sum + (c.unread || 0),
909
+ 0
910
+ );
889
911
  return {
890
912
  status: true,
891
913
  data: { unread_count: count, unread_conversations: count > 0 ? 1 : 0 },
@@ -915,17 +937,20 @@ export class APIService {
915
937
  return { status: true, message: 'Thank you for your feedback!' };
916
938
  }
917
939
 
918
- return this._makeRequest(`/widget/messenger/conversations/${conversationId}/rate`, {
919
- method: 'POST',
920
- headers: {
921
- 'Content-Type': 'application/json',
922
- Authorization: `Bearer ${this.sessionToken}`,
923
- },
924
- body: JSON.stringify({
925
- rating: data.rating,
926
- comment: data.comment || '',
927
- }),
928
- });
940
+ return this._makeRequest(
941
+ `/widget/messenger/conversations/${conversationId}/rate`,
942
+ {
943
+ method: 'POST',
944
+ headers: {
945
+ 'Content-Type': 'application/json',
946
+ Authorization: `Bearer ${this.sessionToken}`,
947
+ },
948
+ body: JSON.stringify({
949
+ rating: data.rating,
950
+ comment: data.comment || '',
951
+ }),
952
+ }
953
+ );
929
954
  }
930
955
 
931
956
  /**
@@ -358,8 +358,10 @@ export class FeedbackSDK {
358
358
  // Check backend tracking first (from init response)
359
359
  if (this.config.last_feedback_at) {
360
360
  try {
361
- const backendTimestamp = new Date(this.config.last_feedback_at).getTime();
362
- if ((now - backendTimestamp) < cooldownMs) {
361
+ const backendTimestamp = new Date(
362
+ this.config.last_feedback_at
363
+ ).getTime();
364
+ if (now - backendTimestamp < cooldownMs) {
363
365
  return true;
364
366
  }
365
367
  } catch (e) {
@@ -374,7 +376,7 @@ export class FeedbackSDK {
374
376
  if (!stored) return false;
375
377
 
376
378
  const data = JSON.parse(stored);
377
- return (now - data.submittedAt) < cooldownMs;
379
+ return now - data.submittedAt < cooldownMs;
378
380
  } catch (e) {
379
381
  return false;
380
382
  }
@@ -208,7 +208,9 @@ export class WebSocketService {
208
208
 
209
209
  this.reconnectAttempts++;
210
210
  const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
211
- console.log(`[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
211
+ console.log(
212
+ `[WebSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`
213
+ );
212
214
 
213
215
  setTimeout(() => {
214
216
  this.connect();
@@ -52,7 +52,7 @@ export class BaseWidget {
52
52
  if (this.options.suppressAfterSubmission && this._hasRecentlySubmitted()) {
53
53
  this.sdk.eventBus.emit('widget:suppressed', {
54
54
  widget: this,
55
- reason: 'recently_submitted'
55
+ reason: 'recently_submitted',
56
56
  });
57
57
  return this;
58
58
  }
@@ -276,8 +276,10 @@ export class BaseWidget {
276
276
  // Check backend tracking first (from init response)
277
277
  if (this.sdk.config.last_feedback_at) {
278
278
  try {
279
- const backendTimestamp = new Date(this.sdk.config.last_feedback_at).getTime();
280
- if ((now - backendTimestamp) < cooldownMs) {
279
+ const backendTimestamp = new Date(
280
+ this.sdk.config.last_feedback_at
281
+ ).getTime();
282
+ if (now - backendTimestamp < cooldownMs) {
281
283
  return true;
282
284
  }
283
285
  } catch (e) {
@@ -296,7 +298,7 @@ export class BaseWidget {
296
298
  const data = JSON.parse(stored);
297
299
  const submittedAt = data.submittedAt;
298
300
 
299
- return (now - submittedAt) < cooldownMs;
301
+ return now - submittedAt < cooldownMs;
300
302
  } catch (e) {
301
303
  // localStorage may not be available or data is corrupted
302
304
  return false;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * MessengerWidget - Full-featured Messenger/Chat widget
3
3
  */
4
+ import { WebSocketService } from '../core/WebSocketService.js';
4
5
  import { BaseWidget } from './BaseWidget.js';
5
6
  import { MessengerState } from './messenger/MessengerState.js';
6
7
  import { MessengerLauncher } from './messenger/components/MessengerLauncher.js';
@@ -10,7 +11,6 @@ import { ChatView } from './messenger/views/ChatView.js';
10
11
  import { ConversationsView } from './messenger/views/ConversationsView.js';
11
12
  import { HelpView } from './messenger/views/HelpView.js';
12
13
  import { HomeView } from './messenger/views/HomeView.js';
13
- import { WebSocketService } from '../core/WebSocketService.js';
14
14
 
15
15
  export class MessengerWidget extends BaseWidget {
16
16
  constructor(options) {
@@ -216,7 +216,10 @@ export class MessengerWidget extends BaseWidget {
216
216
  this.messengerState.addMessage(conversation_id, localMessage);
217
217
 
218
218
  // Update unread count if panel is closed or viewing different conversation
219
- if (!this.messengerState.isOpen || this.messengerState.activeConversationId !== conversation_id) {
219
+ if (
220
+ !this.messengerState.isOpen ||
221
+ this.messengerState.activeConversationId !== conversation_id
222
+ ) {
220
223
  this._updateUnreadCount();
221
224
  }
222
225
  }
@@ -250,7 +253,9 @@ export class MessengerWidget extends BaseWidget {
250
253
  const response = await this.apiService.getUnreadCount();
251
254
  if (response.status && response.data) {
252
255
  this.messengerState.unreadCount = response.data.unread_count || 0;
253
- this.messengerState._notify('unreadCountChange', { count: this.messengerState.unreadCount });
256
+ this.messengerState._notify('unreadCountChange', {
257
+ count: this.messengerState.unreadCount,
258
+ });
254
259
  }
255
260
  } catch (error) {
256
261
  console.error('[MessengerWidget] Failed to get unread count:', error);
@@ -424,9 +429,16 @@ export class MessengerWidget extends BaseWidget {
424
429
  // Transform API response to local format
425
430
  return response.data.map((conv) => ({
426
431
  id: conv.id,
427
- title: conv.subject || `Chat with ${conv.assigned_user?.name || 'Support'}`,
432
+ title:
433
+ conv.subject ||
434
+ `Chat with ${conv.assigned_user?.name || 'Support'}`,
428
435
  participants: conv.assigned_user
429
- ? [{ name: conv.assigned_user.name, avatarUrl: conv.assigned_user.avatar }]
436
+ ? [
437
+ {
438
+ name: conv.assigned_user.name,
439
+ avatarUrl: conv.assigned_user.avatar,
440
+ },
441
+ ]
430
442
  : [{ name: 'Support', avatarUrl: null }],
431
443
  lastMessage: conv.preview || conv.snippet || '',
432
444
  lastMessageTime: conv.last_message_at,
@@ -450,7 +462,8 @@ export class MessengerWidget extends BaseWidget {
450
462
  id: collection.id,
451
463
  title: collection.title || collection.name,
452
464
  description: collection.description || '',
453
- articleCount: collection.article_count || collection.articleCount || 0,
465
+ articleCount:
466
+ collection.article_count || collection.articleCount || 0,
454
467
  icon: collection.icon || 'ph-book-open',
455
468
  url: collection.url || `#/help/${collection.slug || collection.id}`,
456
469
  }));
@@ -475,7 +488,9 @@ export class MessengerWidget extends BaseWidget {
475
488
  isOwn: msg.sender_type === 'customer',
476
489
  timestamp: msg.created_at,
477
490
  sender: {
478
- name: msg.sender_name || (msg.sender_type === 'customer' ? 'You' : 'Support'),
491
+ name:
492
+ msg.sender_name ||
493
+ (msg.sender_type === 'customer' ? 'You' : 'Support'),
479
494
  avatarUrl: msg.sender_avatar || null,
480
495
  },
481
496
  }));
@@ -37,7 +37,10 @@ export class ChatView {
37
37
  data.conversationId === this.state.activeConversationId
38
38
  ) {
39
39
  this._hideTypingIndicator();
40
- } else if (type === 'messagesUpdate' && data.conversationId === this.state.activeConversationId) {
40
+ } else if (
41
+ type === 'messagesUpdate' &&
42
+ data.conversationId === this.state.activeConversationId
43
+ ) {
41
44
  this._updateContent();
42
45
  }
43
46
  });
@@ -97,14 +100,17 @@ export class ChatView {
97
100
  </div>
98
101
  `;
99
102
 
100
- this._typingIndicator = this.element.querySelector('.messenger-typing-indicator');
103
+ this._typingIndicator = this.element.querySelector(
104
+ '.messenger-typing-indicator'
105
+ );
101
106
  this._attachEvents();
102
107
  this._scrollToBottom();
103
108
  }
104
109
 
105
110
  _renderEmptyState(isNewConversation = false) {
106
111
  const avatarHtml = this._renderTeamAvatars();
107
- const responseTime = this.state.responseTime || 'We typically reply within a few minutes';
112
+ const responseTime =
113
+ this.state.responseTime || 'We typically reply within a few minutes';
108
114
  const isOnline = this.state.agentsOnline;
109
115
 
110
116
  return `
@@ -360,7 +366,9 @@ export class ChatView {
360
366
  _showTypingIndicator(userName) {
361
367
  if (this._typingIndicator) {
362
368
  this._typingIndicator.style.display = 'flex';
363
- const textEl = this._typingIndicator.querySelector('.messenger-typing-text');
369
+ const textEl = this._typingIndicator.querySelector(
370
+ '.messenger-typing-text'
371
+ );
364
372
  if (textEl) {
365
373
  textEl.textContent = `${userName || 'Support'} is typing...`;
366
374
  }
@@ -17,7 +17,11 @@ export class HomeView {
17
17
 
18
18
  // Subscribe to state changes to re-render when data loads
19
19
  this._unsubscribe = this.state.subscribe((type) => {
20
- if (type === 'homeChangelogUpdate' || type === 'conversationsUpdate' || type === 'availabilityUpdate') {
20
+ if (
21
+ type === 'homeChangelogUpdate' ||
22
+ type === 'conversationsUpdate' ||
23
+ type === 'availabilityUpdate'
24
+ ) {
21
25
  this._updateContent();
22
26
  }
23
27
  });
@@ -94,7 +98,8 @@ export class HomeView {
94
98
 
95
99
  _renderAvailabilityStatus() {
96
100
  const isOnline = this.state.agentsOnline;
97
- const responseTime = this.state.responseTime || 'We typically reply within a few minutes';
101
+ const responseTime =
102
+ this.state.responseTime || 'We typically reply within a few minutes';
98
103
 
99
104
  if (isOnline) {
100
105
  return `
package/types/index.d.ts CHANGED
@@ -159,6 +159,55 @@ declare module '@product7/feedback-sdk' {
159
159
  refresh(): Promise<void>;
160
160
  }
161
161
 
162
+ export interface MessengerWidgetOptions {
163
+ position?: 'bottom-right' | 'bottom-left';
164
+ theme?: 'light' | 'dark';
165
+ teamName?: string;
166
+ teamAvatars?: string[];
167
+ welcomeMessage?: string;
168
+ enableHelp?: boolean;
169
+ enableChangelog?: boolean;
170
+ logoUrl?: string;
171
+ primaryColor?: string;
172
+ featuredContent?: {
173
+ title: string;
174
+ description: string;
175
+ imageUrl?: string;
176
+ action?: {
177
+ type: 'url' | 'view';
178
+ value: string;
179
+ label: string;
180
+ };
181
+ };
182
+ onSendMessage?: (conversationId: string, message: any) => void;
183
+ onArticleClick?: (article: any) => void;
184
+ onChangelogClick?: (changelog: any) => void;
185
+ }
186
+
187
+ export interface MessengerWidget {
188
+ id: string;
189
+ type: 'messenger';
190
+ mount(container?: string | HTMLElement): this;
191
+ destroy(): void;
192
+ open(): void;
193
+ close(): void;
194
+ toggle(): void;
195
+ navigateTo(view: 'home' | 'messages' | 'chat' | 'help' | 'changelog'): void;
196
+ setConversations(conversations: any[]): void;
197
+ setHelpArticles(articles: any[]): void;
198
+ setChangelogItems(items: any[]): void;
199
+ setHomeChangelogItems(items: any[]): void;
200
+ setUnreadCount(count: number): void;
201
+ loadInitialData(): Promise<void>;
202
+ checkAgentAvailability(): Promise<any>;
203
+ getState(): {
204
+ isOpen: boolean;
205
+ currentView: string;
206
+ unreadCount: number;
207
+ conversations: any[];
208
+ };
209
+ }
210
+
162
211
  const FeedbackSDKDefault: FeedbackSDKExport;
163
212
  export default FeedbackSDKDefault;
164
213
  }