@@ -8,72 +8,133 @@ import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
88const  GTM_ID  =  "AW-17391954825" 
99
1010/** 
11-  * Google Analytics Provider 
12-  * Only loads Google Tag Manager after user gives  consent 
11+  * Google Analytics Provider with Consent Mode v2  
12+  * Implements cookieless pings and advanced  consent management  
1313 */ 
1414export  function  GoogleAnalyticsProvider ( {  children } : {  children : React . ReactNode  } )  { 
1515	const  [ shouldLoad ,  setShouldLoad ]  =  useState ( false ) 
1616
1717	useEffect ( ( )  =>  { 
18+ 		// Initialize consent defaults BEFORE loading gtag.js (required for Consent Mode v2) 
19+ 		initializeConsentDefaults ( ) 
20+ 
1821		// Check initial consent status 
1922		if  ( hasConsent ( ) )  { 
2023			setShouldLoad ( true ) 
21- 			initializeGoogleAnalytics ( ) 
24+ 			updateConsentGranted ( ) 
2225		} 
2326
2427		// Listen for consent changes 
2528		const  unsubscribe  =  onConsentChange ( ( consented )  =>  { 
26- 			if  ( consented  &&  ! shouldLoad )  { 
27- 				setShouldLoad ( true ) 
28- 				initializeGoogleAnalytics ( ) 
29+ 			if  ( consented )  { 
30+ 				if  ( ! shouldLoad )  { 
31+ 					setShouldLoad ( true ) 
32+ 				} 
33+ 				updateConsentGranted ( ) 
34+ 			}  else  { 
35+ 				updateConsentDenied ( ) 
2936			} 
3037		} ) 
3138
3239		return  unsubscribe 
33- 	} ,  [ shouldLoad ] ) 
40+ 		// eslint-disable-next-line react-hooks/exhaustive-deps -- shouldLoad intentionally omitted to prevent re-initialization loop 
41+ 	} ,  [ ] ) 
3442
35- 	const  initializeGoogleAnalytics  =  ( )  =>  { 
36- 		// Initialize the dataLayer and  gtag function  
43+ 	const  initializeConsentDefaults  =  ( )  =>  { 
44+ 		// Set up consent defaults before  gtag loads (Consent Mode v2 requirement)  
3745		if  ( typeof  window  !==  "undefined" )  { 
3846			window . dataLayer  =  window . dataLayer  ||  [ ] 
3947			window . gtag  =  function  ( ...args : GtagArgs )  { 
4048				window . dataLayer . push ( args ) 
4149			} 
42- 			window . gtag ( "js" ,  new  Date ( ) ) 
43- 			window . gtag ( "config" ,  GTM_ID ) 
50+ 
51+ 			// Set default consent state to 'denied' with cookieless pings enabled 
52+ 			window . gtag ( "consent" ,  "default" ,  { 
53+ 				ad_storage : "denied" , 
54+ 				ad_user_data : "denied" , 
55+ 				ad_personalization : "denied" , 
56+ 				analytics_storage : "denied" , 
57+ 				functionality_storage : "denied" , 
58+ 				personalization_storage : "denied" , 
59+ 				security_storage : "granted" ,  // Always granted for security 
60+ 				wait_for_update : 500 ,  // Wait 500ms for consent before sending data 
61+ 			} ) 
62+ 
63+ 			// Enable cookieless pings for Google Ads 
64+ 			window . gtag ( "set" ,  "url_passthrough" ,  true ) 
4465		} 
4566	} 
4667
47- 	// Only render Google Analytics scripts if consent is given 
48- 	if  ( ! shouldLoad )  { 
49- 		return  < > { children } </ > 
68+ 	const  updateConsentGranted  =  ( )  =>  { 
69+ 		// User accepted cookies - update consent to granted 
70+ 		if  ( typeof  window  !==  "undefined"  &&  window . gtag )  { 
71+ 			window . gtag ( "consent" ,  "update" ,  { 
72+ 				ad_storage : "granted" , 
73+ 				ad_user_data : "granted" , 
74+ 				ad_personalization : "granted" , 
75+ 				analytics_storage : "granted" , 
76+ 				functionality_storage : "granted" , 
77+ 				personalization_storage : "granted" , 
78+ 			} ) 
79+ 		} 
5080	} 
5181
82+ 	const  updateConsentDenied  =  ( )  =>  { 
83+ 		// User declined cookies - keep consent denied (cookieless pings still work) 
84+ 		if  ( typeof  window  !==  "undefined"  &&  window . gtag )  { 
85+ 			window . gtag ( "consent" ,  "update" ,  { 
86+ 				ad_storage : "denied" , 
87+ 				ad_user_data : "denied" , 
88+ 				ad_personalization : "denied" , 
89+ 				analytics_storage : "denied" , 
90+ 				functionality_storage : "denied" , 
91+ 				personalization_storage : "denied" , 
92+ 			} ) 
93+ 		} 
94+ 	} 
95+ 
96+ 	// Always render scripts (Consent Mode v2 needs gtag loaded even without consent) 
97+ 	// Cookieless pings will work with denied consent 
98+ 
5299	return  ( 
53100		< > 
54- 			{ /* Google tag (gtag.js) - Only loads after consent  */ } 
101+ 			{ /* Google tag (gtag.js) - Loads immediately for Consent Mode v2  */ } 
55102			< Script 
56103				src = { `https://www.googletagmanager.com/gtag/js?id=${ GTM_ID }  ` } 
57104				strategy = "afterInteractive" 
58105				onLoad = { ( )  =>  { 
59- 					console . log ( "Google Analytics loaded with consent" ) 
106+ 					// Initialize gtag config after script loads 
107+ 					if  ( typeof  window  !==  "undefined"  &&  window . gtag )  { 
108+ 						window . gtag ( "js" ,  new  Date ( ) ) 
109+ 						window . gtag ( "config" ,  GTM_ID ) 
110+ 					} 
60111				} } 
61112			/> 
62- 			< Script  id = "google-analytics-init"  strategy = "afterInteractive" > 
63- 				{ ` 
64- 					window.dataLayer = window.dataLayer || []; 
65- 					function gtag(){dataLayer.push(arguments);} 
66- 					gtag('js', new Date()); 
67- 					gtag('config', '${ GTM_ID }  '); 
68- 				` } 
69- 			</ Script > 
70113			{ children } 
71114		</ > 
72115	) 
73116} 
74117
75- // Type definitions for Google Analytics 
76- type  GtagArgs  =  [ "js" ,  Date ]  |  [ "config" ,  string ,  GtagConfig ?]  |  [ "event" ,  string ,  GtagEventParameters ?] 
118+ // Type definitions for Google Analytics with Consent Mode v2 
119+ type  ConsentState  =  "granted"  |  "denied" 
120+ 
121+ interface  ConsentParams  { 
122+ 	ad_storage ?: ConsentState 
123+ 	ad_user_data ?: ConsentState 
124+ 	ad_personalization ?: ConsentState 
125+ 	analytics_storage ?: ConsentState 
126+ 	functionality_storage ?: ConsentState 
127+ 	personalization_storage ?: ConsentState 
128+ 	security_storage ?: ConsentState 
129+ 	wait_for_update ?: number 
130+ } 
131+ 
132+ type  GtagArgs  = 
133+ 	|  [ "js" ,  Date ] 
134+ 	|  [ "config" ,  string ,  GtagConfig ?] 
135+ 	|  [ "event" ,  string ,  GtagEventParameters ?] 
136+ 	|  [ "consent" ,  "default"  |  "update" ,  ConsentParams ] 
137+ 	|  [ "set" ,  string ,  unknown ] 
77138
78139interface  GtagConfig  { 
79140	[ key : string ] : unknown 
0 commit comments