@product7/feedback-sdk 1.0.2 → 1.0.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/dist/feedback-sdk.js +20 -289
- 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 +1 -1
- package/src/core/APIService.js +6 -143
- package/src/core/FeedbackSDK.js +14 -50
- package/src/index.js +4 -100
- package/types/index.d.ts +63 -37
package/src/core/APIService.js
CHANGED
|
@@ -7,64 +7,36 @@ export class APIService {
|
|
|
7
7
|
this.sessionExpiry = null;
|
|
8
8
|
this.userContext = config.userContext || null;
|
|
9
9
|
|
|
10
|
-
// Construct workspace-specific API URL
|
|
11
10
|
if (config.apiUrl) {
|
|
12
|
-
// If explicitly provided, use it
|
|
13
11
|
this.baseURL = config.apiUrl;
|
|
14
12
|
} else if (this.workspace) {
|
|
15
|
-
// Construct from workspace: workspace.api.staging.product7.io/api/v1
|
|
16
13
|
this.baseURL = `https://${this.workspace}.api.staging.product7.io/api/v1`;
|
|
17
14
|
} else {
|
|
18
|
-
// Fallback to default
|
|
19
15
|
this.baseURL = 'https://api.staging.product7.io/api/v1';
|
|
20
16
|
}
|
|
21
17
|
|
|
22
|
-
console.log('[APIService] Using API URL:', this.baseURL);
|
|
23
|
-
|
|
24
|
-
// Try to load existing session from localStorage
|
|
25
18
|
this._loadStoredSession();
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
async init(userContext = null) {
|
|
29
|
-
console.log('[APIService] Starting initialization...');
|
|
30
|
-
|
|
31
22
|
if (userContext) {
|
|
32
23
|
this.userContext = userContext;
|
|
33
24
|
}
|
|
34
25
|
|
|
35
|
-
// Check if we have a valid session token
|
|
36
26
|
if (this.isSessionValid()) {
|
|
37
|
-
console.log('[APIService] Found valid existing session');
|
|
38
27
|
return { sessionToken: this.sessionToken };
|
|
39
28
|
}
|
|
40
29
|
|
|
41
|
-
// Try to get user context from various sources if not provided
|
|
42
|
-
if (!this.userContext) {
|
|
43
|
-
console.log(
|
|
44
|
-
'[APIService] No user context provided, attempting auto-detection...'
|
|
45
|
-
);
|
|
46
|
-
this.userContext = this._getUserContextFromStorage();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
30
|
if (!this.userContext || !this.workspace) {
|
|
50
31
|
const error = `Missing ${!this.workspace ? 'workspace' : 'user context'} for initialization`;
|
|
51
|
-
console.error('[APIService]', error);
|
|
52
32
|
throw new APIError(400, error);
|
|
53
33
|
}
|
|
54
34
|
|
|
55
|
-
console.log('[APIService] User context detected:', this.userContext);
|
|
56
|
-
|
|
57
35
|
const payload = {
|
|
58
36
|
workspace: this.workspace,
|
|
59
37
|
user: this.userContext,
|
|
60
38
|
};
|
|
61
39
|
|
|
62
|
-
console.log(
|
|
63
|
-
'[APIService] Making init request to:',
|
|
64
|
-
`${this.baseURL}/widget/init`
|
|
65
|
-
);
|
|
66
|
-
console.log('[APIService] Payload:', payload);
|
|
67
|
-
|
|
68
40
|
try {
|
|
69
41
|
const response = await this._makeRequest('/widget/init', {
|
|
70
42
|
method: 'POST',
|
|
@@ -74,13 +46,8 @@ export class APIService {
|
|
|
74
46
|
},
|
|
75
47
|
});
|
|
76
48
|
|
|
77
|
-
console.log('[APIService] Init response:', response);
|
|
78
|
-
|
|
79
|
-
// Store session token and expiry
|
|
80
49
|
this.sessionToken = response.session_token;
|
|
81
50
|
this.sessionExpiry = new Date(Date.now() + response.expires_in * 1000);
|
|
82
|
-
|
|
83
|
-
// Store session in localStorage for persistence
|
|
84
51
|
this._storeSession();
|
|
85
52
|
|
|
86
53
|
return {
|
|
@@ -89,7 +56,6 @@ export class APIService {
|
|
|
89
56
|
expiresIn: response.expires_in,
|
|
90
57
|
};
|
|
91
58
|
} catch (error) {
|
|
92
|
-
console.error('[APIService] Init failed:', error);
|
|
93
59
|
throw new APIError(
|
|
94
60
|
error.status || 500,
|
|
95
61
|
`Failed to initialize widget: ${error.message}`,
|
|
@@ -99,7 +65,6 @@ export class APIService {
|
|
|
99
65
|
}
|
|
100
66
|
|
|
101
67
|
async submitFeedback(feedbackData) {
|
|
102
|
-
// Ensure we have a valid session
|
|
103
68
|
if (!this.isSessionValid()) {
|
|
104
69
|
await this.init();
|
|
105
70
|
}
|
|
@@ -109,8 +74,7 @@ export class APIService {
|
|
|
109
74
|
}
|
|
110
75
|
|
|
111
76
|
const payload = {
|
|
112
|
-
board:
|
|
113
|
-
feedbackData.board_id || feedbackData.board || feedbackData.boardId,
|
|
77
|
+
board: feedbackData.board_id || feedbackData.board || feedbackData.boardId,
|
|
114
78
|
title: feedbackData.title,
|
|
115
79
|
content: feedbackData.content,
|
|
116
80
|
attachments: feedbackData.attachments || [],
|
|
@@ -128,13 +92,10 @@ export class APIService {
|
|
|
128
92
|
|
|
129
93
|
return response;
|
|
130
94
|
} catch (error) {
|
|
131
|
-
// If session expired, try to reinitialize once
|
|
132
95
|
if (error.status === 401) {
|
|
133
96
|
this.sessionToken = null;
|
|
134
97
|
this.sessionExpiry = null;
|
|
135
98
|
await this.init();
|
|
136
|
-
|
|
137
|
-
// Retry the request with new session
|
|
138
99
|
return this.submitFeedback(feedbackData);
|
|
139
100
|
}
|
|
140
101
|
|
|
@@ -154,12 +115,8 @@ export class APIService {
|
|
|
154
115
|
|
|
155
116
|
setUserContext(userContext) {
|
|
156
117
|
this.userContext = userContext;
|
|
157
|
-
// Store in localStorage for persistence
|
|
158
118
|
if (typeof localStorage !== 'undefined') {
|
|
159
|
-
localStorage.setItem(
|
|
160
|
-
'feedbackSDK_userContext',
|
|
161
|
-
JSON.stringify(userContext)
|
|
162
|
-
);
|
|
119
|
+
localStorage.setItem('feedbackSDK_userContext', JSON.stringify(userContext));
|
|
163
120
|
}
|
|
164
121
|
}
|
|
165
122
|
|
|
@@ -172,99 +129,10 @@ export class APIService {
|
|
|
172
129
|
this.sessionExpiry = null;
|
|
173
130
|
if (typeof localStorage !== 'undefined') {
|
|
174
131
|
localStorage.removeItem('feedbackSDK_session');
|
|
132
|
+
localStorage.removeItem('feedbackSDK_userContext');
|
|
175
133
|
}
|
|
176
134
|
}
|
|
177
135
|
|
|
178
|
-
_getUserContextFromStorage() {
|
|
179
|
-
if (typeof localStorage === 'undefined') return null;
|
|
180
|
-
|
|
181
|
-
console.log('[APIService] Attempting to detect user from storage...');
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
// Try to get from feedbackSDK specific storage first
|
|
185
|
-
const stored = localStorage.getItem('feedbackSDK_userContext');
|
|
186
|
-
if (stored) {
|
|
187
|
-
console.log('[APIService] Found user context in feedbackSDK storage');
|
|
188
|
-
return JSON.parse(stored);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Try to get from window object (if set by parent application)
|
|
192
|
-
if (typeof window !== 'undefined' && window.FeedbackSDKUserContext) {
|
|
193
|
-
console.log(
|
|
194
|
-
'[APIService] Found user context in window.FeedbackSDKUserContext'
|
|
195
|
-
);
|
|
196
|
-
return window.FeedbackSDKUserContext;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Check window.currentUser
|
|
200
|
-
if (typeof window !== 'undefined' && window.currentUser) {
|
|
201
|
-
console.log('[APIService] Found user context in window.currentUser');
|
|
202
|
-
return this._mapUserData(window.currentUser);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Try to extract from existing session storage
|
|
206
|
-
const authSources = ['auth', 'currentUser', 'userSession', 'user'];
|
|
207
|
-
|
|
208
|
-
for (const key of authSources) {
|
|
209
|
-
const sessionData = localStorage.getItem(key);
|
|
210
|
-
if (sessionData) {
|
|
211
|
-
console.log(`[APIService] Found data in localStorage.${key}`);
|
|
212
|
-
const parsed = JSON.parse(sessionData);
|
|
213
|
-
|
|
214
|
-
// Handle nested user data (like {user: {...}, token: ...})
|
|
215
|
-
const userData = parsed.user || parsed;
|
|
216
|
-
|
|
217
|
-
if (userData && (userData.id || userData.user_id || userData.email)) {
|
|
218
|
-
console.log('[APIService] Successfully mapped user data');
|
|
219
|
-
return this._mapUserData(userData);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
console.log('[APIService] No user context found in any storage location');
|
|
225
|
-
return null;
|
|
226
|
-
} catch (error) {
|
|
227
|
-
console.warn(
|
|
228
|
-
'[FeedbackSDK] Failed to load user context from storage:',
|
|
229
|
-
error
|
|
230
|
-
);
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
_mapUserData(userData) {
|
|
236
|
-
console.log('[APIService] Mapping user data:', userData);
|
|
237
|
-
|
|
238
|
-
const mapped = {
|
|
239
|
-
user_id: userData.id || userData.user_id || userData.userId,
|
|
240
|
-
email: userData.email,
|
|
241
|
-
name: userData.name || userData.displayName || userData.full_name,
|
|
242
|
-
custom_fields: {},
|
|
243
|
-
company: {},
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// Map company data if available
|
|
247
|
-
if (userData.company || userData.organization) {
|
|
248
|
-
const company = userData.company || userData.organization;
|
|
249
|
-
mapped.company = {
|
|
250
|
-
id: company.id || company.company_id,
|
|
251
|
-
name: company.name || company.company_name,
|
|
252
|
-
monthly_spend: company.monthly_spend || company.spend,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Map any additional custom fields
|
|
257
|
-
const customFieldKeys = ['plan', 'role', 'tier', 'subscription'];
|
|
258
|
-
customFieldKeys.forEach((key) => {
|
|
259
|
-
if (userData[key]) {
|
|
260
|
-
mapped.custom_fields[key] = userData[key];
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
console.log('[APIService] Mapped result:', mapped);
|
|
265
|
-
return mapped;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
136
|
_storeSession() {
|
|
269
137
|
if (typeof localStorage === 'undefined') return;
|
|
270
138
|
|
|
@@ -276,7 +144,7 @@ export class APIService {
|
|
|
276
144
|
};
|
|
277
145
|
localStorage.setItem('feedbackSDK_session', JSON.stringify(sessionData));
|
|
278
146
|
} catch (error) {
|
|
279
|
-
|
|
147
|
+
// Silently fail if localStorage is not available
|
|
280
148
|
}
|
|
281
149
|
}
|
|
282
150
|
|
|
@@ -293,14 +161,12 @@ export class APIService {
|
|
|
293
161
|
|
|
294
162
|
return this.isSessionValid();
|
|
295
163
|
} catch (error) {
|
|
296
|
-
console.warn('[FeedbackSDK] Failed to load stored session:', error);
|
|
297
164
|
return false;
|
|
298
165
|
}
|
|
299
166
|
}
|
|
300
167
|
|
|
301
168
|
async _makeRequest(endpoint, options = {}) {
|
|
302
169
|
const url = `${this.baseURL}${endpoint}`;
|
|
303
|
-
console.log('[APIService] Making request to:', url);
|
|
304
170
|
|
|
305
171
|
try {
|
|
306
172
|
const response = await fetch(url, options);
|
|
@@ -311,8 +177,7 @@ export class APIService {
|
|
|
311
177
|
|
|
312
178
|
try {
|
|
313
179
|
responseData = await response.json();
|
|
314
|
-
errorMessage =
|
|
315
|
-
responseData.message || responseData.error || errorMessage;
|
|
180
|
+
errorMessage = responseData.message || responseData.error || errorMessage;
|
|
316
181
|
} catch (e) {
|
|
317
182
|
errorMessage = (await response.text()) || errorMessage;
|
|
318
183
|
}
|
|
@@ -330,9 +195,7 @@ export class APIService {
|
|
|
330
195
|
if (error instanceof APIError) {
|
|
331
196
|
throw error;
|
|
332
197
|
}
|
|
333
|
-
|
|
334
|
-
// Network or other errors
|
|
335
198
|
throw new APIError(0, error.message, null);
|
|
336
199
|
}
|
|
337
200
|
}
|
|
338
|
-
}
|
|
201
|
+
}
|
package/src/core/FeedbackSDK.js
CHANGED
|
@@ -11,7 +11,6 @@ export class FeedbackSDK {
|
|
|
11
11
|
this.widgets = new Map();
|
|
12
12
|
this.eventBus = new EventBus();
|
|
13
13
|
|
|
14
|
-
// Initialize API service
|
|
15
14
|
this.apiService = new APIService({
|
|
16
15
|
apiUrl: this.config.apiUrl,
|
|
17
16
|
workspace: this.config.workspace,
|
|
@@ -27,10 +26,8 @@ export class FeedbackSDK {
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
try {
|
|
30
|
-
// Initialize the API service (this will handle the /widget/init call)
|
|
31
29
|
const initData = await this.apiService.init(this.config.userContext);
|
|
32
30
|
|
|
33
|
-
// Merge any server-provided config with local config
|
|
34
31
|
if (initData.config) {
|
|
35
32
|
this.config = deepMerge(this.config, initData.config);
|
|
36
33
|
}
|
|
@@ -55,9 +52,7 @@ export class FeedbackSDK {
|
|
|
55
52
|
|
|
56
53
|
createWidget(type = 'button', options = {}) {
|
|
57
54
|
if (!this.initialized) {
|
|
58
|
-
throw new SDKError(
|
|
59
|
-
'SDK must be initialized before creating widgets. Call init() first.'
|
|
60
|
-
);
|
|
55
|
+
throw new SDKError('SDK must be initialized before creating widgets. Call init() first.');
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
const widgetId = generateId('widget');
|
|
@@ -72,7 +67,6 @@ export class FeedbackSDK {
|
|
|
72
67
|
try {
|
|
73
68
|
const widget = WidgetFactory.create(type, widgetOptions);
|
|
74
69
|
this.widgets.set(widgetId, widget);
|
|
75
|
-
|
|
76
70
|
this.eventBus.emit('widget:created', { widget, type });
|
|
77
71
|
return widget;
|
|
78
72
|
} catch (error) {
|
|
@@ -111,7 +105,6 @@ export class FeedbackSDK {
|
|
|
111
105
|
const oldConfig = { ...this.config };
|
|
112
106
|
this.config = this._validateAndMergeConfig(newConfig, this.config);
|
|
113
107
|
|
|
114
|
-
// Update all existing widgets with new config
|
|
115
108
|
for (const widget of this.widgets.values()) {
|
|
116
109
|
widget.handleConfigUpdate(this.config);
|
|
117
110
|
}
|
|
@@ -131,23 +124,17 @@ export class FeedbackSDK {
|
|
|
131
124
|
}
|
|
132
125
|
|
|
133
126
|
getUserContext() {
|
|
134
|
-
return (
|
|
135
|
-
this.config.userContext ||
|
|
136
|
-
(this.apiService ? this.apiService.getUserContext() : null)
|
|
137
|
-
);
|
|
127
|
+
return this.config.userContext || (this.apiService ? this.apiService.getUserContext() : null);
|
|
138
128
|
}
|
|
139
129
|
|
|
140
130
|
async reinitialize(newUserContext = null) {
|
|
141
|
-
// Clear current session
|
|
142
131
|
this.apiService.clearSession();
|
|
143
132
|
this.initialized = false;
|
|
144
133
|
|
|
145
|
-
// Update user context if provided
|
|
146
134
|
if (newUserContext) {
|
|
147
135
|
this.setUserContext(newUserContext);
|
|
148
136
|
}
|
|
149
137
|
|
|
150
|
-
// Reinitialize
|
|
151
138
|
return this.init();
|
|
152
139
|
}
|
|
153
140
|
|
|
@@ -191,24 +178,12 @@ export class FeedbackSDK {
|
|
|
191
178
|
debug: false,
|
|
192
179
|
};
|
|
193
180
|
|
|
194
|
-
const mergedConfig = deepMerge(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Validate required config
|
|
200
|
-
const requiredFields = ['workspace'];
|
|
201
|
-
const missingFields = requiredFields.filter(
|
|
202
|
-
(field) => !mergedConfig[field]
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
if (missingFields.length > 0) {
|
|
206
|
-
throw new ConfigError(
|
|
207
|
-
`Missing required configuration: ${missingFields.join(', ')}`
|
|
208
|
-
);
|
|
181
|
+
const mergedConfig = deepMerge(deepMerge(defaultConfig, existingConfig), newConfig);
|
|
182
|
+
|
|
183
|
+
if (!mergedConfig.workspace) {
|
|
184
|
+
throw new ConfigError('Missing required configuration: workspace');
|
|
209
185
|
}
|
|
210
186
|
|
|
211
|
-
// Validate userContext structure if provided
|
|
212
187
|
if (mergedConfig.userContext) {
|
|
213
188
|
this._validateUserContext(mergedConfig.userContext);
|
|
214
189
|
}
|
|
@@ -218,12 +193,9 @@ export class FeedbackSDK {
|
|
|
218
193
|
|
|
219
194
|
_validateUserContext(userContext) {
|
|
220
195
|
if (!userContext.user_id && !userContext.email) {
|
|
221
|
-
throw new ConfigError(
|
|
222
|
-
'User context must include at least user_id or email'
|
|
223
|
-
);
|
|
196
|
+
throw new ConfigError('User context must include at least user_id or email');
|
|
224
197
|
}
|
|
225
198
|
|
|
226
|
-
// Validate structure matches expected API format
|
|
227
199
|
const validStructure = {
|
|
228
200
|
user_id: 'string',
|
|
229
201
|
email: 'string',
|
|
@@ -234,9 +206,7 @@ export class FeedbackSDK {
|
|
|
234
206
|
|
|
235
207
|
for (const [key, expectedType] of Object.entries(validStructure)) {
|
|
236
208
|
if (userContext[key] && typeof userContext[key] !== expectedType) {
|
|
237
|
-
throw new ConfigError(
|
|
238
|
-
`User context field '${key}' must be of type '${expectedType}'`
|
|
239
|
-
);
|
|
209
|
+
throw new ConfigError(`User context field '${key}' must be of type '${expectedType}'`);
|
|
240
210
|
}
|
|
241
211
|
}
|
|
242
212
|
}
|
|
@@ -247,7 +217,6 @@ export class FeedbackSDK {
|
|
|
247
217
|
this.updateConfig = this.updateConfig.bind(this);
|
|
248
218
|
}
|
|
249
219
|
|
|
250
|
-
// Static helper methods
|
|
251
220
|
static create(config) {
|
|
252
221
|
return new FeedbackSDK(config);
|
|
253
222
|
}
|
|
@@ -258,9 +227,7 @@ export class FeedbackSDK {
|
|
|
258
227
|
return sdk;
|
|
259
228
|
}
|
|
260
229
|
|
|
261
|
-
// Utility methods for external integrations
|
|
262
230
|
static extractUserContextFromAuth(authData) {
|
|
263
|
-
// Helper method to extract user context from common auth structures
|
|
264
231
|
if (!authData) return null;
|
|
265
232
|
|
|
266
233
|
return {
|
|
@@ -272,14 +239,11 @@ export class FeedbackSDK {
|
|
|
272
239
|
plan: authData.plan || authData.subscription?.plan,
|
|
273
240
|
...(authData.custom_fields || {}),
|
|
274
241
|
},
|
|
275
|
-
company:
|
|
276
|
-
authData.company || authData.organization
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
monthly_spend: authData.company?.monthly_spend,
|
|
281
|
-
}
|
|
282
|
-
: undefined,
|
|
242
|
+
company: authData.company || authData.organization ? {
|
|
243
|
+
id: authData.company?.id || authData.organization?.id,
|
|
244
|
+
name: authData.company?.name || authData.organization?.name,
|
|
245
|
+
monthly_spend: authData.company?.monthly_spend,
|
|
246
|
+
} : undefined,
|
|
283
247
|
};
|
|
284
248
|
}
|
|
285
|
-
}
|
|
249
|
+
}
|
package/src/index.js
CHANGED
|
@@ -17,25 +17,11 @@ import { TabWidget } from './widgets/TabWidget.js';
|
|
|
17
17
|
import { WidgetFactory } from './widgets/WidgetFactory.js';
|
|
18
18
|
|
|
19
19
|
function injectStyles() {
|
|
20
|
-
|
|
21
|
-
console.log('document exists:', typeof document !== 'undefined');
|
|
22
|
-
console.log('CSS_STYLES exists:', !!CSS_STYLES);
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
typeof document !== 'undefined' &&
|
|
26
|
-
!document.querySelector('#feedback-sdk-styles')
|
|
27
|
-
) {
|
|
28
|
-
console.log('Injecting CSS...');
|
|
20
|
+
if (typeof document !== 'undefined' && !document.querySelector('#feedback-sdk-styles')) {
|
|
29
21
|
const style = document.createElement('style');
|
|
30
22
|
style.id = 'feedback-sdk-styles';
|
|
31
23
|
style.textContent = CSS_STYLES;
|
|
32
24
|
document.head.appendChild(style);
|
|
33
|
-
console.log(
|
|
34
|
-
'CSS injected, style element created:',
|
|
35
|
-
!!document.querySelector('#feedback-sdk-styles')
|
|
36
|
-
);
|
|
37
|
-
} else {
|
|
38
|
-
console.log('CSS already exists or document not ready');
|
|
39
25
|
}
|
|
40
26
|
}
|
|
41
27
|
|
|
@@ -44,11 +30,6 @@ function autoInit() {
|
|
|
44
30
|
injectStyles();
|
|
45
31
|
|
|
46
32
|
const config = { ...window.FeedbackSDKConfig };
|
|
47
|
-
|
|
48
|
-
if (!config.userContext) {
|
|
49
|
-
config.userContext = getUserContextFromEnvironment();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
33
|
const sdk = new FeedbackSDK(config);
|
|
53
34
|
|
|
54
35
|
sdk
|
|
@@ -76,30 +57,17 @@ function autoInit() {
|
|
|
76
57
|
|
|
77
58
|
if (typeof CustomEvent !== 'undefined') {
|
|
78
59
|
const event = new CustomEvent('FeedbackSDKReady', {
|
|
79
|
-
detail: {
|
|
80
|
-
sdk,
|
|
81
|
-
config: config,
|
|
82
|
-
initData: initData,
|
|
83
|
-
},
|
|
60
|
+
detail: { sdk, config, initData },
|
|
84
61
|
});
|
|
85
62
|
window.dispatchEvent(event);
|
|
86
63
|
}
|
|
87
|
-
|
|
88
|
-
console.log(
|
|
89
|
-
'[FeedbackSDK] Successfully initialized with session:',
|
|
90
|
-
initData.sessionToken ? 'Yes' : 'No'
|
|
91
|
-
);
|
|
92
64
|
})
|
|
93
65
|
.catch((error) => {
|
|
94
66
|
console.error('[FeedbackSDK] Auto-initialization failed:', error);
|
|
95
67
|
|
|
96
68
|
if (typeof CustomEvent !== 'undefined') {
|
|
97
69
|
const event = new CustomEvent('FeedbackSDKError', {
|
|
98
|
-
detail: {
|
|
99
|
-
error,
|
|
100
|
-
config: config,
|
|
101
|
-
phase: 'initialization',
|
|
102
|
-
},
|
|
70
|
+
detail: { error, config, phase: 'initialization' },
|
|
103
71
|
});
|
|
104
72
|
window.dispatchEvent(event);
|
|
105
73
|
}
|
|
@@ -107,70 +75,6 @@ function autoInit() {
|
|
|
107
75
|
}
|
|
108
76
|
}
|
|
109
77
|
|
|
110
|
-
function getUserContextFromEnvironment() {
|
|
111
|
-
if (typeof window === 'undefined') return null;
|
|
112
|
-
|
|
113
|
-
if (window.FeedbackSDKUserContext) {
|
|
114
|
-
return window.FeedbackSDKUserContext;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const authSources = [
|
|
118
|
-
() => window.auth0?.user,
|
|
119
|
-
() => window.firebase?.auth()?.currentUser,
|
|
120
|
-
() => window.amplify?.Auth?.currentAuthenticatedUser(),
|
|
121
|
-
|
|
122
|
-
() => window.currentUser,
|
|
123
|
-
() => window.user,
|
|
124
|
-
() => window.userData,
|
|
125
|
-
|
|
126
|
-
() => window.app?.user,
|
|
127
|
-
() => window.store?.getState?.()?.user,
|
|
128
|
-
() => window.App?.currentUser,
|
|
129
|
-
];
|
|
130
|
-
|
|
131
|
-
for (const getAuth of authSources) {
|
|
132
|
-
try {
|
|
133
|
-
const authData = getAuth();
|
|
134
|
-
if (authData) {
|
|
135
|
-
const userContext = FeedbackSDK.extractUserContextFromAuth(authData);
|
|
136
|
-
if (userContext && (userContext.user_id || userContext.email)) {
|
|
137
|
-
console.log(
|
|
138
|
-
'[FeedbackSDK] Auto-detected user context from',
|
|
139
|
-
getAuth.name || 'unknown source'
|
|
140
|
-
);
|
|
141
|
-
return userContext;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
} catch (error) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
const storedAuth =
|
|
151
|
-
localStorage.getItem('auth') ||
|
|
152
|
-
localStorage.getItem('user') ||
|
|
153
|
-
localStorage.getItem('session');
|
|
154
|
-
if (storedAuth) {
|
|
155
|
-
const authData = JSON.parse(storedAuth);
|
|
156
|
-
const userContext = FeedbackSDK.extractUserContextFromAuth(authData);
|
|
157
|
-
if (userContext && (userContext.user_id || userContext.email)) {
|
|
158
|
-
console.log(
|
|
159
|
-
'[FeedbackSDK] Auto-detected user context from localStorage'
|
|
160
|
-
);
|
|
161
|
-
return userContext;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
} catch (error) {
|
|
165
|
-
// Continue
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
console.warn(
|
|
169
|
-
'[FeedbackSDK] No user context found. Widget initialization may require manual user context setting.'
|
|
170
|
-
);
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
78
|
function handleDOMReady() {
|
|
175
79
|
if (typeof document !== 'undefined') {
|
|
176
80
|
if (document.readyState === 'loading') {
|
|
@@ -278,4 +182,4 @@ export {
|
|
|
278
182
|
ValidationError,
|
|
279
183
|
WidgetError,
|
|
280
184
|
WidgetFactory,
|
|
281
|
-
};
|
|
185
|
+
};
|
package/types/index.d.ts
CHANGED
|
@@ -1,42 +1,68 @@
|
|
|
1
|
+
|
|
1
2
|
declare module '@product7/feedback-sdk' {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export interface FeedbackConfig {
|
|
4
|
+
workspace: string;
|
|
5
|
+
userContext: UserContext;
|
|
6
|
+
debug?: boolean;
|
|
7
|
+
boardId?: string;
|
|
8
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
9
|
+
theme?: 'light' | 'dark';
|
|
10
|
+
apiUrl?: string;
|
|
11
|
+
autoShow?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface UserContext {
|
|
15
|
+
user_id?: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
custom_fields?: Record<string, any>;
|
|
19
|
+
company?: {
|
|
20
|
+
id?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
monthly_spend?: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
8
25
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
export interface ButtonWidget {
|
|
27
|
+
id: string;
|
|
28
|
+
type: 'button';
|
|
29
|
+
mount(container?: string | HTMLElement): this;
|
|
30
|
+
destroy(): void;
|
|
31
|
+
show(): this;
|
|
32
|
+
hide(): this;
|
|
33
|
+
openModal(): void;
|
|
34
|
+
closeModal(): void;
|
|
35
|
+
}
|
|
17
36
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
export class FeedbackSDK {
|
|
38
|
+
constructor(config: FeedbackConfig);
|
|
39
|
+
init(): Promise<{
|
|
40
|
+
initialized: boolean;
|
|
41
|
+
config?: any;
|
|
42
|
+
sessionToken?: string;
|
|
43
|
+
expiresIn?: number;
|
|
44
|
+
}>;
|
|
45
|
+
createWidget(type: 'button', options?: Partial<FeedbackConfig>): ButtonWidget;
|
|
46
|
+
setUserContext(userContext: UserContext): void;
|
|
47
|
+
getUserContext(): UserContext | null;
|
|
48
|
+
destroy(): void;
|
|
49
|
+
readonly initialized: boolean;
|
|
50
|
+
}
|
|
27
51
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
interface FeedbackSDKExport {
|
|
53
|
+
FeedbackSDK: typeof FeedbackSDK;
|
|
54
|
+
ButtonWidget: any;
|
|
55
|
+
create: (config: FeedbackConfig) => FeedbackSDK;
|
|
56
|
+
initWithUser: (config: Omit<FeedbackConfig, 'userContext'>, userContext: UserContext) => Promise<FeedbackSDK>;
|
|
57
|
+
getInstance: () => FeedbackSDK | null;
|
|
58
|
+
isReady: () => boolean;
|
|
59
|
+
setUserContext: (userContext: UserContext) => void;
|
|
60
|
+
onReady: (callback: (sdk: FeedbackSDK) => void) => void;
|
|
61
|
+
onError: (callback: (error: Error) => void) => void;
|
|
62
|
+
version: string;
|
|
63
|
+
instance: FeedbackSDK | null;
|
|
64
|
+
}
|
|
39
65
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
66
|
+
const FeedbackSDKDefault: FeedbackSDKExport;
|
|
67
|
+
export default FeedbackSDKDefault;
|
|
68
|
+
}
|