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: {