From 71832b75d49b1394f778177a5f773dca2f3f5f7a Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Fri, 1 Dec 2023 18:26:59 -0800 Subject: [PATCH 01/10] added file for analytics --- client/src/components/analyticsManager.jsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 client/src/components/analyticsManager.jsx diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx new file mode 100644 index 0000000..e69de29 From 798249fdd2ad6b140db3a1a5e794639e17fae366 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Fri, 1 Dec 2023 19:07:56 -0800 Subject: [PATCH 02/10] working on figuring out how to format data into chart --- client/package-lock.json | 518 +++++++++++++++++++++ client/package.json | 1 + client/src/App.js | 1 + client/src/components/analyticsManager.jsx | 28 ++ client/src/pages/admin-analytics.js | 4 +- client/src/pages/home.js | 1 + 6 files changed, 551 insertions(+), 2 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index f692b13..5919ebd 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -20,6 +20,7 @@ "react-modal": "^3.16.1", "react-router-dom": "^6.16.0", "react-scripts": "5.0.1", + "recharts": "^2.10.3", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -3990,6 +3991,60 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/eslint": { "version": "8.44.3", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", @@ -6040,6 +6095,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6704,6 +6767,116 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6743,6 +6916,11 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -6991,6 +7169,14 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -8169,6 +8355,14 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -9374,6 +9568,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -14998,6 +15200,35 @@ } } }, + "node_modules/react-smooth": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.5.tgz", + "integrity": "sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA==", + "dependencies": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -15030,6 +15261,42 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.10.3.tgz", + "integrity": "sha512-G4J96fKTZdfFQd6aQnZjo2nVNdXhp+uuLb00+cBTGLo85pChvm1+E67K3wBOHDE/77spcYb2Cy9gYWVqiZvQCg==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-smooth": "^2.0.5", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -17139,6 +17406,27 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.7.0", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.7.0.tgz", + "integrity": "sha512-nqYuTkLSdTTeACyXcCLbL7rl0y6jpzLPtTNGOtSnajdR+xxMxBdjMxDjfNJNlhR+ZU8vbXz+QejntcbY7h9/ZA==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -20783,6 +21071,60 @@ "@types/node": "*" } }, + "@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", + "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + }, + "@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "@types/eslint": { "version": "8.44.3", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", @@ -22351,6 +22693,11 @@ "wrap-ansi": "^7.0.0" } }, + "clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -22822,6 +23169,83 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -22850,6 +23274,11 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -23044,6 +23473,14 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -23921,6 +24358,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==" + }, "fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -24780,6 +25222,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -28653,6 +29100,26 @@ "workbox-webpack-plugin": "^6.4.1" } }, + "react-smooth": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.5.tgz", + "integrity": "sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA==", + "requires": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -28679,6 +29146,36 @@ "picomatch": "^2.2.1" } }, + "recharts": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.10.3.tgz", + "integrity": "sha512-G4J96fKTZdfFQd6aQnZjo2nVNdXhp+uuLb00+cBTGLo85pChvm1+E67K3wBOHDE/77spcYb2Cy9gYWVqiZvQCg==", + "requires": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-smooth": "^2.0.5", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, "recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -30227,6 +30724,27 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "victory-vendor": { + "version": "36.7.0", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.7.0.tgz", + "integrity": "sha512-nqYuTkLSdTTeACyXcCLbL7rl0y6jpzLPtTNGOtSnajdR+xxMxBdjMxDjfNJNlhR+ZU8vbXz+QejntcbY7h9/ZA==", + "requires": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/client/package.json b/client/package.json index 4da6a0f..f19004e 100644 --- a/client/package.json +++ b/client/package.json @@ -15,6 +15,7 @@ "react-modal": "^3.16.1", "react-router-dom": "^6.16.0", "react-scripts": "5.0.1", + "recharts": "^2.10.3", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/client/src/App.js b/client/src/App.js index 733b6dc..5256ad9 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -59,6 +59,7 @@ function App() { } /> } /> } /> + } /> } /> } /> diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index e69de29..b75f0d4 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'; + +const Analytics = () => { + const data = [ + { + date: 0, + totalOrders: 10, + guestsServed: 50, + averageSpending: 25, + }, + ]; + + return ( + + + + + + + + + + + ); +}; + +export default Analytics; diff --git a/client/src/pages/admin-analytics.js b/client/src/pages/admin-analytics.js index 79fb6a9..922aede 100644 --- a/client/src/pages/admin-analytics.js +++ b/client/src/pages/admin-analytics.js @@ -1,6 +1,6 @@ import React from 'react' import AdminNavbar from '../components/adminNavbar' - +import Analytics from '../components/analyticsManager' const Admin_analytics = (props) => { const { WebSocketService, setPage } = props; @@ -11,7 +11,7 @@ const Admin_analytics = (props) => {
Analytics
-
{/* Component */}
+
diff --git a/client/src/pages/home.js b/client/src/pages/home.js index ace4594..3777ac6 100644 --- a/client/src/pages/home.js +++ b/client/src/pages/home.js @@ -10,6 +10,7 @@ const Home = () => { Login Menu Table + Analytics Admin dashboard From abf681722fd202e76bc2440f11ecde2e7516b777 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sat, 2 Dec 2023 13:51:11 -0800 Subject: [PATCH 03/10] organizing components --- client/src/components/analyticsManager.jsx | 88 ++++++++++++++++------ 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index b75f0d4..ea21b78 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -1,28 +1,72 @@ -import React from 'react'; +import React, {useState} from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'; const Analytics = () => { - const data = [ - { - date: 0, - totalOrders: 10, - guestsServed: 50, - averageSpending: 25, - }, - ]; - - return ( - - - - - - - - - - - ); + + const [totalSales, setTotalSales] = useState(175000); + const [orderHistory, setOrderHistory] = useState([ + { + _id: '1', + menuItemId: 'menu-item-1', + quantity: 2, + custom: ['extra-cheese'], + price: 20, + createdAt: '2023-01-01T12:30:00.000Z', + }, + { + _id: '2', + menuItemId: 'menu-item-3', + quantity: 1, + custom: ['no-onions'], + price: 15, + createdAt: '2023-01-02T18:45:00.000Z', + }, + { + _id: '3', + menuItemId: 'menu-item-2', + quantity: 3, + custom: ['spicy-sauce', 'extra-veggies'], + price: 30, + createdAt: '2023-01-03T15:20:00.000Z', + }, + ]); + + //get total orders + const totalOrders = orderHistory.length; + + + return ( + +
+ + {/* show total orders */} +
+

Total Orders

+ +
+
hi
+ +

{totalOrders}

+
+ + {/* show total sales price */} + {/* show total guest served, get from table schema and check seats occuiped and count seats avail */} + {/* show avg custoemr spending, get thorugh tables items, check orderItems and count price and quantity */} + {/* figure out filter? */} +
+ + + + + + + + + + +
+ + ); }; export default Analytics; From badb03ad6211e530605a7bffc4e2a5ee515811ac Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sat, 2 Dec 2023 14:04:15 -0800 Subject: [PATCH 04/10] adding mockdata --- client/src/components/analyticsManager.jsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index ea21b78..02d5c85 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -31,6 +31,22 @@ const Analytics = () => { }, ]); + const [tableHistory, setTableHistory] = useState([ + + { + seatCapacity: + isOccupied: + order: + + }, + { + seatCapacity: + isOccupied: + order: + + }, + ]); + //get total orders const totalOrders = orderHistory.length; From d0b442632c3b5563d354bfcd44589a7362062285 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sat, 2 Dec 2023 14:11:38 -0800 Subject: [PATCH 05/10] working on figuring out schema --- client/src/components/analyticsManager.jsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index 02d5c85..bf69f97 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -47,6 +47,17 @@ const Analytics = () => { }, ]); + const [orderItemHistory, setOrderItemHistory] = useState([ + + { + menuItemId: + quantity: + price: + status: + + }, + ]); + //get total orders const totalOrders = orderHistory.length; From 37d6cda71dd669b4039c69f10fb167355ac7189c Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sun, 3 Dec 2023 12:21:24 -0800 Subject: [PATCH 06/10] got orderhistory to work and now fixing ui --- client/src/components/analyticsCard.jsx | 21 + client/src/components/analyticsManager.jsx | 437 +++++++++++++++++---- client/tailwind.config.js | 5 + 3 files changed, 391 insertions(+), 72 deletions(-) create mode 100644 client/src/components/analyticsCard.jsx diff --git a/client/src/components/analyticsCard.jsx b/client/src/components/analyticsCard.jsx new file mode 100644 index 0000000..53d24ec --- /dev/null +++ b/client/src/components/analyticsCard.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +const AnalyticsCard = ({description, icon, stat}) => { + + return ( +
+

