@product7/feedback-sdk 1.3.0 → 1.3.2
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/feedback-sdk.js +2158 -663
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +2 -1
- package/src/core/APIService.js +191 -5
- package/src/core/FeedbackSDK.js +3 -0
- package/src/styles/messengerStyles.js +580 -9
- package/src/widgets/MessengerWidget.js +247 -137
- package/src/widgets/messenger/MessengerState.js +31 -1
- package/src/widgets/messenger/views/ChatView.js +347 -29
- package/src/widgets/messenger/views/ConversationsView.js +20 -5
- package/src/widgets/messenger/views/HomeView.js +50 -10
- package/src/widgets/messenger/views/PreChatFormView.js +224 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@product7/feedback-sdk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
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",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"build:prod": "rollup -c rollup.config.js --environment NODE_ENV:production",
|
|
19
19
|
"dev": "rollup -c rollup.config.js --environment NODE_ENV:development --watch",
|
|
20
20
|
"serve": "http-server . -p 8080 -c-1",
|
|
21
|
+
"localstack": "npm run build:dev && echo 'Open http://localhost:8080/test.html?env=localstack' && http-server . -p 8080 -c-1",
|
|
21
22
|
"dev:test": "npm run build:dev && npm run serve",
|
|
22
23
|
"test": "jest",
|
|
23
24
|
"test:watch": "jest --watch",
|
package/src/core/APIService.js
CHANGED
|
@@ -37,7 +37,29 @@ export class APIService extends BaseAPIService {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async checkAgentsOnline() {
|
|
40
|
-
|
|
40
|
+
if (!this.isSessionValid()) {
|
|
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
|
+
});
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
async getConversations(options) {
|
|
@@ -53,11 +75,119 @@ export class APIService extends BaseAPIService {
|
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
async startConversation(data) {
|
|
56
|
-
|
|
78
|
+
if (!this.isSessionValid()) {
|
|
79
|
+
console.log('[APIService] startConversation: session invalid, calling init...');
|
|
80
|
+
try {
|
|
81
|
+
await this.init();
|
|
82
|
+
console.log('[APIService] startConversation: init result, token:', this.sessionToken ? 'set' : 'null');
|
|
83
|
+
} catch (initError) {
|
|
84
|
+
console.error('[APIService] startConversation: init failed:', initError.message);
|
|
85
|
+
throw initError;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!this.sessionToken) {
|
|
90
|
+
console.error('[APIService] startConversation: no session token after init');
|
|
91
|
+
throw new APIError(401, 'No valid session token available');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log('[APIService] startConversation: sending to', `${this.baseURL}/widget/messenger/conversations`, 'mock:', this.mock);
|
|
95
|
+
|
|
96
|
+
if (this.mock) {
|
|
97
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
98
|
+
const newConv = {
|
|
99
|
+
id: 'conv_' + Date.now(),
|
|
100
|
+
subject: data.subject || 'New conversation',
|
|
101
|
+
status: 'open',
|
|
102
|
+
last_message_at: new Date().toISOString(),
|
|
103
|
+
created_at: new Date().toISOString(),
|
|
104
|
+
messages: [
|
|
105
|
+
{
|
|
106
|
+
id: 'msg_' + Date.now(),
|
|
107
|
+
content: data.message,
|
|
108
|
+
sender_type: 'customer',
|
|
109
|
+
created_at: new Date().toISOString(),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
MOCK_CONVERSATIONS.unshift(newConv);
|
|
114
|
+
MOCK_MESSAGES[newConv.id] = newConv.messages;
|
|
115
|
+
return { status: true, data: newConv };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return this._makeRequest('/widget/messenger/conversations', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
message: data.message,
|
|
126
|
+
subject: data.subject || '',
|
|
127
|
+
}),
|
|
128
|
+
});
|
|
57
129
|
}
|
|
58
130
|
|
|
59
131
|
async sendMessage(conversationId, data) {
|
|
60
|
-
|
|
132
|
+
if (!this.isSessionValid()) {
|
|
133
|
+
await this.init();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (this.mock) {
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
138
|
+
const newMessage = {
|
|
139
|
+
id: 'msg_' + Date.now(),
|
|
140
|
+
content: data.content,
|
|
141
|
+
attachments: data.attachments || [],
|
|
142
|
+
sender_type: 'customer',
|
|
143
|
+
created_at: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
if (!MOCK_MESSAGES[conversationId]) {
|
|
146
|
+
MOCK_MESSAGES[conversationId] = [];
|
|
147
|
+
}
|
|
148
|
+
MOCK_MESSAGES[conversationId].push(newMessage);
|
|
149
|
+
return { status: true, data: newMessage };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const payload = { content: data.content };
|
|
153
|
+
if (data.attachments && data.attachments.length > 0) {
|
|
154
|
+
payload.attachments = data.attachments;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this._makeRequest(`/widget/messenger/conversations/${conversationId}/messages`, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: {
|
|
160
|
+
'Content-Type': 'application/json',
|
|
161
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify(payload),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Upload a file to CDN via widget endpoint
|
|
169
|
+
* @param {string} base64Data - Base64 encoded file data (with or without data URI prefix)
|
|
170
|
+
* @param {string} filename - Original filename
|
|
171
|
+
* @returns {Promise<Object>} Response with url
|
|
172
|
+
*/
|
|
173
|
+
async uploadFile(base64Data, filename) {
|
|
174
|
+
if (!this.isSessionValid()) {
|
|
175
|
+
await this.init();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (this.mock) {
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
180
|
+
return { status: true, url: `https://mock-cdn.example.com/${filename}` };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return this._makeRequest('/widget/messenger/upload', {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: {
|
|
186
|
+
'Content-Type': 'application/json',
|
|
187
|
+
Authorization: `Bearer ${this.sessionToken}`,
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify({ file: base64Data, filename }),
|
|
190
|
+
});
|
|
61
191
|
}
|
|
62
192
|
|
|
63
193
|
async sendTypingIndicator(conversationId, isTyping) {
|
|
@@ -88,7 +218,63 @@ export class APIService extends BaseAPIService {
|
|
|
88
218
|
return this.help.searchHelpArticles(query, options);
|
|
89
219
|
}
|
|
90
220
|
|
|
91
|
-
|
|
92
|
-
|
|
221
|
+
_loadStoredSession() {
|
|
222
|
+
if (typeof localStorage === 'undefined') return false;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const stored = localStorage.getItem('feedbackSDK_session');
|
|
226
|
+
if (!stored) return false;
|
|
227
|
+
|
|
228
|
+
const sessionData = JSON.parse(stored);
|
|
229
|
+
|
|
230
|
+
// Invalidate mock tokens when not in mock mode (and vice versa)
|
|
231
|
+
const isMockToken = sessionData.token && sessionData.token.startsWith('mock_');
|
|
232
|
+
if (isMockToken !== this.mock) {
|
|
233
|
+
localStorage.removeItem('feedbackSDK_session');
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.sessionToken = sessionData.token;
|
|
238
|
+
this.sessionExpiry = new Date(sessionData.expiry);
|
|
239
|
+
|
|
240
|
+
return this.isSessionValid();
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async _makeRequest(endpoint, options = {}) {
|
|
247
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const response = await fetch(url, options);
|
|
251
|
+
|
|
252
|
+
if (!response.ok) {
|
|
253
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
254
|
+
let responseData = null;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
responseData = await response.json();
|
|
258
|
+
errorMessage =
|
|
259
|
+
responseData.message || responseData.error || errorMessage;
|
|
260
|
+
} catch (e) {
|
|
261
|
+
errorMessage = (await response.text()) || errorMessage;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
throw new APIError(response.status, errorMessage, responseData);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const contentType = response.headers.get('content-type');
|
|
268
|
+
if (contentType && contentType.includes('application/json')) {
|
|
269
|
+
return await response.json();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return await response.text();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (error instanceof APIError) {
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
throw new APIError(0, error.message, null);
|
|
278
|
+
}
|
|
93
279
|
}
|
|
94
280
|
}
|
package/src/core/FeedbackSDK.js
CHANGED
|
@@ -14,8 +14,11 @@ export class FeedbackSDK {
|
|
|
14
14
|
this.apiService = new APIService({
|
|
15
15
|
apiUrl: this.config.apiUrl,
|
|
16
16
|
workspace: this.config.workspace,
|
|
17
|
+
siteId: this.config.siteId,
|
|
18
|
+
sessionToken: this.config.sessionToken,
|
|
17
19
|
userContext: this.config.userContext,
|
|
18
20
|
mock: this.config.mock,
|
|
21
|
+
debug: this.config.debug,
|
|
19
22
|
env: this.config.env,
|
|
20
23
|
});
|
|
21
24
|
|