diff --git a/index.html b/index.html index e4b78ea..6010514 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,10 @@ - + - Vite + React + TS + CITC Equipment Tracker
diff --git a/package-lock.json b/package-lock.json index 5704692..733d993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,21 @@ "name": "equipmenttracker-frontend", "version": "0.0.0", "dependencies": { + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.17", + "@mui/styled-engine": "^5.14.17", + "@mui/styled-engine-sc": "^6.0.0-alpha.5", + "@reduxjs/toolkit": "^1.9.7", + "@tanstack/react-query": "^5.8.3", + "axios": "^1.6.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-redux": "^8.1.3", + "react-router-dom": "^6.18.0", + "react-toastify": "^9.1.3", + "reactjs-popup": "^2.0.6", + "styled-components": "^6.1.1" }, "devDependencies": { "@types/react": "^18.2.15", @@ -50,7 +63,6 @@ "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, "dependencies": { "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" @@ -185,7 +197,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, "dependencies": { "@babel/types": "^7.22.15" }, @@ -249,7 +260,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -258,7 +268,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -290,7 +299,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -342,6 +350,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -381,7 +400,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -391,6 +409,156 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", + "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -814,6 +982,40 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", + "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", + "dependencies": { + "@floating-ui/dom": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -895,6 +1097,296 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.23.tgz", + "integrity": "sha512-9L8SQUGAWtd/Qi7Qem26+oSSgpY7f2iQTuvcz/rsGpyZjSomMMO6lwYeQSA0CpWM7+aN7eGoSY/WV6wxJiIxXw==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@floating-ui/react-dom": "^2.0.2", + "@mui/types": "^7.2.8", + "@mui/utils": "^5.14.17", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/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/@mui/core-downloads-tracker": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.17.tgz", + "integrity": "sha512-eE0uxrpJAEL2ZXkeGLKg8HQDafsiXY+6eNpP4lcv3yIjFfGbU6Hj9/P7Adt8jpU+6JIhmxvILGj2r27pX+zdrQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + } + }, + "node_modules/@mui/icons-material": { + "version": "5.14.16", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.16.tgz", + "integrity": "sha512-wmOgslMEGvbHZjFLru8uH5E+pif/ciXAvKNw16q6joK6EWVWU5rDYWFknDaZhCvz8ZE/K8ZnJQ+lMG6GgHzXbg==", + "dependencies": { + "@babel/runtime": "^7.23.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.17.tgz", + "integrity": "sha512-+y0VeOLWfEA4Z98We/UH6KCo8+f2HLZDK45FY+sJf8kSojLy3VntadKtC/u0itqnXXb1Pr4wKB2tSIBW02zY4Q==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/base": "5.0.0-beta.23", + "@mui/core-downloads-tracker": "^5.14.17", + "@mui/system": "^5.14.17", + "@mui/types": "^7.2.8", + "@mui/utils": "^5.14.17", + "@types/react-transition-group": "^4.4.8", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/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/@mui/private-theming": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.17.tgz", + "integrity": "sha512-u4zxsCm9xmQrlhVPug+Ccrtsjv7o2+rehvrgHoh0siSguvVgVQq5O3Hh10+tp/KWQo2JR4/nCEwquSXgITS1+g==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.17", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.17.tgz", + "integrity": "sha512-AqpVjBEA7wnBvKPW168bNlqB6EN7HxTjLOY7oi275AzD/b1C7V0wqELy6NWoJb2yya5sRf7ENf4iNi3+T5cOgw==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine-sc": { + "version": "6.0.0-alpha.5", + "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-6.0.0-alpha.5.tgz", + "integrity": "sha512-LbhqyaAjjwMnJ5ws2ZRAnGoxnnDUawJ7DfYWhF8DbzNVqQWNG+XaNvf+xwPvhW60cQfmLNnYi45YjDIbFWew9A==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "csstype": "^3.1.2", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "styled-components": "^6.0.0" + } + }, + "node_modules/@mui/system": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.17.tgz", + "integrity": "sha512-Ccz3XlbCqka6DnbHfpL3o3TfOeWQPR+ewvNAgm8gnS9M0yVMmzzmY6z0w/C1eebb+7ZP7IoLUj9vojg/GBaTPg==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/private-theming": "^5.14.17", + "@mui/styled-engine": "^5.14.17", + "@mui/types": "^7.2.8", + "@mui/utils": "^5.14.17", + "clsx": "^2.0.0", + "csstype": "^3.1.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/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/@mui/types": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.8.tgz", + "integrity": "sha512-9u0ji+xspl96WPqvrYJF/iO+1tQ1L5GTaDOeG3vCR893yy7VcWwRNiVMmPdPNpMDqx0WV1wtEW9OMwK9acWJzQ==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.14.17", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.17.tgz", + "integrity": "sha512-yxnWgSS4J6DMFPw2Dof85yBkG02VTbEiqsikymMsnZnXDurtVGTIhlNuV24GTmFTuJMzEyTTU9UF+O7zaL8LEQ==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@types/prop-types": "^15.7.9", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -930,6 +1422,80 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", + "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@remix-run/router": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz", + "integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.8.3.tgz", + "integrity": "sha512-SWFMFtcHfttLYif6pevnnMYnBvxKf3C+MHMH7bevyYfpXpTMsLB9O6nNGBdWSoPwnZRXFNyNeVZOw25Wmdasow==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.8.3.tgz", + "integrity": "sha512-EDRrsMgUtKM+SjVmhDYBd4jwWWyHAw3kCaurKLIO90OfPQr/UhpwcqM2l/eQOaUYon9lfDB2ejQi1edHK7zEdA==", + "dependencies": { + "@tanstack/query-core": "5.8.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-native": "*" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/@types/babel__core": { "version": "7.20.3", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", @@ -971,23 +1537,35 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/prop-types": { "version": "15.7.9", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", - "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==", - "dev": true + "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" }, "node_modules/@types/react": { "version": "18.2.31", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.31.tgz", "integrity": "sha512-c2UnPv548q+5DFh03y8lEDeMfDwBn9G3dRwfkrxQMo/dOtRHUUO57k6pHvBIfH/VF4Nh+98mZ5aaSe+2echD5g==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -998,7 +1576,15 @@ "version": "18.2.14", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", - "dev": true, + "devOptional": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.9.tgz", + "integrity": "sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg==", "dependencies": { "@types/react": "*" } @@ -1006,8 +1592,7 @@ "node_modules/@types/scheduler": { "version": "0.16.5", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz", - "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==", - "dev": true + "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==" }, "node_modules/@types/semver": { "version": "7.5.4", @@ -1015,6 +1600,16 @@ "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, + "node_modules/@types/stylis": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.3.tgz", + "integrity": "sha512-86XLCVEmWagiUEbr2AjSbeY4qHN9jMm3pgM3PuBYfLIbT0MpDSnA3GA/4W7KoH/C/eeK77kNaeIxZzjhKYIBgw==" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", @@ -1279,7 +1874,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -1302,6 +1896,35 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1366,11 +1989,18 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001551", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", @@ -1395,7 +2025,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1405,11 +2034,18 @@ "node": ">=4" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -1417,8 +2053,18 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -1432,6 +2078,21 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1446,11 +2107,28 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -1475,6 +2153,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1499,12 +2185,29 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.563", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.563.tgz", "integrity": "sha512-dg5gj5qOgfZNkPNeyKBZQAQitIQ/xwfIDmEQJHCbXaD9ebTZxwJXUsDYcBlAvZGZLi+/354l35J1wkmP6CqYaw==", "dev": true }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/esbuild": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", @@ -1555,7 +2258,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -1899,6 +2601,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1935,6 +2642,38 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1955,6 +2694,14 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2035,11 +2782,34 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/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/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -2049,11 +2819,19 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2090,6 +2868,22 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2170,6 +2964,11 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2216,6 +3015,11 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2279,6 +3083,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2301,7 +3124,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true, "funding": [ { "type": "github", @@ -2327,6 +3149,14 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2387,7 +3217,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -2395,6 +3224,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2422,11 +3268,15 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } @@ -2434,8 +3284,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2453,7 +3302,6 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2477,6 +3325,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2486,6 +3339,26 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/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/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2538,6 +3411,49 @@ "react": "^18.2.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -2547,11 +3463,121 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.18.0.tgz", + "integrity": "sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==", + "dependencies": { + "@remix-run/router": "1.11.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.18.0.tgz", + "integrity": "sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==", + "dependencies": { + "@remix-run/router": "1.11.0", + "react-router": "6.18.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/reactjs-popup": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.6.tgz", + "integrity": "sha512-A+tt+x9wdgZiZjv0e2WzYLD3IfFwJALaRaqwrCSXGjo0iQdsry/EtBEbQXRSmQs7cHmOi5eytCiSlOm8k4C+dg==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -2661,6 +3687,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2691,11 +3722,18 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2724,11 +3762,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.1.tgz", + "integrity": "sha512-cpZZP5RrKRIClBW5Eby4JM1wElLVP4NQrJbJ0h10TidTyJf4SIIwa3zLXOoPb4gJi8MsJ8mjq5mu2IrEhZIAcQ==", + "dependencies": { + "@emotion/is-prop-valid": "^1.2.1", + "@emotion/unitless": "^0.8.0", + "@types/stylis": "^4.0.2", + "css-to-react-native": "^3.2.0", + "csstype": "^3.1.2", + "postcss": "^8.4.31", + "shallowequal": "^1.1.0", + "stylis": "^4.3.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -2736,6 +3810,17 @@ "node": ">=4" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2746,7 +3831,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -2775,6 +3859,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2851,6 +3940,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/vite": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", @@ -2933,6 +4030,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index d2ffc90..57b26fb 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,21 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.17", + "@mui/styled-engine": "^5.14.17", + "@mui/styled-engine-sc": "^6.0.0-alpha.5", + "@reduxjs/toolkit": "^1.9.7", + "@tanstack/react-query": "^5.8.3", + "axios": "^1.6.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-redux": "^8.1.3", + "react-router-dom": "^6.18.0", + "react-toastify": "^9.1.3", + "reactjs-popup": "^2.0.6", + "styled-components": "^6.1.1" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/src/App.css b/src/App.css index b9d355d..902778b 100644 --- a/src/App.css +++ b/src/App.css @@ -4,39 +4,3 @@ padding: 2rem; text-align: center; } - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.tsx b/src/App.tsx index afe48ac..013db34 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,118 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import LandingPage from "./Pages/LandingPage/LandingPage"; +import { createHashRouter, RouterProvider } from "react-router-dom"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { Provider } from "react-redux"; +import "./App.css"; +import store from "./Components/Plugins/Redux/Store/Store"; +import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import ErrorPage from "./Pages/ErrorPage/ErrorPage"; +import DashboardPage from "./Pages/DashboardPage/DashboardPage"; +import Revalidator from "./Components/Revalidator/Revalidator"; +import ActivationPage from "./Pages/ActivationPage/ActivationPage"; +import ResetPasswordPage from "./Pages/ResetPasswordPage/ResetPasswordPage"; +import EquipmentInstancesListPage from "./Pages/EquipmentInstancesListPage/EquipmentInstancesListPage"; +import EquipmentListPage from "./Pages/EquipmentListPage/EquipmentListPage"; +import EquipmentLogsPage from "./Pages/EquipmentLogsPage/EquipmentLogsPage"; +import EquipmentInstanceLogsPage from "./Pages/EquipmentInstanceLogsPage/EquipmentInstanceLogsPage"; -function App() { - const [count, setCount] = useState(0) +const queryClient = new QueryClient(); +const router = createHashRouter([ + { + path: "/", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/dashboard", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/view/equipment_instances", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/view/equipment_instances/logs", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/view/equipments", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/view/equipments/logs", + element: ( + <> + + + + ), + errorElement: , + }, + { + path: "/activation/:uid/:token", + element: ( + <> + + + ), + errorElement: , + }, + { + path: "/reset_password_confirm/:uid/:token", + element: ( + <> + + + ), + errorElement: , + }, +]); +export default function App() { return ( - <> - -

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) + + + + + + + ); } - -export default App diff --git a/src/Components/API/API.tsx b/src/Components/API/API.tsx new file mode 100644 index 0000000..d410b8e --- /dev/null +++ b/src/Components/API/API.tsx @@ -0,0 +1,342 @@ +/* eslint-disable react-refresh/only-export-components */ +import axios from "axios"; +import { + ActivationType, + EquipmentListType, + LoginType, + RegisterType, + ResetPasswordConfirmType, + EquipmentInstanceListType, + EquipmentType, + AddEquipmentType, + AddEquipmentInstanceType, + EquipmentInstanceType, + PatchEquipmentInstanceType, + PatchEquipmentType, + EquipmentLogListType, + EquipmentInstanceLogListType, +} from "../Types/Types"; + +const debug = false; +let backendURL; + +if (debug) { + backendURL = "http://localhost:8000/"; +} else { + backendURL = "https://equipment-tracker-backend.keannu1.duckdns.org/"; +} + +const instance = axios.create({ + baseURL: backendURL, +}); + +// Token Handling +export async function getAccessToken() { + const accessToken = await localStorage.getItem("access_token"); + return accessToken; +} + +export async function getRefreshToken() { + const refreshToken = await localStorage.getItem("refresh_token"); + return refreshToken; +} + +export async function setAccessToken(access: string) { + await localStorage.setItem("access_token", access); + return true; +} + +export async function setRefreshToken(refresh: string) { + await localStorage.setItem("refresh_token", refresh); + return true; +} + +// Header Config Template for REST +export async function GetConfig() { + const accessToken = await getAccessToken(); + return { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }; +} + +export function ParseError(error: { response: { data: string } }) { + if (error.response && error.response.data) { + if (error.response.data.length > 50) { + return "Error truncated (too long)"; + } + return JSON.stringify(error.response.data) + .replace(/[{}]/g, " ") + .replace(/\(/g, " ") + .replace(/\)/g, " ") + .replace(/"/g, " ") + .replace(/,/g, ",") + .replace(/\[/g, "") + .replace(/\]/g, "") + .replace(/\./g, "") + .replace(/non_field_errors/g, "") + .trim(); + } + return "Unable to reach server"; +} +// User APIs + +export function RegisterAPI(info: RegisterType) { + return instance + .post("api/v1/accounts/users/", info) + .then(async (response) => { + console.log(response.data); + return [true, 0]; + }) + .catch((error) => { + console.log("Registration failed"); + return [false, ParseError(error)]; + }); +} + +export function LoginAPI(user: LoginType, remember_session: boolean) { + return instance + .post("api/v1/accounts/jwt/create/", user) + .then(async (response) => { + console.log(response.data); + setAccessToken(response.data.access); + if (remember_session) { + setRefreshToken(response.data.refresh); + } + + console.log("Login Success"); + return true; + }) + .catch((error) => { + console.log("Login Failed", error.response.data); + return false; + }); +} + +export async function JWTRefreshAPI() { + const refresh = await getRefreshToken(); + return instance + .post("api/v1/accounts/jwt/refresh/", { + refresh: refresh, + }) + .then(async (response) => { + setAccessToken(response.data.access); + return true; + }) + .catch(() => { + console.log("Error refreshing token"); + return false; + }); +} + +export async function UserAPI() { + const config = await GetConfig(); + return instance + .get("api/v1/accounts/users/me/", config) + .then((response) => { + return response.data; + }) + .catch(() => { + console.log("Error retrieving user data"); + }); +} + +export function ActivationAPI(activation: ActivationType) { + return instance + .post("api/v1/accounts/users/activation/", activation) + .then(() => { + console.log("Activation Success"); + return true; + }) + .catch(() => { + console.log("Activation failed"); + return false; + }); +} +export function ResetPasswordAPI(email: string) { + return instance + .post("api/v1/accounts/users/reset_password/", { email: email }) + .then(() => { + console.log("Activation Success"); + return true; + }) + .catch(() => { + console.log("Activation failed"); + return false; + }); +} + +export function ResetPasswordConfirmAPI(info: ResetPasswordConfirmType) { + return instance + .post("api/v1/accounts/users/reset_password_confirm/", info) + .then(() => { + console.log("Reset Success"); + return true; + }) + .catch(() => { + console.log("Reset failed"); + return false; + }); +} + +// Equipment APIs + +export async function EquipmentAPI(id: number) { + const config = await GetConfig(); + return instance + .get(`api/v1/equipments/equipments/${id}/`, config) + .then((response) => { + return response.data as EquipmentType; + }) + .catch(() => { + console.log("Error retrieving equipment"); + }); +} + +export async function EquipmentUpdateAPI( + equipment: PatchEquipmentType, + id: number +) { + const config = await GetConfig(); + return instance + .patch(`api/v1/equipments/equipments/${id}/`, equipment, config) + .then((response) => { + return [true, response.data as EquipmentType]; + }) + .catch((error) => { + console.log("Error updating equipment instance"); + return [false, ParseError(error)]; + }); +} + +export async function EquipmentRemoveAPI(id: number) { + const config = await GetConfig(); + return instance + .delete(`api/v1/equipments/equipments/${id}/`, config) + .then((response) => { + return [true, response.data as EquipmentType]; + }) + .catch((error) => { + console.log("Error deleting equipment instance"); + return [false, ParseError(error)]; + }); +} + +export async function EquipmentsAPI() { + const config = await GetConfig(); + return instance + .get("api/v1/equipments/equipments/", config) + .then((response) => { + return response.data as EquipmentListType; + }) + .catch(() => { + console.log("Error retrieving equipments"); + }); +} + +export async function EquipmentCreateAPI(equipment: AddEquipmentType) { + const config = await GetConfig(); + return instance + .post("api/v1/equipments/equipments/", equipment, config) + .then((response) => { + return [true, response.data as EquipmentType]; + }) + .catch((error) => { + console.log("Error creating equipment"); + return [false, ParseError(error)]; + }); +} + +export async function EquipmentLogsAPI() { + const config = await GetConfig(); + return instance + .get("api/v1/equipments/equipments/logs", config) + .then((response) => { + return response.data as EquipmentLogListType; + }) + .catch(() => { + console.log("Error retrieving equipment logs"); + }); +} + +// Equipment Instances APIs + +export async function EquipmentInstanceLogsAPI() { + const config = await GetConfig(); + return instance + .get("api/v1/equipments/equipment_instances/logs", config) + .then((response) => { + return response.data as EquipmentInstanceLogListType; + }) + .catch(() => { + console.log("Error retrieving equipment logs"); + }); +} +export async function EquipmentInstanceAPI(id: number) { + const config = await GetConfig(); + return instance + .get(`api/v1/equipments/equipment_instances/${id}/`, config) + .then((response) => { + return response.data as EquipmentInstanceType; + }) + .catch(() => { + console.log("Error retrieving equipment"); + }); +} + +export async function EquipmentInstanceUpdateAPI( + item: PatchEquipmentInstanceType, + id: number +) { + const config = await GetConfig(); + return instance + .patch(`api/v1/equipments/equipment_instances/${id}/`, item, config) + .then((response) => { + return [true, response.data as EquipmentInstanceType]; + }) + .catch((error) => { + console.log("Error updating equipment instance"); + return [false, ParseError(error)]; + }); +} + +export async function EquipmentInstanceRemoveAPI(id: number) { + const config = await GetConfig(); + return instance + .delete(`api/v1/equipments/equipment_instances/${id}/`, config) + .then((response) => { + return [true, response.data]; + }) + .catch((error) => { + console.log("Error deleting equipment instance"); + return [false, ParseError(error)]; + }); +} + +export async function EquipmentInstancesAPI() { + const config = await GetConfig(); + return instance + .get("api/v1/equipments/equipment_instances/", config) + .then((response) => { + return response.data as EquipmentInstanceListType; + }) + .catch(() => { + console.log("Error retrieving equipments"); + }); +} + +export async function EquipmentInstanceCreateAPI( + equipment_instance: AddEquipmentInstanceType +) { + const config = await GetConfig(); + return instance + .post("api/v1/equipments/equipment_instances/", equipment_instance, config) + .then((response) => { + return [true, response.data as EquipmentInstanceType]; + }) + .catch((error) => { + console.log("Error creating equipment instance"); + return [false, ParseError(error)]; + }); +} diff --git a/src/Components/AddItemModal/AddItemModal.tsx b/src/Components/AddItemModal/AddItemModal.tsx new file mode 100644 index 0000000..76ea5b6 --- /dev/null +++ b/src/Components/AddItemModal/AddItemModal.tsx @@ -0,0 +1,235 @@ +import { useEffect, useState } from "react"; +import styles from "../../styles"; +import { colors } from "../../styles"; +import TextField from "@mui/material/TextField"; +import AddToQueueIcon from "@mui/icons-material/AddToQueue"; +import Button from "../Button/Button"; +import { toast } from "react-toastify"; +import { EquipmentInstanceCreateAPI, EquipmentsAPI } from "../API/API"; +import RadioGroup from "@mui/material/RadioGroup"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import FormControl from "@mui/material/FormControl"; +import FormLabel from "@mui/material/FormLabel"; +import Radio from "@mui/material/Radio"; +import { useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; +import { CircularProgress } from "@mui/material"; +import React from "react"; + +export default function AddItemModal() { + const queryClient = useQueryClient(); + const [item, setItem] = useState({ + equipment: 0, + remarks: "", + status: "WORKING", + }); + const [error, setError] = useState(""); + + const equipments = useQuery({ + queryKey: ["equipments"], + queryFn: EquipmentsAPI, + }); + + useEffect(() => { + if (equipments.data) { + setItem({ ...item, equipment: equipments.data[0].id }); + } + }, [equipments.data, item]); + if (equipments.isLoading) { + return ( +
+ +

+ Loading +

+
+ ); + } + return ( + <> +
+ +

Add Item

+
+ +
+ + + Select Associated SKU + + ) => { + setItem({ ...item, equipment: Number(e.target.value) }); + setError(""); + }} + > +
+ {equipments.data ? ( + equipments.data.map((equipment) => ( + + } + label={equipment.name} + style={styles.text_dark} + /> + + )) + ) : ( + <> + )} +
+
+
+ + + Item Status + + ) => { + setItem({ ...item, status: e.target.value }); + setError(""); + }} + > +
+ } + label="Working" + style={styles.text_dark} + /> + } + label="Broken" + style={styles.text_dark} + /> + } + label="Under Maintenance" + style={styles.text_dark} + /> + } + label="Decomissioned" + style={styles.text_dark} + /> +
+
+
+ ) => { + setItem({ ...item, remarks: e.target.value }); + setError(""); + }} + value={item.remarks} + placeholder={"Optionally add a brief description of the item"} + /> +
+

