From 3c4bb2007655a11d4a23f880cd4b2980f51a768e Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 13:04:44 +0300 Subject: [PATCH 1/9] Install PostHog --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index dc715b19..f45b438b 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "next-auth": "^4.22.1", "pino": "^6.3.1", "pino-pretty": "^4.0.0", + "posthog-js": "^1.154.5", "react-helmet": "^6.1.0", "react-pdf": "^4.1.0", "uuid": "^8.3.2", From a34bff15fa731231a188b54e7da4d4b8ee1014f1 Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 13:26:12 +0300 Subject: [PATCH 2/9] Wrap App component with PostHogProvider to access PostHog in any component --- frontend/src/index.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/index.js b/frontend/src/index.js index 50245eed..7143c9b4 100755 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -16,6 +16,8 @@ import configureStore, { history } from 'redux/configureStore' import config from 'constants/config' // import theme from './material-ui-theme' import theme from './junctionTheme' +import posthog from 'posthog-js' +import { PostHogProvider } from 'posthog-js/react' import { library } from '@fortawesome/fontawesome-svg-core' import { fab } from '@fortawesome/free-brands-svg-icons' @@ -25,7 +27,7 @@ library.add(fab) const { store, persistor } = configureStore() /** Disable log statements in production */ -function noop() {} +function noop() { } if (!config.IS_DEBUG) { console.log = noop @@ -48,6 +50,13 @@ WebFont.load({ }, }) + +// Add PostHog +posthog.init('phc_dvQg8kGuscCmyKR7QCJCcLdDcGRWs61gsNJO5mTgjf4', { + api_host: 'https://us.i.posthog.com', + person_profiles: 'identified_only', +}) + ReactDOM.render( - + + + From 11c86efb4d9e3767007e43618f21b1a6c03d4a61 Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 13:58:23 +0300 Subject: [PATCH 3/9] Add PostHog config keys in constants/config.js --- frontend/src/constants/config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/constants/config.js b/frontend/src/constants/config.js index 7190e238..126721d5 100644 --- a/frontend/src/constants/config.js +++ b/frontend/src/constants/config.js @@ -35,6 +35,14 @@ const settings = { required: false, value: process.env.REACT_APP_GOOGLE_ANALYTICS_ID, }, + POSTHOG_API_KEY: { + required: false, + value: process.env.REACT_APP_POSTHOG_PROJECT_API_KEY, + }, + POSTHOG_API_HOST: { + required: false, + value: process.env.REACT_APP_POSTHOG_API_HOST || 'https://us.i.posthog.com', + }, ID_TOKEN_NAMESPACE: { required: true, value: From 8ec084a10568dc0cfa4deb4e1bf5b10e2f4cac0a Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 13:59:30 +0300 Subject: [PATCH 4/9] Replace hard-coded PostHog values with dynamic variables from constants/config --- frontend/src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/index.js b/frontend/src/index.js index 7143c9b4..e5586f87 100755 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -52,8 +52,8 @@ WebFont.load({ // Add PostHog -posthog.init('phc_dvQg8kGuscCmyKR7QCJCcLdDcGRWs61gsNJO5mTgjf4', { - api_host: 'https://us.i.posthog.com', +posthog.init(config.POSTHOG_API_KEY, { + api_host: config.POSTHOG_API_HOST, person_profiles: 'identified_only', }) From 584427d2984d1a3252eeba4d664553671eb425a5 Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 15:13:23 +0300 Subject: [PATCH 5/9] Set capture_pageview in the PostHog initialization config to false. This turns off autocaptured pageviews and prevents double-capture pageviews on the first load --- frontend/src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/index.js b/frontend/src/index.js index e5586f87..bd93d25d 100755 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -55,6 +55,7 @@ WebFont.load({ posthog.init(config.POSTHOG_API_KEY, { api_host: config.POSTHOG_API_HOST, person_profiles: 'identified_only', + capture_pageview: false }) ReactDOM.render( From 5143f08250d6e832cced3c893ed00cd2d6b245bb Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 15:18:46 +0300 Subject: [PATCH 6/9] Update AnalyticsService to replace the Google Analytics and Facebook Pixel tracking with PostHog tracking --- frontend/src/services/analytics.js | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/frontend/src/services/analytics.js b/frontend/src/services/analytics.js index 17707107..76acf7a6 100644 --- a/frontend/src/services/analytics.js +++ b/frontend/src/services/analytics.js @@ -1,41 +1,31 @@ import ReactGA from 'react-ga' import ReactPixel from 'react-facebook-pixel' import config from 'constants/config' +import posthog from 'posthog-js' const AnalyticsService = {} AnalyticsService.init = () => { - if (config.GOOGLE_ANALYTICS_ID) { - ReactGA.initialize(config.GOOGLE_ANALYTICS_ID) - } - - if (config.FACEBOOK_PIXEL_ID) { - ReactPixel.init( - config.FACEBOOK_PIXEL_ID, - { - autoConfig: true, - debug: false, - }, - {}, - ) + if (config.POSTHOG_API_KEY) { + posthog.init(config.POSTHOG_API_KEY, { api_host: config.POSTHOG_API_HOST }) } } AnalyticsService.pageView = location => { - if (config.GOOGLE_ANALYTICS_ID) { - ReactGA.pageview(location.pathname) + if (config.POSTHOG_API_KEY) { + posthog.capture('$pageview', { path: location.pathname }) } } AnalyticsService.events = { LOG_IN: () => { - ReactPixel.track('CompleteRegistration', { + posthog.capture('CompleteRegistration', { value: 0.1, currency: 'EUR', }) }, VIEW_EVENT: slug => { - ReactPixel.track('ViewContent', { + posthog.capture('ViewContent', { value: 0.1, currency: 'EUR', content_ids: slug, @@ -43,7 +33,7 @@ AnalyticsService.events = { }) }, BEGIN_REGISTRATION: slug => { - ReactPixel.track('AddToCart', { + posthog.capture('AddToCart', { value: 0.5, currency: 'EUR', contents: [ @@ -56,7 +46,7 @@ AnalyticsService.events = { }) }, COMPLETE_REGISTRATION: slug => { - ReactPixel.track('Purchase', { + posthog.capture('Purchase', { value: 1, currency: 'EUR', contents: [ From 6545c0903901a568881fea5ab50666bd8c5aa43f Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 15:46:38 +0300 Subject: [PATCH 7/9] Refactor App.js to use useLocation from react-router-dom for routing --- frontend/src/App.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index daf478ab..90fdf5fa 100755 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,4 +1,5 @@ import React, { useState, useEffect, Suspense } from 'react' +import { useLocation } from 'react-router-dom' import { ConnectedRouter, push } from 'connected-react-router' import { useDispatch, useSelector } from 'react-redux' @@ -15,20 +16,22 @@ import { getCookieConsentValue } from 'react-cookie-consent' import CookieConsentBar from 'components/layouts/CookieConsentBar' import * as SnackbarActions from 'redux/snackbar/actions' -export default ({ history, location }) => { +export default ({ history }) => { const dispatch = useDispatch() const idToken = useSelector(AuthSelectors.getIdToken) const isAuthenticated = useSelector(AuthSelectors.isAuthenticated) const isSessionExpired = useSelector(AuthSelectors.isSessionExpired) const [loading, setLoading] = useState(true) + const location = useLocation() + useEffect(() => { if (getCookieConsentValue() === 'true') { AnalyticsService.init() - AnalyticsService.pageView(window.location) - const unlisten = history.listen(AnalyticsService.pageView) + AnalyticsService.pageView(window.location) // Initial page view tracking + const unlisten = history.listen(AnalyticsService.pageView) // Listen for route changes return () => { - unlisten() + unlisten() // Cleanup listener on component unmount } } }, [location, history]) From db025df93839921616b1df5263d60a9ff07fac18 Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 18:03:25 +0300 Subject: [PATCH 8/9] Pass location as prop to App component --- frontend/src/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/index.js b/frontend/src/index.js index bd93d25d..7308794c 100755 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,5 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom' +import { Router } from 'react-router-dom' import App from './App' import * as serviceWorker from './serviceWorker' import './i18n' @@ -81,7 +82,9 @@ ReactDOM.render( - + + + From 899162bf27bf2b0797683c16fa4a3a3318bb4188 Mon Sep 17 00:00:00 2001 From: David Amebley Date: Thu, 8 Aug 2024 18:04:02 +0300 Subject: [PATCH 9/9] Remove useLocation and use passed location prop in App component --- frontend/src/App.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/App.js b/frontend/src/App.js index 90fdf5fa..fe12615d 100755 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,5 +1,4 @@ import React, { useState, useEffect, Suspense } from 'react' -import { useLocation } from 'react-router-dom' import { ConnectedRouter, push } from 'connected-react-router' import { useDispatch, useSelector } from 'react-redux' @@ -16,15 +15,13 @@ import { getCookieConsentValue } from 'react-cookie-consent' import CookieConsentBar from 'components/layouts/CookieConsentBar' import * as SnackbarActions from 'redux/snackbar/actions' -export default ({ history }) => { +export default ({ history, location }) => { const dispatch = useDispatch() const idToken = useSelector(AuthSelectors.getIdToken) const isAuthenticated = useSelector(AuthSelectors.isAuthenticated) const isSessionExpired = useSelector(AuthSelectors.isSessionExpired) const [loading, setLoading] = useState(true) - const location = useLocation() - useEffect(() => { if (getCookieConsentValue() === 'true') { AnalyticsService.init()