@product7/product7-js 0.2.7 → 0.3.0

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/product7-js",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/product7-js.js",
6
6
  "module": "src/index.js",
@@ -47,8 +47,8 @@ export const designTokens = `
47
47
  --msg-bg-elevated: #FFFFFF;
48
48
  --msg-bg-hover: #F9FAFB;
49
49
  --msg-bg-input: rgba(255, 255, 255, 0.7);
50
- --msg-bg-bubble-own: #FFFFFF;
51
- --msg-bg-bubble-received: #F3F4F6;
50
+ --msg-bg-bubble-own: #F3F4F6;
51
+ --msg-bg-bubble-received: rgba(21, 94, 239, 0.06);
52
52
  --msg-bg-header-gradient: linear-gradient(180deg, #e0e7ff 0%, #f0f4ff 35%, #FFFFFF 65%);
53
53
  --msg-bg-header-glow1: radial-gradient(circle, rgba(21, 94, 239, 0.08) 0%, transparent 70%);
54
54
  --msg-bg-header-glow2: radial-gradient(circle, rgba(139, 92, 246, 0.05) 0%, transparent 70%);
@@ -57,7 +57,7 @@ export const designTokens = `
57
57
  --msg-text-tertiary: #6B7280;
58
58
  --msg-text-muted: #9CA3AF;
59
59
  --msg-border: #E5E7EB;
60
- --msg-border-bubble: #E5E7EB;
60
+ --msg-border-bubble: transparent;
61
61
  --msg-shadow-card: rgba(9, 30, 66, 0.25) 0px 1px 1px, rgba(9, 30, 66, 0.13) 0px 0px 1px 1px;
62
62
  --msg-shadow-card-hover: rgba(9, 30, 66, 0.3) 0px 2px 2px, rgba(9, 30, 66, 0.18) 0px 0px 1px 1px;
63
63
 
@@ -10,6 +10,84 @@ export const messengerComponentsStyles = `
10
10
  max-width: 85%;
11
11
  }
12
12
 
13
+ .messenger-message-system {
14
+ align-self: center;
15
+ display: flex;
16
+ align-items: center;
17
+ gap: var(--spacing-2);
18
+ padding: var(--spacing-2) 0;
19
+ width: 100%;
20
+ }
21
+
22
+ .messenger-message-system::before,
23
+ .messenger-message-system::after {
24
+ content: '';
25
+ flex: 1;
26
+ height: 1px;
27
+ background: var(--msg-border);
28
+ }
29
+
30
+ .messenger-message-system-text {
31
+ font-size: 0.6875rem;
32
+ color: var(--msg-text-tertiary);
33
+ white-space: nowrap;
34
+ padding: 0 var(--spacing-2);
35
+ letter-spacing: 0.01em;
36
+ }
37
+
38
+ /* Rich join/leave system event */
39
+ .messenger-message-system-event {
40
+ align-self: center;
41
+ display: flex;
42
+ flex-direction: column;
43
+ align-items: center;
44
+ gap: var(--spacing-2);
45
+ padding: var(--spacing-4) 0;
46
+ width: 100%;
47
+ text-align: center;
48
+ }
49
+
50
+ .messenger-message-system-event-avatar {
51
+ width: 3rem;
52
+ height: 3rem;
53
+ border-radius: var(--radius-full);
54
+ border: 2px solid var(--msg-border);
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ font-size: 1.125rem;
59
+ font-weight: var(--font-weight-semibold);
60
+ color: #ffffff;
61
+ overflow: hidden;
62
+ flex-shrink: 0;
63
+ }
64
+
65
+ .messenger-message-system-event-avatar img {
66
+ width: 100%;
67
+ height: 100%;
68
+ object-fit: cover;
69
+ }
70
+
71
+ .messenger-message-system-event-name {
72
+ font-size: 0.875rem;
73
+ font-weight: var(--font-weight-semibold);
74
+ color: var(--msg-text);
75
+ line-height: 1.3;
76
+ }
77
+
78
+ .messenger-message-system-event-action {
79
+ font-size: 0.875rem;
80
+ font-weight: 500;
81
+ color: var(--msg-text-secondary);
82
+ margin-top: -2px;
83
+ }
84
+
85
+ .messenger-message-system-event-time {
86
+ font-size: 0.875rem;
87
+ font-weight: 500;
88
+ color: var(--msg-text-tertiary);
89
+ }
90
+
13
91
  .messenger-message-own {
14
92
  align-self: flex-end;
15
93
  flex-direction: column;
@@ -50,7 +128,6 @@ export const messengerComponentsStyles = `
50
128
  .messenger-message-own .messenger-message-bubble {
51
129
  background: var(--msg-bg-bubble-own);
52
130
  color: var(--msg-text);
53
- border: 1px solid var(--msg-border-bubble);
54
131
  border-bottom-right-radius: 0.25rem;
55
132
  }