{error}

+
+ +
+ ); +} diff --git a/src/Components/Drawer/Drawer.tsx b/src/Components/Drawer/Drawer.tsx new file mode 100644 index 0000000..1360ac5 --- /dev/null +++ b/src/Components/Drawer/Drawer.tsx @@ -0,0 +1,110 @@ +import styles, { colors } from "../../styles"; +import AccountCircleIcon from "@mui/icons-material/AccountCircle"; +import HomeIcon from "@mui/icons-material/Home"; +import LogoutIcon from "@mui/icons-material/Logout"; +import { useQuery } from "@tanstack/react-query"; +import { UserAPI, setAccessToken, setRefreshToken } from "../API/API"; +import DrawerButton from "../DrawerButton/DrawerButton"; +import { useDispatch } from "react-redux"; +import { auth_toggle } from "../Plugins/Redux/Slices/AuthSlice/AuthSlice"; +import { toast } from "react-toastify"; +import { useNavigate } from "react-router-dom"; +export default function Drawer() { + const user = useQuery({ queryKey: ["user"], queryFn: UserAPI }); + const dispatch = useDispatch(); + const navigate = useNavigate(); + return ( +
+
+ +

+ {user.data + ? user.data.username + : user.isError + ? "Error loading user" + : "Loading user..."} +

+
+
+ { + navigate("/dashboard"); + }} + icon={ + + } + label={"Dashboard"} + /> + { + navigate("/"); + await dispatch(auth_toggle()); + await setAccessToken(""); + await setRefreshToken(""); + toast("Logged out", { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + }} + icon={ + + } + label={"Log out"} + /> +
+ ); +} diff --git a/src/Components/DrawerButton/DrawerButton.tsx b/src/Components/DrawerButton/DrawerButton.tsx new file mode 100644 index 0000000..22fbf44 --- /dev/null +++ b/src/Components/DrawerButton/DrawerButton.tsx @@ -0,0 +1,55 @@ +import React, { useState } from "react"; +import styles from "../../styles"; +import { colors } from "../../styles"; + +export interface props { + onClick: React.MouseEventHandler; + children?: React.ReactNode; + icon?: React.ReactNode; + label: string; +} +export default function DrawerButton(props: props) { + const [clicked, setClicked] = useState(false); + return ( +
+ +
+ ); +} diff --git a/src/Components/EditItemInstanceModal/EditItemInstanceModal.tsx b/src/Components/EditItemInstanceModal/EditItemInstanceModal.tsx new file mode 100644 index 0000000..9fcc0f4 --- /dev/null +++ b/src/Components/EditItemInstanceModal/EditItemInstanceModal.tsx @@ -0,0 +1,298 @@ +import { useEffect, useState } from "react"; +import styles from "../../styles"; +import { colors } from "../../styles"; +import TextField from "@mui/material/TextField"; +import EditIcon from "@mui/icons-material/Edit"; +import Button from "../Button/Button"; +import { toast } from "react-toastify"; +import { + EquipmentInstanceAPI, + EquipmentInstanceRemoveAPI, + EquipmentInstanceUpdateAPI, +} from "../API/API"; +import RadioGroup from "@mui/material/RadioGroup"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import FormControl from "@mui/material/FormControl"; +import FormLabel from "@mui/material/FormLabel"; +import Radio from "@mui/material/Radio"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; +import { CircularProgress } from "@mui/material"; +import React from "react"; + +export default function EditItemInstanceModal(props: { + id: number; + setOpen: React.Dispatch>; +}) { + const queryClient = useQueryClient(); + const [item, setItem] = useState({ + remarks: "", + status: "", + }); + const [error, setError] = useState(""); + + const equipment = useQuery({ + queryKey: ["equipment_instance", props.id], + queryFn: () => EquipmentInstanceAPI(Number(props.id)), + }); + + useEffect(() => { + if (equipment.data) { + setItem({ + ...item, + remarks: equipment.data.remarks, + status: equipment.data.status, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [equipment.data]); + + const update_mutation = useMutation({ + mutationFn: async () => { + const data = await EquipmentInstanceUpdateAPI(item, props.id); + if (data[0] != true) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["equipment_instances"] }); + queryClient.invalidateQueries({ + queryKey: ["equipment_instance", props.id], + }); + setError("Updated successfully"); + toast( + `Item updated successfuly, ${ + typeof data[1] == "object" ? "ID:" + data[1].id : "" + }`, + { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + } + ); + if (typeof data[1] == "object") { + setItem({ + ...item, + remarks: data[1].remarks, + status: data[1].status, + }); + } + }, + onError: (error) => { + setError(JSON.stringify(error)); + }, + }); + + const delete_mutation = useMutation({ + mutationFn: async () => { + const data = await EquipmentInstanceRemoveAPI(props.id); + if (data[0] != true) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["equipment_instances"] }); + queryClient.invalidateQueries({ + queryKey: ["equipment_instance", props.id], + }); + setError("Deleted successfully"); + toast("Item deleted successfuly", { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + props.setOpen(false); + if (typeof data[1] == "object") { + setItem({ + ...item, + remarks: data[1].remarks, + status: data[1].status, + }); + } + }, + onError: (error) => { + setError(JSON.stringify(error)); + }, + }); + + if (equipment.isLoading) { + return ( +
+ +

+ Loading +

+
+ ); + } + return ( + <> +
+ +

Edit Item

+
+ +
+ +
+

+ Associated SKU: +

+

+ {equipment.data?.equipment_name} + {" (SKU #" + equipment.data?.equipment + ")"} +

+
+ + + Item Status + + ) => { + setItem({ ...item, status: e.target.value }); + setError(""); + }} + > +
+ } + label="Working" + style={styles.text_dark} + /> + } + label="Broken" + style={styles.text_dark} + /> + } + label="Under Maintenance" + style={styles.text_dark} + /> + } + label="Decomissioned" + style={styles.text_dark} + /> +
+
+
+ ) => { + setItem({ ...item, remarks: e.target.value }); + setError(""); + }} + value={item.remarks} + placeholder={"Optionally add a brief description of the item"} + /> +
+

