diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 5a1a50ad..047dd7e4 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -1,13 +1,17 @@ { - "name": "finanzen", + "name": "finanzen-projekt", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "finanzen", + "name": "finanzen-projekt", "version": "0.1.0", "dependencies": { + "@emotion/react": "^11.8.2", + "@emotion/styled": "^11.8.1", + "@mui/icons-material": "^5.5.1", + "@mui/material": "^5.5.2", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", @@ -15,8 +19,11 @@ "@types/node": "^16.11.26", "@types/react": "^17.0.40", "@types/react-dom": "^17.0.13", + "bootstrap": "^5.1.3", + "prop-types": "^15.8.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", "typescript": "^4.6.2", "web-vitals": "^2.1.4" @@ -1975,6 +1982,177 @@ "postcss": "^8.3" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz", + "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "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.7.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", + "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", + "dependencies": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.1.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "4.0.13" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", + "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", + "dependencies": { + "@emotion/memoize": "^0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "node_modules/@emotion/react": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz", + "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/cache": "^11.7.1", + "@emotion/serialize": "^1.0.2", + "@emotion/utils": "^1.1.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", + "dependencies": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", + "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" + }, + "node_modules/@emotion/styled": { + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz", + "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/is-prop-valid": "^1.1.2", + "@emotion/serialize": "^1.0.2", + "@emotion/utils": "^1.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/@emotion/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, "node_modules/@eslint/eslintrc": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", @@ -2727,6 +2905,236 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mui/base": { + "version": "5.0.0-alpha.73", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.73.tgz", + "integrity": "sha512-TEUCIIEAWrngAqpIa+dY3nofGSNj70LC3KC9WcCzyXPK3M4AG2GNi7ndd/g/0DtC55kbxrudzlV8TG3vrB2Vjw==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@emotion/is-prop-valid": "^1.1.2", + "@mui/utils": "^5.4.4", + "@popperjs/core": "^2.11.4", + "clsx": "^1.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^17.0.0", + "react-dom": "^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/icons-material": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.5.1.tgz", + "integrity": "sha512-40f68p5+Yhq3dCn3QYHqQt5RETPyR3AkDw+fma8PtcjqvZ+d+jF84kFmT6NqwA3he7TlwluEtkyAmPzUE4uPdA==", + "dependencies": { + "@babel/runtime": "^7.17.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.0.0", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.5.2.tgz", + "integrity": "sha512-r4p1u9eDlSqW3TS/Iq9yolifWHpuW6e0BSeqEJW3EEIcKfPVVk4WNUNJ+s8DtN7dBoDcveXxcQVVjYXTIv1d9g==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/base": "5.0.0-alpha.73", + "@mui/system": "^5.5.2", + "@mui/types": "^7.1.3", + "@mui/utils": "^5.4.4", + "@types/react-transition-group": "^4.4.4", + "clsx": "^1.1.1", + "csstype": "^3.0.11", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "react-transition-group": "^4.4.2" + }, + "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": "^16.8.6 || ^17.0.0", + "react": "^17.0.0", + "react-dom": "^17.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.4.4.tgz", + "integrity": "sha512-V/gxttr6736yJoU9q+4xxXsa0K/w9Hn9pg99zsOHt7i/O904w2CX5NHh5WqDXtoUzVcayLF0RB17yr6l79CE+A==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/utils": "^5.4.4", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.5.2.tgz", + "integrity": "sha512-jkz5AHHbA43akBo5L3y1X1/X0f+RvXvCp3eXKt+iOf3qnKSAausbtlVz7gBbC4xIWDnP1Jb/6T+t/0/7gObRYA==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@emotion/cache": "^11.7.1", + "prop-types": "^15.7.2" + }, + "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" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.5.2.tgz", + "integrity": "sha512-OATYFI36nliud8xh0u+ZNqDo0jWjxpO0vZLlzqNB+ZtkR5Q/+1X3GgboA9ruiB8Rq+udnJlMBQNGW0qqjvAOHQ==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@mui/private-theming": "^5.4.4", + "@mui/styled-engine": "^5.5.2", + "@mui/types": "^7.1.3", + "@mui/utils": "^5.4.4", + "clsx": "^1.1.1", + "csstype": "^3.0.11", + "prop-types": "^15.7.2" + }, + "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": "^16.8.6 || ^17.0.0", + "react": "^17.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.3.tgz", + "integrity": "sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA==", + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.4.4.tgz", + "integrity": "sha512-hfYIXEuhc2mXMGN5nUPis8beH6uE/zl3uMWJcyHX0/LN/+QxO9zhYuV6l8AsAaphHFyS/fBv0SW3Nid7jw5hKQ==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@types/prop-types": "^15.7.4", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "react": "^17.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2816,6 +3224,15 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz", + "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3596,6 +4013,22 @@ "@types/react": "*" } }, + "node_modules/@types/react-is": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", + "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", + "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -4886,6 +5319,18 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5178,6 +5623,14 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6162,6 +6615,15 @@ "utila": "~0.4" } }, + "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/dom-serializer": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", @@ -7443,6 +7905,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "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", @@ -7991,6 +8458,27 @@ "he": "bin/he" } }, + "node_modules/history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "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/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -13191,6 +13679,30 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.2.tgz", + "integrity": "sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==", + "dependencies": { + "history": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.2.tgz", + "integrity": "sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ==", + "dependencies": { + "history": "^5.2.0", + "react-router": "6.2.2" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz", @@ -13263,6 +13775,21 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "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/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -14340,6 +14867,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -17351,6 +17883,142 @@ "postcss-value-parser": "^4.2.0" } }, + "@emotion/babel-plugin": { + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz", + "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + }, + "dependencies": { + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "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==" + } + } + }, + "@emotion/cache": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", + "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", + "requires": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.1.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "4.0.13" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "@emotion/is-prop-valid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", + "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", + "requires": { + "@emotion/memoize": "^0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "@emotion/react": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz", + "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/cache": "^11.7.1", + "@emotion/serialize": "^1.0.2", + "@emotion/utils": "^1.1.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", + "requires": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", + "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" + }, + "@emotion/styled": { + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz", + "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.7.1", + "@emotion/is-prop-valid": "^1.1.2", + "@emotion/serialize": "^1.0.2", + "@emotion/utils": "^1.1.0" + } + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "@emotion/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==" + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, "@eslint/eslintrc": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", @@ -17908,6 +18576,100 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@mui/base": { + "version": "5.0.0-alpha.73", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.73.tgz", + "integrity": "sha512-TEUCIIEAWrngAqpIa+dY3nofGSNj70LC3KC9WcCzyXPK3M4AG2GNi7ndd/g/0DtC55kbxrudzlV8TG3vrB2Vjw==", + "requires": { + "@babel/runtime": "^7.17.2", + "@emotion/is-prop-valid": "^1.1.2", + "@mui/utils": "^5.4.4", + "@popperjs/core": "^2.11.4", + "clsx": "^1.1.1", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + } + }, + "@mui/icons-material": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.5.1.tgz", + "integrity": "sha512-40f68p5+Yhq3dCn3QYHqQt5RETPyR3AkDw+fma8PtcjqvZ+d+jF84kFmT6NqwA3he7TlwluEtkyAmPzUE4uPdA==", + "requires": { + "@babel/runtime": "^7.17.2" + } + }, + "@mui/material": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.5.2.tgz", + "integrity": "sha512-r4p1u9eDlSqW3TS/Iq9yolifWHpuW6e0BSeqEJW3EEIcKfPVVk4WNUNJ+s8DtN7dBoDcveXxcQVVjYXTIv1d9g==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/base": "5.0.0-alpha.73", + "@mui/system": "^5.5.2", + "@mui/types": "^7.1.3", + "@mui/utils": "^5.4.4", + "@types/react-transition-group": "^4.4.4", + "clsx": "^1.1.1", + "csstype": "^3.0.11", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "react-transition-group": "^4.4.2" + } + }, + "@mui/private-theming": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.4.4.tgz", + "integrity": "sha512-V/gxttr6736yJoU9q+4xxXsa0K/w9Hn9pg99zsOHt7i/O904w2CX5NHh5WqDXtoUzVcayLF0RB17yr6l79CE+A==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/utils": "^5.4.4", + "prop-types": "^15.7.2" + } + }, + "@mui/styled-engine": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.5.2.tgz", + "integrity": "sha512-jkz5AHHbA43akBo5L3y1X1/X0f+RvXvCp3eXKt+iOf3qnKSAausbtlVz7gBbC4xIWDnP1Jb/6T+t/0/7gObRYA==", + "requires": { + "@babel/runtime": "^7.17.2", + "@emotion/cache": "^11.7.1", + "prop-types": "^15.7.2" + } + }, + "@mui/system": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.5.2.tgz", + "integrity": "sha512-OATYFI36nliud8xh0u+ZNqDo0jWjxpO0vZLlzqNB+ZtkR5Q/+1X3GgboA9ruiB8Rq+udnJlMBQNGW0qqjvAOHQ==", + "requires": { + "@babel/runtime": "^7.17.2", + "@mui/private-theming": "^5.4.4", + "@mui/styled-engine": "^5.5.2", + "@mui/types": "^7.1.3", + "@mui/utils": "^5.4.4", + "clsx": "^1.1.1", + "csstype": "^3.0.11", + "prop-types": "^15.7.2" + } + }, + "@mui/types": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.3.tgz", + "integrity": "sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA==", + "requires": {} + }, + "@mui/utils": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.4.4.tgz", + "integrity": "sha512-hfYIXEuhc2mXMGN5nUPis8beH6uE/zl3uMWJcyHX0/LN/+QxO9zhYuV6l8AsAaphHFyS/fBv0SW3Nid7jw5hKQ==", + "requires": { + "@babel/runtime": "^7.17.2", + "@types/prop-types": "^15.7.4", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -17954,6 +18716,11 @@ } } }, + "@popperjs/core": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz", + "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==" + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -18541,6 +19308,22 @@ "@types/react": "*" } }, + "@types/react-is": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", + "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", + "requires": { + "@types/react": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", + "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -19510,6 +20293,12 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "requires": {} + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -19721,6 +20510,11 @@ "wrap-ansi": "^7.0.0" } }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -20443,6 +21237,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dom-serializer": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", @@ -21387,6 +22190,11 @@ "pkg-dir": "^4.1.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -21754,6 +22562,29 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "requires": { + "@babel/runtime": "^7.7.6" + } + }, + "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==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -25379,6 +26210,23 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" }, + "react-router": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.2.tgz", + "integrity": "sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==", + "requires": { + "history": "^5.2.0" + } + }, + "react-router-dom": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.2.tgz", + "integrity": "sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ==", + "requires": { + "history": "^5.2.0", + "react-router": "6.2.2" + } + }, "react-scripts": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz", @@ -25434,6 +26282,17 @@ "workbox-webpack-plugin": "^6.4.1" } }, + "react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -26229,6 +27088,11 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index a0fcdeda..407b7508 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -3,6 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { + "@emotion/react": "^11.8.2", + "@emotion/styled": "^11.8.1", + "@mui/icons-material": "^5.5.1", + "@mui/material": "^5.5.2", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", @@ -11,6 +15,7 @@ "@types/react": "^17.0.40", "@types/react-dom": "^17.0.13", "bootstrap": "^5.1.3", + "prop-types": "^15.8.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^6.2.2", diff --git a/Frontend/public/assets/images/allianz-logo.jpeg b/Frontend/public/assets/images/allianz-logo.jpeg new file mode 100644 index 00000000..47fe591f Binary files /dev/null and b/Frontend/public/assets/images/allianz-logo.jpeg differ diff --git a/Frontend/public/assets/images/swe-finance-logo.png b/Frontend/public/assets/images/swe-finance-logo.png new file mode 100644 index 00000000..5a712873 Binary files /dev/null and b/Frontend/public/assets/images/swe-finance-logo.png differ diff --git a/Frontend/src/App.css b/Frontend/src/App.css index 74b5e053..78b8850c 100644 --- a/Frontend/src/App.css +++ b/Frontend/src/App.css @@ -1,38 +1,38 @@ .App { - text-align: center; + text-align: center; } .App-logo { - height: 40vmin; - pointer-events: none; + height: 40vmin; + pointer-events: none; } @media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } + .App-logo { + animation: App-logo-spin infinite 20s linear; + } } .App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: white; } .App-link { - color: #61dafb; + color: #61dafb; } @keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx new file mode 100644 index 00000000..01065a8b --- /dev/null +++ b/Frontend/src/App.jsx @@ -0,0 +1,18 @@ +import React, {Suspense} from 'react'; +import {BrowserRouter as Router} from 'react-router-dom'; +import AppRoutes from './routes'; +import './App.css'; + +/** + * App root component with link navigations and routes + * @constructor + */ +const App = () => ( + + Loading...}> + + + +); + +export default App; diff --git a/Frontend/src/App.test.tsx b/Frontend/src/App.test.jsx similarity index 72% rename from Frontend/src/App.test.tsx rename to Frontend/src/App.test.jsx index 2a68616d..91a8cdfb 100644 --- a/Frontend/src/App.test.tsx +++ b/Frontend/src/App.test.jsx @@ -1,9 +1,9 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import {render, screen} from '@testing-library/react'; import App from './App'; test('renders learn react link', () => { - render(); + render(); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); }); diff --git a/Frontend/src/App.tsx b/Frontend/src/App.tsx deleted file mode 100644 index bc55e586..00000000 --- a/Frontend/src/App.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { Suspense } from 'react'; -import { BrowserRouter as Router, Link } from 'react-router-dom'; -import AppRoutes from './routes/index'; -import './App.css'; - -/** - * App root component with link navigations and routes - * @constructor - */ -const App = () => ( - -
- - Loading...
}> - - - -
- ); - -export default App; diff --git a/Frontend/src/components/About.tsx b/Frontend/src/components/About.tsx deleted file mode 100644 index f3689675..00000000 --- a/Frontend/src/components/About.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const About = () => ( -

Dummy Data in About Section!

-); - -export default About; \ No newline at end of file diff --git a/Frontend/src/components/Home.tsx b/Frontend/src/components/Home.tsx deleted file mode 100644 index 2bc41e34..00000000 --- a/Frontend/src/components/Home.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const Home = () => ( -

Welcome to Finance App SWE- DHBW!

-); - -export default Home; \ No newline at end of file diff --git a/Frontend/src/components/ScreensTemplate.jsx b/Frontend/src/components/ScreensTemplate.jsx new file mode 100644 index 00000000..f038dd4a --- /dev/null +++ b/Frontend/src/components/ScreensTemplate.jsx @@ -0,0 +1,81 @@ +import React, {useState, useEffect} from 'react'; +import PropTypes from 'prop-types'; +import AppBar from '@mui/material/AppBar'; +import Box from '@mui/material/Box'; +import CssBaseline from '@mui/material/CssBaseline'; +import MenuIcon from '@mui/icons-material/Menu'; +import IconButton from '@mui/material/IconButton'; +import Toolbar from '@mui/material/Toolbar'; +import SideNavLeft from "./common/SideNavLeft"; + +const drawerWidth = 12; // This is the value in rem units, for responsiveness +/** + * Drawer navigation menu throughout the app + * @param props + * @returns {JSX.Element} + * @constructor + */ +const ScreensTemplate = (props) => { + const [openInMobile, setOpenInMobile] = useState(false); + + const handleDrawerToggle = () => { + setOpenInMobile(!openInMobile); + }; + + return ( + + + + + + + + + {props.headerComponent()} + + + + + + + + {props.bodyComponent()} + + + ); +} + +ScreensTemplate.propTypes = { + headerComponent: PropTypes.func, + bodyComponent: PropTypes.func, + selectedNavLinkIndex: PropTypes.number +}; + +export default ScreensTemplate; \ No newline at end of file diff --git a/Frontend/src/components/common/CustomModal.jsx b/Frontend/src/components/common/CustomModal.jsx new file mode 100644 index 00000000..d53bd866 --- /dev/null +++ b/Frontend/src/components/common/CustomModal.jsx @@ -0,0 +1,76 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Box, + Modal, + Container, + Typography, IconButton +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; + +/** + * Drawer navigation menu throughout the app + * @param props + * @returns {JSX.Element} + * @constructor + */ +const CustomModal = (props) => { + return ( + props.handleClose()} + aria-labelledby={props.labelledby} + > + + + + {props.modalTitle} + + props.handleClose()} + className='px-0 py-1 align-self-start' + > + + + + + {props.modalBody ? props.modalBody() : null} + {props.modalButton()} + + + ); +} + +const style = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: { + xs: '70vw', + sm: '55vw', + md: '40vw', + lg: '30vw' + }, + minHeight: '10vh', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + backgroundColor: 'background.paper', + border: '2px solid #000', + boxShadow: 24, + padding: '1.5rem' +}; + +CustomModal.propTypes = { + open: PropTypes.bool, + handleClose: PropTypes.func, + labelledby: PropTypes.string, + modalTitle: PropTypes.string, + modalBody: PropTypes.func, + modalButton: PropTypes.func, +}; + +export default CustomModal; \ No newline at end of file diff --git a/Frontend/src/components/common/CustomTable.jsx b/Frontend/src/components/common/CustomTable.jsx new file mode 100644 index 00000000..4be0f2a6 --- /dev/null +++ b/Frontend/src/components/common/CustomTable.jsx @@ -0,0 +1,131 @@ +import React, {useState} from 'react'; +import { + TableContainer, + Paper, + Table, + TableBody, + TableRow, + TableCell, + Avatar, + Typography +} from '@mui/material'; +import PropTypes from 'prop-types'; +import DeleteIcon from '@mui/icons-material/Delete'; +import DropdownMenu from "../screens/WatchLists/DropdownMenu"; + + +/** + * Table to show the different assets in watchlist + * @returns {JSX.Element} + * @constructor + */ +const CustomTable = (props) => { + const [listDropdownIndex, setListDropdownIndex] = useState(0); + + return ( + + + + {props.assetsArray[props.selectedListIndex].map((row, index) => ( + + + + + + + {row.name} + + + + + {row.price} + + + + + {row.change} + + + + ]} + functionOptions={[ + () => {} + ]} + /> + + + ))} + +
+
+ ); +} + +CustomTable.propTypes = { + assetsArray: PropTypes.array, + selectedListIndex: PropTypes.number, + watchListsArray: PropTypes.array, + setWatchListsArray: PropTypes.func, +}; + +export default CustomTable; \ No newline at end of file diff --git a/Frontend/src/components/common/SearchField.jsx b/Frontend/src/components/common/SearchField.jsx new file mode 100644 index 00000000..66234cc3 --- /dev/null +++ b/Frontend/src/components/common/SearchField.jsx @@ -0,0 +1,54 @@ +import React from 'react'; +import TextField from '@mui/material/TextField'; +import {Container, InputAdornment, IconButton} from "@mui/material"; +import SearchIcon from '@mui/icons-material/Search'; + +//TODO: function for the search +const search = () => console.log('Searching for asset'); + +/** + * Component for the search field in the screens' header + * @returns {JSX.Element} + * @constructor + */ +const SearchField = () => { + return ( + + + + + + + ), + }} + /> + + ); +} + +export default SearchField; \ No newline at end of file diff --git a/Frontend/src/components/common/SideNavLeft.jsx b/Frontend/src/components/common/SideNavLeft.jsx new file mode 100644 index 00000000..136b5110 --- /dev/null +++ b/Frontend/src/components/common/SideNavLeft.jsx @@ -0,0 +1,143 @@ +import React from 'react'; +import {Link} from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { + Box, + Drawer, + List, + ListItem, + ListItemIcon, + ListItemText +} from '@mui/material'; +import HomeIcon from '@mui/icons-material/Home'; +import AutoGraphIcon from '@mui/icons-material/AutoGraph'; +import ShutterSpeedIcon from '@mui/icons-material/ShutterSpeed'; +import SettingsIcon from '@mui/icons-material/Settings'; + +const drawerWidth = 12; // This is the value in rem units, for responsiveness +const routesArray = [ + {routeName: 'Home', path: '/', icon: }, + {routeName: 'Dashboard', path: '/dashboard', icon: }, + {routeName: 'Watchlists', path: '/watchlists', icon: }, + {routeName: 'Settings', path: '/settings', icon: }, +]; + +/** + * Drawer navigation menu throughout the app + * @param props + * @returns {JSX.Element} + * @constructor + */ +const SideNavLeft = (props) => { + const {window} = props; + + const handleNavLinkClick = () => { + props.setOpenInMobile(false) + }; + + const drawer = (routesArray) => ( + + + Application logo +

