@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.3.0",
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",
@@ -37,7 +37,29 @@ export class APIService extends BaseAPIService {
37
37
  }
38
38
 
39
39
  async checkAgentsOnline() {
40
- return this.messenger.checkAgentsOnline();
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
- return this.messenger.startConversation(data);
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
- return this.messenger.sendMessage(conversationId, data);
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
- async getChangelogs(options) {
92
- return this.changelog.getChangelogs(options);
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
  }
@@ -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