{description}

+ +
+
{icon}
+ +

{stat}

+
+ + +
+ + ) +} + +export default AnalyticsCard; \ No newline at end of file diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index bf69f97..dccc73c 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -1,99 +1,392 @@ -import React, {useState} from 'react'; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'; +import React, { useState } from 'react'; +//import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'; +import { PieChart, Pie, Tooltip, Legend, Cell } from 'recharts'; +import { BarChart, Bar, XAxis, YAxis} from 'recharts'; +import AnalyticsCard from './analyticsCard' +import { MdFastfood } from "react-icons/md"; +import { MdAttachMoney } from "react-icons/md"; +import { MdPeopleAlt } from "react-icons/md"; const Analytics = () => { - const [totalSales, setTotalSales] = useState(175000); - const [orderHistory, setOrderHistory] = useState([ + const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#AF19FF', '#FF4D4F', '#36CFC9']; + + // const [selectedTimePeriod, setSelectedTimePeriod] = useState('all'); + + // const handleTimePeriodChange = (newTimePeriod) => { + // setSelectedTimePeriod(newTimePeriod); + // }; + //mock data for fake restaurant containing menu , tables, and history + const [restaurantData, setRestaurantData] = useState({ + restaurantName: 'Your Restaurant', + restaurantMenu: { + totalItemCount: 2, + menuList: [ { - _id: '1', - menuItemId: 'menu-item-1', - quantity: 2, - custom: ['extra-cheese'], - price: 20, - createdAt: '2023-01-01T12:30:00.000Z', + menuId: '1', + image: 'menu-item-1.jpg', + filter: ['filter1', 'filter2'], + name: 'Menu Item 1', + price: 10, + description: 'Description for Menu Item 1', + diet: ['vegan'], + customizable: true, + custom: [ + { + name: 'Custom Option 1', + multipleSelection: true, + option: [ + { customName: 'Option A', price: 2 }, + { customName: 'Option B', price: 3 }, + ], + }, + ], }, { - _id: '2', - menuItemId: 'menu-item-3', - quantity: 1, - custom: ['no-onions'], - price: 15, - createdAt: '2023-01-02T18:45:00.000Z', + menuId: '2', + image: 'menu-item-2.jpg', + filter: ['filter1', 'filter3'], + name: 'Menu Item 2', + price: 15, + description: 'Description for Menu Item 2', + diet: ['vegetarian'], + customizable: false, + custom: [], + }, + { + menuId: '3', + image: 'menu-item-3.jpg', + filter: ['filter1', 'filter3'], + name: 'Menu Item 3', + price: 15, + description: 'Description for Menu Item 3', + diet: ['vegan'], + customizable: false, + custom: [], }, + ], + }, + table: { + totalTableCount: 5, + tableList: [ { - _id: '3', - menuItemId: 'menu-item-2', + seatCapacity: 4, + isOccupied: false, + order: [], + }, + + ], + }, + history: [ + { + userId: 'user1', + userHistory: [ + { + menuItemId: '1', + quantity: 2, + custom: [], + price: 20, + createdAt: new Date('2023-01-02T18:45:00'), + }, + { + menuItemId: '2', + quantity: 1, + custom: ['extra cheese'], + price: 15, + createdAt: new Date('2023-01-02T18:45:00'), + }, + ], + }, + { + userId: 'user2', + userHistory: [ + { + menuItemId: '1', quantity: 3, - custom: ['spicy-sauce', 'extra-veggies'], + custom: ['extra sauce'], price: 30, - createdAt: '2023-01-03T15:20:00.000Z', - }, - ]); + createdAt: new Date('2023-12-12T18:45:00'), + }, + { + menuItemId: '3', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-12-12T18:45:00'), + }, + // Add more order histories as needed + ], + }, + { + userId: 'user3', + userHistory: [ + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T12:45:00'), + }, + { + menuItemId: '3', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T12:45:00'), + }, + // Add more order histories as needed + ], + }, + { + userId: 'user4', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + // Add more order histories as needed + ], + }, + { + userId: 'user5', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + // Add more order histories as needed + ], + }, + // Add more user data as needed + ], + }); - const [tableHistory, setTableHistory] = useState([ - { - seatCapacity: - isOccupied: - order: - }, - { - seatCapacity: - isOccupied: - order: + //total orders across all users + const calculateTotalOrders = () => { + if (!restaurantData || !restaurantData.history) { + return 0; + } - }, - ]); + return restaurantData.history.reduce( + (total, history) => total + (history.userHistory ? history.userHistory.length : 0), + 0 + ); + }; - const [orderItemHistory, setOrderItemHistory] = useState([ + const calculateTotalSales = () => { + if (!restaurantData || !restaurantData.history) { + return 0; + } - { - menuItemId: - quantity: - price: - status: + return restaurantData.history.reduce((totalSales, history) => { + if (history.userHistory) { + history.userHistory.forEach(order => { + totalSales += order.price || 0; + }); + } + return totalSales; + }, 0); - }, - ]); + } + + + + const calculateMenuItemCounts = () => { + if (!restaurantData || !restaurantData.history) { + return []; + } + + //keep count in dictionary here of all items + const itemCounts = {}; + + restaurantData.history.forEach((history) => { + if (history.userHistory) { + history.userHistory.forEach((order) => { + const menuItemId = order.menuItemId; + itemCounts[menuItemId] = (itemCounts[menuItemId] || 0) + 1; + }); + } + }); + + // turn the itemCounts object to an array + return Object.keys(itemCounts).map((menuItemId) => ({ + menuItemId, + count: itemCounts[menuItemId], + })); + }; + + const calculatePopularTimes = () => { + if (!restaurantData || !restaurantData.history) { + return []; + } + + const userCounts = {}; + + restaurantData.history.forEach((history) => { + if (history.userHistory) { + history.userHistory.forEach((order) => { + if (order.createdAt) { + const hour = new Date(order.createdAt).getHours(); + const timeKey = `${hour}:00`; + + if (!userCounts[timeKey]) { + userCounts[timeKey] = new Set(); + } + + userCounts[timeKey].add(history.userId); + } + }); + } + }); + + // Convert the userCounts object to an array of objects + return Object.keys(userCounts).map((timeKey) => ({ + hour: timeKey, + count: userCounts[timeKey].size, + })); + + }; + + const calculateTotalCustomers = () => { + if (!restaurantData || !restaurantData.history) { + return 0; + } + + + const uniqueUserIds = new Set(); - //get total orders - const totalOrders = orderHistory.length; + restaurantData.history.forEach((history) => { + if (history.userId) { + uniqueUserIds.add(history.userId); + } + }); + return uniqueUserIds.size; + }; - return ( + //for popular items + const menuItemCounts = calculateMenuItemCounts(); + + const totalOrders = calculateTotalOrders(); + + const totalSales = calculateTotalSales(); + + const totalCustomers = calculateTotalCustomers(); + + const popularTimesData = calculatePopularTimes(); + + return ( +
+ +
+ } stat={totalOrders}/> + } stat={totalSales}/> + } stat={totalCustomers}/> + - {/* show total orders */} -
-

