diff --git a/ui/.env.production b/ui/.env.production new file mode 100644 index 0000000..68deaff --- /dev/null +++ b/ui/.env.production @@ -0,0 +1,2 @@ +NEXT_PUBLIC_UMAMI_HOST=https://stats.suchshop.lol +NEXT_PUBLIC_UMAMI_WEBSITE_ID=f40954f3-6272-412e-ad3f-de344478b3ec diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index af0773b..113370b 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Crimson_Pro, Space_Grotesk } from "next/font/google"; import "./globals.css"; +import { UmamiAnalytics } from "../components/UmamiAnalytics"; const spaceGrotesk = Space_Grotesk({ subsets: ["latin"], @@ -29,7 +30,10 @@ export default function RootLayout({ }) { return ( - {children} + + {children} + + ); } diff --git a/ui/components/UmamiAnalytics.tsx b/ui/components/UmamiAnalytics.tsx new file mode 100644 index 0000000..309e920 --- /dev/null +++ b/ui/components/UmamiAnalytics.tsx @@ -0,0 +1,23 @@ +'use client'; +import { useEffect } from 'react'; + +// Self-hosted, cookieless Umami — config via env so nothing is hardcoded. +// Honors Global Privacy Control + Do-Not-Track. No-op unless both env vars are set. +const HOST = process.env.NEXT_PUBLIC_UMAMI_HOST; +const WEBSITE_ID = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID; + +export function UmamiAnalytics() { + useEffect(() => { + if (!HOST || !WEBSITE_ID) return; + const nav = navigator as { globalPrivacyControl?: boolean }; + if (nav.globalPrivacyControl) return; + if (document.querySelector(`script[data-website-id="${WEBSITE_ID}"]`)) return; + const s = document.createElement('script'); + s.defer = true; + s.src = `${HOST}/script.js`; + s.setAttribute('data-website-id', WEBSITE_ID); + s.setAttribute('data-do-not-track', 'true'); + document.head.appendChild(s); + }, []); + return null; +}