56
133
 
@@ -61,8 +138,8 @@ export const messengerComponentsStyles = `
61
138
  }
62
139
 
63
140
  .messenger-message-content {
64
- font-size: var(--font-size-base);
65
- font-weight: var(--font-weight-medium);
141
+ font-size: 0.875rem;
142
+ font-weight: 500;
66
143
  line-height: var(--line-height-relaxed);
67
144
  }
68
145
 
@@ -87,10 +164,14 @@ export const messengerComponentsStyles = `
87
164
  gap: 0.375rem;
88
165
  font-size: var(--font-size-xs);
89
166
  color: var(--msg-text-tertiary);
90
- margin-top: 0.375rem;
167
+ margin-top: 0.25rem;
91
168
  padding: 0 var(--spacing-1);
92
169
  }
93
170
 
171
+ .messenger-message-meta-own {
172
+ justify-content: flex-end;
173
+ }
174
+
94
175
  .messenger-message-image {
95
176
  max-width: 220px;
96
177
  max-height: 200px;
@@ -203,8 +203,8 @@ export const messengerCoreStyles = `
203
203
  --msg-bg-elevated: #232930;
204
204
  --msg-bg-hover: #232930;
205
205
  --msg-bg-input: #1a1e24;
206
- --msg-bg-bubble-own: #2a3140;
207
- --msg-bg-bubble-received: #1a1e24;
206
+ --msg-bg-bubble-own: #1e2330;
207
+ --msg-bg-bubble-received: rgba(21, 94, 239, 0.12);
208
208
  --msg-bg-header-gradient: linear-gradient(180deg, #1a1e2e 0%, #141720 50%, #0f1317 100%);
209
209
  --msg-bg-header-glow1: radial-gradient(circle, rgba(21, 94, 239, 0.07) 0%, transparent 70%);
210
210
  --msg-bg-header-glow2: radial-gradient(circle, rgba(139, 92, 246, 0.05) 0%, transparent 70%);
@@ -48,6 +48,9 @@ export class MessengerWidget extends BaseWidget {
48
48
  teamAvatars: options.teamAvatars || [],
49
49
  greetingMessage: options.greetingMessage || 'Hi there 👋',
50
50
  welcomeMessage: options.welcomeMessage || 'How can we help?',
51
+ onlineMessage: options.onlineMessage || "We're online now",
52
+ responseTime:
53
+ options.responseTime || 'We typically reply within a few minutes',
51
54
  enableHelp: options.enableHelp !== false,
52
55
  enableChangelog: resolvedEnableChangelog,
53
56
  autoLoadData: options.autoLoadData !== false,
@@ -69,6 +72,8 @@ export class MessengerWidget extends BaseWidget {
69
72
  teamAvatars: this.messengerOptions.teamAvatars,
70
73
  greetingMessage: this.messengerOptions.greetingMessage,
71
74
  welcomeMessage: this.messengerOptions.welcomeMessage,
75
+ onlineMessage: this.messengerOptions.onlineMessage,
76
+ responseTime: this.messengerOptions.responseTime,
72
77
  enableHelp: this.messengerOptions.enableHelp,
73
78
  enableChangelog: this.messengerOptions.enableChangelog,
74
79
  metadata: this.sdk?.apiService?.getMetadata() || null,
@@ -390,6 +395,7 @@ export class MessengerWidget extends BaseWidget {
390
395
  id: message.id,
391
396
  content: message.content,
392
397
  isOwn: message.sender_type === 'customer',
398
+ isSystem: message.sender_type === 'system',
393
399
  timestamp: message.created_at,
394
400
  attachments: attachments.length > 0 ? attachments : undefined,
395
401
  sender: {
@@ -575,7 +581,9 @@ export class MessengerWidget extends BaseWidget {
575
581
  if (data.availability && typeof data.availability === 'object') {
576
582
  const availability = data.availability;
577
583
  this.messengerState.agentsOnline = Boolean(
578
- availability.agentsOnline ?? availability.agents_online
584
+ availability.agentsOnline ??
585
+ availability.agents_online ??
586
+ availability.is_online
579
587
  );
580
588
  this.messengerState.onlineCount =
581
589
  availability.onlineCount ?? availability.online_count ?? 0;
@@ -703,6 +711,7 @@ export class MessengerWidget extends BaseWidget {
703
711
  id: msg.id,
704
712
  content: msg.content,
705
713
  isOwn: msg.sender_type === 'customer',
714
+ isSystem: msg.sender_type === 'system',
706
715
  timestamp: msg.created_at,
707
716
  attachments:
708
717
  attachments && attachments.length > 0 ? attachments : undefined,
@@ -753,14 +762,25 @@ export class MessengerWidget extends BaseWidget {
753
762
 
754
763
  this.messengerState.addConversation(newConversation);
755
764
 
756
- this.messengerState.setMessages(conv.id, [
765
+ const initialMessages = [
757
766
  {
758
767
  id: 'msg_' + Date.now(),
759
768
  content: message,
760
769
  isOwn: true,
761
770
  timestamp: new Date().toISOString(),
762
771
  },
763
- ]);
772
+ ];
773
+
774
+ initialMessages.push({
775
+ id: 'system_rt_' + Date.now(),
776
+ content: this.messengerState.agentsOnline
777
+ ? 'One of our customer support agents will be with you shortly.'
778
+ : this.messengerState.responseTime,
779
+ isSystem: true,
780
+ timestamp: new Date().toISOString(),
781
+ });
782
+
783
+ this.messengerState.setMessages(conv.id, initialMessages);
764
784
 
765
785
  this.messengerState.setActiveConversation(conv.id);
766
786
  this.messengerState.setView('chat');
@@ -786,7 +806,8 @@ export class MessengerWidget extends BaseWidget {
786
806
  try {
787
807
  const response = await this.apiService.checkAgentsOnline();
788
808
  if (response.status && response.data) {
789
- this.messengerState.agentsOnline = response.data.agents_online;
809
+ this.messengerState.agentsOnline =
810
+ response.data.agents_online ?? response.data.is_online ?? false;
790
811
  this.messengerState.onlineCount = response.data.online_count || 0;
791
812
  this.messengerState.responseTime = response.data.response_time || '';
792
813
 
@@ -28,7 +28,9 @@ export class MessengerState {
28
28
 
29
29
  this.agentsOnline = false;
30
30
  this.onlineCount = 0;
31
- this.responseTime = 'Usually replies within a few minutes';
31
+ this.onlineMessage = options.onlineMessage || "We're online now";
32
+ this.responseTime =
33
+ options.responseTime || 'We typically reply within a few minutes';
32
34
 
33
35
  this.typingUsers = {};
34
36
 
@@ -186,6 +186,10 @@ export class ChatView {
186
186
  }
187
187
 
188
188
  _renderMessage(message) {
189
+ if (message.isSystem) {
190
+ return this._renderSystemMessage(message);
191
+ }
192
+
189
193
  const isOwn = message.isOwn;
190
194
  const messageClass = isOwn
191
195
  ? 'messenger-message-own'
@@ -205,12 +209,11 @@ export class ChatView {
205
209
  <div class="messenger-message ${messageClass}">
206
210
  ${bubbleHtml}
207
211
  ${attachmentsHtml}
212
+ ${timeStr ? `<div class="messenger-message-meta messenger-message-meta-own"><span>${timeStr}</span></div>` : ''}
208
213
  </div>
209
214
  `;
210
215
  }
