@product7/product7-js 0.3.1 → 0.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/README.md +53 -29
- package/dist/README.md +53 -29
- package/dist/product7-js.js +495 -132
- package/dist/product7-js.js.map +1 -1
- package/dist/product7-js.min.js +1 -1
- package/dist/product7-js.min.js.map +1 -1
- package/package.json +1 -1
- package/src/api/services/MessengerService.js +1 -30
- package/src/core/APIService.js +35 -1
- package/src/core/BaseAPIService.js +124 -11
- package/src/core/Product7.js +182 -18
- package/src/docs/api.md +253 -89
- package/src/docs/example.md +203 -153
- package/src/docs/framework-integrations.md +236 -358
- package/src/docs/installation.md +171 -143
- package/src/index.js +48 -41
- package/src/widgets/MessengerWidget.js +67 -22
- package/src/widgets/SurveyWidget.js +20 -0
- package/src/widgets/messenger/views/ChatView.js +14 -8
- package/src/widgets/messenger/views/HomeView.js +5 -2
- package/types/index.d.ts +34 -0
|
@@ -17,6 +17,7 @@ import { PreChatFormView } from './messenger/views/PreChatFormView.js';
|
|
|
17
17
|
export class MessengerWidget extends BaseWidget {
|
|
18
18
|
constructor(options) {
|
|
19
19
|
super({ ...options, type: 'messenger' });
|
|
20
|
+
this._explicitOptions = options || {};
|
|
20
21
|
const resolvedTheme = options.theme || 'light';
|
|
21
22
|
const hasExplicitTextColor = Object.prototype.hasOwnProperty.call(
|
|
22
23
|
options,
|
|
@@ -99,6 +100,10 @@ export class MessengerWidget extends BaseWidget {
|
|
|
99
100
|
this._handleConversationClosed = this._handleConversationClosed.bind(this);
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
_hasTrigger() {
|
|
104
|
+
return this.options.trigger === true || this.options.trigger === undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
102
107
|
_createInternalFeedbackWidget() {
|
|
103
108
|
try {
|
|
104
109
|
const widget = this.sdk.createWidget('button', {
|
|
@@ -134,11 +139,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
134
139
|
this._feedbackWidget = this._createInternalFeedbackWidget();
|
|
135
140
|
}
|
|
136
141
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
if (this._hasTrigger()) {
|
|
143
|
+
this.launcher = new MessengerLauncher(this.messengerState, {
|
|
144
|
+
position: this.messengerOptions.position,
|
|
145
|
+
primaryColor: this.messengerOptions.primaryColor,
|
|
146
|
+
});
|
|
147
|
+
container.appendChild(this.launcher.render());
|
|
148
|
+
}
|
|
142
149
|
|
|
143
150
|
this.panel = new MessengerPanel(this.messengerState, {
|
|
144
151
|
position: this.messengerOptions.position,
|
|
@@ -273,23 +280,13 @@ export class MessengerWidget extends BaseWidget {
|
|
|
273
280
|
|
|
274
281
|
async _handleIdentifyContact(contactData) {
|
|
275
282
|
try {
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
// Route through sdk.identify() so the SDK-level identity state is updated
|
|
284
|
+
// and applyIdentity() handles the messenger state + WebSocket as a side effect.
|
|
285
|
+
const result = await this.sdk.identify({
|
|
278
286
|
email: contactData.email,
|
|
287
|
+
name: contactData.name,
|
|
279
288
|
});
|
|
280
|
-
|
|
281
|
-
if (response.status) {
|
|
282
|
-
console.log(
|
|
283
|
-
'[MessengerWidget] Contact identified:',
|
|
284
|
-
response.data.contact_id
|
|
285
|
-
);
|
|
286
|
-
this.messengerState.setIdentified(true, {
|
|
287
|
-
name: contactData.name,
|
|
288
|
-
email: contactData.email,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return response;
|
|
289
|
+
return result;
|
|
293
290
|
} catch (error) {
|
|
294
291
|
console.error('[MessengerWidget] Failed to identify contact:', error);
|
|
295
292
|
throw error;
|
|
@@ -297,8 +294,17 @@ export class MessengerWidget extends BaseWidget {
|
|
|
297
294
|
}
|
|
298
295
|
|
|
299
296
|
markAsIdentified(name, email) {
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
// Called externally by the app when the user is already known.
|
|
298
|
+
// No API call needed — identity was already established via sdk.identify().
|
|
299
|
+
this.applyIdentity({ name, email });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
applyIdentity(metadata = {}) {
|
|
303
|
+
this.messengerState.setIdentified(true, metadata);
|
|
304
|
+
|
|
305
|
+
if (this.apiService?.sessionToken && !this.wsService?.isConnected) {
|
|
306
|
+
this._initWebSocket();
|
|
307
|
+
}
|
|
302
308
|
}
|
|
303
309
|
|
|
304
310
|
async _handleUploadFile(base64Data, filename) {
|
|
@@ -809,6 +815,42 @@ export class MessengerWidget extends BaseWidget {
|
|
|
809
815
|
}
|
|
810
816
|
}
|
|
811
817
|
|
|
818
|
+
async _fetchAndApplySettings() {
|
|
819
|
+
try {
|
|
820
|
+
const response = await this.apiService.getMessengerSettings();
|
|
821
|
+
if (!response?.status || !response?.data) return;
|
|
822
|
+
|
|
823
|
+
const s = response.data;
|
|
824
|
+
|
|
825
|
+
// Only apply values that were NOT explicitly passed in options
|
|
826
|
+
if (s.team_name && !this._hasExplicitOption('teamName')) {
|
|
827
|
+
this.messengerOptions.teamName = s.team_name;
|
|
828
|
+
this.messengerState.teamName = s.team_name;
|
|
829
|
+
}
|
|
830
|
+
if (s.logo_url && !this._hasExplicitOption('logoUrl')) {
|
|
831
|
+
this.messengerOptions.logoUrl = s.logo_url;
|
|
832
|
+
}
|
|
833
|
+
if (s.greeting_message && !this._hasExplicitOption('greetingMessage')) {
|
|
834
|
+
this.messengerState.greetingMessage = s.greeting_message;
|
|
835
|
+
}
|
|
836
|
+
if (s.response_time && !this._hasExplicitOption('responseTime')) {
|
|
837
|
+
this.messengerState.responseTime = s.response_time;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Notify views to re-render with new values
|
|
841
|
+
this.messengerState._notify('availabilityUpdate', {});
|
|
842
|
+
} catch (e) {
|
|
843
|
+
// non-fatal
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
_hasExplicitOption(key) {
|
|
848
|
+
return Object.prototype.hasOwnProperty.call(
|
|
849
|
+
this._explicitOptions || {},
|
|
850
|
+
key
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
|
|
812
854
|
async checkAgentAvailability() {
|
|
813
855
|
try {
|
|
814
856
|
const response = await this.apiService.checkAgentsOnline();
|
|
@@ -954,6 +996,9 @@ export class MessengerWidget extends BaseWidget {
|
|
|
954
996
|
this._applyPreviewData();
|
|
955
997
|
|
|
956
998
|
if (this.messengerOptions.autoLoadData) {
|
|
999
|
+
// Fetch workspace settings and apply only if not explicitly configured
|
|
1000
|
+
this._fetchAndApplySettings();
|
|
1001
|
+
|
|
957
1002
|
this.loadInitialData();
|
|
958
1003
|
|
|
959
1004
|
if (this.apiService?.sessionToken) {
|
|
@@ -92,6 +92,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
92
92
|
|
|
93
93
|
SurveyWidget.removeDanglingElements();
|
|
94
94
|
this._renderSurvey();
|
|
95
|
+
this.state.isOpen = true;
|
|
95
96
|
this.surveyState.isVisible = true;
|
|
96
97
|
this.sdk.eventBus.emit('survey:shown', {
|
|
97
98
|
widget: this,
|
|
@@ -101,6 +102,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
hide() {
|
|
105
|
+
this.state.isOpen = false;
|
|
104
106
|
this._closeSurvey();
|
|
105
107
|
return this;
|
|
106
108
|
}
|
|
@@ -175,6 +177,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
175
177
|
this._attachSurveyEvents();
|
|
176
178
|
|
|
177
179
|
requestAnimationFrame(() => {
|
|
180
|
+
if (!this.surveyElement) return;
|
|
178
181
|
this.surveyElement.style.opacity = '1';
|
|
179
182
|
this.surveyElement.style.transform =
|
|
180
183
|
this.surveyOptions.position === 'center'
|
|
@@ -1312,6 +1315,7 @@ export class SurveyWidget extends BaseWidget {
|
|
|
1312
1315
|
}
|
|
1313
1316
|
|
|
1314
1317
|
_closeSurvey(resetState = true, immediate = false) {
|
|
1318
|
+
this.state.isOpen = false;
|
|
1315
1319
|
if (this._escapeHandler) {
|
|
1316
1320
|
document.removeEventListener('keydown', this._escapeHandler);
|
|
1317
1321
|
this._escapeHandler = null;
|
|
@@ -1374,6 +1378,22 @@ export class SurveyWidget extends BaseWidget {
|
|
|
1374
1378
|
this.sdk.eventBus.emit('survey:closed', { widget: this });
|
|
1375
1379
|
}
|
|
1376
1380
|
|
|
1381
|
+
open() {
|
|
1382
|
+
return this.show();
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
close() {
|
|
1386
|
+
return this.hide();
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
toggle() {
|
|
1390
|
+
if (this.surveyState.isVisible) {
|
|
1391
|
+
return this.hide();
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return this.show();
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1377
1397
|
destroy() {
|
|
1378
1398
|
this._closeSurvey(true, true);
|
|
1379
1399
|
super.destroy();
|
|
@@ -21,7 +21,9 @@ export class ChatView {
|
|
|
21
21
|
|
|
22
22
|
this._unsubscribe = this.state.subscribe((type, data) => {
|
|
23
23
|
if (type === 'connectionChange') {
|
|
24
|
-
const banner = this.element?.querySelector(
|
|
24
|
+
const banner = this.element?.querySelector(
|
|
25
|
+
'.messenger-connection-banner'
|
|
26
|
+
);
|
|
25
27
|
if (banner) {
|
|
26
28
|
banner.style.display = data.connected ? 'none' : 'flex';
|
|
27
29
|
}
|
|
@@ -71,9 +73,10 @@ export class ChatView {
|
|
|
71
73
|
? this._renderEmptyState(isNewConversation)
|
|
72
74
|
: this._renderGroupedMessages(messages);
|
|
73
75
|
|
|
74
|
-
const defaultPlaceholder =
|
|
76
|
+
const defaultPlaceholder =
|
|
77
|
+
this.options.composePlaceholder || 'Write a message...';
|
|
75
78
|
const placeholder = isNewConversation
|
|
76
|
-
?
|
|
79
|
+
? this.options.composePlaceholder || 'Start typing your message...'
|
|
77
80
|
: isClosed
|
|
78
81
|
? 'Conversation closed'
|
|
79
82
|
: defaultPlaceholder;
|
|
@@ -94,7 +97,7 @@ export class ChatView {
|
|
|
94
97
|
</div>
|
|
95
98
|
<div class="messenger-chat-header-info">
|
|
96
99
|
<span class="messenger-chat-title">${this._escapeHtml(teamName)}</span>
|
|
97
|
-
<span class="messenger-chat-subtitle">${isClosed ? 'Conversation resolved' :
|
|
100
|
+
<span class="messenger-chat-subtitle">${isClosed ? 'Conversation resolved' : this.state.responseTime || 'Typically replies within minutes'}</span>
|
|
98
101
|
</div>
|
|
99
102
|
<div class="messenger-chat-header-actions">
|
|
100
103
|
<button class="sdk-btn-icon sdk-close-btn messenger-mobile-close-btn" aria-label="Close">
|
|
@@ -211,7 +214,9 @@ export class ChatView {
|
|
|
211
214
|
const messageClass = isOwn
|
|
212
215
|
? 'messenger-message-own'
|
|
213
216
|
: 'messenger-message-received';
|
|
214
|
-
const timeStr = isLastInGroup
|
|
217
|
+
const timeStr = isLastInGroup
|
|
218
|
+
? this._formatMessageTime(message.timestamp)
|
|
219
|
+
: '';
|
|
215
220
|
const attachmentsHtml = this._renderMessageAttachments(message.attachments);
|
|
216
221
|
const isOptimistic = message.isOptimistic;
|
|
217
222
|
|
|
@@ -225,9 +230,10 @@ export class ChatView {
|
|
|
225
230
|
if (isOwn) {
|
|
226
231
|
const sentIndicator = isLastInGroup
|
|
227
232
|
? `<div class="messenger-message-meta messenger-message-meta-own">
|
|
228
|
-
${
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
${
|
|
234
|
+
isOptimistic
|
|
235
|
+
? `<span class="messenger-message-sent-status">Sending…</span>`
|
|
236
|
+
: `<span class="messenger-message-sent-status">Sent</span>`
|
|
231
237
|
}
|
|
232
238
|
${timeStr ? `<span>·</span><span>${timeStr}</span>` : ''}
|
|
233
239
|
</div>`
|
|
@@ -137,7 +137,8 @@ export class HomeView {
|
|
|
137
137
|
const title = conversation.title || teamName;
|
|
138
138
|
const timeAgo = this._formatTimeAgo(conversation.lastMessageTime);
|
|
139
139
|
const preview = conversation.lastMessage
|
|
140
|
-
? conversation.lastMessage.substring(0, 48) +
|
|
140
|
+
? conversation.lastMessage.substring(0, 48) +
|
|
141
|
+
(conversation.lastMessage.length > 48 ? '...' : '')
|
|
141
142
|
: '';
|
|
142
143
|
const hasUnread = conversation.unread > 0;
|
|
143
144
|
|
|
@@ -253,7 +254,9 @@ export class HomeView {
|
|
|
253
254
|
}
|
|
254
255
|
|
|
255
256
|
_attachEvents() {
|
|
256
|
-
const recentCard = this.element.querySelector(
|
|
257
|
+
const recentCard = this.element.querySelector(
|
|
258
|
+
'.messenger-home-recent-card'
|
|
259
|
+
);
|
|
257
260
|
if (recentCard) {
|
|
258
261
|
recentCard.addEventListener('click', () => {
|
|
259
262
|
const convId = recentCard.dataset.conversationId;
|
package/types/index.d.ts
CHANGED
|
@@ -19,11 +19,13 @@ declare module '@product7/product7-js' {
|
|
|
19
19
|
user_id?: string;
|
|
20
20
|
email?: string;
|
|
21
21
|
name?: string;
|
|
22
|
+
profile_picture?: string;
|
|
22
23
|
custom_fields?: Record<string, any>;
|
|
23
24
|
company?: {
|
|
24
25
|
id?: string;
|
|
25
26
|
name?: string;
|
|
26
27
|
monthly_spend?: number;
|
|
28
|
+
custom_fields?: Record<string, any>;
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -44,11 +46,15 @@ declare module '@product7/product7-js' {
|
|
|
44
46
|
triggerText?: string;
|
|
45
47
|
label?: string;
|
|
46
48
|
text?: string;
|
|
49
|
+
headless?: boolean;
|
|
47
50
|
customStyles?: Record<string, string>;
|
|
48
51
|
trigger?: boolean | string | Element;
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
export interface FeedbackWidgetOptions extends ButtonWidgetOptions {}
|
|
55
|
+
|
|
51
56
|
export interface WidgetConfigMap {
|
|
57
|
+
feedback?: Partial<FeedbackWidgetOptions> | Record<string, any>;
|
|
52
58
|
button?: Partial<ButtonWidgetOptions> | Record<string, any>;
|
|
53
59
|
survey?: Partial<SurveyWidgetOptions> | Record<string, any>;
|
|
54
60
|
messenger?: Partial<MessengerWidgetOptions> | Record<string, any>;
|
|
@@ -63,6 +69,9 @@ declare module '@product7/product7-js' {
|
|
|
63
69
|
destroy(): void;
|
|
64
70
|
show(): this;
|
|
65
71
|
hide(): this;
|
|
72
|
+
open(): this;
|
|
73
|
+
close(): this;
|
|
74
|
+
toggle(): this;
|
|
66
75
|
openPanel(): void;
|
|
67
76
|
closePanel(): void;
|
|
68
77
|
openModal(): void;
|
|
@@ -73,6 +82,8 @@ declare module '@product7/product7-js' {
|
|
|
73
82
|
): void;
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
export interface FeedbackWidget extends ButtonWidget {}
|
|
86
|
+
|
|
76
87
|
export class Product7 {
|
|
77
88
|
constructor(config: FeedbackConfig);
|
|
78
89
|
static create(config: FeedbackConfig): Product7;
|
|
@@ -84,7 +95,21 @@ declare module '@product7/product7-js' {
|
|
|
84
95
|
status?: boolean;
|
|
85
96
|
message?: string | null;
|
|
86
97
|
configVersion?: number | null;
|
|
98
|
+
identified?: boolean;
|
|
99
|
+
}>;
|
|
100
|
+
identify(metadata: Metadata): Promise<{
|
|
101
|
+
identified: boolean;
|
|
102
|
+
metadata: Metadata | null;
|
|
103
|
+
response: any;
|
|
87
104
|
}>;
|
|
105
|
+
createFeedbackWidget(options?: FeedbackWidgetOptions): FeedbackWidget;
|
|
106
|
+
createMessengerWidget(options?: MessengerWidgetOptions): MessengerWidget;
|
|
107
|
+
createChangelogWidget(options?: ChangelogWidgetOptions): ChangelogWidget;
|
|
108
|
+
createSurveyWidget(options?: SurveyWidgetOptions): SurveyWidget;
|
|
109
|
+
createWidget(
|
|
110
|
+
type: 'feedback',
|
|
111
|
+
options?: FeedbackWidgetOptions
|
|
112
|
+
): FeedbackWidget;
|
|
88
113
|
createWidget(type: 'button', options?: ButtonWidgetOptions): ButtonWidget;
|
|
89
114
|
createWidget(type: 'survey', options?: SurveyWidgetOptions): SurveyWidget;
|
|
90
115
|
createWidget(
|
|
@@ -182,8 +207,12 @@ declare module '@product7/product7-js' {
|
|
|
182
207
|
}
|
|
183
208
|
|
|
184
209
|
export interface SurveyWidget {
|
|
210
|
+
mount(container?: string | HTMLElement): this;
|
|
185
211
|
show(): this;
|
|
186
212
|
hide(): this;
|
|
213
|
+
open(): this;
|
|
214
|
+
close(): this;
|
|
215
|
+
toggle(): this;
|
|
187
216
|
destroy(): void;
|
|
188
217
|
surveyOptions: SurveyWidgetOptions;
|
|
189
218
|
surveyState: {
|
|
@@ -235,6 +264,7 @@ declare module '@product7/product7-js' {
|
|
|
235
264
|
enabled?: boolean;
|
|
236
265
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
237
266
|
theme?: 'light' | 'dark';
|
|
267
|
+
headless?: boolean;
|
|
238
268
|
showBackdrop?: boolean;
|
|
239
269
|
triggerText?: string;
|
|
240
270
|
showBadge?: boolean;
|
|
@@ -251,6 +281,9 @@ declare module '@product7/product7-js' {
|
|
|
251
281
|
type: 'changelog';
|
|
252
282
|
mount(container?: string | HTMLElement): this;
|
|
253
283
|
destroy(): void;
|
|
284
|
+
open(): this;
|
|
285
|
+
close(): this;
|
|
286
|
+
toggle(): this;
|
|
254
287
|
openModal(): void;
|
|
255
288
|
closeModal(): void;
|
|
256
289
|
openSidebar(): void;
|
|
@@ -266,6 +299,7 @@ declare module '@product7/product7-js' {
|
|
|
266
299
|
enabled?: boolean;
|
|
267
300
|
position?: 'bottom-right' | 'bottom-left';
|
|
268
301
|
theme?: 'light' | 'dark';
|
|
302
|
+
headless?: boolean;
|
|
269
303
|
teamName?: string;
|
|
270
304
|
teamAvatars?: string[];
|
|
271
305
|
greetingMessage?: string;
|