Skip to content

Commit

Permalink
feat(GoogleAnalytics): added react-ga
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Dobkowski committed Dec 12, 2024
1 parent aa5fc74 commit 211fbeb
Show file tree
Hide file tree
Showing 13 changed files with 105 additions and 18 deletions.
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"react-dates": "^21.8.0",
"react-dates-gte-react-17-21.8.0-version-fixed": "^21.8.0",
"react-dom": "^18.2.0",
"react-ga4": "^2.1.0",
"react-helmet-async": "^1.3.0",
"react-i18next": "^11.17.2",
"react-markdown": "^8.0.0",
Expand Down
31 changes: 17 additions & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { apolloClient } from "api/apolloClient";
import Footer from "components/Footer";
import Header from "components/Header";
import { AnalyticsWrapper } from "features/analytics";
import StateModal from "features/confirmation/components/StateModal";
import "features/i18n";
import ThemeWrapper from "features/theme/components/ThemeWrapper";
Expand All @@ -15,20 +16,22 @@ import { AppLayoutWrapper } from "./components/layout/AppLayoutWrapper";
function App() {
return (
<RecoilRoot>
<Router>
<ApolloProvider client={apolloClient}>
<ThemeWrapper>
<GlobalStyles />
<AppLayoutWrapper>
<Header />
<PageSwitcher>
<StateModal />
</PageSwitcher>
<Footer />
</AppLayoutWrapper>
</ThemeWrapper>
</ApolloProvider>
</Router>
<AnalyticsWrapper>
<Router>
<ApolloProvider client={apolloClient}>
<ThemeWrapper>
<GlobalStyles />
<AppLayoutWrapper>
<Header />
<PageSwitcher>
<StateModal />
</PageSwitcher>
<Footer />
</AppLayoutWrapper>
</ThemeWrapper>
</ApolloProvider>
</Router>
</AnalyticsWrapper>
</RecoilRoot>
);
}
Expand Down
9 changes: 9 additions & 0 deletions src/features/analytics/AnalyticsWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React, { PropsWithChildren } from "react";
import { AnalyticsContext } from "./contexts/AnalyticsContext";
import { useGoogleAnalytics } from "./hooks";

export const AnalyticsWrapper: React.FC<PropsWithChildren> = ({ children }) => {
const googleAnalytics = useGoogleAnalytics();

return <AnalyticsContext.Provider value={googleAnalytics}>{children}</AnalyticsContext.Provider>;
};
4 changes: 4 additions & 0 deletions src/features/analytics/contexts/AnalyticsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from "react";
import { UseGoogleAnalyticsReturn } from "../types";

export const AnalyticsContext = React.createContext({} as UseGoogleAnalyticsReturn);
1 change: 1 addition & 0 deletions src/features/analytics/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useGoogleAnalytics } from "./useGoogleAnalytics";
38 changes: 38 additions & 0 deletions src/features/analytics/hooks/useGoogleAnalytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useState } from "react";
import ReactGA from "react-ga4";
import { GoogleAnalyticsEvent, UseGoogleAnalytics } from "../types";

export const useGoogleAnalytics: UseGoogleAnalytics = () => {
const [trackingId, setTrackingId] = useState<string>("");

const init = (id: string) => {
if (!id) {
throw new Error("Google Analytics tracking ID is required");
}
ReactGA.initialize(id);
setTrackingId(id);
console.log(`Google Analytics initialized with tracking ID: ${id}`);
};

const sendEvent = (event: GoogleAnalyticsEvent) => {
if (!trackingId) {
throw new Error("Google Analytics is not initialized");
}
ReactGA.event(event);
console.log(`Google Analytics event sent: ${JSON.stringify(event)}`);
};

const send = (path: any) => {
if (!trackingId) {
throw new Error("Google Analytics is not initialized");
}
ReactGA.send(path);
console.log(`Google Analytics pageview sent: ${path}`);
};

return {
init,
sendEvent,
send,
};
};
1 change: 1 addition & 0 deletions src/features/analytics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { AnalyticsWrapper } from "./AnalyticsWrapper";
14 changes: 14 additions & 0 deletions src/features/analytics/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface UseGoogleAnalyticsReturn {
init: (trackingId: string) => void;
sendEvent: (event: any) => void;
send: (fieldObject: any) => void;
}

export type UseGoogleAnalytics = () => UseGoogleAnalyticsReturn;

export interface GoogleAnalyticsEvent {
category: string;
action: string;
label?: string;
value?: number;
}
1 change: 1 addition & 0 deletions src/features/service/api/queries/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ServiceQueryResult {
theme: "DARK" | "LIGHT";
defaultLocale: string;
localTimeZone: string;
googleTagId: string | null;
};
title: string;
description: string;
Expand Down
1 change: 1 addition & 0 deletions src/features/service/api/queries/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const GET_SERVICE = gql`
theme
defaultLocale
localTimeZone
googleTagId
}
title
description
Expand Down
10 changes: 9 additions & 1 deletion src/features/service/components/ServiceHeaders.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import React, { useContext, useEffect } from "react";
import { AnalyticsContext } from "features/analytics/contexts/AnalyticsContext";
import { Helmet, HelmetProvider } from "react-helmet-async";
import { useRecoilValue } from "recoil";
import { serviceAtom } from "state/atoms/service";
Expand All @@ -7,6 +8,13 @@ import { headerSelector } from "state/selectors/headerSelector";
const ServiceHeaders = () => {
const icon = useRecoilValue(headerSelector)?.logoUrl;
const service = useRecoilValue(serviceAtom);
const { init } = useContext(AnalyticsContext);

useEffect(() => {
if (service?.project?.googleTagId) {
init(service.project.googleTagId);
}
}, [init, service?.project?.googleTagId]);

if (service === undefined) return null;

Expand Down
1 change: 1 addition & 0 deletions src/models/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface Service {
theme: "DARK" | "LIGHT";
defaultLocale: string;
localTimeZone: string;
googleTagId: string | null;
};
title: string;
description: string;
Expand Down

0 comments on commit 211fbeb

Please sign in to comment.