From 2453c5cdbd9d412a59c6fe32656aff69d234a0ed Mon Sep 17 00:00:00 2001 From: omriformula Date: Mon, 15 Sep 2025 08:21:31 +0300 Subject: [PATCH 1/5] Acme - Company name change Co-authored-by: Administrator --- src/components/logo/LogoMain.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/logo/LogoMain.jsx b/src/components/logo/LogoMain.jsx index ef7044b8..4492906b 100644 --- a/src/components/logo/LogoMain.jsx +++ b/src/components/logo/LogoMain.jsx @@ -24,8 +24,7 @@ export default function LogoMain() { d="M6.94549 13.5496L6.9479 13.552L9.12272 15.7275L17.4999 24.1041L28.0544 13.5496H6.94549Z" fill={theme.palette.primary.main} /> - Acme fill={theme.palette.common.black} fillOpacity="0.85" /> From c1fb404e2a159e936405a14b11e7051acc923afc Mon Sep 17 00:00:00 2001 From: omriformula Date: Tue, 16 Sep 2025 12:56:00 +0300 Subject: [PATCH 2/5] MixPanel Co-authored-by: Omri Herman --- package.json | 1 + src/App.jsx | 17 ++++ src/analytics/mixpanel.js | 80 +++++++++++++++++ src/hooks/useMixpanelTracking.js | 26 ++++++ .../Dashboard/Header/HeaderContent/index.jsx | 5 ++ src/layout/Dashboard/index.jsx | 6 ++ src/pages/dashboard/default.jsx | 29 +++++- yarn.lock | 88 ++++++++++++++++++- 8 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 src/analytics/mixpanel.js create mode 100644 src/hooks/useMixpanelTracking.js diff --git a/package.json b/package.json index 098fc903..2362bf2e 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "formik": "2.4.6", "framer-motion": "12.8.2", "lodash-es": "4.17.21", + "mixpanel-browser": "^2.70.0", "react": "19.1.0", "react-device-detect": "2.2.3", "react-dom": "19.1.0", diff --git a/src/App.jsx b/src/App.jsx index 7b1e4ad9..d44e9269 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import { RouterProvider } from 'react-router-dom'; // project imports @@ -6,9 +7,25 @@ import ThemeCustomization from 'themes'; import ScrollTop from 'components/ScrollTop'; +// analytics +import analytics from 'analytics/mixpanel'; + // ==============================|| APP - THEME, ROUTER, LOCAL ||============================== // export default function App() { + useEffect(() => { + // Initialize Mixpanel + analytics.init(); + + // Track app load + analytics.track('App Loaded', { + timestamp: new Date().toISOString(), + user_agent: navigator.userAgent, + screen_width: window.screen.width, + screen_height: window.screen.height + }); + }, []); + return ( diff --git a/src/analytics/mixpanel.js b/src/analytics/mixpanel.js new file mode 100644 index 00000000..571e7f10 --- /dev/null +++ b/src/analytics/mixpanel.js @@ -0,0 +1,80 @@ +import mixpanel from 'mixpanel-browser'; + +const MIXPANEL_TOKEN = 'ff3948c5215e478a83d6047952d7302a'; + +class MixpanelAnalytics { + constructor() { + this.initialized = false; + } + + init() { + if (this.initialized) return; + + mixpanel.init(MIXPANEL_TOKEN, { + debug: true, + track_pageview: true, + persistence: 'localStorage' + }); + + this.initialized = true; + console.log('Mixpanel initialized successfully'); + } + + track(eventName, properties = {}) { + if (!this.initialized) { + console.warn('Mixpanel not initialized. Call init() first.'); + return; + } + + mixpanel.track(eventName, properties); + console.log(`Mixpanel Event Tracked: ${eventName}`, properties); + } + + identify(userId, userProperties = {}) { + if (!this.initialized) { + console.warn('Mixpanel not initialized. Call init() first.'); + return; + } + + mixpanel.identify(userId); + mixpanel.people.set(userProperties); + console.log(`Mixpanel User Identified: ${userId}`, userProperties); + } + + trackPageView(pageName, properties = {}) { + this.track('Page View', { + page: pageName, + url: window.location.href, + path: window.location.pathname, + referrer: document.referrer, + ...properties + }); + } + + trackButtonClick(buttonName, properties = {}) { + this.track('Button Click', { + button_name: buttonName, + page: window.location.pathname, + ...properties + }); + } + + trackFormSubmit(formName, properties = {}) { + this.track('Form Submit', { + form_name: formName, + page: window.location.pathname, + ...properties + }); + } + + reset() { + if (this.initialized) { + mixpanel.reset(); + console.log('Mixpanel reset'); + } + } +} + +const analytics = new MixpanelAnalytics(); + +export default analytics; \ No newline at end of file diff --git a/src/hooks/useMixpanelTracking.js b/src/hooks/useMixpanelTracking.js new file mode 100644 index 00000000..e20bd1ac --- /dev/null +++ b/src/hooks/useMixpanelTracking.js @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; +import analytics from 'analytics/mixpanel'; + +const useMixpanelTracking = () => { + const location = useLocation(); + + useEffect(() => { + // Track page views on route change + const pageName = location.pathname === '/' ? 'Home' : location.pathname.slice(1).replace(/-/g, ' '); + + analytics.trackPageView(pageName, { + timestamp: new Date().toISOString(), + search: location.search, + hash: location.hash + }); + }, [location]); + + return { + trackClick: (buttonName, properties) => analytics.trackButtonClick(buttonName, properties), + trackEvent: (eventName, properties) => analytics.track(eventName, properties), + trackForm: (formName, properties) => analytics.trackFormSubmit(formName, properties) + }; +}; + +export default useMixpanelTracking; \ No newline at end of file diff --git a/src/layout/Dashboard/Header/HeaderContent/index.jsx b/src/layout/Dashboard/Header/HeaderContent/index.jsx index 738529cb..cdb335f8 100644 --- a/src/layout/Dashboard/Header/HeaderContent/index.jsx +++ b/src/layout/Dashboard/Header/HeaderContent/index.jsx @@ -13,10 +13,14 @@ import MobileSection from './MobileSection'; // project import import { GithubOutlined } from '@ant-design/icons'; +// hooks +import useMixpanelTracking from 'hooks/useMixpanelTracking'; + // ==============================|| HEADER - CONTENT ||============================== // export default function HeaderContent() { const downLG = useMediaQuery((theme) => theme.breakpoints.down('lg')); + const { trackClick } = useMixpanelTracking(); return ( <> @@ -30,6 +34,7 @@ export default function HeaderContent() { color="secondary" title="Download Free Version" sx={{ color: 'text.primary', bgcolor: 'grey.100' }} + onClick={() => trackClick('GitHub Link', { location: 'Header' })} > diff --git a/src/layout/Dashboard/index.jsx b/src/layout/Dashboard/index.jsx index e3e2bb9a..bdc08033 100644 --- a/src/layout/Dashboard/index.jsx +++ b/src/layout/Dashboard/index.jsx @@ -14,12 +14,18 @@ import Breadcrumbs from 'components/@extended/Breadcrumbs'; import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; +// hooks +import useMixpanelTracking from 'hooks/useMixpanelTracking'; + // ==============================|| MAIN LAYOUT ||============================== // export default function DashboardLayout() { const { menuMasterLoading } = useGetMenuMaster(); const downXL = useMediaQuery((theme) => theme.breakpoints.down('xl')); + // Initialize Mixpanel tracking + useMixpanelTracking(); + // set media wise responsive drawer useEffect(() => { handlerDrawerOpen(!downXL); diff --git a/src/pages/dashboard/default.jsx b/src/pages/dashboard/default.jsx index 231f5ea2..f3c770b9 100644 --- a/src/pages/dashboard/default.jsx +++ b/src/pages/dashboard/default.jsx @@ -48,9 +48,28 @@ const actionSX = { transform: 'none' }; +// hooks +import useMixpanelTracking from 'hooks/useMixpanelTracking'; + // ==============================|| DASHBOARD - DEFAULT ||============================== // export default function DashboardDefault() { + const { trackClick } = useMixpanelTracking(); + + const handleViewAllOrders = () => { + trackClick('View All Orders', { + location: 'Dashboard', + section: 'Recent Orders' + }); + }; + + const handleViewTransaction = () => { + trackClick('View Transaction', { + location: 'Dashboard', + section: 'Transactions' + }); + }; + return ( {/* row 1 */} @@ -158,6 +177,7 @@ export default function DashboardDefault() { trackClick('Transaction Item', { order_id: '#002434', amount: '$1,430' })} secondaryAction={ @@ -179,6 +199,7 @@ export default function DashboardDefault() { trackClick('Transaction Item', { order_id: '#984947', amount: '$302' })} secondaryAction={ @@ -199,6 +220,7 @@ export default function DashboardDefault() { trackClick('Transaction Item', { order_id: '#988784', amount: '$682' })} secondaryAction={ @@ -241,7 +263,12 @@ export default function DashboardDefault() { - diff --git a/yarn.lock b/yarn.lock index cdc191c1..8b6a0b19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -865,6 +865,54 @@ __metadata: languageName: node linkType: hard +"@mixpanel/rrdom@npm:^2.0.0-alpha.18": + version: 2.0.0-alpha.18.1 + resolution: "@mixpanel/rrdom@npm:2.0.0-alpha.18.1" + dependencies: + "@mixpanel/rrweb-snapshot": "npm:^2.0.0-alpha.18" + checksum: 10c0/1b403e38ae1948b9b1bdda8b62a6eaafbe75791c6c043494ea5142fa8d9a353cef7e85be3ca504efdcc25ef9aef0b0de43a3852348d291754e107302c254e84d + languageName: node + linkType: hard + +"@mixpanel/rrweb-snapshot@npm:^2.0.0-alpha.18": + version: 2.0.0-alpha.18.1 + resolution: "@mixpanel/rrweb-snapshot@npm:2.0.0-alpha.18.1" + dependencies: + postcss: "npm:^8.4.38" + checksum: 10c0/09af33442d80516e48d48204694d37886bc3fb0ee66f639763658fe94d008684244909c8a17f7944df908abd7d59df82c518bddf6234851c4ffb28db9d1b1035 + languageName: node + linkType: hard + +"@mixpanel/rrweb-types@npm:^2.0.0-alpha.18": + version: 2.0.0-alpha.18.1 + resolution: "@mixpanel/rrweb-types@npm:2.0.0-alpha.18.1" + checksum: 10c0/df66fee7a424e92b7dda3bda749a159ce21073baaeb1224e48ebc216eb95dd9a783608710a80148a518312c76c52405b58ac0f8a2abc36897115649933f0077f + languageName: node + linkType: hard + +"@mixpanel/rrweb-utils@npm:^2.0.0-alpha.18": + version: 2.0.0-alpha.18.1 + resolution: "@mixpanel/rrweb-utils@npm:2.0.0-alpha.18.1" + checksum: 10c0/a0e25cd5b4ed8030711f39b28b2566d73dae06b1b5473a0a6a1a9cfb8d600c9f8656c4c5e6fa6897e8ff14d03aa0eee08f0be645a19b194e277bbc2cf05cc68f + languageName: node + linkType: hard + +"@mixpanel/rrweb@npm:2.0.0-alpha.18.1": + version: 2.0.0-alpha.18.1 + resolution: "@mixpanel/rrweb@npm:2.0.0-alpha.18.1" + dependencies: + "@mixpanel/rrdom": "npm:^2.0.0-alpha.18" + "@mixpanel/rrweb-snapshot": "npm:^2.0.0-alpha.18" + "@mixpanel/rrweb-types": "npm:^2.0.0-alpha.18" + "@mixpanel/rrweb-utils": "npm:^2.0.0-alpha.18" + "@types/css-font-loading-module": "npm:0.0.7" + "@xstate/fsm": "npm:^1.4.0" + base64-arraybuffer: "npm:^1.0.1" + mitt: "npm:^3.0.0" + checksum: 10c0/d86a346e30070255ecdaa1f1408e25448b4ffc99670e250d666390bef83b82554107e277b8e8b2b45a984be24366797d611018992d9a326528c3fee1bf8b2887 + languageName: node + linkType: hard + "@mui/base@npm:5.0.0-beta.70": version: 5.0.0-beta.70 resolution: "@mui/base@npm:5.0.0-beta.70" @@ -1404,6 +1452,13 @@ __metadata: languageName: node linkType: hard +"@types/css-font-loading-module@npm:0.0.7": + version: 0.0.7 + resolution: "@types/css-font-loading-module@npm:0.0.7" + checksum: 10c0/a74759a14bcc7d60a1a1d863b53b7638d4aa7f88f1d97347426262cc6fe8f9335d8fa80c7e0608cd67e33ff0067608e9b5475a1227a684e1dfad3cac87df1405 + languageName: node + linkType: hard + "@types/d3-color@npm:*, @types/d3-color@npm:^3.1.3": version: 3.1.3 resolution: "@types/d3-color@npm:3.1.3" @@ -1545,6 +1600,13 @@ __metadata: languageName: node linkType: hard +"@xstate/fsm@npm:^1.4.0": + version: 1.6.5 + resolution: "@xstate/fsm@npm:1.6.5" + checksum: 10c0/472fe625b84b9e7102b8774e80c441b8b7dbc9585e700223d9c7a39c583d38ba50636909a5d749d44b361555daa841862f932a29c52b81c8829a74884e715bf4 + languageName: node + linkType: hard + "abbrev@npm:^3.0.0": version: 3.0.1 resolution: "abbrev@npm:3.0.1" @@ -1787,6 +1849,13 @@ __metadata: languageName: node linkType: hard +"base64-arraybuffer@npm:^1.0.1": + version: 1.0.2 + resolution: "base64-arraybuffer@npm:1.0.2" + checksum: 10c0/3acac95c70f9406e87a41073558ba85b6be9dbffb013a3d2a710e3f2d534d506c911847d5d9be4de458af6362c676de0a5c4c2d7bdf4def502d00b313368e72f + languageName: node + linkType: hard + "bezier-easing@npm:^2.1.0": version: 2.1.0 resolution: "bezier-easing@npm:2.1.0" @@ -3754,6 +3823,7 @@ __metadata: formik: "npm:2.4.6" framer-motion: "npm:12.8.2" lodash-es: "npm:4.17.21" + mixpanel-browser: "npm:^2.70.0" prettier: "npm:3.6.2" react: "npm:19.1.0" react-device-detect: "npm:2.2.3" @@ -3878,6 +3948,22 @@ __metadata: languageName: node linkType: hard +"mitt@npm:^3.0.0": + version: 3.0.1 + resolution: "mitt@npm:3.0.1" + checksum: 10c0/3ab4fdecf3be8c5255536faa07064d05caa3dd332bd318ff02e04621f7b3069ca1de9106cfe8e7ced675abfc2bec2ce4c4ef321c4a1bb1fb29df8ae090741913 + languageName: node + linkType: hard + +"mixpanel-browser@npm:^2.70.0": + version: 2.70.0 + resolution: "mixpanel-browser@npm:2.70.0" + dependencies: + "@mixpanel/rrweb": "npm:2.0.0-alpha.18.1" + checksum: 10c0/5e58c1a3b3bc2b84d76ed983c15be629720546dc69419b7179f5488ef62701fa577894f409392b05a6844b9b16152b0aaf201a3e5cef71689de88ecc5b350f0d + languageName: node + linkType: hard + "mkdirp@npm:^3.0.1": version: 3.0.1 resolution: "mkdirp@npm:3.0.1" @@ -4197,7 +4283,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.5.6": +"postcss@npm:^8.4.38, postcss@npm:^8.5.6": version: 8.5.6 resolution: "postcss@npm:8.5.6" dependencies: From be370710c6564dd111def192f0a03832cc186d94 Mon Sep 17 00:00:00 2001 From: omriformula Date: Wed, 17 Sep 2025 12:21:13 +0300 Subject: [PATCH 3/5] Activation metric improvement Co-authored-by: Administrator --- netlify.toml | 11 +++++++++++ vite.config.mjs | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 netlify.toml diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 00000000..4b3b4c34 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,11 @@ +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + +[build] + command = "npm run build" + publish = "dist" + +[build.environment] + VITE_APP_BASE_NAME = "/" \ No newline at end of file diff --git a/vite.config.mjs b/vite.config.mjs index 45b83d26..92da93e8 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -5,7 +5,8 @@ import path from 'path'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ''); - const API_URL = env.VITE_APP_BASE_NAME || '/'; + // Use '/' for production (Netlify), '/free' for development + const API_URL = process.env.NETLIFY ? '/' : (env.VITE_APP_BASE_NAME || '/'); const PORT = 3000; return { From 421358647fc71b203505c341d5246eaed36dc37e Mon Sep 17 00:00:00 2001 From: omriformula Date: Wed, 17 Sep 2025 20:28:21 +0300 Subject: [PATCH 4/5] Activation Improvement Co-authored-by: Administrator --- src/components/ActivationChecklist.jsx | 279 +++++++++++++++++++++++++ src/pages/dashboard/default.jsx | 4 + 2 files changed, 283 insertions(+) create mode 100644 src/components/ActivationChecklist.jsx diff --git a/src/components/ActivationChecklist.jsx b/src/components/ActivationChecklist.jsx new file mode 100644 index 00000000..7b913a37 --- /dev/null +++ b/src/components/ActivationChecklist.jsx @@ -0,0 +1,279 @@ +import { useState, useEffect } from 'react'; + +// material-ui +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import Checkbox from '@mui/material/Checkbox'; +import IconButton from '@mui/material/IconButton'; +import Collapse from '@mui/material/Collapse'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import LinearProgress from '@mui/material/LinearProgress'; + +// project imports +import MainCard from 'components/MainCard'; + +// assets +import CloseOutlined from '@ant-design/icons/CloseOutlined'; +import DatabaseOutlined from '@ant-design/icons/DatabaseOutlined'; +import UserAddOutlined from '@ant-design/icons/UserAddOutlined'; +import DashboardOutlined from '@ant-design/icons/DashboardOutlined'; +import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined'; + +// hooks +import useMixpanelTracking from 'hooks/useMixpanelTracking'; + +const checklistItems = [ + { + id: 'connect-data-source', + title: 'Connect data source', + description: 'Link your first data source to start analyzing', + icon: DatabaseOutlined, + autoComplete: () => { + // Check if user has connected any data source + // This would typically check API or local storage + return localStorage.getItem('hasDataSource') === 'true'; + } + }, + { + id: 'invite-teammate', + title: 'Invite a teammate', + description: 'Collaborate better with your team', + icon: UserAddOutlined, + autoComplete: () => { + // Check if user has invited team members + return localStorage.getItem('hasInvitedTeammate') === 'true'; + } + }, + { + id: 'create-dashboard', + title: 'Create first dashboard', + description: 'Build your first data visualization', + icon: DashboardOutlined, + autoComplete: () => { + // Check if user has created a dashboard + return localStorage.getItem('hasCreatedDashboard') === 'true'; + } + } +]; + +export default function ActivationChecklist() { + const [isVisible, setIsVisible] = useState(true); + const [completedItems, setCompletedItems] = useState(new Set()); + const { trackClick } = useMixpanelTracking(); + + // Check for auto-completion on mount and periodically + useEffect(() => { + const checkAutoComplete = () => { + const newCompleted = new Set(); + checklistItems.forEach(item => { + if (item.autoComplete()) { + newCompleted.add(item.id); + } + }); + setCompletedItems(newCompleted); + }; + + checkAutoComplete(); + + // Check every 5 seconds for auto-completion + const interval = setInterval(checkAutoComplete, 5000); + return () => clearInterval(interval); + }, []); + + // Don't show if dismissed + useEffect(() => { + const isDismissed = localStorage.getItem('activationChecklistDismissed') === 'true'; + if (isDismissed) { + setIsVisible(false); + } + }, []); + + const handleDismiss = () => { + localStorage.setItem('activationChecklistDismissed', 'true'); + setIsVisible(false); + trackClick('Activation Checklist Dismissed', { + completed_items: completedItems.size, + total_items: checklistItems.length + }); + }; + + const handleItemClick = (item) => { + trackClick('Activation Checklist Item Clicked', { + item_id: item.id, + item_title: item.title, + is_completed: completedItems.has(item.id) + }); + }; + + const completionPercentage = (completedItems.size / checklistItems.length) * 100; + const isFullyCompleted = completedItems.size === checklistItems.length; + + // Hide if fully completed for more than a day + useEffect(() => { + if (isFullyCompleted) { + const completionTime = localStorage.getItem('activationChecklistCompletionTime'); + if (!completionTime) { + localStorage.setItem('activationChecklistCompletionTime', Date.now().toString()); + } else { + const daysSinceCompletion = (Date.now() - parseInt(completionTime)) / (1000 * 60 * 60 * 24); + if (daysSinceCompletion > 1) { + setIsVisible(false); + } + } + } + }, [isFullyCompleted]); + + if (!isVisible) return null; + + return ( + + + theme.customShadows.z8 + } + }} + > + + + + + + + + {isFullyCompleted ? ( + + ) : ( + + {completedItems.size} + + )} + + {isFullyCompleted ? 'Setup Complete! 🎉' : 'Complete your setup'} + + + + {!isFullyCompleted && ( + + Get the most out of your dashboard by completing these steps + + )} + + + + + {completedItems.size} of {checklistItems.length} completed + + + + + {checklistItems.map((item) => { + const ItemIcon = item.icon; + const isCompleted = completedItems.has(item.id); + + return ( + handleItemClick(item)} + > + + + + + + + + {item.title} + + } + secondary={ + + {item.description} + + } + /> + + ); + })} + + + + + + + ); +} \ No newline at end of file diff --git a/src/pages/dashboard/default.jsx b/src/pages/dashboard/default.jsx index f3c770b9..9486f5a7 100644 --- a/src/pages/dashboard/default.jsx +++ b/src/pages/dashboard/default.jsx @@ -14,6 +14,7 @@ import Box from '@mui/material/Box'; // project imports import MainCard from 'components/MainCard'; +import ActivationChecklist from 'components/ActivationChecklist'; import AnalyticEcommerce from 'components/cards/statistics/AnalyticEcommerce'; import MonthlyBarChart from 'sections/dashboard/default/MonthlyBarChart'; import ReportAreaChart from 'sections/dashboard/default/ReportAreaChart'; @@ -76,6 +77,9 @@ export default function DashboardDefault() { Dashboard + + + From 0509f7def7a5929aa900add9376bd1f734e946dd Mon Sep 17 00:00:00 2001 From: omriformula Date: Thu, 18 Sep 2025 12:32:27 +0300 Subject: [PATCH 5/5] Activation Test Co-authored-by: Administrator --- src/components/ActivationChecklist.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ActivationChecklist.jsx b/src/components/ActivationChecklist.jsx index 7b913a37..206d19b3 100644 --- a/src/components/ActivationChecklist.jsx +++ b/src/components/ActivationChecklist.jsx @@ -127,7 +127,7 @@ export default function ActivationChecklist() { } }, [isFullyCompleted]); - if (!isVisible) return null; + return null; return (