Files
mintel.me/apps/web/app/(site)/layout.tsx
T
mmintel a54df524c7
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Successful in 6m31s
Build & Deploy / 🚀 Deploy (push) Successful in 19s
Build & Deploy / 🩺 Smoke Test (push) Successful in 5s
Build & Deploy / 🔔 Notify (push) Successful in 2s
feat(analytics): record pageviews server-side only
Umami never received data: the client-side path (browser beacon to
/stats/api/send) has been dead in production. Pageviews are now
classified and recorded entirely on the server in the request proxy;
no tracking script ships to the browser anymore.

- Pure domain decision (decidePageview) filters real page views from
  assets, infrastructure paths, prefetches and bot user agents
- Pure payload builder maps facts onto the Umami send contract as an
  unnamed event, with the client identity travelling in headers
- proxy.ts wires request facts into the domain and fires the send
  without delaying the response; configured via UMAMI_WEBSITE_ID and
  UMAMI_API_ENDPOINT
- Removes the dead client analytics stack (components, adapters,
  /stats proxy route) and its layout mount
2026-10-04 12:24:13 +02:00

78 lines
1.9 KiB
TypeScript

import type { Metadata } from "next";
import { Inter, JetBrains_Mono, Newsreader } from "next/font/google";
import { BitBurst } from "@/src/components/bits/BitBurst";
import { Footer } from "@/src/components/Footer";
import { Header } from "@/src/components/Header";
import { InteractiveElements } from "@/src/components/InteractiveElements";
import {
SITE_DESCRIPTION,
SITE_NAME,
SITE_TITLE,
SITE_URL,
} from "@/src/content/site";
import "./globals.css";
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrains-mono",
display: "swap",
});
const newsreader = Newsreader({
subsets: ["latin"],
variable: "--font-newsreader",
style: "italic",
display: "swap",
});
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
title: {
default: SITE_TITLE,
template: `%s | ${SITE_NAME}`,
},
description: SITE_DESCRIPTION,
applicationName: SITE_NAME,
authors: [{ name: SITE_NAME, url: SITE_URL }],
creator: SITE_NAME,
icons: {
icon: "/favicon.svg",
apple: "/apple-touch-icon.png",
},
// The images come from opengraph-image.png and twitter-image.png next to this file.
openGraph: {
type: "website",
url: "/",
siteName: SITE_NAME,
locale: "en_US",
title: SITE_TITLE,
description: SITE_DESCRIPTION,
},
twitter: {
card: "summary_large_image",
title: SITE_TITLE,
description: SITE_DESCRIPTION,
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html
lang="en"
className={`${inter.variable} ${newsreader.variable} ${jetbrainsMono.variable}`}
>
<body className="min-h-screen bg-white">
<Header />
<main>{children}</main>
<Footer />
<InteractiveElements />
<BitBurst />
</body>
</html>
);
}