diff --git a/web/.env.example b/web/.env.example index 8eefbb5..008df45 100644 --- a/web/.env.example +++ b/web/.env.example @@ -16,4 +16,7 @@ SPACES_CDN_FORCE_PATH_STYLE="true" MODAL_BUILDER_URL= JWT_SECRET="openssl rand -hex 32" -PLAUSIBLE_DOMAIN= \ No newline at end of file +PLAUSIBLE_DOMAIN= + +NEXT_PUBLIC_POSTHOG_KEY="your-api-key" +NEXT_PUBLIC_POSTHOG_HOST="your-ph-address" diff --git a/web/bun.lockb b/web/bun.lockb index 8ca5312..6f53ecc 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/package.json b/web/package.json index 290c261..fd3fa9a 100644 --- a/web/package.json +++ b/web/package.json @@ -76,6 +76,8 @@ "next-themes": "^0.2.1", "next-usequerystate": "^1.13.2", "pg": "^8.11.3", + "posthog-js": "^1.100.0", + "posthog-node": "^3.5.0", "react": "^18", "react-day-picker": "^8.9.1", "react-dom": "^18", diff --git a/web/src/app/(app)/PostHogPageView.tsx b/web/src/app/(app)/PostHogPageView.tsx new file mode 100644 index 0000000..3c05d4b --- /dev/null +++ b/web/src/app/(app)/PostHogPageView.tsx @@ -0,0 +1,29 @@ +// app/PostHogPageView.tsx +'use client' + +import { usePathname, useSearchParams } from "next/navigation"; +import { useEffect } from "react"; +import { usePostHog } from 'posthog-js/react'; + +export default function PostHogPageView() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const posthog = usePostHog(); + // Track pageviews + useEffect(() => { + if (pathname && posthog) { + let url = window.origin + pathname + if (searchParams.toString()) { + url = url + `?${searchParams.toString()}` + } + posthog.capture( + '$pageview', + { + '$current_url': url, + } + ) + } + }, [pathname, searchParams, posthog]) + + return null +} diff --git a/web/src/app/(app)/layout.tsx b/web/src/app/(app)/layout.tsx index ae372fe..332b36c 100644 --- a/web/src/app/(app)/layout.tsx +++ b/web/src/app/(app)/layout.tsx @@ -7,6 +7,13 @@ import meta from "next-gen/config"; import PlausibleProvider from "next-plausible"; import { Inter } from "next/font/google"; import { Toaster } from "sonner"; +import { PHProvider } from "./providers"; + +import dynamic from "next/dynamic"; + +const PostHogPageView = dynamic(() => import("./PostHogPageView"), { + ssr: false, +}); const inter = Inter({ subsets: ["latin"] }); @@ -39,20 +46,23 @@ export default function RootLayout({ )} - -
-
-
-
-
- -
-
- {children} -
- -
- + + + +
+
+
+
+
+ +
+
+ {children} +
+ +
+ +
diff --git a/web/src/app/(app)/providers.tsx b/web/src/app/(app)/providers.tsx new file mode 100644 index 0000000..7ccd3ae --- /dev/null +++ b/web/src/app/(app)/providers.tsx @@ -0,0 +1,19 @@ +// app/providers.tsx +'use client' +import posthog from 'posthog-js' +import { PostHogProvider } from 'posthog-js/react' + +if (typeof window !== 'undefined') { + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { + api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST, + capture_pageview: false // Disable automatic pageview capture, as we capture manually + }) +} + +export function PHProvider({ + children, +}: { + children: React.ReactNode +}) { + return {children} +}