Total Orders

- -
-
hi
- -

{totalOrders}

-
- - {/* show total sales price */} - {/* show total guest served, get from table schema and check seats occuiped and count seats avail */} - {/* show avg custoemr spending, get thorugh tables items, check orderItems and count price and quantity */} - {/* figure out filter? */} -
- - - - - - - - - -
- - ); + + +
+

Popular menu items

+ + + {menuItemCounts.map((entry, index) => ( + + ))} + +
Total: {payload[0]?.payload.menuItemId}
} /> + +
+ + + +
+
+

Popular times

+ + { + const originalHour = parseInt(value, 10); + const formattedHour = originalHour % 12 || 12; // Convert to 12-hour format + const ampm = originalHour >= 12 ? 'PM' : 'AM'; + return `${formattedHour} ${ampm}`; + }} + /> + + + + + +
+ + +
+ +
+ ); + }; export default Analytics; diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 574cfcc..576ca2d 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -6,6 +6,11 @@ module.exports = { fontFamily: { montserrat: ['Montserrat', 'sans'],//Define Montserrat font }, + fontSize: { + '4xl': '2.5rem', + '5xl': '3rem', + + }, From 2edb5d3939a52d31cbf4b8e019c87bab5a50fb35 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sun, 3 Dec 2023 14:03:48 -0800 Subject: [PATCH 07/10] working on figuring out layout --- client/src/components/analyticsCard.jsx | 4 +- client/src/components/analyticsManager.jsx | 214 ++++++++++++++++----- 2 files changed, 165 insertions(+), 53 deletions(-) diff --git a/client/src/components/analyticsCard.jsx b/client/src/components/analyticsCard.jsx index 53d24ec..90b948b 100644 --- a/client/src/components/analyticsCard.jsx +++ b/client/src/components/analyticsCard.jsx @@ -3,10 +3,10 @@ import React from 'react'; const AnalyticsCard = ({description, icon, stat}) => { return ( -
+

{description}

-
+
{icon}

{stat}

diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index dccc73c..a5a4b86 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -7,6 +7,7 @@ import AnalyticsCard from './analyticsCard' import { MdFastfood } from "react-icons/md"; import { MdAttachMoney } from "react-icons/md"; import { MdPeopleAlt } from "react-icons/md"; +import { FaMoneyBillWave } from "react-icons/fa"; const Analytics = () => { const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#AF19FF', '#FF4D4F', '#36CFC9']; @@ -16,7 +17,11 @@ const Analytics = () => { // const handleTimePeriodChange = (newTimePeriod) => { // setSelectedTimePeriod(newTimePeriod); // }; + + //mock data for fake restaurant containing menu , tables, and history + + const [restaurantData, setRestaurantData] = useState({ restaurantName: 'Your Restaurant', restaurantMenu: { @@ -205,12 +210,111 @@ const Analytics = () => { // Add more order histories as needed ], }, + { + userId: 'use6', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T10:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T10:45:00'), + }, + { + menuItemId: '3', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T10:45:00'), + }, + + // Add more order histories as needed + ], + }, + + { + userId: 'user7', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T13:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T13:45:00'), + }, + + + // Add more order histories as needed + ], + }, + + { + userId: 'user9', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T13:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T13:45:00'), + }, + + + // Add more order histories as needed + ], + }, + { + userId: 'user10', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T13:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T13:45:00'), + }, + + + // Add more order histories as needed + ], + }, // Add more user data as needed ], }); - + if (!restaurantData) { + + return

Loading...

; + } //total orders across all users const calculateTotalOrders = () => { if (!restaurantData || !restaurantData.history) { @@ -323,67 +427,75 @@ const Analytics = () => { const totalCustomers = calculateTotalCustomers(); + const avgCustomerSpending = totalCustomers > 0 ? (totalSales / totalCustomers).toFixed(2) : 0; + const popularTimesData = calculatePopularTimes(); + const sortedPopularTimesData = popularTimesData.sort((a, b) => b.count - a.count); return (
-
-
- } stat={totalOrders}/> - } stat={totalSales}/> - } stat={totalCustomers}/> - - -
- - -
-

