@product7/product7-js 0.3.5 → 0.3.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/dist/product7-js.js +98 -238
- 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/FeedbackService.js +7 -0
- package/src/api/services/MessengerService.js +42 -0
- package/src/api/services/SurveyService.js +6 -0
- package/src/core/APIService.js +5 -210
- package/src/core/BaseAPIService.js +10 -0
package/package.json
CHANGED
|
@@ -21,12 +21,19 @@ export class FeedbackService {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const contact = this.api.getContactIdentity();
|
|
25
|
+
|
|
24
26
|
const payload = {
|
|
25
27
|
board:
|
|
26
28
|
feedbackData.board_id || feedbackData.board || feedbackData.boardName,
|
|
27
29
|
title: feedbackData.title,
|
|
28
30
|
content: feedbackData.content,
|
|
29
31
|
attachments: feedbackData.attachments || [],
|
|
32
|
+
...(contact?.contactId && {
|
|
33
|
+
contact_id: contact.contactId,
|
|
34
|
+
contact_email: contact.contactEmail,
|
|
35
|
+
contact_name: contact.contactName,
|
|
36
|
+
}),
|
|
30
37
|
};
|
|
31
38
|
|
|
32
39
|
return this.api._handleAuthRetry(async () => {
|
|
@@ -37,6 +37,10 @@ export class MessengerService {
|
|
|
37
37
|
agents_online: true,
|
|
38
38
|
online_count: 2,
|
|
39
39
|
response_time: 'Usually replies within a few minutes',
|
|
40
|
+
available_agents: [
|
|
41
|
+
{ full_name: 'Sarah', picture: '' },
|
|
42
|
+
{ full_name: 'Tom', picture: '' },
|
|
43
|
+
],
|
|
40
44
|
},
|
|
41
45
|
};
|
|
42
46
|
}
|
|
@@ -224,10 +228,48 @@ export class MessengerService {
|
|
|
224
228
|
});
|
|
225
229
|
}
|
|
226
230
|
|
|
231
|
+
async uploadFile(base64Data, filename) {
|
|
232
|
+
await this.api._ensureSession();
|
|
233
|
+
|
|
234
|
+
if (this.api.mock) {
|
|
235
|
+
await delay(300);
|
|
236
|
+
return { status: true, url: `https://mock-cdn.example.com/${filename}` };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return this.api._makeRequest('/widget/messenger/upload', {
|
|
240
|
+
method: 'POST',
|
|
241
|
+
headers: {
|
|
242
|
+
'Content-Type': 'application/json',
|
|
243
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
244
|
+
},
|
|
245
|
+
body: JSON.stringify({ file: base64Data, filename }),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
227
249
|
async identifyContact(data) {
|
|
228
250
|
return this.api.identify(data);
|
|
229
251
|
}
|
|
230
252
|
|
|
253
|
+
async submitRating(conversationId, data) {
|
|
254
|
+
await this.api._ensureSession();
|
|
255
|
+
|
|
256
|
+
if (this.api.mock) {
|
|
257
|
+
return { status: true, data: { rated: true } };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return this.api._makeRequest(
|
|
261
|
+
`/widget/messenger/conversations/${conversationId}/rating`,
|
|
262
|
+
{
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
Authorization: `Bearer ${this.api.sessionToken}`,
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify(data),
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
231
273
|
async sendTypingIndicator(conversationId, isTyping) {
|
|
232
274
|
await this.api._ensureSession();
|
|
233
275
|
|
|
@@ -78,6 +78,7 @@ export class SurveyService {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const respondent = this._getRespondentContext(responseData);
|
|
81
|
+
const contact = this.api.getContactIdentity?.() || null;
|
|
81
82
|
|
|
82
83
|
const payload = {
|
|
83
84
|
rating: responseData.rating,
|
|
@@ -87,6 +88,11 @@ export class SurveyService {
|
|
|
87
88
|
respondent_id: respondent.respondent_id,
|
|
88
89
|
}),
|
|
89
90
|
...(respondent.email && { email: respondent.email }),
|
|
91
|
+
...(contact?.contactId && {
|
|
92
|
+
contact_id: contact.contactId,
|
|
93
|
+
contact_email: contact.contactEmail,
|
|
94
|
+
contact_name: contact.contactName,
|
|
95
|
+
}),
|
|
90
96
|
};
|
|
91
97
|
|
|
92
98
|
return this.api._handleAuthRetry(async () => {
|
package/src/core/APIService.js
CHANGED
|
@@ -37,29 +37,7 @@ export class APIService extends BaseAPIService {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async checkAgentsOnline() {
|
|
40
|
-
|
|
41
|
-
await this.init();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (this.mock) {
|
|
45
|
-
return {
|
|
46
|
-
status: true,
|
|
47
|
-
data: {
|
|
48
|
-
agents_online: true,
|
|
49
|
-
online_count: 2,
|
|
50
|
-
response_time: 'Usually replies within a few minutes',
|
|
51
|
-
available_agents: [
|
|
52
|
-
{ full_name: 'Sarah', picture: '' },
|
|
53
|
-
{ full_name: 'Tom', picture: '' },
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return this._makeRequest('/widget/messenger/agents/online', {
|
|
60
|
-
method: 'GET',
|
|
61
|
-
headers: { Authorization: `Bearer ${this.sessionToken}` },
|
|
62
|
-
});
|
|
40
|
+
return this.messenger.checkAgentsOnline();
|
|
63
41
|
}
|
|
64
42
|
|
|
65
43
|
async getConversations(options) {
|
|
@@ -75,137 +53,15 @@ export class APIService extends BaseAPIService {
|
|
|
75
53
|
}
|
|
76
54
|
|
|
77
55
|
async startConversation(data) {
|
|
78
|
-
|
|
79
|
-
console.log(
|
|
80
|
-
'[APIService] startConversation: session invalid, calling init...'
|
|
81
|
-
);
|
|
82
|
-
try {
|
|
83
|
-
await this.init();
|
|
84
|
-
console.log(
|
|
85
|
-
'[APIService] startConversation: init result, token:',
|
|
86
|
-
this.sessionToken ? 'set' : 'null'
|
|
87
|
-
);
|
|
88
|
-
} catch (initError) {
|
|
89
|
-
console.error(
|
|
90
|
-
'[APIService] startConversation: init failed:',
|
|
91
|
-
initError.message
|
|
92
|
-
);
|
|
93
|
-
throw initError;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!this.sessionToken) {
|
|
98
|
-
console.error(
|
|
99
|
-
'[APIService] startConversation: no session token after init'
|
|
100
|
-
);
|
|
101
|
-
throw new APIError(401, 'No valid session token available');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
console.log(
|
|
105
|
-
'[APIService] startConversation: sending to',
|
|
106
|
-
`${this.baseURL}/widget/messenger/conversations`,
|
|
107
|
-
'mock:',
|
|
108
|
-
this.mock
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
if (this.mock) {
|
|
112
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
113
|
-
const newConv = {
|
|
114
|
-
id: 'conv_' + Date.now(),
|
|
115
|
-
subject: data.subject || 'New conversation',
|
|
116
|
-
status: 'open',
|
|
117
|
-
last_message_at: new Date().toISOString(),
|
|
118
|
-
created_at: new Date().toISOString(),
|
|
119
|
-
messages: [
|
|
120
|
-
{
|
|
121
|
-
id: 'msg_' + Date.now(),
|
|
122
|
-
content: data.message,
|
|
123
|
-
sender_type: 'customer',
|
|
124
|
-
created_at: new Date().toISOString(),
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
};
|
|
128
|
-
MOCK_CONVERSATIONS.unshift(newConv);
|
|
129
|
-
MOCK_MESSAGES[newConv.id] = newConv.messages;
|
|
130
|
-
return { status: true, data: newConv };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return this._makeRequest('/widget/messenger/conversations', {
|
|
134
|
-
method: 'POST',
|
|
135
|
-
headers: {
|
|
136
|
-
'Content-Type': 'application/json',
|
|
137
|
-
Authorization: `Bearer ${this.sessionToken}`,
|
|
138
|
-
},
|
|
139
|
-
body: JSON.stringify({
|
|
140
|
-
message: data.message,
|
|
141
|
-
subject: data.subject || '',
|
|
142
|
-
}),
|
|
143
|
-
});
|
|
56
|
+
return this.messenger.startConversation(data);
|
|
144
57
|
}
|
|
145
58
|
|
|
146
59
|
async sendMessage(conversationId, data) {
|
|
147
|
-
|
|
148
|
-
await this.init();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (this.mock) {
|
|
152
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
153
|
-
const newMessage = {
|
|
154
|
-
id: 'msg_' + Date.now(),
|
|
155
|
-
content: data.content,
|
|
156
|
-
attachments: data.attachments || [],
|
|
157
|
-
sender_type: 'customer',
|
|
158
|
-
created_at: new Date().toISOString(),
|
|
159
|
-
};
|
|
160
|
-
if (!MOCK_MESSAGES[conversationId]) {
|
|
161
|
-
MOCK_MESSAGES[conversationId] = [];
|
|
162
|
-
}
|
|
163
|
-
MOCK_MESSAGES[conversationId].push(newMessage);
|
|
164
|
-
return { status: true, data: newMessage };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const payload = { content: data.content };
|
|
168
|
-
if (data.attachments && data.attachments.length > 0) {
|
|
169
|
-
payload.attachments = data.attachments;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return this._makeRequest(
|
|
173
|
-
`/widget/messenger/conversations/${conversationId}/messages`,
|
|
174
|
-
{
|
|
175
|
-
method: 'POST',
|
|
176
|
-
headers: {
|
|
177
|
-
'Content-Type': 'application/json',
|
|
178
|
-
Authorization: `Bearer ${this.sessionToken}`,
|
|
179
|
-
},
|
|
180
|
-
body: JSON.stringify(payload),
|
|
181
|
-
}
|
|
182
|
-
);
|
|
60
|
+
return this.messenger.sendMessage(conversationId, data);
|
|
183
61
|
}
|
|
184
62
|
|
|
185
|
-
/**
|
|
186
|
-
* Upload a file to CDN via widget endpoint
|
|
187
|
-
* @param {string} base64Data - Base64 encoded file data (with or without data URI prefix)
|
|
188
|
-
* @param {string} filename - Original filename
|
|
189
|
-
* @returns {Promise<Object>} Response with url
|
|
190
|
-
*/
|
|
191
63
|
async uploadFile(base64Data, filename) {
|
|
192
|
-
|
|
193
|
-
await this.init();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (this.mock) {
|
|
197
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
198
|
-
return { status: true, url: `https://mock-cdn.example.com/${filename}` };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return this._makeRequest('/widget/messenger/upload', {
|
|
202
|
-
method: 'POST',
|
|
203
|
-
headers: {
|
|
204
|
-
'Content-Type': 'application/json',
|
|
205
|
-
Authorization: `Bearer ${this.sessionToken}`,
|
|
206
|
-
},
|
|
207
|
-
body: JSON.stringify({ file: base64Data, filename }),
|
|
208
|
-
});
|
|
64
|
+
return this.messenger.uploadFile(base64Data, filename);
|
|
209
65
|
}
|
|
210
66
|
|
|
211
67
|
async sendTypingIndicator(conversationId, isTyping) {
|
|
@@ -242,7 +98,7 @@ export class APIService extends BaseAPIService {
|
|
|
242
98
|
return mockResponse;
|
|
243
99
|
}
|
|
244
100
|
|
|
245
|
-
const response = await this._makeRequest('/widget/
|
|
101
|
+
const response = await this._makeRequest('/widget/identify', {
|
|
246
102
|
method: 'POST',
|
|
247
103
|
headers: {
|
|
248
104
|
'Content-Type': 'application/json',
|
|
@@ -325,65 +181,4 @@ export class APIService extends BaseAPIService {
|
|
|
325
181
|
async getChangelogs(options) {
|
|
326
182
|
return this.changelog.getChangelogs(options);
|
|
327
183
|
}
|
|
328
|
-
|
|
329
|
-
_loadStoredSession() {
|
|
330
|
-
if (typeof localStorage === 'undefined') return false;
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
const stored = localStorage.getItem('product7_session');
|
|
334
|
-
if (!stored) return false;
|
|
335
|
-
|
|
336
|
-
const sessionData = JSON.parse(stored);
|
|
337
|
-
|
|
338
|
-
// Invalidate mock tokens when not in mock mode (and vice versa)
|
|
339
|
-
const isMockToken =
|
|
340
|
-
sessionData.token && sessionData.token.startsWith('mock_');
|
|
341
|
-
if (isMockToken !== this.mock) {
|
|
342
|
-
localStorage.removeItem('product7_session');
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
this.sessionToken = sessionData.token;
|
|
347
|
-
this.sessionExpiry = new Date(sessionData.expiry);
|
|
348
|
-
|
|
349
|
-
return this.isSessionValid();
|
|
350
|
-
} catch (error) {
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async _makeRequest(endpoint, options = {}) {
|
|
356
|
-
const url = `${this.baseURL}${endpoint}`;
|
|
357
|
-
|
|
358
|
-
try {
|
|
359
|
-
const response = await fetch(url, options);
|
|
360
|
-
|
|
361
|
-
if (!response.ok) {
|
|
362
|
-
let errorMessage = `HTTP ${response.status}`;
|
|
363
|
-
let responseData = null;
|
|
364
|
-
|
|
365
|
-
try {
|
|
366
|
-
responseData = await response.json();
|
|
367
|
-
errorMessage =
|
|
368
|
-
responseData.message || responseData.error || errorMessage;
|
|
369
|
-
} catch (e) {
|
|
370
|
-
errorMessage = (await response.text()) || errorMessage;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
throw new APIError(response.status, errorMessage, responseData);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const contentType = response.headers.get('content-type');
|
|
377
|
-
if (contentType && contentType.includes('application/json')) {
|
|
378
|
-
return await response.json();
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return await response.text();
|
|
382
|
-
} catch (error) {
|
|
383
|
-
if (error instanceof APIError) {
|
|
384
|
-
throw error;
|
|
385
|
-
}
|
|
386
|
-
throw new APIError(0, error.message, null);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
184
|
}
|
|
@@ -275,6 +275,15 @@ export class BaseAPIService {
|
|
|
275
275
|
if (!stored) return false;
|
|
276
276
|
|
|
277
277
|
const sessionData = JSON.parse(stored);
|
|
278
|
+
|
|
279
|
+
// Invalidate mock tokens when not in mock mode (and vice versa)
|
|
280
|
+
const isMockToken =
|
|
281
|
+
sessionData.token && sessionData.token.startsWith('mock_');
|
|
282
|
+
if (isMockToken !== this.mock) {
|
|
283
|
+
localStorage.removeItem('product7_session');
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
278
287
|
if (
|
|
279
288
|
this.workspace &&
|
|
280
289
|
sessionData.workspace &&
|
|
@@ -283,6 +292,7 @@ export class BaseAPIService {
|
|
|
283
292
|
localStorage.removeItem('product7_session');
|
|
284
293
|
return false;
|
|
285
294
|
}
|
|
295
|
+
|
|
286
296
|
this.sessionToken = sessionData.token;
|
|
287
297
|
this.sessionExpiry = new Date(sessionData.expiry);
|
|
288
298
|
|