{error}

+
+
+
+ + ); +} diff --git a/src/Components/EditSKUModal/EditSKUModal.tsx b/src/Components/EditSKUModal/EditSKUModal.tsx new file mode 100644 index 0000000..35fed94 --- /dev/null +++ b/src/Components/EditSKUModal/EditSKUModal.tsx @@ -0,0 +1,300 @@ +import { useEffect, useState } from "react"; +import styles from "../../styles"; +import { colors } from "../../styles"; +import TextField from "@mui/material/TextField"; +import EditIcon from "@mui/icons-material/Edit"; +import Button from "../Button/Button"; +import { toast } from "react-toastify"; +import { + EquipmentAPI, + EquipmentRemoveAPI, + EquipmentUpdateAPI, +} from "../API/API"; +import RadioGroup from "@mui/material/RadioGroup"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import FormControl from "@mui/material/FormControl"; +import FormLabel from "@mui/material/FormLabel"; +import Radio from "@mui/material/Radio"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; +import { CircularProgress } from "@mui/material"; +import React from "react"; + +export default function EditSKUModal(props: { + id: number; + setOpen: React.Dispatch>; +}) { + const queryClient = useQueryClient(); + const [item, setItem] = useState({ + name: "", + description: "", + category: "", + }); + const [error, setError] = useState(""); + + const equipment = useQuery({ + queryKey: ["equipment", props.id], + queryFn: () => EquipmentAPI(Number(props.id)), + }); + + useEffect(() => { + if (equipment.data) { + setItem({ + ...item, + name: equipment.data.name, + description: equipment.data.description, + category: equipment.data.category, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [equipment.data]); + + const update_mutation = useMutation({ + mutationFn: async () => { + const data = await EquipmentUpdateAPI(item, props.id); + if (data[0] != true) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["equipments"] }); + queryClient.invalidateQueries({ + queryKey: ["equipment", props.id], + }); + setError("Updated successfully"); + toast( + `Item updated successfuly, ${ + typeof data[1] == "object" ? "ID:" + data[1].id : "" + }`, + { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + } + ); + if (typeof data[1] == "object") { + setItem({ + ...item, + name: data[1].name, + description: data[1].description, + category: data[1].category, + }); + } + }, + onError: (error) => { + setError(JSON.stringify(error)); + }, + }); + + const delete_mutation = useMutation({ + mutationFn: async () => { + const data = await EquipmentRemoveAPI(props.id); + if (data[0] != true) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["equipments"] }); + queryClient.invalidateQueries({ + queryKey: ["equipment", props.id], + }); + setError("Deleted successfully"); + toast("SKU deleted successfuly", { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + props.setOpen(false); + if (typeof data[1] == "object") { + setItem({ + ...item, + name: data[1].name, + description: data[1].description, + category: data[1].category, + }); + } + }, + onError: (error) => { + setError(JSON.stringify(error)); + }, + }); + + if (equipment.isLoading) { + return ( +
+ +

+ Loading +

+
+ ); + } + return ( + <> +
+ +

Edit SKU

+
+ +
+ + ) => { + setItem({ ...item, name: e.target.value }); + setError(""); + }} + value={item.name} + placeholder={"Enter SKU name"} + /> + ) => + setItem({ ...item, description: e.target.value }) + } + value={item.description} + placeholder={"Give a brief description of the SKU"} + /> + + Category + + ) => { + setItem({ ...item, category: e.target.value }); + setError(""); + }} + value={item.category} + > +
+
+ } + label="Workstation" + style={styles.text_dark} + /> + } + label="Networking" + style={styles.text_dark} + /> + } + label="CCTV" + style={styles.text_dark} + /> +
+
+ } + label="Furniture" + style={styles.text_dark} + /> + } + label="Peripherals" + style={styles.text_dark} + /> + } + label="Miscellaneous" + style={styles.text_dark} + /> +
+
+
+
+
+

