@phygitallabs/tapquest-core 2.8.0 → 2.9.1

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.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +29 -35
  3. package/dist/index.js +1015 -721
  4. package/dist/index.js.map +1 -1
  5. package/package.json +6 -3
  6. package/src/modules/auth/constants/index.ts +6 -0
  7. package/src/modules/auth/helpers/index.ts +1 -4
  8. package/src/modules/auth/hooks/index.ts +2 -0
  9. package/src/modules/auth/hooks/useGoogleLogin.ts +169 -0
  10. package/src/modules/auth/hooks/useTokenRefresher.ts +39 -0
  11. package/src/modules/auth/index.ts +8 -2
  12. package/src/modules/auth/providers/AuthProvider.tsx +214 -186
  13. package/src/modules/auth/store/authStore.ts +577 -0
  14. package/src/modules/auth/types/auth.ts +29 -0
  15. package/src/modules/auth/types/user-data.ts +38 -0
  16. package/src/modules/auth/utils/user.ts +21 -0
  17. package/src/modules/data-tracking/hooks/index.ts +25 -1
  18. package/src/modules/generate-certificate/helpers/index.ts +3 -0
  19. package/src/modules/generate-certificate/hooks/index.ts +15 -6
  20. package/src/modules/generate-certificate/index.ts +3 -1
  21. package/src/modules/notification/providers/index.tsx +3 -3
  22. package/src/modules/reward/hooks/useRewardService.ts +6 -6
  23. package/src/modules/session-replay/README.md +334 -0
  24. package/src/modules/session-replay/hooks/useSessionReplay.ts +16 -0
  25. package/src/modules/session-replay/index.ts +10 -0
  26. package/src/modules/session-replay/providers/SessionReplayProvider.tsx +189 -0
  27. package/src/modules/session-replay/types/index.ts +147 -0
  28. package/src/modules/session-replay/utils/index.ts +12 -0
  29. package/src/providers/ServicesProvider.tsx +4 -76
  30. package/src/providers/TapquestCoreProvider.tsx +33 -46
  31. package/tsup.config.ts +1 -1
  32. package/dist/index.cjs +0 -1531
  33. package/dist/index.cjs.map +0 -1
  34. package/dist/index.d.cts +0 -690
  35. package/src/modules/auth/helpers/refreshToken.ts +0 -63
  36. 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