@phygitallabs/tapquest-core 6.7.8 → 6.7.10
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/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/data-tracking/GtagConsentSync.tsx +62 -0
- package/src/modules/data-tracking/hooks/index.ts +70 -65
- package/src/modules/data-tracking/index.ts +2 -1
- package/src/providers/ServicesProvider.tsx +132 -131
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePhygitalConsent } from "@phygitallabs/phygital-consent";
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
type GtagConsent = (
|
|
7
|
+
command: "consent",
|
|
8
|
+
action: "default" | "update",
|
|
9
|
+
params: Record<string, string>
|
|
10
|
+
) => void;
|
|
11
|
+
|
|
12
|
+
/** Consent Mode v2: block analytics and ad tags until user accepts. */
|
|
13
|
+
const GTAG_CONSENT_DENIED = {
|
|
14
|
+
analytics_storage: "denied",
|
|
15
|
+
ad_storage: "denied",
|
|
16
|
+
ad_user_data: "denied",
|
|
17
|
+
ad_personalization: "denied",
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
const GTAG_CONSENT_GRANTED_ANALYTICS = {
|
|
21
|
+
analytics_storage: "granted",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sets gtag/GTM consent default to denied on mount and updates to granted
|
|
26
|
+
* when user has accepted analytics (must be under PhygitalConsentProvider).
|
|
27
|
+
*
|
|
28
|
+
* When using GTM: add the script from getGtmConsentDefaultSnippet() in your
|
|
29
|
+
* HTML before the GTM container script (gtm.js) so consent is denied before
|
|
30
|
+
* any tags run. This component then pushes consent update when the user accepts.
|
|
31
|
+
* Renders nothing.
|
|
32
|
+
*/
|
|
33
|
+
export function GtagConsentSync() {
|
|
34
|
+
const consent = usePhygitalConsent();
|
|
35
|
+
const isAnalyticsAllowed =
|
|
36
|
+
(consent as { isAnalyticsAllowed?: boolean }).isAnalyticsAllowed ?? false;
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (typeof window === "undefined") return;
|
|
40
|
+
const w = window as Window & { dataLayer?: unknown[]; gtag?: GtagConsent };
|
|
41
|
+
w.dataLayer = w.dataLayer || [];
|
|
42
|
+
const gtag = w.gtag;
|
|
43
|
+
if (typeof gtag === "function") {
|
|
44
|
+
gtag("consent", "default", { ...GTAG_CONSENT_DENIED });
|
|
45
|
+
} else {
|
|
46
|
+
w.dataLayer.push(["consent", "default", { ...GTAG_CONSENT_DENIED }]);
|
|
47
|
+
}
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!isAnalyticsAllowed || typeof window === "undefined") return;
|
|
52
|
+
const w = window as Window & { gtag?: GtagConsent; dataLayer?: unknown[] };
|
|
53
|
+
const gtag = w.gtag;
|
|
54
|
+
if (typeof gtag === "function") {
|
|
55
|
+
gtag("consent", "update", { ...GTAG_CONSENT_GRANTED_ANALYTICS });
|
|
56
|
+
} else if (Array.isArray(w.dataLayer)) {
|
|
57
|
+
w.dataLayer.push(["consent", "update", { ...GTAG_CONSENT_GRANTED_ANALYTICS }]);
|
|
58
|
+
}
|
|
59
|
+
}, [isAnalyticsAllowed]);
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
@@ -1,92 +1,97 @@
|
|
|
1
|
+
import { usePhygitalConsent } from "@phygitallabs/phygital-consent";
|
|
1
2
|
import posthog from "posthog-js";
|
|
2
3
|
import { useSessionReplay } from "../../../modules/session-replay";
|
|
3
4
|
|
|
4
5
|
declare global {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
interface Window {
|
|
7
|
+
dataLayer: unknown[];
|
|
8
|
+
}
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
declare function gtag(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
command: "event",
|
|
13
|
+
eventName: string,
|
|
14
|
+
eventParameters: Record<string, any>
|
|
14
15
|
): void;
|
|
15
16
|
|
|
16
|
-
|
|
17
17
|
// GTM
|
|
18
18
|
const pushEventToDataLayer = (event: string, data: Record<string, any>) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
19
|
+
try {
|
|
20
|
+
window.dataLayer = window.dataLayer || [];
|
|
21
|
+
|
|
22
|
+
(window.dataLayer as Record<string, unknown>[]).push({
|
|
23
|
+
event,
|
|
24
|
+
...data,
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(error);
|
|
28
|
+
}
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
// GA
|
|
33
32
|
const pushEventToGA = (eventName: string, eventData: Record<string, any>) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
if (typeof gtag != "function") {
|
|
34
|
+
console.error("gtag is not a function");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
gtag("event", eventName, eventData);
|
|
39
38
|
};
|
|
40
39
|
|
|
41
40
|
// Posthog
|
|
42
41
|
const pushEventToPosthog = (eventName: string, eventData: Record<string, any>) => {
|
|
43
|
-
|
|
42
|
+
posthog.capture(eventName, eventData);
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
function useDataTracking() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
useTools = useTools || ["gtm"];
|
|
53
|
-
|
|
54
|
-
if (useTools.includes("gtm") && typeof window !== "undefined") {
|
|
55
|
-
pushEventToDataLayer(eventName, eventData);
|
|
56
|
-
}
|
|
57
|
-
if (useTools.includes("ga") && typeof gtag == "function") {
|
|
58
|
-
pushEventToGA(eventName, eventData);
|
|
59
|
-
}
|
|
60
|
-
if (useTools.includes("posthog") && typeof posthog == "function") {
|
|
61
|
-
pushEventToPosthog(eventName, eventData);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const trackUserIdentify = (userInfo: Record<string, any>) => {
|
|
66
|
-
posthog.identify(userInfo.email, {
|
|
67
|
-
email: userInfo.email,
|
|
68
|
-
name: userInfo.name,
|
|
69
|
-
avatar: userInfo.avatar,
|
|
70
|
-
uid: userInfo.uid,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
setUserId(userInfo.id);
|
|
74
|
-
|
|
75
|
-
setMetadata({
|
|
76
|
-
user_email: userInfo.email
|
|
77
|
-
})
|
|
78
|
-
}
|
|
46
|
+
const { setUserId, setMetadata } = useSessionReplay();
|
|
47
|
+
const consent = usePhygitalConsent();
|
|
48
|
+
const isAnalyticsAllowed =
|
|
49
|
+
(consent as { isAnalyticsAllowed?: boolean }).isAnalyticsAllowed ?? false;
|
|
79
50
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
51
|
+
const trackEvent = (
|
|
52
|
+
eventName: string,
|
|
53
|
+
eventData: Record<string, any>,
|
|
54
|
+
useTools?: ("gtm" | "ga" | "posthog")[]
|
|
55
|
+
) => {
|
|
56
|
+
console.log("trackEvent ", eventName, eventData);
|
|
83
57
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
58
|
+
useTools = useTools || ["gtm"];
|
|
59
|
+
|
|
60
|
+
if (useTools.includes("gtm") && typeof window !== "undefined") {
|
|
61
|
+
pushEventToDataLayer(eventName, eventData);
|
|
62
|
+
}
|
|
63
|
+
if (useTools.includes("ga") && isAnalyticsAllowed && typeof gtag == "function") {
|
|
64
|
+
pushEventToGA(eventName, eventData);
|
|
65
|
+
}
|
|
66
|
+
if (useTools.includes("posthog") && typeof posthog == "function") {
|
|
67
|
+
pushEventToPosthog(eventName, eventData);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const trackUserIdentify = (userInfo: Record<string, any>) => {
|
|
72
|
+
posthog.identify(userInfo.email, {
|
|
73
|
+
email: userInfo.email,
|
|
74
|
+
name: userInfo.name,
|
|
75
|
+
avatar: userInfo.avatar,
|
|
76
|
+
uid: userInfo.uid,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
setUserId(userInfo.id);
|
|
80
|
+
|
|
81
|
+
setMetadata({
|
|
82
|
+
user_email: userInfo.email,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const trackLogoutEvent = () => {
|
|
87
|
+
posthog.capture("user_signed_out");
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
trackEvent,
|
|
92
|
+
trackUserIdentify,
|
|
93
|
+
trackLogoutEvent,
|
|
94
|
+
};
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
export { useDataTracking };
|
|
92
|
-
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./GtagConsentSync";
|
|
2
|
+
export * from "./hooks";
|
|
@@ -5,6 +5,7 @@ import { RewardServiceProvider } from "@phygitallabs/reward";
|
|
|
5
5
|
import { AchievementServiceProvider } from "@phygitallabs/achievement";
|
|
6
6
|
import { GenerateCertificateServiceProvider } from "@phygitallabs/generate-certificate";
|
|
7
7
|
import { PhygitalConsentProvider } from "@phygitallabs/phygital-consent";
|
|
8
|
+
import { GtagConsentSync } from "../modules/data-tracking";
|
|
8
9
|
|
|
9
10
|
import serviceApiUrl from "../constants/service";
|
|
10
11
|
import { APIConfig, ServiceConfig } from "../types/service";
|
|
@@ -20,142 +21,142 @@ import { tokenStorage } from "@phygitallabs/authentication";
|
|
|
20
21
|
import axios from "axios";
|
|
21
22
|
|
|
22
23
|
interface ServicesProviderProps {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
queryClient: QueryClient;
|
|
26
|
+
apiConfig: APIConfig;
|
|
27
|
+
firebaseConfig?: any;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export const ServicesProvider: React.FC<ServicesProviderProps> = ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
children,
|
|
32
|
+
queryClient,
|
|
33
|
+
apiConfig = {
|
|
34
|
+
environment: "dev",
|
|
35
|
+
version: "v1",
|
|
36
|
+
},
|
|
36
37
|
}) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const requestInterceptors = ({
|
|
92
|
-
onFulfilled: (config: any) => config,
|
|
93
|
-
onRejected: (error: any) => Promise.reject(error),
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const axiosConfig = {
|
|
97
|
-
headers: {
|
|
98
|
-
"Content-Type": "application/json",
|
|
99
|
-
"Device-UID": deviceUid,
|
|
100
|
-
}
|
|
38
|
+
const { signOut, refreshToken } = useAuth();
|
|
39
|
+
const { environment, version } = apiConfig;
|
|
40
|
+
const [commonServiceConfig, setCommonServiceConfig] = useState<ServiceConfig | null>(null);
|
|
41
|
+
|
|
42
|
+
const [deviceUid, setDeviceUid] = useState<string>("");
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const initDeviceUid = async () => {
|
|
46
|
+
const deviceUid = await checkDeviceUid();
|
|
47
|
+
setDeviceUid(deviceUid);
|
|
48
|
+
};
|
|
49
|
+
initDeviceUid();
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
// Init client
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const initClient = async () => {
|
|
55
|
+
try {
|
|
56
|
+
const deviceUid = await checkDeviceUid();
|
|
57
|
+
|
|
58
|
+
const responseInterceptors = {
|
|
59
|
+
onFulfilled: (response: any) => response,
|
|
60
|
+
onRejected: async (error: any) => {
|
|
61
|
+
const originalRequest = error.config;
|
|
62
|
+
const token = tokenStorage.getAuthToken();
|
|
63
|
+
|
|
64
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
65
|
+
const retryAttempts = parseInt(getFromLS(retryAttemptsRefreshToken) || "0", 10);
|
|
66
|
+
if (retryAttempts >= httpMaxRetries) {
|
|
67
|
+
await signOut();
|
|
68
|
+
setToLS(retryAttemptsRefreshToken, 0);
|
|
69
|
+
return Promise.reject(error);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setToLS(retryAttemptsRefreshToken, `${retryAttempts + 1}`);
|
|
73
|
+
originalRequest._retry = true;
|
|
74
|
+
|
|
75
|
+
if (token) {
|
|
76
|
+
try {
|
|
77
|
+
const result = await refreshToken();
|
|
78
|
+
if (result?.data?.idToken) {
|
|
79
|
+
originalRequest.headers.Authorization = `Bearer ${result?.data?.idToken}`;
|
|
80
|
+
return axios(originalRequest);
|
|
81
|
+
}
|
|
82
|
+
} catch (refreshError) {
|
|
83
|
+
console.error("Failed to refresh token:", refreshError);
|
|
84
|
+
await signOut();
|
|
85
|
+
return Promise.reject(refreshError);
|
|
101
86
|
}
|
|
102
|
-
|
|
103
|
-
const config: ServiceConfig = {
|
|
104
|
-
queryClient,
|
|
105
|
-
axiosConfig,
|
|
106
|
-
responseInterceptors,
|
|
107
|
-
requestInterceptors,
|
|
108
|
-
useDevTool: true,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
setCommonServiceConfig(config);
|
|
112
|
-
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error(error);
|
|
87
|
+
}
|
|
115
88
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const requestInterceptors = {
|
|
93
|
+
onFulfilled: (config: any) => config,
|
|
94
|
+
onRejected: (error: any) => Promise.reject(error),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const axiosConfig = {
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
"Device-UID": deviceUid,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const config: ServiceConfig = {
|
|
105
|
+
queryClient,
|
|
106
|
+
axiosConfig,
|
|
107
|
+
responseInterceptors,
|
|
108
|
+
requestInterceptors,
|
|
109
|
+
useDevTool: true,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
setCommonServiceConfig(config);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(error);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
initClient();
|
|
119
|
+
}, [queryClient]);
|
|
120
|
+
|
|
121
|
+
if (!commonServiceConfig) {
|
|
122
|
+
return <></>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<PGLCoreServiceProvider
|
|
127
|
+
{...commonServiceConfig}
|
|
128
|
+
baseURL={`${serviceApiUrl[environment].API_BASE_URL}/${version}`}
|
|
129
|
+
baseCoreURL={`${serviceApiUrl[environment].API_BASE_CORE_URL}/${version}`}
|
|
130
|
+
>
|
|
131
|
+
<RewardServiceProvider
|
|
132
|
+
{...commonServiceConfig}
|
|
133
|
+
// baseURL={`${serviceApiUrl[environment].API_REWARD_URL}/${version}`}
|
|
134
|
+
baseURL={`${serviceApiUrl[environment].API_REWARD_URL}/v1`} // todo: using v1 until backend fully migrate
|
|
135
|
+
>
|
|
136
|
+
<AchievementServiceProvider
|
|
137
|
+
{...commonServiceConfig}
|
|
138
|
+
baseURL={`${serviceApiUrl[environment].API_ACHIEVEMENT_URL}/${version}`}
|
|
130
139
|
>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
<GenerateCertificateServiceProvider
|
|
141
|
+
{...commonServiceConfig}
|
|
142
|
+
baseURL={`${serviceApiUrl[environment].API_GENERATE_CERTIFICATE_URL}/v1`}
|
|
143
|
+
>
|
|
144
|
+
<PhygitalConsentProvider
|
|
145
|
+
{...commonServiceConfig}
|
|
146
|
+
deviceId={deviceUid}
|
|
147
|
+
baseURL={`${serviceApiUrl[environment].API_CONSENT_URL}/v1`}
|
|
148
|
+
axiosConfig={{
|
|
149
|
+
headers: {
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
},
|
|
152
|
+
}}
|
|
135
153
|
>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{...commonServiceConfig}
|
|
146
|
-
deviceId={deviceUid}
|
|
147
|
-
baseURL={`${serviceApiUrl[environment].API_CONSENT_URL}/v1`}
|
|
148
|
-
axiosConfig={{
|
|
149
|
-
headers: {
|
|
150
|
-
"Content-Type": "application/json",
|
|
151
|
-
}
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
{children}
|
|
155
|
-
</PhygitalConsentProvider>
|
|
156
|
-
</GenerateCertificateServiceProvider>
|
|
157
|
-
</AchievementServiceProvider>
|
|
158
|
-
</RewardServiceProvider>
|
|
159
|
-
</PGLCoreServiceProvider>
|
|
160
|
-
);
|
|
161
|
-
};
|
|
154
|
+
<GtagConsentSync />
|
|
155
|
+
{children}
|
|
156
|
+
</PhygitalConsentProvider>
|
|
157
|
+
</GenerateCertificateServiceProvider>
|
|
158
|
+
</AchievementServiceProvider>
|
|
159
|
+
</RewardServiceProvider>
|
|
160
|
+
</PGLCoreServiceProvider>
|
|
161
|
+
);
|
|
162
|
+
};
|