{error}

+
+
+
+ + ); +} diff --git a/src/Components/Header/Header.tsx b/src/Components/Header/Header.tsx new file mode 100644 index 0000000..739477a --- /dev/null +++ b/src/Components/Header/Header.tsx @@ -0,0 +1,50 @@ +import { useState } from "react"; +import styles, { colors } from "../../styles"; +import MenuIcon from "@mui/icons-material/Menu"; +import SidebarModal from "../Drawer/Drawer"; +import { Drawer } from "@mui/material"; +export interface props { + label: string; +} + +export default function Header(props: props) { + const [SidebarOpen, SetSidebarOpen] = useState(false); + return ( +
+
+ { + SetSidebarOpen(true); + }} + /> +
+

+ {props.label} +

+
+ SetSidebarOpen(false)}> + + +
+ ); +} diff --git a/src/Components/LoginModal/LoginModal.tsx b/src/Components/LoginModal/LoginModal.tsx new file mode 100644 index 0000000..fb6a5c0 --- /dev/null +++ b/src/Components/LoginModal/LoginModal.tsx @@ -0,0 +1,145 @@ +import { useState } from "react"; +import styles from "../../styles"; +import { colors } from "../../styles"; +import TextField from "@mui/material/TextField"; +import InputAdornment from "@mui/material/InputAdornment"; +import IconButton from "@mui/material/IconButton"; +import Visibility from "@mui/icons-material/Visibility"; +import VisibilityOff from "@mui/icons-material/VisibilityOff"; +import LoginIcon from "@mui/icons-material/Login"; +import Checkbox from "@mui/material/Checkbox"; +import Button from "../Button/Button"; +import { useNavigate } from "react-router-dom"; +import { LoginAPI } from "../API/API"; +import { useDispatch } from "react-redux"; +import { auth_toggle } from "../Plugins/Redux/Slices/AuthSlice/AuthSlice"; +import { toast } from "react-toastify"; +export default function LoginModal() { + const navigate = useNavigate(); + const [showPassword, setShowPassword] = useState(false); + const [remember_session, setRememberSession] = useState(true); + const [error, setError] = useState(""); + const [user, setUser] = useState({ + username: "", + password: "", + }); + const dispatch = useDispatch(); + return ( + <> +
+ +

Welcome back!

+
+ +
+ ) => { + setUser({ ...user, username: e.target.value }); + setError(""); + }} + value={user.username} + placeholder={"Enter username"} + /> + + { + setShowPassword(!showPassword); + setError(""); + }} + edge="end" + > + {showPassword ? : } + + + ), + }} + label="Password" + placeholder={"Enter password"} + onChange={(e: React.ChangeEvent) => + setUser({ ...user, password: e.target.value }) + } + value={user.password} + /> +
+
+ setRememberSession(!remember_session)} + /> +

