diff --git a/.changeset/nasty-chefs-applaud.md b/.changeset/nasty-chefs-applaud.md new file mode 100644 index 00000000000..273d92bd20e --- /dev/null +++ b/.changeset/nasty-chefs-applaud.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": patch +--- + +Add product analytics on cloud diff --git a/.github/workflows/deploy-cloud.yaml b/.github/workflows/deploy-cloud.yaml index f507770adc0..8589f238d26 100644 --- a/.github/workflows/deploy-cloud.yaml +++ b/.github/workflows/deploy-cloud.yaml @@ -19,6 +19,8 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} APPS_MARKETPLACE_API_URI: "https://apps.saleor.io/api/v2/saleor-apps" IS_CLOUD_INSTANCE: true + POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} + POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} steps: - name: Check region if: ${{ !contains(fromJSON('["eu", "us"]'), env.REGION) }} diff --git a/.github/workflows/deploy-master-staging.yaml b/.github/workflows/deploy-master-staging.yaml index b87c9b430db..ce61adf45b1 100644 --- a/.github/workflows/deploy-master-staging.yaml +++ b/.github/workflows/deploy-master-staging.yaml @@ -19,6 +19,8 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} APPS_MARKETPLACE_API_URI: "https://apps.staging.saleor.io/api/v2/saleor-apps" IS_CLOUD_INSTANCE: true + POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} + POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} steps: - uses: actions/checkout@v2 - name: Set custom version diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml index 2fbc2e43472..ef992a8d836 100644 --- a/.github/workflows/deploy-staging.yaml +++ b/.github/workflows/deploy-staging.yaml @@ -33,6 +33,8 @@ jobs: APPS_MARKETPLACE_API_URI: "https://apps.staging.saleor.io/api/v2/saleor-apps" VERSION: ${{ github.event.inputs.git_ref || github.ref_name }} IS_CLOUD_INSTANCE: true + POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} + POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index a21c9a3f639..2f85139da95 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -53,6 +53,8 @@ jobs: APPS_MARKETPLACE_API_URI: "https://apps.staging.saleor.io/api/v2/saleor-apps" VERSION: ${{ github.event.inputs.git_ref || github.ref_name }} IS_CLOUD_INSTANCE: true + POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} + POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} steps: - name: Checkout Repo uses: actions/checkout@v3 diff --git a/package-lock.json b/package-lock.json index 8dd0e641200..fe1b05bf2e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,7 @@ "marked": "^4.0.17", "moment-timezone": "^0.5.32", "pixelmatch": "^5.3.0", + "posthog-js": "^1.105.9", "qs": "^6.10.3", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -28281,6 +28282,11 @@ "integrity": "sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==", "optional": true }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -37077,6 +37083,24 @@ "postcss": "^8.2.9" } }, + "node_modules/posthog-js": { + "version": "1.105.9", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.105.9.tgz", + "integrity": "sha512-i9DnfyXDktugF5E9x0ejihQ8Xdh1kdVnVh7GdL7+PvFtULGMoN/b4pDmWBCwmH+ciubmqTCImVuNrZYy8+eTbw==", + "dependencies": { + "fflate": "^0.4.8", + "preact": "^10.19.3" + } + }, + "node_modules/preact": { + "version": "10.19.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.4.tgz", + "integrity": "sha512-dwaX5jAh0Ga8uENBX1hSOujmKWgx9RtL80KaKUFLc6jb4vCEAc3EeZ0rnQO/FO4VgjfPMfoLFWnNG8bHuZ9VLw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/precinct": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/precinct/-/precinct-11.0.5.tgz", @@ -62893,6 +62917,11 @@ "integrity": "sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==", "optional": true }, + "fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -68934,6 +68963,20 @@ "quote-unquote": "^1.0.0" } }, + "posthog-js": { + "version": "1.105.9", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.105.9.tgz", + "integrity": "sha512-i9DnfyXDktugF5E9x0ejihQ8Xdh1kdVnVh7GdL7+PvFtULGMoN/b4pDmWBCwmH+ciubmqTCImVuNrZYy8+eTbw==", + "requires": { + "fflate": "^0.4.8", + "preact": "^10.19.3" + } + }, + "preact": { + "version": "10.19.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.4.tgz", + "integrity": "sha512-dwaX5jAh0Ga8uENBX1hSOujmKWgx9RtL80KaKUFLc6jb4vCEAc3EeZ0rnQO/FO4VgjfPMfoLFWnNG8bHuZ9VLw==" + }, "precinct": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/precinct/-/precinct-11.0.5.tgz", diff --git a/package.json b/package.json index f7cf7ec82e7..c1be6d632ce 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "marked": "^4.0.17", "moment-timezone": "^0.5.32", "pixelmatch": "^5.3.0", + "posthog-js": "^1.105.9", "qs": "^6.10.3", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/components/ProductAnalytics/ProductAnalytics.test.tsx b/src/components/ProductAnalytics/ProductAnalytics.test.tsx new file mode 100644 index 00000000000..6abd47d234e --- /dev/null +++ b/src/components/ProductAnalytics/ProductAnalytics.test.tsx @@ -0,0 +1,55 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; + +import { ProductAnalytics } from "."; + +jest.mock("posthog-js/react", () => ({ + PostHogProvider: ({ children }: { children: React.ReactNode }) => ( + <>{children} with posthog + ), +})); + +describe("ProductAnalytics", () => { + const ENV_COPY = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...ENV_COPY }; // Make a copy + }); + + afterAll(() => { + process.env = ENV_COPY; // Restore old environment + }); + + it("renders analytics when it is cloud instance and config was provided", async () => { + // Arrange + process.env.IS_CLOUD_INSTANCE = "true"; + process.env.POSTHOG_KEY = "key"; + process.env.POSTHOG_HOST = "host"; + + // Act + render(Test); + + // Assert + await screen.findByText("Test with posthog"); + }); + + it("does not renders analytics when there is no config", async () => { + // Arrange + process.env.IS_CLOUD_INSTANCE = "true"; + + // Act + render(Test); + + // Assert + await screen.findByText("Test"); + }); + + it("does not renders analytics it is not cloud instance", async () => { + // Act + render(Test); + + // Assert + await screen.findByText("Test"); + }); +}); diff --git a/src/components/ProductAnalytics/index.tsx b/src/components/ProductAnalytics/index.tsx new file mode 100644 index 00000000000..a58b30350a2 --- /dev/null +++ b/src/components/ProductAnalytics/index.tsx @@ -0,0 +1,47 @@ +import { PostHogProvider } from "posthog-js/react"; +import React from "react"; + +const useConfig = () => { + const options = { + api_host: process.env.POSTHOG_HOST, + }; + const apiKey = process.env.POSTHOG_KEY; + const isCloudInstance = process.env.IS_CLOUD_INSTANCE; + const canRenderAnalytics = () => { + if (!isCloudInstance) { + return false; + } + + if (!options.api_host || !apiKey) { + return false; + } + + return true; + }; + + return { + config: { + options, + apiKey, + }, + canRenderAnalytics, + }; +}; + +interface ProductAnalyticsProps { + children: React.ReactNode; +} + +export const ProductAnalytics = ({ children }: ProductAnalyticsProps) => { + const { canRenderAnalytics, config } = useConfig(); + + if (!canRenderAnalytics()) { + return <>{children}; + } + + return ( + + {children} + + ); +}; diff --git a/src/index.tsx b/src/index.tsx index e2e4c0fd34d..06d9590f400 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -39,6 +39,7 @@ import ErrorPage from "./components/ErrorPage"; import ExitFormDialogProvider from "./components/Form/ExitFormDialogProvider"; import { LocaleProvider } from "./components/Locale"; import MessageManagerProvider from "./components/messages"; +import { ProductAnalytics } from "./components/ProductAnalytics"; import { ShopProvider } from "./components/Shop"; import { WindowTitle } from "./components/WindowTitle"; import { DEMO_MODE, getAppMountUri, GTM_ID } from "./config"; @@ -121,7 +122,9 @@ const App: React.FC = () => ( - + + + diff --git a/vite.config.js b/vite.config.js index 6ea285dac7c..3c45000431a 100644 --- a/vite.config.js +++ b/vite.config.js @@ -43,6 +43,8 @@ export default defineConfig(({ command, mode }) => { CUSTOM_VERSION, FLAGS_SERVICE_ENABLED, LOCALE_CODE, + POSTHOG_KEY, + POSTHOG_HOST } = env; const base = STATIC_URL ?? "/"; @@ -65,6 +67,8 @@ export default defineConfig(({ command, mode }) => { APPS_TUNNEL_URL_KEYWORDS, IS_CLOUD_INSTANCE, LOCALE_CODE, + POSTHOG_KEY, + POSTHOG_HOST, injectOgTags: DEMO_MODE && ` @@ -147,7 +151,9 @@ export default defineConfig(({ command, mode }) => { CUSTOM_VERSION, LOCALE_CODE, SENTRY_RELEASE, - STATIC_URL + STATIC_URL, + POSTHOG_KEY, + POSTHOG_HOST }, }, build: {