diff --git a/ui/.env.production b/ui/.env.production
new file mode 100644
index 0000000..4d127ef
--- /dev/null
+++ b/ui/.env.production
@@ -0,0 +1,2 @@
+NEXT_PUBLIC_UMAMI_HOST=https://stats.suchshop.lol
+NEXT_PUBLIC_UMAMI_WEBSITE_ID=f7bf24bc-836d-434c-81eb-265f1ae67699
diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx
index 8df5263..831285b 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;
+}