Remember me

+
+
+
+
+

{error}

+
+ + ); +} diff --git a/src/Components/Revalidator/Revalidator.tsx b/src/Components/Revalidator/Revalidator.tsx new file mode 100644 index 0000000..7631fcc --- /dev/null +++ b/src/Components/Revalidator/Revalidator.tsx @@ -0,0 +1,60 @@ +import { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate, useLocation } from "react-router-dom"; +import { JWTRefreshAPI, setAccessToken, setRefreshToken } from "../API/API"; +import { auth_toggle } from "../Plugins/Redux/Slices/AuthSlice/AuthSlice"; +import { RootState } from "../Plugins/Redux/Store/Store"; +import { toast } from "react-toastify"; + +export default function Revalidator() { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const location = useLocation(); + const authenticated = useSelector((state: RootState) => state.auth.value); + const [rechecked, setRechecked] = useState(false); + + useEffect(() => { + if (!authenticated && rechecked) { + if (location.pathname !== "/") { + navigate("/"); + toast("Please log in to continue", { + position: "bottom-center", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + } + } + }, [authenticated, location.pathname, navigate, rechecked]); + + useEffect(() => { + if (!authenticated) { + JWTRefreshAPI().then(async (response) => { + if (response) { + await dispatch(auth_toggle()); + toast("User session restored", { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + } else { + await setRefreshToken(""); + await setAccessToken(""); + } + setRechecked(true); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return <>; +} diff --git a/src/Components/Types/Types.tsx b/src/Components/Types/Types.tsx new file mode 100644 index 0000000..b2af722 --- /dev/null +++ b/src/Components/Types/Types.tsx @@ -0,0 +1,98 @@ +export type RegisterType = { + email: string; + username: string; + password: string; + first_name: string; + last_name: string; +}; + +export type LoginType = { + username: string; + password: string; +}; + +export type ActivationType = { + uid: string; + token: string; +}; + +export type ResetPasswordConfirmType = { + uid: string; + token: string; + new_password: string; +}; + +export type AddEquipmentType = { + name: string; + description: string; + category?: string; +}; + +export type PatchEquipmentType = { + name: string; + description: string; + category?: string; +}; + +export type EquipmentType = { + id: number; + name: string; + description: string; + last_updated: string; + last_updated_by: string; + date_added: string; + category: string; +}; + +export type EquipmentListType = Array; + +export type EquipmentLogType = { + history_id: number; + id: number; + name: string; + category: string; + description: string; + history_date: string; + history_user: string; +}; + +export type EquipmentLogListType = Array; + +export type AddEquipmentInstanceType = { + equipment: number; + status: string; + remarks?: string; +}; + +export type PatchEquipmentInstanceType = { + status: string; + remarks?: string; +}; + +export type EquipmentInstanceType = { + id: number; + equipment: string; + equipment_name: string; + status: string; + remarks: string; + last_updated: string; + last_updated_by: string; + date_added: string; + category: string; +}; + +export type EquipmentInstanceListType = Array; + +export type EquipmentInstanceLogType = { + history_id: number; + id: number; + equipment: number; + equipment_name: string; + category: string; + status: string; + remarks: string; + history_date: string; + history_user: string; +}; + +export type EquipmentInstanceLogListType = Array; diff --git a/src/Pages/ActivationPage/ActivationPage.tsx b/src/Pages/ActivationPage/ActivationPage.tsx new file mode 100644 index 0000000..11e4f0a --- /dev/null +++ b/src/Pages/ActivationPage/ActivationPage.tsx @@ -0,0 +1,98 @@ +import { useNavigate, useParams } from "react-router-dom"; +import styles, { colors } from "../../styles"; +import { ActivationAPI } from "../../Components/API/API"; +import { useEffect, useState } from "react"; +import { CircularProgress } from "@mui/material"; +import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline"; +import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; +import { toast } from "react-toastify"; +export default function ActivationPage() { + const { uid, token } = useParams(); + const [feedback, setFeedback] = useState(""); + const [error, setError] = useState(false); + const [loading, setLoading] = useState(true); + const navigate = useNavigate(); + useEffect(() => { + if (uid && token && feedback == "") { + ActivationAPI({ uid, token }).then((response) => { + if (response) { + setFeedback("Activation successful"); + toast("Activation successful", { + position: "top-right", + autoClose: 2000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + toast("Please login to continue", { + position: "top-right", + autoClose: 6000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "light", + }); + setTimeout(() => { + navigate("/"); + }); + } else { + setFeedback("Invalid activation link"); + setError(true); + } + }); + } + if (!uid || !token) { + setFeedback("Missing uid or token"); + } + setLoading(false); + }, [uid, token, feedback, navigate]); + return ( +
+
+ {loading ? ( + + ) : ( + <> + )} + {error && !loading ? ( + + ) : ( + + )} + +

{feedback}

+
+

+ Activating your CITC Equipment Tracker Account +

+
+
+ ); +} diff --git a/src/Pages/DashboardPage/DashboardPage.tsx b/src/Pages/DashboardPage/DashboardPage.tsx new file mode 100644 index 0000000..92cf0f1 --- /dev/null +++ b/src/Pages/DashboardPage/DashboardPage.tsx @@ -0,0 +1,616 @@ +import Header from "../../Components/Header/Header"; +import styles from "../../styles"; +import { useQueries } from "@tanstack/react-query"; +import { EquipmentsAPI, EquipmentInstancesAPI } from "../../Components/API/API"; +import { Button, CircularProgress } from "@mui/material"; +import ComputerIcon from "@mui/icons-material/Computer"; +import RouterIcon from "@mui/icons-material/Router"; +import CameraOutdoorIcon from "@mui/icons-material/CameraOutdoor"; +import ChairIcon from "@mui/icons-material/Chair"; +import FormatListBulletedIcon from "@mui/icons-material/FormatListBulleted"; +import AddToQueueIcon from "@mui/icons-material/AddToQueue"; +import NoteAddIcon from "@mui/icons-material/NoteAdd"; +import NoteIcon from "@mui/icons-material/Note"; +import ManageSearchIcon from "@mui/icons-material/ManageSearch"; +import { colors } from "../../styles"; +import { useNavigate } from "react-router-dom"; +import { useState } from "react"; +import AddSKUModal from "../../Components/AddSKUModal/AddSKUModal"; +import Popup from "reactjs-popup"; +import AddItemModal from "../../Components/AddItemModal/AddItemModal"; +export default function Dashboard() { + const navigate = useNavigate(); + + const queries = useQueries({ + queries: [ + { + queryKey: ["equipments"], + queryFn: EquipmentsAPI, + }, + { + queryKey: ["equipment_instances"], + queryFn: EquipmentInstancesAPI, + }, + ], + }); + const isLoading = queries.some((result) => result.isLoading); + + const [addSKUmodalOpen, SetAddSKUModalOpen] = useState(false); + const [additemmodalOpen, SetAddItemModalOpen] = useState(false); + + if (isLoading) { + return ( +
+
+
+ +

+ Loading +

+
+
+ ); + } + return ( +
+
+
+
+
+

+ SKUs in Database +

+ +

+ {queries[0].data ? queries[0].data.length : 0} +

+
+
+

+ Items in Database +

+ +

+ {queries[1].data ? queries[1].data.length : 0} +

+
+
+
+
+

+ Functional Items +

+ +

+ {queries[1].data + ? queries[1].data.filter( + (equipment) => equipment.status == "WORKING" + ).length + : 0} +

+
+
+

+ Broken Items +

+ +

+ {queries[1].data + ? queries[1].data.filter( + (equipment) => equipment.status == "BROKEN" + ).length + : 0} +

+
+
+
+

+ Equipments +

+
+ + + + +
+ +
+ + + + + +
+

+ Logs +

+
+ + +
+ SetAddSKUModalOpen(false)} + modal + position={"top center"} + contentStyle={{ + width: "32rem", + borderRadius: 16, + borderColor: "grey", + borderStyle: "solid", + borderWidth: 1, + padding: 16, + alignContent: "center", + justifyContent: "center", + textAlign: "center", + }} + > + + + SetAddItemModalOpen(false)} + modal + position={"top center"} + contentStyle={{ + width: "32rem", + borderRadius: 16, + borderColor: "grey", + borderStyle: "solid", + borderWidth: 1, + padding: 16, + alignContent: "center", + justifyContent: "center", + textAlign: "center", + }} + > + + +
+ ); +} diff --git a/src/Pages/EquipmentInstanceLogsPage/EquipmentInstanceLogsPage.tsx b/src/Pages/EquipmentInstanceLogsPage/EquipmentInstanceLogsPage.tsx new file mode 100644 index 0000000..80dd527 --- /dev/null +++ b/src/Pages/EquipmentInstanceLogsPage/EquipmentInstanceLogsPage.tsx @@ -0,0 +1,140 @@ +import { useQuery } from "@tanstack/react-query"; +import Header from "../../Components/Header/Header"; +import styles from "../../styles"; +import { EquipmentInstanceLogsAPI } from "../../Components/API/API"; +import { CircularProgress } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { colors } from "../../styles"; + +export default function EquipmentInstanceLogsPage() { + const equipment_instance_logs = useQuery({ + queryKey: ["equipment_instance_logs"], + queryFn: EquipmentInstanceLogsAPI, + }); + if (equipment_instance_logs.isLoading) { + return ( +
+
+
+ +

+ Loading +

+
+
+ ); + } + return ( +
+
+
+
+ + + + + + Transaction ID + + + Item ID + + + SKU + + + Remarks + + + Status + + + Date Modified + + + + + {equipment_instance_logs.data ? ( + equipment_instance_logs.data.map((equipment_instance_log) => ( + + + {equipment_instance_log.history_id} + + + {equipment_instance_log.id} + + + {`SKU #${equipment_instance_log.equipment} - ${equipment_instance_log.equipment_name}`} + + + + {equipment_instance_log.remarks} + + + {equipment_instance_log.status} + + +
+
{equipment_instance_log.history_date}
+
+ {equipment_instance_log.history_user + ? "by " + equipment_instance_log.history_user + : ""} +
+
+
+
+ )) + ) : ( + <> + )} +
+
+
+
+
+
+ ); +} diff --git a/src/Pages/EquipmentInstancesListPage/EquipmentInstancesListPage.tsx b/src/Pages/EquipmentInstancesListPage/EquipmentInstancesListPage.tsx new file mode 100644 index 0000000..5762f60 --- /dev/null +++ b/src/Pages/EquipmentInstancesListPage/EquipmentInstancesListPage.tsx @@ -0,0 +1,148 @@ +import { useQuery } from "@tanstack/react-query"; +import Header from "../../Components/Header/Header"; +import styles from "../../styles"; +import { EquipmentInstancesAPI } from "../../Components/API/API"; +import { CircularProgress } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { colors } from "../../styles"; +import EditItemModal from "../../Components/EditItemInstanceModal/EditItemInstanceModal"; +import { useState } from "react"; +import Popup from "reactjs-popup"; + +export default function EquipmentInstancesListPage() { + const [editmodalOpen, SetEditModalOpen] = useState(false); + const [selectedItem, SetSelectedItem] = useState(0); + const equipment_instances = useQuery({ + queryKey: ["equipment_instances"], + queryFn: EquipmentInstancesAPI, + }); + if (equipment_instances.isLoading) { + return ( +
+
+
+ +

+ Loading +

+
+
+ ); + } + return ( +
+
+
+
+ + + + + ID + + Name + + + Status + + + Category + + + Last Modified + + + + + {equipment_instances.data ? ( + equipment_instances.data.map((equipment) => ( + { + SetSelectedItem(equipment.id); + SetEditModalOpen(true); + }} + > + + {equipment.id} + + + {equipment.equipment_name} + + + + {equipment.status} + + + {equipment.category} + + +
+
{equipment.last_updated}
+
+ {equipment.last_updated_by + ? "by " + equipment.last_updated_by + : ""} +
+
+
+
+ )) + ) : ( + <> + )} +
+
+
+
+
+ SetEditModalOpen(false)} + modal + position={"top center"} + contentStyle={styles.popup_center} + > + + +
+ ); +} diff --git a/src/Pages/EquipmentListPage/EquipmentListPage.tsx b/src/Pages/EquipmentListPage/EquipmentListPage.tsx new file mode 100644 index 0000000..c2033e8 --- /dev/null +++ b/src/Pages/EquipmentListPage/EquipmentListPage.tsx @@ -0,0 +1,148 @@ +import { useQuery } from "@tanstack/react-query"; +import Header from "../../Components/Header/Header"; +import styles from "../../styles"; +import { EquipmentsAPI } from "../../Components/API/API"; +import { CircularProgress } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { colors } from "../../styles"; +import EditSKUModal from "../../Components/EditSKUModal/EditSKUModal"; +import Popup from "reactjs-popup"; +import { useState } from "react"; + +export default function EquipmentListPage() { + const [editmodalOpen, SetEditModalOpen] = useState(false); + const [selectedItem, SetSelectedItem] = useState(0); + const equipments = useQuery({ + queryKey: ["equipments"], + queryFn: EquipmentsAPI, + }); + if (equipments.isLoading) { + return ( +
+
+
+ +

+ Loading +

+
+
+ ); + } + return ( +
+
+
+
+ + + + + ID + + Name + + + Description + + + Category + + + Last Modified + + + + + {equipments.data ? ( + equipments.data.map((equipment) => ( + { + SetSelectedItem(equipment.id); + SetEditModalOpen(true); + }} + > + + {equipment.id} + + + {equipment.name} + + + + {equipment.description} + + + {equipment.category} + + +
+
{equipment.last_updated}
+
+ {equipment.last_updated_by + ? "by " + equipment.last_updated_by + : ""} +
+
+
+
+ )) + ) : ( + <> + )} +
+
+
+
+
+ SetEditModalOpen(false)} + modal + position={"top center"} + contentStyle={styles.popup_center} + > + + +
+ ); +} diff --git a/src/Pages/EquipmentLogsPage/EquipmentLogsPage.tsx b/src/Pages/EquipmentLogsPage/EquipmentLogsPage.tsx new file mode 100644 index 0000000..8f30b2b --- /dev/null +++ b/src/Pages/EquipmentLogsPage/EquipmentLogsPage.tsx @@ -0,0 +1,138 @@ +import { useQuery } from "@tanstack/react-query"; +import Header from "../../Components/Header/Header"; +import styles from "../../styles"; +import { EquipmentLogsAPI } from "../../Components/API/API"; +import { CircularProgress } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { colors } from "../../styles"; + +export default function EquipmentLogsPage() { + const equipment_logs = useQuery({ + queryKey: ["equipment_logs"], + queryFn: EquipmentLogsAPI, + }); + if (equipment_logs.isLoading) { + return ( +
+
+
+ +

+ Loading +

+
+
+ ); + } + return ( +
+
+
+
+ + + + + + Transaction ID + + + SKU ID + + + Name + + + Description + + + Category + + + Date Modified + + + + + {equipment_logs.data ? ( + equipment_logs.data.map((equipment_log) => ( + + + {equipment_log.history_id} + + + {equipment_log.id} + + + {equipment_log.name} + + + + {equipment_log.description} + + + {equipment_log.category} + + +
+
{equipment_log.history_date}
+
+ {equipment_log.history_user + ? "by " + equipment_log.history_user + : ""} +
+
+
+
+ )) + ) : ( + <> + )} +
+
+
+
+
+
+ ); +} diff --git a/src/Pages/ErrorPage/ErrorPage.tsx b/src/Pages/ErrorPage/ErrorPage.tsx new file mode 100644 index 0000000..5045ac2 --- /dev/null +++ b/src/Pages/ErrorPage/ErrorPage.tsx @@ -0,0 +1,3 @@ +export default function ErrorPage() { + return
{"ErrorPage"}
; +} diff --git a/src/Pages/LandingPage/LandingPage.tsx b/src/Pages/LandingPage/LandingPage.tsx new file mode 100644 index 0000000..2381447 --- /dev/null +++ b/src/Pages/LandingPage/LandingPage.tsx @@ -0,0 +1,161 @@ +import Button from "../../Components/Button/Button"; +import styles from "../../styles"; +import citc_logo from "../../assets/citc_logo.jpg"; +import Popup from "reactjs-popup"; +import "reactjs-popup/dist/index.css"; +import { useEffect, useState } from "react"; +import LoginModal from "../../Components/LoginModal/LoginModal"; +import RegisterModal from "../../Components/RegisterModal/RegisterModal"; +import { useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { RootState } from "../../Components/Plugins/Redux/Store/Store"; +import ResetPasswordModal from "../../Components/ResetPasswordModal/ResetPasswordModal"; + +export default function LandingPage() { + const [loginmodalOpen, SetloginmodalOpen] = useState(false); + const [registermodalOpen, SetRegisterModalOpen] = useState(false); + const [resetmodalOpen, SetResetModalOpen] = useState(false); + const authenticated = useSelector((state: RootState) => state.auth.value); + const navigate = useNavigate(); + + useEffect(() => { + if (authenticated) { + navigate("/dashboard"); + console.log("Already logged in. Redirecting to dashboard page"); + } + }, [authenticated, navigate]); + return ( +
+
+
+ +
+
+
+

+ CITC EQUIPMENT +
+ TRACKER +

+
+
+
+
+
+
+ ); +} diff --git a/src/Pages/ResetPasswordPage/ResetPasswordPage.tsx b/src/Pages/ResetPasswordPage/ResetPasswordPage.tsx new file mode 100644 index 0000000..5ca2a75 --- /dev/null +++ b/src/Pages/ResetPasswordPage/ResetPasswordPage.tsx @@ -0,0 +1,148 @@ +import { useNavigate, useParams } from "react-router-dom"; +import styles, { colors } from "../../styles"; +import { ResetPasswordConfirmAPI } from "../../Components/API/API"; +import { useState } from "react"; +import { toast } from "react-toastify"; +import { VisibilityOff, Visibility } from "@mui/icons-material"; +import { TextField, InputAdornment, IconButton } from "@mui/material"; +import Button from "../../Components/Button/Button"; +export default function ResetPasswordPage() { + const { uid, token } = useParams(); + const [feedback, setFeedback] = useState(""); + const [user, setUser] = useState({ + password: "", + confirm_password: "", + }); + const [showPassword, setShowPassword] = useState(false); + const navigate = useNavigate(); + return ( +
+
+

+ Confirm Password Reset +

+ + { + setShowPassword(!showPassword); + setFeedback(""); + }} + edge="end" + > + {showPassword ? : } + + + ), + }} + label="New Password" + placeholder={"Enter new password"} + onChange={(e: React.ChangeEvent) => + setUser({ ...user, password: e.target.value }) + } + value={user.password} + /> + + { + setShowPassword(!showPassword); + setFeedback(""); + }} + edge="end" + > + {showPassword ? : } + + + ), + }} + label="Confirm Password" + placeholder={"Re-enter password"} + onChange={(e: React.ChangeEvent) => + setUser({ ...user, confirm_password: e.target.value }) + } + value={user.confirm_password} + /> +
+
+
+ +

