feat: add Google Tag Manager to marketing website (#9148)

* feat: add Google Tag Manager to marketing website using Next.js Script component

* refactor: remove Google Ads implementation in favor of Tag Manager

* fix: wrap GTM script in consent-checking client component for GDPR compliance

---------

Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
roomote[bot] 2025-11-10 01:51:53 -08:00 • committed by GitHub
parent 4da9c3adb1
commit 62636add14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 83 deletions

View file

@ -19,7 +19,6 @@ import { Button } from "@/components/ui"
import { AnimatedBackground } from "@/components/homepage"
import { AgentCarousel } from "@/components/reviewer/agent-carousel"
import { EXTERNAL_LINKS } from "@/lib/constants"
import { trackGoogleAdsConversion } from "@/lib/analytics/google-ads"
import { type AgentPageContent, type IconName } from "./agent-page-content"
/**
@ -97,7 +96,6 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}
target="_blank"
rel="noopener noreferrer"
onClick={trackGoogleAdsConversion}
className="flex w-full items-center justify-center">
{content.hero.cta.buttonText}
<ArrowRight className="ml-2" />
@ -219,7 +217,6 @@ export function AgentLandingContent({ content }: { content: AgentPageContent })
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}
target="_blank"
rel="noopener noreferrer"
onClick={trackGoogleAdsConversion}
className="flex items-center justify-center">
{content.cta.buttonText}
<ArrowRight className="ml-2 h-4 w-4" />

View file

@ -1,60 +0,0 @@
"use client"
import { useEffect, useState } from "react"
import Script from "next/script"
import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
// Google Tag Manager ID
const GTM_ID = "AW-17391954825"
/**
* Google Analytics Provider
* Implements Google's standard gtag.js loading pattern
*/
export function GoogleAnalyticsProvider({ children }: { children: React.ReactNode }) {
const [shouldLoad, setShouldLoad] = useState(false)
useEffect(() => {
// Check initial consent status
if (hasConsent()) {
setShouldLoad(true)
}
// Listen for consent changes
const unsubscribe = onConsentChange((consented) => {
if (consented) {
setShouldLoad(true)
}
})
return unsubscribe
}, [])
return (
<>
{shouldLoad && (
<>
{/* Google tag (gtag.js) */}
<Script src={`https://www.googletagmanager.com/gtag/js?id=${GTM_ID}`} strategy="afterInteractive" />
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GTM_ID}');
`}
</Script>
</>
)}
{children}
</>
)
}
// Declare global types for TypeScript
declare global {
interface Window {
dataLayer: unknown[]
gtag: (...args: unknown[]) => void
}
}

View file

@ -0,0 +1,65 @@
"use client"
import { useEffect, useState } from "react"
import Script from "next/script"
import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
// Google Tag Manager Container ID
const GTM_ID = "GTM-M2JZHV8N"
/**
* Google Tag Manager Provider
* Loads GTM only after user consent is given, following GDPR requirements
*/
export function GoogleTagManagerProvider({ children }: { children: React.ReactNode }) {
const [shouldLoad, setShouldLoad] = useState(false)
useEffect(() => {
// Check initial consent status
if (hasConsent()) {
setShouldLoad(true)
}
// Listen for consent changes
const unsubscribe = onConsentChange((consented) => {
if (consented) {
setShouldLoad(true)
}
})
return unsubscribe
}, [])
return (
<>
{shouldLoad && (
<>
{/* Google Tag Manager Script */}
<Script
id="google-tag-manager"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${GTM_ID}');
`,
}}
/>
{/* Google Tag Manager (noscript) */}
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${GTM_ID}`}
height="0"
width="0"
style={{ display: "none", visibility: "hidden" }}
/>
</noscript>
</>
)}
{children}
</>
)
}

View file

@ -3,21 +3,21 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ThemeProvider } from "next-themes"
import { GoogleTagManagerProvider } from "./google-tag-manager-provider"
import { PostHogProvider } from "./posthog-provider"
import { GoogleAnalyticsProvider } from "./google-analytics-provider"
const queryClient = new QueryClient()
export const Providers = ({ children }: { children: React.ReactNode }) => {
return (
<QueryClientProvider client={queryClient}>
<GoogleAnalyticsProvider>
<GoogleTagManagerProvider>
<PostHogProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem={false}>
{children}
</ThemeProvider>
</PostHogProvider>
</GoogleAnalyticsProvider>
</GoogleTagManagerProvider>
</QueryClientProvider>
)
}

View file

@ -1,17 +0,0 @@
/**
* Google Ads conversion tracking utilities
*/
/**
* Track a Google Ads conversion event
* This should only be called after user consent has been given
*/
export function trackGoogleAdsConversion() {
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", "conversion", {
send_to: "AW-17391954825/VtOZCJe_77MbEInXkOVA",
value: 10.0,
currency: "USD",
})
}
}