diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7867ade --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +VITE_GA_ID=G-XXXXXXXXXX + +# Add your Google Analytics Measurement ID above. +# Example: VITE_GA_ID=G-1A2B3C4D5E diff --git a/src/App.tsx b/src/App.tsx index c0a2710..fb6c0aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,17 +3,21 @@ import Index from "./pages/Index"; import Privacy from "./pages/Privacy"; import NotFound from "./pages/NotFound"; import Waitlist from "./pages/Waitlist"; +import CookieConsent from "./components/ui/CookieConsent"; const App = () => ( - - - } /> - } /> - } /> - {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} - } /> - - + <> + + + } /> + } /> + } /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + + + + ); export default App; diff --git a/src/components/landing/Footer.tsx b/src/components/landing/Footer.tsx index d77e7b6..68c8d5f 100644 --- a/src/components/landing/Footer.tsx +++ b/src/components/landing/Footer.tsx @@ -24,6 +24,8 @@ const Footer = () => {
diff --git a/src/components/ui/CookieConsent.tsx b/src/components/ui/CookieConsent.tsx new file mode 100644 index 0000000..510ff6b --- /dev/null +++ b/src/components/ui/CookieConsent.tsx @@ -0,0 +1,84 @@ +import React, { useEffect, useState } from "react"; + +declare global { + interface Window { + dataLayer?: any[]; + gtag?: (...args: any[]) => void; + } +} + +const STORAGE_KEY = "tt_cookie_consent"; + +function loadGtag(id: string) { + if (!id) return; + if (document.querySelector(`script[data-gtag="${id}"]`)) return; + + const script = document.createElement("script"); + script.async = true; + script.src = `https://www.googletagmanager.com/gtag/js?id=${id}`; + script.setAttribute("data-gtag", id); + document.head.appendChild(script); + + window.dataLayer = window.dataLayer || []; + window.gtag = function () { + // @ts-ignore - forward arguments to the dataLayer + window.dataLayer!.push(arguments); + }; + + window.gtag("js", new Date()); + window.gtag("config", id, { anonymize_ip: true }); +} + +const CookieConsent: React.FC = () => { + const [consent, setConsent] = useState(null); + + useEffect(() => { + const v = localStorage.getItem(STORAGE_KEY); + setConsent(v); + if (v === "granted") { + const id = (import.meta.env as any).VITE_GA_ID as string | undefined; + if (id) loadGtag(id); + } + }, []); + + function accept() { + localStorage.setItem(STORAGE_KEY, "granted"); + setConsent("granted"); + const id = (import.meta.env as any).VITE_GA_ID as string | undefined; + if (id) loadGtag(id); + } + + function deny() { + localStorage.setItem(STORAGE_KEY, "denied"); + setConsent("denied"); + } + + if (consent !== null) return null; + + return ( +
+
+
+ We use cookies for analytics. +
+ +
+ + +
+
+
+ ); +}; + +export default CookieConsent;