{feedback}

+
+
+ ); +} diff --git a/src/assets/citc_logo.jpg b/src/assets/citc_logo.jpg new file mode 100644 index 0000000..a2a23c8 Binary files /dev/null and b/src/assets/citc_logo.jpg differ diff --git a/src/main.tsx b/src/main.tsx index 3d7150d..eb0a604 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,5 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import "./index.css"; -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -) +ReactDOM.createRoot(document.getElementById("root")!).render(); diff --git a/src/styles.tsx b/src/styles.tsx new file mode 100644 index 0000000..d0ed01d --- /dev/null +++ b/src/styles.tsx @@ -0,0 +1,92 @@ +export const colors = { + background: "#FFFFFF", + header_color: "#141762", + font_dark: "#141762", + font_light: "#FFFFFF", + button_dark: "#141762", + button_light: "#FFFFFF", + button_border: "#141762", + red: "#a44141", + orange: "#c57331", + green: "#80b28a", +}; +const styles: { [key: string]: React.CSSProperties } = { + background: { + backgroundColor: colors.background, + position: "fixed", + top: 0, + left: 0, + height: "100%", + width: "100%", + minHeight: "100%", + minWidth: "100%", + overflowY: "scroll", + }, + text_dark: { + color: colors.font_dark, + fontWeight: "bold", + }, + text_light: { + color: colors.font_light, + fontWeight: "bold", + }, + text_red: { + color: colors.red, + fontWeight: "bold", + }, + text_orange: { + color: colors.orange, + fontWeight: "bold", + }, + text_green: { + color: colors.green, + fontWeight: "bold", + }, + text_XL: { + fontSize: "clamp(2rem, 3rem, 8rem)", + }, + text_L: { + fontSize: "clamp(1.5rem, 2rem, 6rem)", + }, + text_M: { + fontSize: "clamp(1rem, 1rem, 4rem)", + }, + text_S: { + fontSize: "clamp(0.6rem, 0.8rem, 1rem)", + }, + text_XS: { + fontSize: "clamp(0.5rem, 0.6rem, 0.8rem)", + }, + flex_row: { + display: "flex", + flexDirection: "row", + }, + flex_column: { + display: "flex", + flexDirection: "column", + }, + input_form: { + color: colors.font_dark, + fontWeight: "bold", + fontSize: "clamp(1vw, 1rem, 2vw)", + background: "none", + borderRadius: 8, + maxWidth: "128px", + minWidth: "100%", + marginTop: 16, + }, + popup_center: { + width: "32rem", + borderRadius: 16, + borderColor: "grey", + borderStyle: "solid", + borderWidth: 1, + padding: 16, + alignContent: "center", + justifyContent: "center", + textAlign: "center", + overflowY: "scroll", + }, +}; + +export default styles; diff --git a/tsconfig.json b/tsconfig.json index a7fc6fb..723f5e0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,11 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + + "paths":{ + "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"] + } }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/woodpecker.yml b/woodpecker.yml new file mode 100644 index 0000000..a58b1ea --- /dev/null +++ b/woodpecker.yml @@ -0,0 +1,17 @@ +pipeline: + - name: build + image: node:14 + commands: + - npm install + - npm run build + - name: copy + image: alpine + environment: + - SSH_KEY: + from_secret: ssh_key + commands: + - apk add --no-cache openssh-client + - echo "$SSH_KEY" | tr -d '\r' > /root/.ssh/id_rsa + - chmod 600 /root/.ssh/id_rsa + - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > /root/.ssh/config + - scp -r dist/* username@10.0.10.4:/mnt/sda1/projects/equipment_tracker_frontend