Add PostHog analytics with heatmap support
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import { PostHogProvider } from "@/lib/posthog";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
@@ -27,7 +28,7 @@ export default function RootLayout({
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<PostHogProvider>{children}</PostHogProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PostHogTestButton } from "./posthog-test-button";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
@@ -13,6 +15,7 @@ export default function Home() {
|
||||
<span>+</span>
|
||||
<span>.NET 9</span>
|
||||
</div>
|
||||
<PostHogTestButton />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
18
frontend/src/app/posthog-test-button.tsx
Normal file
18
frontend/src/app/posthog-test-button.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
|
||||
export function PostHogTestButton() {
|
||||
const posthog = usePostHog();
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() =>
|
||||
posthog.capture("my_custom_event", { property: "value" })
|
||||
}
|
||||
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
Send PostHog Test Event
|
||||
</button>
|
||||
);
|
||||
}
|
||||
52
frontend/src/lib/posthog.tsx
Normal file
52
frontend/src/lib/posthog.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client";
|
||||
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider as PHProvider, usePostHog } from "posthog-js/react";
|
||||
import { useEffect, Suspense } from "react";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
|
||||
const POSTHOG_KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
||||
const POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://eu.i.posthog.com";
|
||||
|
||||
function PostHogPageView() {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const ph = usePostHog();
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname && ph) {
|
||||
let url = window.origin + pathname;
|
||||
if (searchParams.toString()) {
|
||||
url += "?" + searchParams.toString();
|
||||
}
|
||||
ph.capture("$pageview", { $current_url: url });
|
||||
}
|
||||
}, [pathname, searchParams, ph]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||
useEffect(() => {
|
||||
if (POSTHOG_KEY) {
|
||||
posthog.init(POSTHOG_KEY, {
|
||||
api_host: POSTHOG_HOST,
|
||||
capture_pageview: false, // manual pageview tracking for SPA
|
||||
capture_pageleave: true,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!POSTHOG_KEY) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<PHProvider client={posthog}>
|
||||
<Suspense fallback={null}>
|
||||
<PostHogPageView />
|
||||
</Suspense>
|
||||
{children}
|
||||
</PHProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user