Popular menu items

- - - {menuItemCounts.map((entry, index) => ( - - ))} - -
Total: {payload[0]?.payload.menuItemId}
} /> - -
+
+
+ +
+ } stat={totalOrders}/> + } stat={totalSales}/> + } stat={totalCustomers}/> + } stat={avgCustomerSpending}/> + + +
+ + + +
+
+

Popular menu items

+ + + {menuItemCounts.map((entry, index) => ( + + ))} + +
Total: {payload[0]?.payload.menuItemId}
} /> + +
+
+
+
+ +
+

Most to Least Popular Times

+ + { + const originalHour = parseInt(value, 10); + const formattedHour = originalHour % 12 || 12; // Convert to 12-hour format + const ampm = originalHour >= 12 ? 'PM' : 'AM'; + return `${formattedHour} ${ampm}`; + }} + /> + + + +
-
-

Popular times

- - { - const originalHour = parseInt(value, 10); - const formattedHour = originalHour % 12 || 12; // Convert to 12-hour format - const ampm = originalHour >= 12 ? 'PM' : 'AM'; - return `${formattedHour} ${ampm}`; - }} - /> - - - - - -
- - -
+
); From bed870aa2e239bf7264a19cc48eaafd200b38da7 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sun, 3 Dec 2023 16:15:52 -0800 Subject: [PATCH 08/10] fixing labels --- client/src/components/analyticsCard.jsx | 4 +- client/src/components/analyticsManager.jsx | 98 ++++++++++++++++++---- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/client/src/components/analyticsCard.jsx b/client/src/components/analyticsCard.jsx index 90b948b..674802e 100644 --- a/client/src/components/analyticsCard.jsx +++ b/client/src/components/analyticsCard.jsx @@ -3,11 +3,11 @@ import React from 'react'; const AnalyticsCard = ({description, icon, stat}) => { return ( -
+

{description}

-
{icon}
+
{icon}

{stat}

diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index a5a4b86..339744b 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -5,9 +5,10 @@ import { PieChart, Pie, Tooltip, Legend, Cell } from 'recharts'; import { BarChart, Bar, XAxis, YAxis} from 'recharts'; import AnalyticsCard from './analyticsCard' import { MdFastfood } from "react-icons/md"; -import { MdAttachMoney } from "react-icons/md"; +import { FiDollarSign } from "react-icons/fi"; import { MdPeopleAlt } from "react-icons/md"; import { FaMoneyBillWave } from "react-icons/fa"; +import { TbArrowZigZag } from "react-icons/tb"; const Analytics = () => { const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#AF19FF', '#FF4D4F', '#36CFC9']; @@ -303,6 +304,50 @@ const Analytics = () => { }, + // Add more order histories as needed + ], + }, + { + userId: 'user11', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-03T13:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-03T13:45:00'), + }, + + + // Add more order histories as needed + ], + }, + { + userId: 'user12', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-05-03T13:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-05-03T13:45:00'), + }, + + // Add more order histories as needed ], }, @@ -357,15 +402,24 @@ const Analytics = () => { if (history.userHistory) { history.userHistory.forEach((order) => { const menuItemId = order.menuItemId; - itemCounts[menuItemId] = (itemCounts[menuItemId] || 0) + 1; + const menuItem = restaurantData.restaurantMenu.menuList.find((item) => item.menuId === menuItemId); + + if (menuItem) { + const menuItemName = menuItem.name; + itemCounts[menuItemId] = { + count: (itemCounts[menuItemId]?.count || 0) + 1, + name: menuItemName, + }; + } }); } }); - - // turn the itemCounts object to an array + + // Turn the itemCounts object to an array return Object.keys(itemCounts).map((menuItemId) => ({ menuItemId, - count: itemCounts[menuItemId], + name: itemCounts[menuItemId].name, + count: itemCounts[menuItemId].count, })); }; @@ -423,7 +477,7 @@ const Analytics = () => { const totalOrders = calculateTotalOrders(); - const totalSales = calculateTotalSales(); + const totalSales = calculateTotalSales().toLocaleString(); const totalCustomers = calculateTotalCustomers(); @@ -437,13 +491,13 @@ const Analytics = () => {
-
+
} stat={totalOrders}/> - } stat={totalSales}/> + } stat={totalSales}/> } stat={totalCustomers}/> - } stat={avgCustomerSpending}/> + } stat={avgCustomerSpending}/>
@@ -451,16 +505,16 @@ const Analytics = () => {
-
-

Popular menu items

- +
+

Popular Menu Items

+ @@ -468,8 +522,15 @@ const Analytics = () => { ))} -
Total: {payload[0]?.payload.menuItemId}
} /> - + ( +
+

{`Menu Item: ${payload[0]?.payload.name}`}

+

{`Total Orders: ${payload[0]?.payload.count}`}

+
+ )} + /> +
@@ -477,9 +538,9 @@ const Analytics = () => { -
-

Most to Least Popular Times

- +
+

Most to Least Popular Times

+ { const originalHour = parseInt(value, 10); @@ -488,6 +549,7 @@ const Analytics = () => { return `${formattedHour} ${ampm}`; }} /> + From cd8ecc371e1ca7bc6b50ba7148021dbbaaa382d3 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Sun, 3 Dec 2023 19:56:09 -0800 Subject: [PATCH 09/10] finished layout and adding more stats --- client/src/components/analyticsCard.jsx | 2 +- client/src/components/analyticsManager.jsx | 181 ++++++++++++++++----- 2 files changed, 141 insertions(+), 42 deletions(-) diff --git a/client/src/components/analyticsCard.jsx b/client/src/components/analyticsCard.jsx index 674802e..fd47776 100644 --- a/client/src/components/analyticsCard.jsx +++ b/client/src/components/analyticsCard.jsx @@ -3,7 +3,7 @@ import React from 'react'; const AnalyticsCard = ({description, icon, stat}) => { return ( -
+

{description}

diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index 339744b..faedaba 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; - +import { IoMdRestaurant } from "react-icons/io"; //import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'; import { PieChart, Pie, Tooltip, Legend, Cell } from 'recharts'; import { BarChart, Bar, XAxis, YAxis} from 'recharts'; @@ -13,13 +13,6 @@ const Analytics = () => { const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#AF19FF', '#FF4D4F', '#36CFC9']; - // const [selectedTimePeriod, setSelectedTimePeriod] = useState('all'); - - // const handleTimePeriodChange = (newTimePeriod) => { - // setSelectedTimePeriod(newTimePeriod); - // }; - - //mock data for fake restaurant containing menu , tables, and history @@ -315,14 +308,14 @@ const Analytics = () => { quantity: 3, custom: ['extra sauce'], price: 30, - createdAt: new Date('2021-01-03T13:45:00'), + createdAt: new Date('2021-01-03T20:45:00'), }, { menuItemId: '1', quantity: 3, custom: ['extra sauce'], price: 30, - createdAt: new Date('2023-01-03T13:45:00'), + createdAt: new Date('2023-01-03T20:45:00'), }, @@ -337,14 +330,36 @@ const Analytics = () => { quantity: 3, custom: ['extra sauce'], price: 30, - createdAt: new Date('2021-05-03T13:45:00'), + createdAt: new Date('2021-05-03T11:45:00'), }, { menuItemId: '1', quantity: 3, custom: ['extra sauce'], price: 30, - createdAt: new Date('2023-05-03T13:45:00'), + createdAt: new Date('2023-05-03T11:45:00'), + }, + + + // Add more order histories as needed + ], + }, + { + userId: 'user13', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-05-03T09:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-05-03T09:45:00'), }, @@ -372,6 +387,7 @@ const Analytics = () => { ); }; + // get count of all item order price const calculateTotalSales = () => { if (!restaurantData || !restaurantData.history) { return 0; @@ -390,6 +406,7 @@ const Analytics = () => { + //get the count of each menu item to display on pie chart to visualize most popular items const calculateMenuItemCounts = () => { if (!restaurantData || !restaurantData.history) { return []; @@ -472,51 +489,109 @@ const Analytics = () => { return uniqueUserIds.size; }; + + //get the min and max range of menu item price + const calculatePriceRange = (menuList) => { + if (!menuList || menuList.length === 0) { + return null; + } + + + const prices = menuList.map(item => item.price); + + // Find the minimum and maximum prices + const minPrice = Math.min(...prices); + const maxPrice = Math.max(...prices); + + + return { minPrice, maxPrice }; + }; + + const priceRange = calculatePriceRange(restaurantData.restaurantMenu.menuList); + + //for popular items const menuItemCounts = calculateMenuItemCounts(); + //count the orders const totalOrders = calculateTotalOrders(); + //total revenue from items only const totalSales = calculateTotalSales().toLocaleString(); + //count users from history const totalCustomers = calculateTotalCustomers(); + //get avg spending of custoemr total const avgCustomerSpending = totalCustomers > 0 ? (totalSales / totalCustomers).toFixed(2) : 0; + //get times that people come in on based on order time const popularTimesData = calculatePopularTimes(); const sortedPopularTimesData = popularTimesData.sort((a, b) => b.count - a.count); return ( -
+
-
+
+
+
+ +

{restaurantData.restaurantName}

+ +
+ +
+
+ {restaurantData.table.totalTableCount} +
+ tables + +
+
+
+ {restaurantData.restaurantMenu.totalItemCount} +
+ items on menu + +
+
+
+ {priceRange && `$${priceRange.minPrice}-${priceRange.maxPrice}`} +
+ menu price range + +
-
- } stat={totalOrders}/> - } stat={totalSales}/> - } stat={totalCustomers}/> - } stat={avgCustomerSpending}/> + + + + +
+
+ } stat={totalOrders}/> + } stat={totalSales}/> + -
+
-
-
+ +

