diff --git a/package-lock.json b/package-lock.json index c0d99be..e90cad7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@ionic/vue": "^7.6.0", "firebase": "^10.3.1", "luxon": "^3.3.0", + "mixpanel-browser": "^2.53.0", "pinia": "2.0.36", "pinia-plugin-persistedstate": "^3.1.0", "register-service-worker": "^1.7.2", @@ -24,7 +25,9 @@ "devDependencies": { "@babel/types": "^7.22.11", "@types/luxon": "^3.3.0", + "@types/mixpanel-browser": "^2.49.1", "@types/node": "^20.5.7", + "@types/vue": "^2.0.0", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-typescript": "^11.0.3", @@ -1269,6 +1272,14 @@ "node": ">= 8.0.0" } }, + "node_modules/@rrweb/types": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.16.tgz", + "integrity": "sha512-E6cACNVsm+NUhn7dzocQoKyXI7BHrHRRm5Ab23yrAzEQ2caWocCEYJhqDlc4KRVJBkQfXZfyWm8+2d0uggFuZg==", + "dependencies": { + "rrweb-snapshot": "^2.0.0-alpha.16" + } + }, "node_modules/@stencil/core": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.9.0.tgz", @@ -1281,6 +1292,11 @@ "npm": ">=7.10.0" } }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -1293,6 +1309,12 @@ "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==", "dev": true }, + "node_modules/@types/mixpanel-browser": { + "version": "2.49.1", + "resolved": "https://registry.npmjs.org/@types/mixpanel-browser/-/mixpanel-browser-2.49.1.tgz", + "integrity": "sha512-W9VZxD7haNMenkRwXxPZBJLhED7Sx1l89nZsGcWi3WzdIk417k/KnpmfDFn2sEyL31G/h0rY1E6erAny+8ItOw==", + "dev": true + }, "node_modules/@types/node": { "version": "20.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", @@ -1309,6 +1331,16 @@ "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", "dev": true }, + "node_modules/@types/vue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/vue/-/vue-2.0.0.tgz", + "integrity": "sha512-WDElkBv/o4lVwu6wYHB06AXs4Xo2fwDjJUpvPRc1QQdzkUSiGFjrYuSCy8raxLE5FObgKq8ND7R5gSZTFLK60w==", + "deprecated": "This is a stub types definition for vuejs (https://github.com/vuejs/vue). vuejs provides its own type definitions, so you don't need @types/vue installed!", + "dev": true, + "dependencies": { + "vue": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -1853,6 +1885,11 @@ "@vue/language-core": "1.8.11" } }, + "node_modules/@xstate/fsm": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz", + "integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==" + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -1938,6 +1975,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -2811,6 +2856,11 @@ "node": ">=0.8.0" } }, + "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/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3533,6 +3583,19 @@ "node": "*" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "node_modules/mixpanel-browser": { + "version": "2.53.0", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.53.0.tgz", + "integrity": "sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==", + "dependencies": { + "rrweb": "2.0.0-alpha.13" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4159,6 +4222,34 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/rrdom": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/rrdom/-/rrdom-2.0.0-alpha.16.tgz", + "integrity": "sha512-m8aoeORWUz7AFdEb7hES7wPeL6fl/oP23RoAlzLXyA/f2+NqCDM7KEyCXY4sHu6CChN3OAUP2BaUGEXn0zynlw==", + "dependencies": { + "rrweb-snapshot": "^2.0.0-alpha.16" + } + }, + "node_modules/rrweb": { + "version": "2.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/rrweb/-/rrweb-2.0.0-alpha.13.tgz", + "integrity": "sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw==", + "dependencies": { + "@rrweb/types": "^2.0.0-alpha.13", + "@types/css-font-loading-module": "0.0.7", + "@xstate/fsm": "^1.4.0", + "base64-arraybuffer": "^1.0.1", + "fflate": "^0.4.4", + "mitt": "^3.0.0", + "rrdom": "^2.0.0-alpha.13", + "rrweb-snapshot": "^2.0.0-alpha.13" + } + }, + "node_modules/rrweb-snapshot": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.16.tgz", + "integrity": "sha512-p81OrzUiCmUMZzJu4fGHeLB00PIbVIqsV/zhqzr2pitHTUXpMYcyOvDWt0vHdla0vnowEPaHq3Wsu6cUc732/w==" + }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -5831,11 +5922,24 @@ "picomatch": "^2.2.2" } }, + "@rrweb/types": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.16.tgz", + "integrity": "sha512-E6cACNVsm+NUhn7dzocQoKyXI7BHrHRRm5Ab23yrAzEQ2caWocCEYJhqDlc4KRVJBkQfXZfyWm8+2d0uggFuZg==", + "requires": { + "rrweb-snapshot": "^2.0.0-alpha.16" + } + }, "@stencil/core": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.9.0.tgz", "integrity": "sha512-aWSkhBmk3yPwRAkUwBbzRwmdhb8hKiQ/JMr9m5jthpBZLjtppYbzz6PN2MhSMDfRp6K93eQw5WogSEH4HHuB6w==" }, + "@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" + }, "@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -5848,6 +5952,12 @@ "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==", "dev": true }, + "@types/mixpanel-browser": { + "version": "2.49.1", + "resolved": "https://registry.npmjs.org/@types/mixpanel-browser/-/mixpanel-browser-2.49.1.tgz", + "integrity": "sha512-W9VZxD7haNMenkRwXxPZBJLhED7Sx1l89nZsGcWi3WzdIk417k/KnpmfDFn2sEyL31G/h0rY1E6erAny+8ItOw==", + "dev": true + }, "@types/node": { "version": "20.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", @@ -5864,6 +5974,15 @@ "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", "dev": true }, + "@types/vue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/vue/-/vue-2.0.0.tgz", + "integrity": "sha512-WDElkBv/o4lVwu6wYHB06AXs4Xo2fwDjJUpvPRc1QQdzkUSiGFjrYuSCy8raxLE5FObgKq8ND7R5gSZTFLK60w==", + "dev": true, + "requires": { + "vue": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -6253,6 +6372,11 @@ "@vue/language-core": "1.8.11" } }, + "@xstate/fsm": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz", + "integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==" + }, "acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -6317,6 +6441,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + }, "big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -6943,6 +7072,11 @@ "websocket-driver": ">=0.5.1" } }, + "fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -7480,6 +7614,19 @@ "brace-expansion": "^1.1.7" } }, + "mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, + "mixpanel-browser": { + "version": "2.53.0", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.53.0.tgz", + "integrity": "sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==", + "requires": { + "rrweb": "2.0.0-alpha.13" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7881,6 +8028,34 @@ } } }, + "rrdom": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/rrdom/-/rrdom-2.0.0-alpha.16.tgz", + "integrity": "sha512-m8aoeORWUz7AFdEb7hES7wPeL6fl/oP23RoAlzLXyA/f2+NqCDM7KEyCXY4sHu6CChN3OAUP2BaUGEXn0zynlw==", + "requires": { + "rrweb-snapshot": "^2.0.0-alpha.16" + } + }, + "rrweb": { + "version": "2.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/rrweb/-/rrweb-2.0.0-alpha.13.tgz", + "integrity": "sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw==", + "requires": { + "@rrweb/types": "^2.0.0-alpha.13", + "@types/css-font-loading-module": "0.0.7", + "@xstate/fsm": "^1.4.0", + "base64-arraybuffer": "^1.0.1", + "fflate": "^0.4.4", + "mitt": "^3.0.0", + "rrdom": "^2.0.0-alpha.13", + "rrweb-snapshot": "^2.0.0-alpha.13" + } + }, + "rrweb-snapshot": { + "version": "2.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.0.0-alpha.16.tgz", + "integrity": "sha512-p81OrzUiCmUMZzJu4fGHeLB00PIbVIqsV/zhqzr2pitHTUXpMYcyOvDWt0vHdla0vnowEPaHq3Wsu6cUc732/w==" + }, "run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", diff --git a/package.json b/package.json index 1b488b3..fc03502 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@ionic/vue": "^7.6.0", "firebase": "^10.3.1", "luxon": "^3.3.0", + "mixpanel-browser": "^2.53.0", "pinia": "2.0.36", "pinia-plugin-persistedstate": "^3.1.0", "register-service-worker": "^1.7.2", @@ -39,7 +40,9 @@ "devDependencies": { "@babel/types": "^7.22.11", "@types/luxon": "^3.3.0", + "@types/mixpanel-browser": "^2.49.1", "@types/node": "^20.5.7", + "@types/vue": "^2.0.0", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-typescript": "^11.0.3", diff --git a/src/index.ts b/src/index.ts index 74f8185..4bfc4c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,8 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import { createI18n } from 'vue-i18n' import { useUserStore } from "./store/user"; import { IonicVue } from '@ionic/vue'; +import useAnalytics from "./mixins/analytics"; + import "./service-worker" @@ -140,5 +142,6 @@ export { useAuthStore, useProductIdentificationStore, useUserStore, - userContext + userContext, + useAnalytics } diff --git a/src/mixins/analytics.ts b/src/mixins/analytics.ts new file mode 100644 index 0000000..9bb20e6 --- /dev/null +++ b/src/mixins/analytics.ts @@ -0,0 +1,78 @@ +import { computed, onMounted, onBeforeUnmount } from 'vue'; +import mixpanel from 'mixpanel-browser'; +import { useAuthStore } from '../index'; +import { appContext } from '../index'; +declare var process: any; + +interface TrackableMetadata { //defining the interface for trackable metadata + label?: string; + id?: string; + [key: string]: any; +} + +function useAnalytics() { + const handleButtonClick = (event: MouseEvent) => { + event.stopPropagation(); //method to stop the click event from propagating up the DOM tree + if (event.button === 0) { // Left mouse button + const target = event.target as HTMLElement; + const button = target.closest('ion-button, ion-item') as HTMLElement; //finds the closest parent element that matches the selectors ion-button or ion-item + const authStore = useAuthStore(); + const oms = computed(() => authStore.getOms); + + if (button && button.hasAttribute('trackable')) { + const trackableData = button.getAttribute('trackable'); + let metadata: TrackableMetadata = {}; //object initialization for trackable metadata + + try { + metadata = trackableData ? JSON.parse(trackableData) : {}; + } catch (error) { + console.error('Error parsing trackable attribute:', error); + } + + const buttonLabel = metadata.label || button.innerText || button.getAttribute('aria-label') || 'Unnamed button'; + const buttonId = button.id || metadata.id || 'no-id'; + mixpanel.track(buttonLabel, { + oms: oms.value, + label: buttonLabel, + id: buttonId, + ...metadata, + }); + } + } + }; + + onMounted(() => { + try { + mixpanel.init( process.env.VUE_APP_MIXPANEL_TOKEN , { debug: true }); + } catch (error) { + console.log(error); + return; + } + const authStore = useAuthStore(); + const oms = computed(() => authStore.getOms); + const appState = appContext.config.globalProperties.$store; + const userProfile = appState.getters['user/getUserProfile']; //retrieves the user profile using a Vuex getter. + const userEmail = userProfile.email; //fetch user email from userProfile + const userID = userProfile.userLoginId; // fetch user ID from userProfile + + try { + mixpanel.identify(userID); + mixpanel.people.set({ + $email: userEmail, + $userId: userID, + $oms: oms.value + }); + } catch (error) { + console.log(error); + return; + } + + document.addEventListener('click', handleButtonClick); //adds the event listener + }); + + onBeforeUnmount(() => { + document.removeEventListener('click', handleButtonClick); //removes the event listener , to prevent memory leaks + }); +} + +export default useAnalytics;