diff --git a/cypress/e2e/with-users/analytics.ts b/cypress/e2e/with-users/analytics.ts index 052540557d..e89f7a2b48 100644 --- a/cypress/e2e/with-users/analytics.ts +++ b/cypress/e2e/with-users/analytics.ts @@ -2,27 +2,27 @@ import { generateMAASURL } from "../utils"; context("Google Analytics", () => { beforeEach(function () { - const ga = cy.stub().as("ga"); + const gtag = cy.stub().as("gtag"); cy.intercept({ hostname: "www.google-analytics.com" }, { statusCode: 503 }); Cypress.on("window:before:load", (win) => { - Object.defineProperty(win, "ga", { + Object.defineProperty(win, "gtag", { configurable: false, - get: () => ga, + get: () => gtag, set: () => {}, }); }); cy.login(); }); - it("window.ga is called correctly", function () { + it("window.gtag is called correctly", function () { cy.visit(generateMAASURL("/machines")); - cy.get("@ga") + cy.get("@gtag") // ensure GA was created with our google analytics ID .should("be.calledWith", "create", "UA-1018242-63") // ensure that the initial pageview is sent - .and("be.calledWith", "send", "pageview", "/MAAS/r/machines"); + .and("be.calledWith", "send", "pageview", "machines"); cy.visit(generateMAASURL("/devices")); - cy.get("@ga").and("be.calledWith", "send", "pageview", "/MAAS/r/devices"); + cy.get("@gtag").and("be.calledWith", "send", "pageview", "devices"); }); }); diff --git a/package.json b/package.json index b12f8ef593..3cf0ffb6ff 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-dropzone": "14.2.3", + "react-ga4": "2.1.0", "react-portal": "4.2.2", "react-redux": "8.0.5", "react-router-dom": "6.22.3", diff --git a/src/app/base/hooks/analytics.ts b/src/app/base/hooks/analytics.ts index 8549a84693..9e34770936 100644 --- a/src/app/base/hooks/analytics.ts +++ b/src/app/base/hooks/analytics.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect } from "react"; +import ReactGA from "react-ga4"; import { useSelector } from "react-redux"; import { useLocation } from "react-router-dom"; @@ -10,7 +11,6 @@ import { version as versionSelectors } from "@/app/store/general/selectors"; declare global { interface Window { - ga: (...args: unknown[]) => void; lightningjs: { require: (variable: string, url: string) => Window["usabilla_live"]; }; @@ -35,8 +35,11 @@ const sendAnalytics = ( eventAction = "", eventLabel = "" ) => { - window.ga && - window.ga("send", "event", eventCategory, eventAction, eventLabel); + ReactGA.event({ + category: eventCategory, + action: eventAction, + label: eventLabel, + }); }; /** @@ -93,66 +96,27 @@ export const useGoogleAnalytics = (): boolean => { useEffect(() => { if (allowGoogleAnalytics) { - (function (w, d, s, l, i) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - w[l] = w[l] || []; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); - const f = d.getElementsByTagName(s)[0], - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - j = d.createElement(s), - dl = l !== "dataLayer" ? "&l=" + l : ""; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - j.async = true; - const src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - j.src = src; - if (document.querySelectorAll(`script[src="${src}"]`).length === 0) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - f.parentNode.insertBefore(j, f); - } - })(window, document, "script", "dataLayer", "GTM-P4TGJR9"); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.ga = - window.ga || - function () { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // eslint-disable-next-line prefer-rest-params - (window.ga.q = window.ga.q || []).push(arguments); - return window.ga; - }; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.ga.l = +new Date(); - window.ga("create", "UA-1018242-63", "auto", { - userId: `${uuid}-${authUser.id}`, + ReactGA.initialize("G-V64NN1TC9B", { + gaOptions: { + user_id: `${uuid}-${authUser.id}`, + dimension1: version, + dimension2: uuid, + }, + }); + ReactGA.send({ + hitType: "pageview", + page: window.location.pathname + window.location.search, + title: window.location.pathname, }); - window.ga("set", "dimension1", version); - window.ga("set", "dimension2", uuid); - - window.ga( - "send", - "pageview", - window.location.pathname + window.location.search - ); } }, [allowGoogleAnalytics, authUser, uuid, version]); useEffect(() => { - window.ga && - window.ga( - "send", - "pageview", - window.location.pathname + window.location.search - ); + ReactGA.send({ + hitType: "pageview", + page: location.pathname + location.search, + title: location.pathname, + }); }, [location.pathname, location.search]); return allowGoogleAnalytics; diff --git a/yarn.lock b/yarn.lock index cac7ae27a2..62b91a7db1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11446,6 +11446,11 @@ react-fast-compare@^2.0.1: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== +react-ga4@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-ga4/-/react-ga4-2.1.0.tgz#56601f59d95c08466ebd6edfbf8dede55c4678f9" + integrity sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ== + react-is@18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" @@ -12455,7 +12460,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12473,15 +12478,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -12547,7 +12543,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12561,13 +12557,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -13745,7 +13734,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -13763,15 +13752,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"