Popular Menu Items

- + `${(percent * 100).toFixed(0)}%`} > {menuItemCounts.map((entry, index) => ( @@ -530,31 +605,55 @@ const Analytics = () => {
)} /> + +
-
+ -
-

Most to Least Popular Times

- - { - const originalHour = parseInt(value, 10); - const formattedHour = originalHour % 12 || 12; // Convert to 12-hour format - const ampm = originalHour >= 12 ? 'PM' : 'AM'; - return `${formattedHour} ${ampm}`; - }} - /> - - - +
+
+

Most to Least Popular Times

+ + { + const originalHour = parseInt(value, 10); + const formattedHour = originalHour % 12 || 12; // Convert to 12-hour format + const ampm = originalHour >= 12 ? 'PM' : 'AM'; + return `${formattedHour} ${ampm}`; + }} + /> + + + -
+ + +
+
+ } stat={totalCustomers}/> + } stat={avgCustomerSpending}/> + + + +
+ + + +
+
From cb7b8d1f1cc3449cece84cea012de729304d02a3 Mon Sep 17 00:00:00 2001 From: ipsitabisht Date: Mon, 4 Dec 2023 11:32:19 -0800 Subject: [PATCH 10/10] added comments and fixed label --- client/src/components/admin-dashboard.jsx | 306 +++++++++++++++++++++ client/src/components/analyticsManager.jsx | 66 +++-- 2 files changed, 343 insertions(+), 29 deletions(-) create mode 100644 client/src/components/admin-dashboard.jsx diff --git a/client/src/components/admin-dashboard.jsx b/client/src/components/admin-dashboard.jsx new file mode 100644 index 0000000..6960db6 --- /dev/null +++ b/client/src/components/admin-dashboard.jsx @@ -0,0 +1,306 @@ +import React, {useState, useEffect} from 'react'; +import Orders from './orderManager.jsx' +import DashInfoCard from './dashboardInfoCard'; +import { FaTag } from "react-icons/fa6"; +import { BsArrowRepeat } from "react-icons/bs"; +import { FcOk } from "react-icons/fc"; +import { FaStar } from "react-icons/fa"; +import { MdTableRestaurant } from "react-icons/md"; + +const Dashboard = () => { + + // const [sales, setSales] = useState(172845); + //for the incomplete/open orders + const [pending, setPending] = useState(0); + //for the completed / processed orders; + const [completed, setCompleted] = useState(0); + const [rating, setRatings] = useState(4); + // const [tablesAvailable, setTablesAvailable] = useState(8); + + //mock restaurant data for getting user and order history + const [restaurantData, setRestaurantData] = useState({ + restaurantName: 'Your Restaurant', + restaurantMenu: { + totalItemCount: 2, + menuList: [ + { + menuId: '1', + image: 'menu-item-1.jpg', + filter: ['filter1', 'filter2'], + name: 'Menu Item 1', + price: 10, + description: 'Description for Menu Item 1', + diet: ['vegan'], + customizable: true, + custom: [ + { + name: 'Custom Option 1', + multipleSelection: true, + option: [ + { customName: 'Option A', price: 2 }, + { customName: 'Option B', price: 3 }, + ], + }, + ], + }, + { + menuId: '2', + image: 'menu-item-2.jpg', + filter: ['filter1', 'filter3'], + name: 'Menu Item 2', + price: 15, + description: 'Description for Menu Item 2', + diet: ['vegetarian'], + customizable: false, + custom: [], + }, + { + menuId: '3', + image: 'menu-item-3.jpg', + filter: ['filter1', 'filter3'], + name: 'Menu Item 3', + price: 15, + description: 'Description for Menu Item 3', + diet: ['vegan'], + customizable: false, + custom: [], + }, + ], + }, + table: { + totalTableCount: 5, + tableList: [ + { + seatCapacity: 4, + isOccupied: false, + order: [ { + menuItemId: 1, + quantity: 2, + custom: [], + price: 20, + status: 'completed', + + }, + + ], + }, + { + seatCapacity: 4, + isOccupied: true, + order: [ { + menuItemId: 1, + quantity: 2, + custom: [], + price: 20, + status: 'pending', + + }, + + ], + }, + + ], + }, + history: [ + { + userId: 'user1', + userHistory: [ + { + menuItemId: '1', + quantity: 2, + custom: [], + price: 20, + createdAt: new Date('2023-01-02T18:45:00'), + }, + { + menuItemId: '2', + quantity: 1, + custom: ['extra cheese'], + price: 15, + createdAt: new Date('2023-01-02T18:45:00'), + }, + ], + }, + { + userId: 'user2', + userHistory: [ + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-12-12T18:45:00'), + }, + { + menuItemId: '3', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-12-12T18:45:00'), + }, + + ], + }, + { + userId: 'user3', + userHistory: [ + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T12:45:00'), + }, + { + menuItemId: '3', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T12:45:00'), + }, + + ], + }, + { + userId: 'user4', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + + ], + }, + { + userId: 'user5', + userHistory: [ + { + menuItemId: '2', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2021-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + { + menuItemId: '1', + quantity: 3, + custom: ['extra sauce'], + price: 30, + createdAt: new Date('2023-01-02T14:45:00'), + }, + // add user + ], + }, + //add more history + + ], + }); + + //get the order status of tables checking if processed or pending + useEffect(() => { + // Calculate total processed orders based on restaurantData + const calculateOrderStats = () => { + if (!restaurantData || !restaurantData.table || !restaurantData.table.tableList) { + return; + } + + let totalCompletedOrder = 0; + let totalPendingOrder = 0 + restaurantData.table.tableList.forEach(table => { + if (table.order && table.order.length > 0) { + totalPendingOrder += table.order.filter(order => order.status === 'pending').length; + totalCompletedOrder += table.order.filter(order => order.status === 'completed').length; + } + }); + + setCompleted(totalCompletedOrder); + setPending(totalPendingOrder); + + }; + + + calculateOrderStats(); + }, [restaurantData]); + + //total sales from restaurnat data history + const calculateTotalSales = () => { + if (!restaurantData || !restaurantData.history) { + return 0; + } + + return restaurantData.history.reduce((totalSales, history) => { + if (history.userHistory) { + history.userHistory.forEach(order => { + totalSales += order.price || 0; + }); + } + return totalSales; + }, 0); + + } + + + + const totalSales = calculateTotalSales().toLocaleString(); + + + + return( +
+
+ +
+ } stats={totalSales}/> + } stats={completed}/> + } stats={pending}/> + } stats={rating}/> + {/* } stats={tablesAvailable}/> */} + + + +
+
+ +
+ ) +} + +export default Dashboard; diff --git a/client/src/components/analyticsManager.jsx b/client/src/components/analyticsManager.jsx index faedaba..f101cfb 100644 --- a/client/src/components/analyticsManager.jsx +++ b/client/src/components/analyticsManager.jsx @@ -11,11 +11,10 @@ import { FaMoneyBillWave } from "react-icons/fa"; import { TbArrowZigZag } from "react-icons/tb"; const Analytics = () => { + //colors for the pie chart const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#AF19FF', '#FF4D4F', '#36CFC9']; //mock data for fake restaurant containing menu , tables, and history - - const [restaurantData, setRestaurantData] = useState({ restaurantName: 'Your Restaurant', restaurantMenu: { @@ -371,11 +370,13 @@ const Analytics = () => { }); + //if restaurant data is not there if (!restaurantData) { return

Loading...

; } - //total orders across all users + + //total orders across all users in restaurant history const calculateTotalOrders = () => { if (!restaurantData || !restaurantData.history) { return 0; @@ -387,7 +388,7 @@ const Analytics = () => { ); }; - // get count of all item order price + // get total revenue of restaurant based on user order history const calculateTotalSales = () => { if (!restaurantData || !restaurantData.history) { return 0; @@ -415,12 +416,18 @@ const Analytics = () => { //keep count in dictionary here of all items const itemCounts = {}; + //go through restaurant history and each user's history restaurantData.history.forEach((history) => { + //check if user history exists if (history.userHistory) { + //go through each order in history history.userHistory.forEach((order) => { + // get the item id const menuItemId = order.menuItemId; + // find the item in list that matches id const menuItem = restaurantData.restaurantMenu.menuList.find((item) => item.menuId === menuItemId); + //if it exists, update count of item to keep track of total num orders of each item if (menuItem) { const menuItemName = menuItem.name; itemCounts[menuItemId] = { @@ -432,7 +439,7 @@ const Analytics = () => { } }); - // Turn the itemCounts object to an array + // Turn the itemCounts object to an array, this is for the pie chart data visualization return Object.keys(itemCounts).map((menuItemId) => ({ menuItemId, name: itemCounts[menuItemId].name, @@ -440,31 +447,41 @@ const Analytics = () => { })); }; + + //get the most popular times based on when users come to place order const calculatePopularTimes = () => { if (!restaurantData || !restaurantData.history) { return []; } + //get count of users at a time const userCounts = {}; + // go through history restaurantData.history.forEach((history) => { if (history.userHistory) { + //go through user history history.userHistory.forEach((order) => { + //check when order was created if (order.createdAt) { + //get the hour of the order time const hour = new Date(order.createdAt).getHours(); + // const timeKey = `${hour}:00`; + // if there is no existing user at that time, create set if (!userCounts[timeKey]) { userCounts[timeKey] = new Set(); } + //update the list of users at that time userCounts[timeKey].add(history.userId); } }); } }); - // Convert the userCounts object to an array of objects + // Convert the userCounts object to an array of objects, used for the bar chart return Object.keys(userCounts).map((timeKey) => ({ hour: timeKey, count: userCounts[timeKey].size, @@ -472,20 +489,24 @@ const Analytics = () => { }; + //Get total unique users const calculateTotalCustomers = () => { if (!restaurantData || !restaurantData.history) { return 0; } - + //set for keeping unique users id const uniqueUserIds = new Set(); + //go through history restaurantData.history.forEach((history) => { + // check if user exists, then add to set if (history.userId) { uniqueUserIds.add(history.userId); } }); + //return the size to show how many users there are return uniqueUserIds.size; }; @@ -496,7 +517,7 @@ const Analytics = () => { return null; } - + //map each item in the menuList to a price const prices = menuList.map(item => item.price); // Find the minimum and maximum prices @@ -507,6 +528,7 @@ const Analytics = () => { return { minPrice, maxPrice }; }; + //get the price range based on restaurants menu list const priceRange = calculatePriceRange(restaurantData.restaurantMenu.menuList); @@ -532,10 +554,11 @@ const Analytics = () => { return (
-
+ {/* Basic Restaurant Card to get the name, table count, menu count, and price range */}
+

{restaurantData.restaurantName}

@@ -563,13 +586,9 @@ const Analytics = () => { menu price range
- - - - -
+ {/* Stat card for total orders and total revenue */}
} stat={totalOrders}/> } stat={totalSales}/> @@ -577,9 +596,7 @@ const Analytics = () => {
- - - + {/* pie chart to show the items and visualize popularity of each item */}

Popular Menu Items

@@ -616,11 +633,9 @@ const Analytics = () => {
- - -
+ {/* Bar Graph to show the times that are most popular based on number of users that have come in at that time in restaurant history */}

Most to Least Popular Times

@@ -632,26 +647,19 @@ const Analytics = () => { return `${formattedHour} ${ampm}`; }} /> - - - -
+ {/* Stat cards for total users and avg spending of users */}
} stat={totalCustomers}/> } stat={avgCustomerSpending}/> - -
- - -