diff --git a/env.example b/env.example index fc7d984..3cbc0c9 100644 --- a/env.example +++ b/env.example @@ -132,6 +132,12 @@ NEXT_PUBLIC_SELINE_TOKEN="" # ----------------------------------------------------------------------------- NEXT_PUBLIC_DATAFAST_WEBSITE_ID="" NEXT_PUBLIC_DATAFAST_DOMAIN="" +# ----------------------------------------------------------------------------- +# PostHog Analytics (https://posthog.com) +# https://mksaas.com/docs/analytics#posthog +# ----------------------------------------------------------------------------- +NEXT_PUBLIC_POSTHOG_KEY="" +NEXT_PUBLIC_POSTHOG_HOST="" # ----------------------------------------------------------------------------- diff --git a/package.json b/package.json index c7dd5fb..3fb0334 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "next-themes": "^0.4.4", "nuqs": "^2.5.1", "postgres": "^3.4.5", + "posthog-js": "^1.261.7", "radix-ui": "^1.4.2", "react": "^19.0.0", "react-day-picker": "8.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d90938..aa81f24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -272,6 +272,9 @@ importers: postgres: specifier: ^3.4.5 version: 3.4.5 + posthog-js: + specifier: ^1.261.7 + version: 1.261.7 radix-ui: specifier: ^1.4.2 version: 1.4.2(@types/react-dom@19.0.3(@types/react@19.0.9))(@types/react@19.0.9)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -1926,6 +1929,9 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@posthog/core@1.0.2': + resolution: {integrity: sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==} + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -4025,6 +4031,9 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + core-js@3.45.1: + resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -4503,6 +4512,9 @@ packages: picomatch: optional: true + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -5627,6 +5639,20 @@ packages: resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} engines: {node: '>=12'} + posthog-js@1.261.7: + resolution: {integrity: sha512-Fjpbz6VfIMsEbKIN/UyTWhU1DGgVIngqoRjPGRolemIMOVzTfI77OZq8WwiBhMug+rU+wNhGCQhC41qRlR5CxA==} + peerDependencies: + '@rrweb/types': 2.0.0-alpha.17 + rrweb-snapshot: 2.0.0-alpha.17 + peerDependenciesMeta: + '@rrweb/types': + optional: true + rrweb-snapshot: + optional: true + + preact@10.27.1: + resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -6355,6 +6381,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -7593,6 +7622,8 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@posthog/core@1.0.2': {} + '@radix-ui/number@1.1.0': {} '@radix-ui/number@1.1.1': {} @@ -9808,6 +9839,8 @@ snapshots: cookie@1.0.2: {} + core-js@3.45.1: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -10335,6 +10368,8 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fflate@0.4.8: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -11784,6 +11819,16 @@ snapshots: postgres@3.4.5: {} + posthog-js@1.261.7: + dependencies: + '@posthog/core': 1.0.2 + core-js: 3.45.1 + fflate: 0.4.8 + preact: 10.27.1 + web-vitals: 4.2.4 + + preact@10.27.1: {} + prelude-ls@1.2.1: {} prettier@3.4.2: {} @@ -12744,6 +12789,8 @@ snapshots: web-namespaces@2.0.1: {} + web-vitals@4.2.4: {} + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/src/analytics/posthog-analytics.tsx b/src/analytics/posthog-analytics.tsx new file mode 100644 index 0000000..3808723 --- /dev/null +++ b/src/analytics/posthog-analytics.tsx @@ -0,0 +1,35 @@ +'use client'; + +import posthog from 'posthog-js'; +import { PostHogProvider as PHProvider } from 'posthog-js/react'; +import { useEffect } from 'react'; + +/** + * PostHog Analytics + * + * https://posthog.com + * https://posthog.com/docs/libraries/next-js?tab=PostHog+provider + * https://mksaas.com/docs/analytics#posthog + */ +export function PostHogProvider({ children }: { children: React.ReactNode }) { + const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY; + const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST; + const isProduction = process.env.NODE_ENV === 'production'; + const isPostHogEnabled = posthogKey && posthogHost && isProduction; + + useEffect(() => { + if (isPostHogEnabled) { + posthog.init(posthogKey, { + api_host: posthogHost, + defaults: '2025-05-24', + }); + } + }, [isPostHogEnabled, posthogKey, posthogHost]); + + // If PostHog is not enabled, just return children without the provider + if (!isPostHogEnabled) { + return <>{children}; + } + + return {children}; +} diff --git a/src/app/[locale]/providers.tsx b/src/app/[locale]/providers.tsx index c3e89a9..a6c8ebf 100644 --- a/src/app/[locale]/providers.tsx +++ b/src/app/[locale]/providers.tsx @@ -1,5 +1,6 @@ 'use client'; +import { PostHogProvider } from '@/analytics/posthog-analytics'; import { ActiveThemeProvider } from '@/components/layout/active-theme-provider'; import { QueryProvider } from '@/components/providers/query-provider'; import { TooltipProvider } from '@/components/ui/tooltip'; @@ -20,12 +21,12 @@ interface ProvidersProps { * * This component is used to wrap the app in the providers. * + * - PostHogProvider: Provides the PostHog analytics to the app. + * - QueryProvider: Provides the query client to the app. * - ThemeProvider: Provides the theme to the app. * - ActiveThemeProvider: Provides the active theme to the app. * - RootProvider: Provides the root provider for Fumadocs UI. * - TooltipProvider: Provides the tooltip to the app. - * - PaymentProvider: Provides the payment state to the app. - * - CreditsProvider: Provides the credits state to the app. */ export function Providers({ children, locale }: ProvidersProps) { const theme = useTheme(); @@ -53,19 +54,24 @@ export function Providers({ children, locale }: ProvidersProps) { }; return ( - - - - - {children} - - - - + + + + + + {children} + + + + + ); }