Portfolio

+ {routesArray.map((element, index) => ( + handleNavLinkClick()} + style={{textDecoration: 'none',}} + > + + + {element.icon} + + + + + ))} +
+
+ ); + + const container = window !== undefined ? () => window().document.body : undefined; + + return ( + + + {drawer(routesArray)} + + + {drawer(routesArray)} + + + ); +} + +SideNavLeft.propTypes = { + window: PropTypes.func, + openInMobile: PropTypes.bool, + setOpenInMobile: PropTypes.func, + handleDrawerToggle: PropTypes.func, + selectedNavLinkIndex: PropTypes.number +}; + +export default SideNavLeft; \ No newline at end of file diff --git a/Frontend/src/components/common/index.jsx b/Frontend/src/components/common/index.jsx new file mode 100644 index 00000000..920fa81f --- /dev/null +++ b/Frontend/src/components/common/index.jsx @@ -0,0 +1,11 @@ +import SideNavLeft from './SideNavLeft'; +import SearchField from './SearchField'; +import CustomModal from './CustomModal'; +import CustomTable from './CustomTable'; + +export { + SideNavLeft, + SearchField, + CustomModal, + CustomTable +} \ No newline at end of file diff --git a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx new file mode 100644 index 00000000..db272ab2 --- /dev/null +++ b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import ScreensTemplate from '../../ScreensTemplate'; +import {SearchField} from '../../common'; + +const DashboardScreen = () => { + const renderHeader = () => ( + + ); + + const renderBody = () => ( +

Welcome to Dashboard!

+ ); + + return ( + + + + + ); +} + +export default DashboardScreen; \ No newline at end of file diff --git a/Frontend/src/components/screens/Home.jsx b/Frontend/src/components/screens/Home.jsx new file mode 100644 index 00000000..2bf6040f --- /dev/null +++ b/Frontend/src/components/screens/Home.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import ScreensTemplate from '../ScreensTemplate'; +import Typography from '@mui/material/Typography'; + +const Home = () => { + const renderHeader = () => ( + + Header of Home Page + + ); + + const renderBody = () => ( +

Welcome to Finance App SWE- DHBW!

+ ); + + return ( + + + + + ); +} + +export default Home; \ No newline at end of file diff --git a/Frontend/src/components/screens/Settings/SettingsScreen.jsx b/Frontend/src/components/screens/Settings/SettingsScreen.jsx new file mode 100644 index 00000000..68cd84c9 --- /dev/null +++ b/Frontend/src/components/screens/Settings/SettingsScreen.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import ScreensTemplate from '../../ScreensTemplate'; +import Typography from '@mui/material/Typography'; + +const SettingsScreen = () => { + const renderHeader = () => ( + + Header of Settings Page + + ); + + const renderBody = () => ( +

Welcome to Settings!

+ ); + + return ( + + + + + ); +} + +export default SettingsScreen; \ No newline at end of file diff --git a/Frontend/src/components/screens/WatchLists/AssetsList.jsx b/Frontend/src/components/screens/WatchLists/AssetsList.jsx new file mode 100644 index 00000000..c5eb5352 --- /dev/null +++ b/Frontend/src/components/screens/WatchLists/AssetsList.jsx @@ -0,0 +1,130 @@ +import React, {useState} from 'react'; +import {CustomModal} from '../../common/index'; +import { + Container, + Typography, + Stack, + Button, + IconButton, + TextField +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import PropTypes from 'prop-types'; +import {CustomTable} from '../../common/index'; + + +const createData = (name, price, change) => { + return { name, price, change }; +} + +const assetsArrayTest = [ + [ + createData('Allianz', '212.25$', '-1.80%'), + createData('Fuijitsu', '212.25$', '-1.80%'), + createData('Capgemini', '212.25$', '-1.80%'), + createData('IBM', '212.25$', '-1.80%'), + ], + [ + createData('Thales', '212.25$', '-1.80%'), + createData('WW', '212.25$', '-1.80%'), + createData('DHBW', '212.25$', '-1.80%'), + createData('Netto', '212.25$', '-1.80%'), + ] +]; + +/** + * Show all the assets corresponding a watchlist + * @param props + * @returns {JSX.Element} + * @constructor + */ +const AssetsList = (props) => { + //const [assetsArray, setAssetsArray] = useState([]); + //const [selectedAssetIndex, setSelectedAssetIndex] = useState(0); + const [showAssetModal, setShowAssetModal] = useState(false); + const [asset, setAsset] = useState(''); + const [errorModal, setErrorModal] = useState(false); + + + const handleClose = () => { + setErrorModal(false); + setShowAssetModal(false); + } + + const addAsset = () => { + if (asset !== '') { + setShowAssetModal(false); + setErrorModal(false); + setAsset(''); + } else { + setErrorModal(true); + } + } + + const renderAddAssetModal = () => ( + handleClose()} + labelledby='add_asset-modal-title' + modalTitle='New asset' + modalBody={() => ( + setAsset(data.target.value)} + /> + )} + modalButton={() => ( + + )} + /> + ); + + return ( + + + + {props.watchListsArray[props.selectedListIndex]}: + + { + }} + className='pe-0' + > + + + + + + {renderAddAssetModal()} + + ); +} + +AssetsList.propTypes = { + watchListsArray: PropTypes.array, + selectedListIndex: PropTypes.number, +}; + +export default AssetsList; \ No newline at end of file diff --git a/Frontend/src/components/screens/WatchLists/DropdownMenu.jsx b/Frontend/src/components/screens/WatchLists/DropdownMenu.jsx new file mode 100644 index 00000000..2148fd4e --- /dev/null +++ b/Frontend/src/components/screens/WatchLists/DropdownMenu.jsx @@ -0,0 +1,107 @@ +import React, {useState} from 'react'; +import PropTypes from 'prop-types'; +import { + Button, + Menu, + MenuItem, + Divider, + Typography, + Container +} from '@mui/material'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; + +/** + * Component to display a dropdown with different actions + * @param props + * @returns {JSX.Element} + * @constructor + */ +const DropdownMenu = (props) => { + const [anchorEl, setAnchorEl] = useState(null); + const [open, setOpen] = useState(false); + + const handleClick = (event) => { + setOpen(true); + setAnchorEl(event.currentTarget); + props.setListDropdownIndex(props.listIndex); + }; + + const handleClose = () => { + setAnchorEl(null); + setOpen(false); + }; + + const onOptionClick = index => { + handleClose(); + props.functionOptions[index](); + }; + + return ( + + + + {props.menuOptions.map((option, index) => ( + + onOptionClick(index)} + > + + {props.iconOptions[index]} {option} + + + { + props.menuOptions.length - 1 === index + ? null + : + } + + ))} + + + ); +} + +DropdownMenu.propTypes = { + listIndex: PropTypes.number, + selectedListIndex: PropTypes.number, + listName: PropTypes.string, + setListDropdownIndex: PropTypes.func, + menuOptions: PropTypes.array, + iconOptions: PropTypes.array, + functionOptions: PropTypes.array +}; + +export default DropdownMenu; \ No newline at end of file diff --git a/Frontend/src/components/screens/WatchLists/WatchLists.jsx b/Frontend/src/components/screens/WatchLists/WatchLists.jsx new file mode 100644 index 00000000..afb9e040 --- /dev/null +++ b/Frontend/src/components/screens/WatchLists/WatchLists.jsx @@ -0,0 +1,265 @@ +import React, {useState} from 'react'; +import {CustomModal} from '../../common/index'; +import { + Container, + Box, + ListItem, + ListItemButton, + ListItemText, + List, + Typography, + Stack, + Button, + IconButton, + TextField +} from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import PropTypes from 'prop-types'; +import DropdownMenu from './DropdownMenu'; + +/** + * Show all the watchLists + * @param props + * @returns {JSX.Element} + * @constructor + */ +const WatchLists = (props) => { + const [addListModal, setAddListModal] = useState(false); + const [editListModal, setEditListModal] = useState(false); + const [removeListModal, setRemoveListModal] = useState(false); + const [watchlist, setWatchlist] = useState(''); + const [errorModal, setErrorModal] = useState(false); + const [listDropdownIndex, setListDropdownIndex] = useState(0); + + const handleWatchListItemClick = (event, index) => { + props.setSelectedListIndex(index); + }; + + const handleClose = () => { + setErrorModal(false); + setAddListModal(false); + setEditListModal(false); + setRemoveListModal(false); + } + + const onTextChange = data => { + if (addListModal || editListModal) { + setWatchlist(data.target.value); + data.target.value === '' ? setErrorModal(true) : setErrorModal(false); + } + } + + const addWatchlist = () => { + if (watchlist !== '') { + props.setWatchListsArray([...props.watchListsArray, watchlist]); + setAddListModal(false); + setErrorModal(false); + setWatchlist(''); + } else { + setErrorModal(true); + } + } + + const editWatchList = () => { + let tempArray = [...props.watchListsArray]; + if (watchlist !== '') { + if (watchlist !== props.watchListsArray[listDropdownIndex]) { + tempArray[listDropdownIndex] = watchlist; + props.setWatchListsArray(tempArray); + setEditListModal(false); + setErrorModal(false); + setWatchlist(''); + } else { + setErrorModal(true); + } + } else { + setErrorModal(true); + } + } + + const removeWatchList = () => { + if (props.watchListsArray.length !== 0) { + props.setWatchListsArray(props.watchListsArray.filter( + (element, index) => index !== listDropdownIndex + )); + setRemoveListModal(false); + } + }; + + const renderAddWatchlistModal = () => ( + handleClose()} + labelledby='add_list-modal-title' + describedby='add_list-modal-description' + modalTitle='New watchlist:' + modalBody={() => ( + onTextChange(data)} + error={errorModal} + helperText={errorModal ? '*Name cannot be empty' : false} + defaultValue='' + sx={{display: 'flex', flexGrow: 2}} + /> + )} + modalButton={() => ( + + )} + /> + ); + + const renderEditListModal = () => { + return ( + handleClose()} + labelledby='edit_list-modal-title' + describedby='edit_list-modal-description' + modalTitle='Rename watchlist:' + modalBody={() => ( + onTextChange(data)} + error={errorModal} + helperText={errorModal ? '*Name is the same or empty' : false} + defaultValue={`${props.watchListsArray[listDropdownIndex]}`} + sx={{display: 'flex', flexGrow: 2}} + /> + )} + modalButton={() => ( + + )} + /> + ); + } + + const renderRemoveListModal = () => { + return ( + handleClose()} + labelledby='remove_list-modal-title' + describedby='remove_list-modal-description' + modalTitle='Watchlist will be removed:' + modalButton={() => ( + + )} + /> + ); + } + + return ( + + + + Watchlists: + + setAddListModal(true)} + className='pe-0' + > + + + + + + + {props.watchListsArray.map((element, index) => ( + + handleWatchListItemClick(event, index)} + + > + + + , ]} + functionOptions={[ + () => setEditListModal(true), + () => setRemoveListModal(true) + ]} + /> + + ))} + {renderEditListModal()} + {renderAddWatchlistModal()} + {renderRemoveListModal()} + + + + ); +} + +WatchLists.propTypes = { + watchListsArray: PropTypes.array, + setWatchListsArray: PropTypes.func, + selectedListIndex: PropTypes.number, + setSelectedListIndex: PropTypes.func, +}; + +export default WatchLists; \ No newline at end of file diff --git a/Frontend/src/components/screens/WatchLists/WatchListsScreen.jsx b/Frontend/src/components/screens/WatchLists/WatchListsScreen.jsx new file mode 100644 index 00000000..0511af94 --- /dev/null +++ b/Frontend/src/components/screens/WatchLists/WatchListsScreen.jsx @@ -0,0 +1,62 @@ +import React, {useEffect, useState} from 'react'; +import ScreensTemplate from '../../ScreensTemplate'; +import {SearchField} from '../../common'; +import {Grid} from '@mui/material'; +import WatchLists from './WatchLists'; +import AssetsList from './AssetsList'; +import PropTypes from 'prop-types'; + +/** + * Component related to the watchLists page + * @param props + * @returns {JSX.Element} + * @constructor + */ +const WatchListsScreen = (props) => { + const [selectedListIndex, setSelectedListIndex] = useState(0); + + const renderHeader = () => ( + + ); + + const renderBody = () => ( + + + + + + + + + ); + + return ( + + + + + ); +} + +ScreensTemplate.propTypes = { + usePersistedState: PropTypes.func, + watchListsArray: PropTypes.array, + setWatchListsArray: PropTypes.func, +}; + +export default WatchListsScreen; \ No newline at end of file diff --git a/Frontend/src/components/screens/index.jsx b/Frontend/src/components/screens/index.jsx new file mode 100644 index 00000000..42a7e7c9 --- /dev/null +++ b/Frontend/src/components/screens/index.jsx @@ -0,0 +1,11 @@ +import Home from './Home'; +import DashboardScreen from './Dashboard/DashboardScreen'; +import WatchListsScreen from './WatchLists/WatchListsScreen'; +import SettingsScreen from './Settings/SettingsScreen'; + +export { + Home, + DashboardScreen, + WatchListsScreen, + SettingsScreen +} \ No newline at end of file diff --git a/Frontend/src/index.css b/Frontend/src/index.css index ec2585e8..da92873c 100644 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -1,13 +1,13 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } diff --git a/Frontend/src/index.tsx b/Frontend/src/index.jsx similarity index 82% rename from Frontend/src/index.tsx rename to Frontend/src/index.jsx index 8d48bcea..daa4fb6c 100644 --- a/Frontend/src/index.tsx +++ b/Frontend/src/index.jsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import 'bootstrap/dist/css/bootstrap.min.css'; import './index.css'; import App from './App'; @@ -8,7 +9,7 @@ import App from './App'; */ ReactDOM.render( - + , document.getElementById('root') ); diff --git a/Frontend/src/logo.svg b/Frontend/src/logo.svg index 9dfc1c05..63a2ad5f 100644 --- a/Frontend/src/logo.svg +++ b/Frontend/src/logo.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/Frontend/src/reportWebVitals.ts b/Frontend/src/reportWebVitals.ts index 49a2a16e..e28f08aa 100644 --- a/Frontend/src/reportWebVitals.ts +++ b/Frontend/src/reportWebVitals.ts @@ -1,15 +1,15 @@ -import { ReportHandler } from 'web-vitals'; +import {ReportHandler} from 'web-vitals'; const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } }; export default reportWebVitals; diff --git a/Frontend/src/routes/index.jsx b/Frontend/src/routes/index.jsx new file mode 100644 index 00000000..713a9f17 --- /dev/null +++ b/Frontend/src/routes/index.jsx @@ -0,0 +1,53 @@ +import React, {lazy, useEffect, useState} from "react"; +import {Route, Routes} from 'react-router-dom'; + +/** + * Optional the component could load lazily, allowing to borrow more + * time for it to completely load + */ +const Home = lazy(() => import('../components/screens/Home')); +const DashboardScreen = lazy(() => import('../components/screens/Dashboard/DashboardScreen')); +const WatchListsScreen = lazy(() => import('../components/screens/WatchLists/WatchListsScreen')); +const SettingsScreen = lazy(() => import('../components/screens/Settings/SettingsScreen')); + +/** + * Get value from key in local storage + * @param keyName + * @param defaultValue + * @returns {any|string} + */ +const persistState = (keyName, defaultValue) => { + const savedData = localStorage.getItem(keyName); + return savedData !== '' ? JSON.parse(savedData) : defaultValue; +}; + +/** + * Defines the routes for the different pages + * @constructor + */ +const AppRoutes = () => { + const [watchListsArray, setWatchListsArray] = useState(() => persistState('watchListsArray', [])); + + useEffect(() => { + localStorage.setItem("watchListsArray", JSON.stringify(watchListsArray)); + }, [watchListsArray]); + + return ( + + }/> + }/> + + } + /> + }/> + + ); +} + +export default AppRoutes; \ No newline at end of file diff --git a/Frontend/src/routes/index.tsx b/Frontend/src/routes/index.tsx deleted file mode 100644 index 0372856a..00000000 --- a/Frontend/src/routes/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, {lazy} from "react"; -import { Route, Routes } from 'react-router-dom'; - -/** - * Optional the component could load lazily, allowing to borrow more - * time for it to completely load - */ -const Home = lazy(() => import("../components/Home")); -const About = lazy(() => import("../components/About")); - -/** - * Defines the routes for the different pages - * @constructor - */ -let AppRoutes = () => ( - - } /> - } /> - -); - -export default AppRoutes; \ No newline at end of file