@phygitallabs/tapquest-core 2.7.0 → 2.9.0
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 +1 -1
- package/dist/index.cjs +1035 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -35
- package/dist/index.d.ts +29 -35
- package/dist/index.js +1017 -717
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
- package/src/modules/auth/constants/index.ts +6 -0
- package/src/modules/auth/helpers/index.ts +1 -4
- package/src/modules/auth/hooks/index.ts +2 -0
- package/src/modules/auth/hooks/useGoogleLogin.ts +169 -0
- package/src/modules/auth/hooks/useTokenRefresher.ts +39 -0
- package/src/modules/auth/index.ts +8 -2
- package/src/modules/auth/providers/AuthProvider.tsx +214 -186
- package/src/modules/auth/store/authStore.ts +577 -0
- package/src/modules/auth/types/auth.ts +29 -0
- package/src/modules/auth/types/user-data.ts +38 -0
- package/src/modules/auth/utils/user.ts +21 -0
- package/src/modules/data-tracking/hooks/index.ts +25 -1
- package/src/modules/generate-certificate/helpers/index.ts +3 -0
- package/src/modules/generate-certificate/hooks/index.ts +15 -6
- package/src/modules/generate-certificate/index.ts +3 -1
- package/src/modules/notification/providers/index.tsx +3 -3
- package/src/modules/reward/hooks/useRewardService.ts +6 -6
- package/src/modules/session-replay/README.md +334 -0
- package/src/modules/session-replay/hooks/useSessionReplay.ts +16 -0
- package/src/modules/session-replay/index.ts +10 -0
- package/src/modules/session-replay/providers/SessionReplayProvider.tsx +189 -0
- package/src/modules/session-replay/types/index.ts +147 -0
- package/src/modules/session-replay/utils/index.ts +12 -0
- package/src/providers/ServicesProvider.tsx +4 -76
- package/src/providers/TapquestCoreProvider.tsx +31 -36
- package/src/modules/auth/helpers/refreshToken.ts +0 -63
- package/src/modules/auth/store/authSlice.ts +0 -137
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import { immer } from "zustand/middleware/immer";
|
|
3
|
+
import { UserData } from "../types/user-data";
|
|
4
|
+
import {
|
|
5
|
+
createJSONStorage,
|
|
6
|
+
devtools,
|
|
7
|
+
persist,
|
|
8
|
+
subscribeWithSelector,
|
|
9
|
+
} from "zustand/middleware";
|
|
10
|
+
import { AuthEventCallbacks } from "../types/auth";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
authService,
|
|
14
|
+
ProtoSendVerifyCodeResponse,
|
|
15
|
+
ProtoSignInResponse,
|
|
16
|
+
ProtoSignUpResponse,
|
|
17
|
+
ProtoVerifyEmailResponse,
|
|
18
|
+
SendVerifyCodeRequest,
|
|
19
|
+
verifyCodeService,
|
|
20
|
+
VerifyEmailRequest,
|
|
21
|
+
tokenStorage,
|
|
22
|
+
ProtoRefreshTokenResponse,
|
|
23
|
+
OAuthSignInResponse,
|
|
24
|
+
ResetPasswordRequest,
|
|
25
|
+
ProtoResetPasswordResponse,
|
|
26
|
+
ChangePasswordRequest,
|
|
27
|
+
ProtoChangePasswordResponse,
|
|
28
|
+
} from "@phygitallabs/authentication";
|
|
29
|
+
|
|
30
|
+
import { CALLBACK_URL } from "../constants";
|
|
31
|
+
|
|
32
|
+
// Cleanup functions interface
|
|
33
|
+
export interface AuthCleanupFunctions {
|
|
34
|
+
clearQueryCache?: () => void;
|
|
35
|
+
clearOrganization?: () => void;
|
|
36
|
+
clearHeaders?: () => void;
|
|
37
|
+
clearCustomData?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Simplified auth state interface
|
|
41
|
+
interface AuthState {
|
|
42
|
+
// User data
|
|
43
|
+
user: UserData | null;
|
|
44
|
+
|
|
45
|
+
isSignedIn: boolean;
|
|
46
|
+
isInitialized: boolean;
|
|
47
|
+
|
|
48
|
+
// Single loading state
|
|
49
|
+
isLoading: boolean;
|
|
50
|
+
|
|
51
|
+
// Error state
|
|
52
|
+
error: string | null;
|
|
53
|
+
|
|
54
|
+
// Cleanup functions
|
|
55
|
+
cleanupFunctions: AuthCleanupFunctions;
|
|
56
|
+
|
|
57
|
+
// Event callbacks
|
|
58
|
+
eventCallbacks: AuthEventCallbacks;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Simplified actions interface
|
|
62
|
+
interface AuthActions {
|
|
63
|
+
setCleanupFunctions: (
|
|
64
|
+
cleanupFunctions: Partial<AuthCleanupFunctions>
|
|
65
|
+
) => void;
|
|
66
|
+
|
|
67
|
+
// Event management
|
|
68
|
+
addEventCallbacks: (callbacks: AuthEventCallbacks) => () => void;
|
|
69
|
+
|
|
70
|
+
// Core auth actions
|
|
71
|
+
signInWithEmail: (
|
|
72
|
+
email: string,
|
|
73
|
+
password: string,
|
|
74
|
+
updateHeaders?: (headers: Record<string, string>) => void,
|
|
75
|
+
updateAuthenticationHeaders?: (headers: Record<string, string>) => void
|
|
76
|
+
) => Promise<ProtoSignInResponse>;
|
|
77
|
+
signInWithGoogle: () => Promise<OAuthSignInResponse>;
|
|
78
|
+
signUpWithEmail: (
|
|
79
|
+
email: string,
|
|
80
|
+
password: string
|
|
81
|
+
) => Promise<ProtoSignUpResponse>;
|
|
82
|
+
signOut: () => Promise<void>;
|
|
83
|
+
sendPasswordResetEmail: (email: string) => Promise<void>;
|
|
84
|
+
resetPassword: (
|
|
85
|
+
data: ResetPasswordRequest
|
|
86
|
+
) => Promise<ProtoResetPasswordResponse>;
|
|
87
|
+
changePassword: (
|
|
88
|
+
data: ChangePasswordRequest
|
|
89
|
+
) => Promise<ProtoChangePasswordResponse>;
|
|
90
|
+
verifyEmailCode: (
|
|
91
|
+
data: VerifyEmailRequest
|
|
92
|
+
) => Promise<ProtoVerifyEmailResponse>;
|
|
93
|
+
sendVerifyCode: (
|
|
94
|
+
data: SendVerifyCodeRequest
|
|
95
|
+
) => Promise<ProtoSendVerifyCodeResponse>;
|
|
96
|
+
refreshToken: (refreshToken?: string) => Promise<ProtoRefreshTokenResponse>;
|
|
97
|
+
|
|
98
|
+
// Simple state management
|
|
99
|
+
clearError: () => void;
|
|
100
|
+
|
|
101
|
+
// Setters
|
|
102
|
+
setUser: (user: UserData | null) => void;
|
|
103
|
+
patchUser: (user: Partial<UserData>) => void;
|
|
104
|
+
setIsSignedIn: (isSignedIn: boolean) => void;
|
|
105
|
+
setIsInitialized: (isInitialized: boolean) => void;
|
|
106
|
+
setIsLoading: (isLoading: boolean) => void;
|
|
107
|
+
|
|
108
|
+
// Initialization
|
|
109
|
+
initialize: () => void;
|
|
110
|
+
syncAuthState: () => void;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Store structure with actions grouped
|
|
114
|
+
interface AuthStoreState extends AuthState {
|
|
115
|
+
actions: AuthActions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type AuthStore = AuthStoreState;
|
|
119
|
+
|
|
120
|
+
// Initial state
|
|
121
|
+
const initialState: AuthState = {
|
|
122
|
+
user: null,
|
|
123
|
+
isSignedIn: false,
|
|
124
|
+
isInitialized: false,
|
|
125
|
+
isLoading: false,
|
|
126
|
+
error: null,
|
|
127
|
+
cleanupFunctions: {},
|
|
128
|
+
eventCallbacks: {},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Create the auth store
|
|
132
|
+
export const useAuthStore :any = create<AuthStore>()(
|
|
133
|
+
devtools(
|
|
134
|
+
persist(
|
|
135
|
+
subscribeWithSelector(
|
|
136
|
+
immer((set, get) => ({
|
|
137
|
+
...initialState,
|
|
138
|
+
actions: {
|
|
139
|
+
setIsLoading: (isLoading: boolean) =>
|
|
140
|
+
set((state) => {
|
|
141
|
+
state.isLoading = isLoading;
|
|
142
|
+
}),
|
|
143
|
+
|
|
144
|
+
setCleanupFunctions: (
|
|
145
|
+
newCleanupFunctions: Partial<AuthCleanupFunctions>
|
|
146
|
+
) =>
|
|
147
|
+
set((state) => {
|
|
148
|
+
state.cleanupFunctions = {
|
|
149
|
+
...state.cleanupFunctions,
|
|
150
|
+
...newCleanupFunctions,
|
|
151
|
+
};
|
|
152
|
+
}),
|
|
153
|
+
|
|
154
|
+
// Event management
|
|
155
|
+
addEventCallbacks: (callbacks: AuthEventCallbacks) => {
|
|
156
|
+
set((state) => {
|
|
157
|
+
state.eventCallbacks = {
|
|
158
|
+
...state.eventCallbacks,
|
|
159
|
+
...callbacks,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Return cleanup function
|
|
164
|
+
return () => {
|
|
165
|
+
set((state) => {
|
|
166
|
+
state.eventCallbacks = {
|
|
167
|
+
...state.eventCallbacks,
|
|
168
|
+
...callbacks,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// Sign in with email and password
|
|
175
|
+
signInWithEmail: async (email: string, password: string) => {
|
|
176
|
+
const { eventCallbacks } = get();
|
|
177
|
+
set((state) => {
|
|
178
|
+
state.isLoading = true;
|
|
179
|
+
state.error = null;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const response = await authService.signIn({
|
|
184
|
+
email,
|
|
185
|
+
password,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (response?.data?.idToken && response?.data?.refreshToken) {
|
|
189
|
+
// Store tokens in tokenStorage only
|
|
190
|
+
tokenStorage.setTokens({
|
|
191
|
+
idToken: response.data.idToken,
|
|
192
|
+
refreshToken: response.data.refreshToken,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Update auth state
|
|
196
|
+
set((state) => {
|
|
197
|
+
state.isSignedIn = true;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
eventCallbacks.onLoginSuccess?.(response.data.idToken);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return response;
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
// Check if error has code 7 (email not verified)
|
|
206
|
+
if (error?.response?.data?.code === 7) {
|
|
207
|
+
// Return a special response for email verification needed
|
|
208
|
+
return {
|
|
209
|
+
data: undefined,
|
|
210
|
+
message: "Email verification required",
|
|
211
|
+
code: 7,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const errorMessage =
|
|
216
|
+
error instanceof Error ? error.message : "Login failed";
|
|
217
|
+
|
|
218
|
+
set((state) => {
|
|
219
|
+
state.error = errorMessage;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
eventCallbacks.onLoginError?.(new Error(errorMessage));
|
|
223
|
+
|
|
224
|
+
throw error;
|
|
225
|
+
} finally {
|
|
226
|
+
set((state) => {
|
|
227
|
+
state.isLoading = false;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Sign in with Google
|
|
233
|
+
signInWithGoogle: async () => {
|
|
234
|
+
const { eventCallbacks } = get();
|
|
235
|
+
try {
|
|
236
|
+
//get current domain
|
|
237
|
+
const currentDomain = CALLBACK_URL;
|
|
238
|
+
|
|
239
|
+
const userData =
|
|
240
|
+
await authService.getOAuthSignInUrl(currentDomain);
|
|
241
|
+
return userData;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
const errorMessage =
|
|
244
|
+
error instanceof Error
|
|
245
|
+
? error.message
|
|
246
|
+
: "Google sign in failed";
|
|
247
|
+
|
|
248
|
+
set((state) => {
|
|
249
|
+
state.error = errorMessage;
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
eventCallbacks.onLoginError?.(new Error(errorMessage));
|
|
253
|
+
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// Sign up with email and password
|
|
259
|
+
signUpWithEmail: async (email: string, password: string) => {
|
|
260
|
+
const { eventCallbacks } = get();
|
|
261
|
+
|
|
262
|
+
set((state) => {
|
|
263
|
+
state.isLoading = true;
|
|
264
|
+
state.error = null;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const response = await authService.signUp({
|
|
269
|
+
email,
|
|
270
|
+
password,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (response.data) {
|
|
274
|
+
eventCallbacks.onSignupSuccess?.();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return response;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
const errorMessage =
|
|
280
|
+
error instanceof Error ? error.message : "Signup failed";
|
|
281
|
+
|
|
282
|
+
set((state) => {
|
|
283
|
+
state.error = errorMessage;
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
eventCallbacks.onSignupError?.(new Error(errorMessage));
|
|
287
|
+
|
|
288
|
+
throw error;
|
|
289
|
+
} finally {
|
|
290
|
+
set((state) => {
|
|
291
|
+
state.isLoading = false;
|
|
292
|
+
state.user = null;
|
|
293
|
+
state.isSignedIn = false;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// Sign out
|
|
299
|
+
signOut: async () => {
|
|
300
|
+
const { eventCallbacks, cleanupFunctions } = get();
|
|
301
|
+
set((state) => {
|
|
302
|
+
state.isLoading = true;
|
|
303
|
+
state.error = null;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
set((state) => {
|
|
308
|
+
state.user = null;
|
|
309
|
+
state.isSignedIn = false;
|
|
310
|
+
state.error = null;
|
|
311
|
+
});
|
|
312
|
+
const isTokenExpired = tokenStorage.isTokenExpired();
|
|
313
|
+
if (isTokenExpired) {
|
|
314
|
+
tokenStorage.clearTokens();
|
|
315
|
+
localStorage.clear();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
await authService.logout();
|
|
319
|
+
|
|
320
|
+
// Clear tokenStorage first
|
|
321
|
+
localStorage.clear();
|
|
322
|
+
tokenStorage.clearTokens();
|
|
323
|
+
|
|
324
|
+
// Execute default cleanup functions
|
|
325
|
+
try {
|
|
326
|
+
cleanupFunctions.clearQueryCache?.();
|
|
327
|
+
cleanupFunctions.clearOrganization?.();
|
|
328
|
+
cleanupFunctions.clearCustomData?.();
|
|
329
|
+
cleanupFunctions.clearHeaders?.();
|
|
330
|
+
} catch (cleanupError) {
|
|
331
|
+
console.warn("Error during logout cleanup:", cleanupError);
|
|
332
|
+
// Don't fail logout if cleanup fails
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Call onLogoutSuccess callback
|
|
336
|
+
eventCallbacks.onLogoutSuccess?.();
|
|
337
|
+
|
|
338
|
+
// Update state AFTER callback to prevent AuthLayout interference
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.log(error);
|
|
341
|
+
const errorMessage =
|
|
342
|
+
error instanceof Error ? error.message : "Logout failed";
|
|
343
|
+
|
|
344
|
+
set((state) => {
|
|
345
|
+
state.error = errorMessage;
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
eventCallbacks.onLogoutError?.(new Error(errorMessage));
|
|
349
|
+
|
|
350
|
+
throw error;
|
|
351
|
+
} finally {
|
|
352
|
+
set((state) => {
|
|
353
|
+
state.isLoading = false;
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
// Send password reset email
|
|
359
|
+
sendPasswordResetEmail: async (email: string) => {
|
|
360
|
+
set((state) => {
|
|
361
|
+
state.isLoading = true;
|
|
362
|
+
state.error = null;
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
await verifyCodeService.forgotPassword({
|
|
367
|
+
email,
|
|
368
|
+
});
|
|
369
|
+
} catch (error) {
|
|
370
|
+
const errorMessage =
|
|
371
|
+
error instanceof Error
|
|
372
|
+
? error.message
|
|
373
|
+
: "Failed to send reset email";
|
|
374
|
+
set((state) => {
|
|
375
|
+
state.error = errorMessage;
|
|
376
|
+
});
|
|
377
|
+
throw error;
|
|
378
|
+
} finally {
|
|
379
|
+
set((state) => {
|
|
380
|
+
state.isLoading = false;
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
resetPassword: async (data: ResetPasswordRequest) => {
|
|
386
|
+
set((state) => {
|
|
387
|
+
state.isLoading = true;
|
|
388
|
+
state.error = null;
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
const resetPasswordResponse =
|
|
393
|
+
await verifyCodeService.resetPassword(data);
|
|
394
|
+
return resetPasswordResponse;
|
|
395
|
+
} catch (error) {
|
|
396
|
+
const errorMessage =
|
|
397
|
+
error instanceof Error
|
|
398
|
+
? error.message
|
|
399
|
+
: "Failed to reset password";
|
|
400
|
+
set((state) => {
|
|
401
|
+
state.error = errorMessage;
|
|
402
|
+
});
|
|
403
|
+
throw error;
|
|
404
|
+
} finally {
|
|
405
|
+
set((state) => {
|
|
406
|
+
state.isLoading = false;
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
changePassword: async (data: ChangePasswordRequest) => {
|
|
412
|
+
set((state) => {
|
|
413
|
+
state.isLoading = true;
|
|
414
|
+
state.error = null;
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
const changePasswordResponse =
|
|
419
|
+
await verifyCodeService.changePassword(data);
|
|
420
|
+
return changePasswordResponse;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
throw error;
|
|
423
|
+
} finally {
|
|
424
|
+
set((state) => {
|
|
425
|
+
state.isLoading = false;
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
verifyEmailCode: async (data: VerifyEmailRequest) => {
|
|
431
|
+
set((state) => {
|
|
432
|
+
state.isLoading = true;
|
|
433
|
+
state.error = null;
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const verifyEmailResponse =
|
|
438
|
+
await verifyCodeService.verifyEmail(data);
|
|
439
|
+
return verifyEmailResponse;
|
|
440
|
+
} catch (error) {
|
|
441
|
+
const errorMessage =
|
|
442
|
+
error instanceof Error
|
|
443
|
+
? error.message
|
|
444
|
+
: "Failed to send reset email";
|
|
445
|
+
set((state) => {
|
|
446
|
+
state.error = errorMessage;
|
|
447
|
+
});
|
|
448
|
+
throw error;
|
|
449
|
+
} finally {
|
|
450
|
+
set((state) => {
|
|
451
|
+
state.isLoading = false;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
sendVerifyCode: async (data: SendVerifyCodeRequest) => {
|
|
456
|
+
set((state) => {
|
|
457
|
+
state.isLoading = true;
|
|
458
|
+
state.error = null;
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
const sendVerifyCodeResponse =
|
|
463
|
+
await verifyCodeService.sendVerifyCode(data);
|
|
464
|
+
return sendVerifyCodeResponse;
|
|
465
|
+
} catch (error) {
|
|
466
|
+
const errorMessage =
|
|
467
|
+
error instanceof Error
|
|
468
|
+
? error.message
|
|
469
|
+
: "Failed to send verify code";
|
|
470
|
+
set((state) => {
|
|
471
|
+
state.error = errorMessage;
|
|
472
|
+
});
|
|
473
|
+
throw error;
|
|
474
|
+
} finally {
|
|
475
|
+
set((state) => {
|
|
476
|
+
state.isLoading = false;
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
refreshToken: async (refreshToken?: string) => {
|
|
481
|
+
const refreshTokenResponse = await authService.refreshToken({
|
|
482
|
+
refreshToken:
|
|
483
|
+
refreshToken ?? tokenStorage.getRefreshToken() ?? "",
|
|
484
|
+
});
|
|
485
|
+
return refreshTokenResponse;
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
// Clear error
|
|
489
|
+
clearError: () =>
|
|
490
|
+
set((state) => {
|
|
491
|
+
state.error = null;
|
|
492
|
+
}),
|
|
493
|
+
|
|
494
|
+
setUser: (user: UserData | null) =>
|
|
495
|
+
set((state) => {
|
|
496
|
+
state.user = user;
|
|
497
|
+
}),
|
|
498
|
+
|
|
499
|
+
setIsSignedIn: (isSignedIn: boolean) =>
|
|
500
|
+
set((state) => {
|
|
501
|
+
state.isSignedIn = isSignedIn;
|
|
502
|
+
}),
|
|
503
|
+
|
|
504
|
+
setIsInitialized: (isInitialized: boolean) =>
|
|
505
|
+
set((state) => {
|
|
506
|
+
state.isInitialized = isInitialized;
|
|
507
|
+
}),
|
|
508
|
+
|
|
509
|
+
patchUser: (user: Partial<UserData>) =>
|
|
510
|
+
set((state) => {
|
|
511
|
+
if (state.user) {
|
|
512
|
+
state.user = { ...state.user, ...user };
|
|
513
|
+
} else {
|
|
514
|
+
state.user = user as UserData;
|
|
515
|
+
}
|
|
516
|
+
}),
|
|
517
|
+
|
|
518
|
+
// Initialize the store - check if user is already authenticated from tokenStorage
|
|
519
|
+
initialize: () => {
|
|
520
|
+
set((state) => {
|
|
521
|
+
const token = tokenStorage.getAuthToken();
|
|
522
|
+
state.isSignedIn = !!token;
|
|
523
|
+
state.isInitialized = true;
|
|
524
|
+
});
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
// Sync auth state from tokenStorage (called when axios interceptor updates tokens)
|
|
528
|
+
syncAuthState: () => {
|
|
529
|
+
set((state) => {
|
|
530
|
+
const token = tokenStorage.getAuthToken();
|
|
531
|
+
const wasSignedIn = state.isSignedIn;
|
|
532
|
+
state.isSignedIn = !!token;
|
|
533
|
+
|
|
534
|
+
// If auth state changed, trigger callbacks
|
|
535
|
+
if (wasSignedIn !== state.isSignedIn) {
|
|
536
|
+
if (state.isSignedIn) {
|
|
537
|
+
state.eventCallbacks?.onAuthStateChange?.(state.user, true);
|
|
538
|
+
} else {
|
|
539
|
+
state.eventCallbacks?.onAuthStateChange?.(null, false);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
}))
|
|
546
|
+
),
|
|
547
|
+
{
|
|
548
|
+
version: 1,
|
|
549
|
+
name: "auth-store",
|
|
550
|
+
storage: createJSONStorage(() => localStorage),
|
|
551
|
+
partialize: (state) => ({
|
|
552
|
+
isSignedIn: state.isSignedIn,
|
|
553
|
+
user: state.user,
|
|
554
|
+
}),
|
|
555
|
+
}
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
export const useAuth = () => {
|
|
561
|
+
const user = useAuthStore((state: any) => state.user);
|
|
562
|
+
const isSignedIn = useAuthStore((state: any) => state.isSignedIn);
|
|
563
|
+
const isInitialized = useAuthStore((state: any) => state.isInitialized);
|
|
564
|
+
const isLoading = useAuthStore((state: any) => state.isLoading);
|
|
565
|
+
const error = useAuthStore((state: any) => state.error);
|
|
566
|
+
const actions = useAuthStore((state: any) => state.actions);
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
user,
|
|
570
|
+
isSignedIn,
|
|
571
|
+
isInitialized,
|
|
572
|
+
isLoading,
|
|
573
|
+
error,
|
|
574
|
+
...actions,
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { UserData } from "./user-data";
|
|
2
|
+
|
|
3
|
+
export interface LoginForm {
|
|
4
|
+
email: string;
|
|
5
|
+
password: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ForgotPasswordForm {
|
|
9
|
+
email: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SignUpForm {
|
|
13
|
+
email: string;
|
|
14
|
+
password: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Event callback types
|
|
18
|
+
export interface AuthEventCallbacks {
|
|
19
|
+
onLoginSuccess?: (token: string) => void | Promise<void>;
|
|
20
|
+
onLoginError?: (error: Error) => void | Promise<void>;
|
|
21
|
+
onLogoutSuccess?: () => void | Promise<void>;
|
|
22
|
+
onLogoutError?: (error: Error) => void | Promise<void>;
|
|
23
|
+
onSignupSuccess?: () => void | Promise<void>;
|
|
24
|
+
onSignupError?: (error: Error) => void | Promise<void>;
|
|
25
|
+
onAuthStateChange?: (
|
|
26
|
+
user?: UserData | null,
|
|
27
|
+
isSignedIn?: boolean
|
|
28
|
+
) => void | Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UserAddressDetail, UserModel } from "@phygitallabs/api-core";
|
|
2
|
+
|
|
3
|
+
export enum OrgUserRole {
|
|
4
|
+
ORGANIZATION_OWNER = "ORGANIZATION_OWNER",
|
|
5
|
+
ORGANIZATION_ADMIN = "ORGANIZATION_ADMIN",
|
|
6
|
+
ORGANIZATION_EDITOR = "ORGANIZATION_EDITOR",
|
|
7
|
+
ORGANIZATION_VIEWER = "ORGANIZATION_VIEWER",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum UserType {
|
|
11
|
+
C,
|
|
12
|
+
B,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
enum PortalUserRole {
|
|
16
|
+
ADMIN = "ADMIN",
|
|
17
|
+
USER = "USER",
|
|
18
|
+
PORTAL_ADMIN_RW = "PORTAL_ADMIN_RW",
|
|
19
|
+
PORTAL_ADMIN_RO = "PORTAL_ADMIN_RO",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export enum UserRole {
|
|
23
|
+
ADMIN = "ADMIN",
|
|
24
|
+
USER = "USER",
|
|
25
|
+
ORGANIZATION_OWNER = OrgUserRole.ORGANIZATION_OWNER,
|
|
26
|
+
ORGANIZATION_ADMIN = OrgUserRole.ORGANIZATION_ADMIN,
|
|
27
|
+
ORGANIZATION_EDITOR = OrgUserRole.ORGANIZATION_EDITOR,
|
|
28
|
+
ORGANIZATION_VIEWER = OrgUserRole.ORGANIZATION_VIEWER,
|
|
29
|
+
PORTAL_ADMIN_RW = PortalUserRole.PORTAL_ADMIN_RW,
|
|
30
|
+
PORTAL_ADMIN_RO = PortalUserRole.PORTAL_ADMIN_RO,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UserData extends UserModel {
|
|
34
|
+
provider?: string;
|
|
35
|
+
accessToken: string;
|
|
36
|
+
exp: number;
|
|
37
|
+
addressDetail: UserAddressDetail;
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { UserData, UserRole, UserType } from "../types/user-data";
|
|
2
|
+
import { UserModel } from "@phygitallabs/api-core";
|
|
3
|
+
import jwtDecode from "jwt-decode";
|
|
4
|
+
|
|
5
|
+
export const transformProtoUserData = (user: UserModel, accessToken: string): UserData => {
|
|
6
|
+
const userData = { ...user } as UserData;
|
|
7
|
+
let tokenDecoded: any = {};
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
tokenDecoded = jwtDecode(accessToken);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.warn("Failed to decode token in transformUserData:", error);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
userData.exp = tokenDecoded.exp || 0;
|
|
16
|
+
userData.accessToken = accessToken || "";
|
|
17
|
+
userData.roles = tokenDecoded?.roles || [UserRole.USER];
|
|
18
|
+
userData.account_type = tokenDecoded?.account_type || UserType.C;
|
|
19
|
+
userData.picture = user.picture || "";
|
|
20
|
+
return userData;
|
|
21
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import posthog from "posthog-js";
|
|
2
|
+
import { useSessionReplay } from "../../../modules/session-replay";
|
|
2
3
|
|
|
3
4
|
declare global {
|
|
4
5
|
interface Window {
|
|
@@ -43,6 +44,8 @@ const pushEventToPosthog = (eventName: string, eventData: Record<string, any>) =
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
function useDataTracking() {
|
|
47
|
+
const { setUserId, setMetadata } = useSessionReplay();
|
|
48
|
+
|
|
46
49
|
const trackEvent = (eventName: string, eventData: Record<string, any>, useTools?: ("gtm" | "ga" | "posthog")[]) => {
|
|
47
50
|
|
|
48
51
|
useTools = useTools || ["gtm"];
|
|
@@ -58,8 +61,29 @@ function useDataTracking() {
|
|
|
58
61
|
}
|
|
59
62
|
};
|
|
60
63
|
|
|
64
|
+
const trackUserIdentify = (userInfo: Record<string, any>) => {
|
|
65
|
+
posthog.identify(userInfo.email, {
|
|
66
|
+
email: userInfo.email,
|
|
67
|
+
name: userInfo.name,
|
|
68
|
+
avatar: userInfo.avatar,
|
|
69
|
+
uid: userInfo.uid,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
setUserId(userInfo.id);
|
|
73
|
+
|
|
74
|
+
setMetadata({
|
|
75
|
+
user_email: userInfo.email
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const trackLogoutEvent = () => {
|
|
80
|
+
posthog.capture("user_signed_out");
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
return {
|
|
62
|
-
trackEvent
|
|
84
|
+
trackEvent,
|
|
85
|
+
trackUserIdentify,
|
|
86
|
+
trackLogoutEvent
|
|
63
87
|
};
|
|
64
88
|
}
|
|
65
89
|
|