211
216
 
212
- const senderName = message.sender?.name || 'Support';
213
- const senderRole = message.sender?.role || 'Agent';
214
217
  const avatarHtml = this._renderSenderAvatar(message.sender);
215
218
  return `
216
219
  <div class="messenger-message ${messageClass}">
@@ -221,13 +224,53 @@ export class ChatView {
221
224
  ${attachmentsHtml}
222
225
  </div>
223
226
  </div>
224
- <div class="messenger-message-meta">
225
- <span>${senderName}</span>
226
- <span>·</span>
227
- <span>${senderRole}</span>
228
- <span>·</span>
229
- <span>${timeStr}</span>
227
+ ${timeStr ? `<div class="messenger-message-meta"><span>${timeStr}</span></div>` : ''}
228
+ </div>
229
+ `;
230
+ }
231
+
232
+ _renderSystemMessage(message) {
233
+ const content = message.content || '';
234
+ const isJoinLeave =
235
+ content.includes('joined the chat') ||
236
+ content.includes('left the chat') ||
237
+ content.includes('joined the conversation') ||
238
+ content.includes('left the conversation');
239
+
240
+ if (isJoinLeave && message.sender) {
241
+ const rawName = message.sender.name || '';
242
+ const name = rawName
243
+ .split(' ')
244
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
245
+ .join(' ');
246
+ const avatarUrl = message.sender.avatarUrl;
247
+ const initial = name.charAt(0).toUpperCase() || '?';
248
+ const colors = ['#5856d6', '#007aff', '#34c759', '#ff9500'];
249
+ const colorIndex = rawName.charCodeAt(0) % colors.length;
250
+ const avatarHtml = avatarUrl
251
+ ? `<img src="${this._escapeHtml(avatarUrl)}" alt="${this._escapeHtml(name)}" />`
252
+ : initial;
253
+ const avatarStyle = avatarUrl
254
+ ? ''
255
+ : `style="background: ${colors[colorIndex]};"`;
256
+ const timeStr = this._formatMessageTime(message.timestamp);
257
+
258
+ const rawAction = content.replace(rawName, '').trim();
259
+ const action = rawAction.charAt(0).toUpperCase() + rawAction.slice(1);
260
+
261
+ return `
262
+ <div class="messenger-message-system-event">
263
+ <div class="messenger-message-system-event-avatar" ${avatarStyle}>${avatarHtml}</div>
264
+ <span class="messenger-message-system-event-name">${this._escapeHtml(name)}</span>
265
+ <span class="messenger-message-system-event-action">${this._escapeHtml(action)}</span>
266
+ ${timeStr ? `<span class="messenger-message-system-event-time">${timeStr}</span>` : ''}
230
267
  </div>
268
+ `;
269
+ }
270
+
271
+ return `
272
+ <div class="messenger-message-system">
273
+ <span class="messenger-message-system-text">${this._escapeHtml(content)}</span>
231
274
  </div>
232
275
  `;
233
276
  }
@@ -243,12 +286,7 @@ export class ChatView {
243
286
  _renderTeamAvatars() {
244
287
  const avatars = this.state.teamAvatars;
245
288
  if (!avatars || avatars.length === 0) {
246
- return `
247
- <div class="messenger-avatar-stack">
248
- <div class="sdk-avatar sdk-avatar-md">S</div>
249
- <div class="sdk-avatar sdk-avatar-md">T</div>
250
- </div>
251
- `;
289
+ return '';
252
290
  }
253
291
 
254
292
  const avatarItems = avatars
@@ -267,11 +305,17 @@ export class ChatView {
267
305
  _formatMessageTime(timestamp) {
268
306
  if (!timestamp) return '';
269
307
  const date = new Date(timestamp);
270
- return date.toLocaleTimeString('en-US', {
271
- hour: 'numeric',
308
+ const datePart = date.toLocaleDateString('en-GB', {
309
+ day: '2-digit',
310
+ month: 'short',
311
+ year: 'numeric',
312
+ });
313
+ const timePart = date.toLocaleTimeString('en-GB', {
314
+ hour: '2-digit',
272
315
  minute: '2-digit',
273
- hour12: true,
316
+ hour12: false,
274
317
  });
318
+ return `${datePart}, ${timePart}`;
275
319
  }
276
320
 
277
321
  _formatMessageContent(content) {
@@ -41,6 +41,7 @@ export class HomeView {
41
41
  <div class="messenger-home-welcome">
42
42
  <span class="messenger-home-greeting">${this.state.greetingMessage}</span>
43
43
  <span class="messenger-home-question">${this.state.welcomeMessage}</span>
44
+ ${this._renderAvailabilityStatus()}
44
45
  </div>
45
46
  </div>
46
47
 
@@ -60,12 +61,7 @@ export class HomeView {
60
61
  const colors = ['#5856d6', '#007aff', '#34c759', '#ff9500'];
61
62
 
62
63
  if (!avatars || avatars.length === 0) {
63
- return `
64
- <div class="messenger-avatar-stack">
65
- <div class="sdk-avatar sdk-avatar-lg" style="background: ${colors[0]};">S</div>
66
- <div class="sdk-avatar sdk-avatar-lg" style="background: ${colors[1]};">T</div>
67
- </div>
68
- `;
64
+ return '';
69
65
  }
70
66
 
71
67
  const avatarItems = avatars
@@ -83,14 +79,12 @@ export class HomeView {
83
79
 
84
80
  _renderAvailabilityStatus() {
85
81
  const isOnline = this.state.agentsOnline;
86
- const responseTime =
87
- this.state.responseTime || 'We typically reply within a few minutes';
88
82
 
89
83
  if (isOnline) {
90
84
  return `
91
85
  <div class="messenger-home-availability">
92
86
  <span class="messenger-availability-dot messenger-availability-online"></span>
93
- <span class="messenger-availability-text">We're online now</span>
87
+ <span class="messenger-availability-text">${this.state.onlineMessage}</span>
94
88
  </div>
95
89
  `;
96
90
  }
@@ -98,7 +92,7 @@ export class HomeView {
98
92
  return `
99
93
  <div class="messenger-home-availability">
100
94
  <span class="messenger-availability-dot messenger-availability-away"></span>
101
- <span class="messenger-availability-text">${responseTime}</span>
95
+ <span class="messenger-availability-text">${this.state.responseTime}</span>
102
96
  </div>
103
97
  `;
104
98
  }