From 854dcada1de71ec196833f7873bc477921eb50dd Mon Sep 17 00:00:00 2001 From: DenysShchypt Date: Tue, 4 Jun 2024 23:39:51 +0300 Subject: [PATCH 1/2] work with AreaChart --- frontend/package-lock.json | 715 ++++++++++-------- frontend/package.json | 4 + frontend/src/common/types/assets/index.ts | 46 +- frontend/src/components/SideBar/index.tsx | 1 - frontend/src/components/TopBar/index.tsx | 1 + .../src/components/charts/areaChart/index.tsx | 74 ++ frontend/src/pages/Home/index.tsx | 93 ++- frontend/src/store/slice/assets/index.ts | 13 +- frontend/src/store/thunks/assets/index.ts | 44 +- frontend/src/utils/axios/index.ts | 16 +- 10 files changed, 656 insertions(+), 351 deletions(-) create mode 100644 frontend/src/components/charts/areaChart/index.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7d8c93e8..833338cb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,7 @@ "@mui/icons-material": "^5.15.18", "@mui/lab": "^5.0.0-alpha.170", "@mui/material": "^5.15.19", + "@mui/x-charts": "^7.6.1", "@reduxjs/toolkit": "^1.9.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -21,7 +22,9 @@ "@types/jest": "^27.5.2", "@types/node": "^16.18.97", "axios": "^1.7.2", + "chart.js": "^4.4.3", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-redux": "^8.0.5", @@ -32,6 +35,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@faker-js/faker": "^8.4.1", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@typescript-eslint/eslint-plugin": "^5.42.0", @@ -2578,9 +2582,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2645,6 +2649,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, "node_modules/@floating-ui/core": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", @@ -3620,6 +3640,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -3911,6 +3936,44 @@ } } }, + "node_modules/@mui/x-charts": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.6.1.tgz", + "integrity": "sha512-LuAVQX4lzhmsqk2NRdWCP+aQZ8kRwG7KOy8ulf8hR5NI5J1VsiOtH41J+qgV3uAC6hDMWL7fTgffrRDFTMci/g==", + "dependencies": { + "@babel/runtime": "^7.24.6", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.15.15", + "@mui/utils": "^5.15.14", + "@react-spring/rafz": "^9.7.3", + "@react-spring/web": "^9.7.3", + "clsx": "^2.1.1", + "d3-color": "^3.1.0", + "d3-delaunay": "^6.0.4", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.2.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3966,17 +4029,17 @@ } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.13.tgz", - "integrity": "sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", + "integrity": "sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==", "dev": true, "dependencies": { - "ansi-html-community": "^0.0.8", + "ansi-html": "^0.0.9", "core-js-pure": "^3.23.3", "error-stack-parser": "^2.0.6", "html-entities": "^2.1.0", "loader-utils": "^2.0.4", - "schema-utils": "^3.0.0", + "schema-utils": "^4.2.0", "source-map": "^0.7.3" }, "engines": { @@ -4031,6 +4094,71 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-spring/animated": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", + "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", + "dependencies": { + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", + "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.3.tgz", + "integrity": "sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ==" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", + "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", + "dependencies": { + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", + "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" + }, + "node_modules/@react-spring/web": { + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", + "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", + "dependencies": { + "@react-spring/animated": "~9.7.3", + "@react-spring/core": "~9.7.3", + "@react-spring/shared": "~9.7.3", + "@react-spring/types": "~9.7.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", @@ -4838,9 +4966,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.2.tgz", - "integrity": "sha512-dPSEQElyVJ97BuGduAqQjpBocZWAs0GR94z+ptL7JXQJeJdHw2WBG3EWdFrK36b8Q6j8P4cXOMhgUoi0IIfIsg==", + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", + "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", "dev": true, "dependencies": { "@types/node": "*", @@ -5722,6 +5850,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-html": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", + "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -5952,16 +6092,19 @@ } }, "node_modules/array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/arraybuffer.prototype.slice": { @@ -6685,9 +6828,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001625", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001625.tgz", - "integrity": "sha512-4KE9N2gcRH+HQhpeiRZXd+1niLB/XNLAhSy4z7fI8EzcbcPoAqjNInxVHTiTwWfTIV4w096XG8OtCOCQQKPv3w==", + "version": "1.0.30001627", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001627.tgz", + "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", "dev": true, "funding": [ { @@ -6743,6 +6886,17 @@ "node": ">=10" } }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -7271,59 +7425,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", - "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7582,6 +7683,111 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -7654,9 +7860,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -7781,6 +7987,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8078,9 +8292,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.784", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.784.tgz", - "integrity": "sha512-9CZwh+sDrhDAeOEFh8s3PqwduzTyYIeYwZolc1b9ENAUt3ePu7R1sJSCWr/820ISssRxCJUyHI9Wb7j+0Uo1AA==", + "version": "1.4.788", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz", + "integrity": "sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==", "dev": true }, "node_modules/emittery": { @@ -8853,34 +9067,6 @@ "webpack": "^5.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", - "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/eslint-webpack-plugin/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8904,31 +9090,6 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/eslint-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -9404,6 +9565,24 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -10658,6 +10837,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -13994,59 +14181,6 @@ "webpack": "^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", - "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -15418,9 +15552,9 @@ } }, "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.3.tgz", + "integrity": "sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==", "dev": true, "bin": { "yaml": "bin.mjs" @@ -16498,6 +16632,15 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -17222,6 +17365,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", @@ -17452,23 +17600,58 @@ } }, "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" } }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -18688,6 +18871,24 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -19425,59 +19626,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", - "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-dev-server": { "version": "4.15.2", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", @@ -19537,59 +19685,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", - "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-dev-server/node_modules/ws": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", @@ -19658,6 +19753,24 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5554e56a..ec990b3c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "@mui/icons-material": "^5.15.18", "@mui/lab": "^5.0.0-alpha.170", "@mui/material": "^5.15.19", + "@mui/x-charts": "^7.6.1", "@reduxjs/toolkit": "^1.9.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -16,7 +17,9 @@ "@types/jest": "^27.5.2", "@types/node": "^16.18.97", "axios": "^1.7.2", + "chart.js": "^4.4.3", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-redux": "^8.0.5", @@ -27,6 +30,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.14.5", + "@faker-js/faker": "^8.4.1", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", "@typescript-eslint/eslint-plugin": "^5.42.0", diff --git a/frontend/src/common/types/assets/index.ts b/frontend/src/common/types/assets/index.ts index a1f28947..e51d9536 100644 --- a/frontend/src/common/types/assets/index.ts +++ b/frontend/src/common/types/assets/index.ts @@ -1,11 +1,45 @@ export interface IAsset { - asset_id: string; - name: string; - price_usd: number; - volume_1hrs_usd: number; + Data: { + ID: number; + LOGO_URL: string; + NAME: string; + ASSET_DESCRIPTION: string; + PRICE_USD: number; + SEO_TITLE: string; + TOTAL_MKT_CAP_USD: number; + CIRCULATING_MKT_CAP_USD: number; + SEO_DESCRIPTION: string; + PRICE_USD_LAST_UPDATE_TS: number; + }; +} + +export interface IAssetPriceData { + close: number; + high: number; + low: number; + open: number; + time: number; +} + +export interface IAssetPrice { + Aggregated: boolean; + TimeFrom: number; + TimeTo: number; + Data: IAssetPriceData[]; } export interface IAssetsState { - assets: IAsset[]; - favoriteAssets: IAsset[]; + assets: IAssetFavoriteResponses[]; + favoriteAssets: IAssetFavoriteResponses[]; + historyPrice: IAssetPriceResponses[]; +} + +export interface IAssetFavoriteResponses { + name: string; + data: IAsset; +} + +export interface IAssetPriceResponses { + name: string; + data: IAssetPrice; } diff --git a/frontend/src/components/SideBar/index.tsx b/frontend/src/components/SideBar/index.tsx index 51470668..86ae3934 100644 --- a/frontend/src/components/SideBar/index.tsx +++ b/frontend/src/components/SideBar/index.tsx @@ -1,7 +1,6 @@ import { FC, useEffect, useState } from 'react'; import { ChevronLeftOutlined, - ChevronRightOutlined, LogoutOutlined, PortraitOutlined, } from '@mui/icons-material'; diff --git a/frontend/src/components/TopBar/index.tsx b/frontend/src/components/TopBar/index.tsx index decbb471..3dc61934 100644 --- a/frontend/src/components/TopBar/index.tsx +++ b/frontend/src/components/TopBar/index.tsx @@ -25,6 +25,7 @@ const TopBarComponent: FC = ( props: ITopBarProps, ): JSX.Element => { const { user } = useAppSelector(state => state.auth.user); + console.log(user); const theme = useTheme(); const colorMode: any = useContext(ColorModeContext); const { isOpen, setIsOpen } = props; diff --git a/frontend/src/components/charts/areaChart/index.tsx b/frontend/src/components/charts/areaChart/index.tsx new file mode 100644 index 00000000..e60c1568 --- /dev/null +++ b/frontend/src/components/charts/areaChart/index.tsx @@ -0,0 +1,74 @@ +import { FC } from 'react'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Filler, + Legend, +} from 'chart.js'; +import type { ChartData, ChartOptions, ScriptableContext } from 'chart.js'; +import { Line } from 'react-chartjs-2'; +import { IAssetPriceData } from '../../../common/types/assets'; + +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Filler, + Legend, +); +interface IAreaChartProps { + data: IAssetPriceData[]; +} +const options: ChartOptions<'line'> = { + responsive: true, + scales: { + x: { + grid: { + display: false, + }, + }, + y: { + display: false, + grid: { + display: false, + }, + }, + }, + plugins: { + legend: { + display: false, + }, + }, +}; + +const AreaChart: FC = ({ data }) => { + console.log(data); + const values: ChartData<'line'> = { + labels: data.map(el => new Date(el.time).getUTCDate()), + datasets: [ + { + label: 'Price', + data: data.map(el => el.close), + fill: 'start', + backgroundColor: (context: ScriptableContext<'line'>) => { + const ctx = context.chart.ctx; + const gradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height); + gradient.addColorStop(0, '#C1EF00'); + gradient.addColorStop(1, '#232323'); + return gradient; + }, + }, + ], + }; + return ; +}; + +export default AreaChart; diff --git a/frontend/src/pages/Home/index.tsx b/frontend/src/pages/Home/index.tsx index ccad625d..ae33e84d 100644 --- a/frontend/src/pages/Home/index.tsx +++ b/frontend/src/pages/Home/index.tsx @@ -1,42 +1,81 @@ -import { Box, Grid, useTheme } from '@mui/material'; -import React, { useEffect, useMemo, useRef } from 'react'; +import { Grid, useTheme } from '@mui/material'; +import { FC, useCallback, useEffect, useMemo, useRef } from 'react'; import { useAppDispatch, useAppSelector } from '../../utils/hook'; -import { getFavoriteAssets } from '../../store/thunks/assets'; -import { IAsset } from '../../common/types/assets'; +import { getFavoriteAssets, getPricePeriod } from '../../store/thunks/assets'; +import { + IAssetFavoriteResponses, + IAssetPriceData, + IAssetPriceResponses, +} from '../../common/types/assets'; import { BoxStyled } from './styles'; +import AreaChart from '../../components/charts/areaChart'; -const Home: React.FC = (): JSX.Element => { - const { favoriteAssets } = useAppSelector(state => state.assets); - console.log(favoriteAssets); - const dispatch = useAppDispatch(); - const favoriteAssetsName = useMemo(() => 'BTC;ETH', []); +const Home: FC = (): JSX.Element => { const useFavoriteRef = useRef(false); const theme = useTheme(); + const favoriteAssets: IAssetFavoriteResponses[] = useAppSelector( + state => state.assets.favoriteAssets, + ); + const historyPrice: IAssetPriceResponses[] = useAppSelector( + state => state.assets.historyPrice, + ); + const dispatch = useAppDispatch(); + const favoriteAssetsName = useMemo(() => ['BTC', 'ETH'], []); + + const fetchDataAsset = useCallback( + (data: string[]) => { + data.forEach((element: string) => { + dispatch(getFavoriteAssets(element)); + dispatch(getPricePeriod(element)); + }); + }, + [dispatch], + ); useEffect(() => { if (useFavoriteRef.current) return; useFavoriteRef.current = true; - dispatch(getFavoriteAssets(favoriteAssetsName)); - }, []); + fetchDataAsset(favoriteAssetsName); + }, [dispatch, favoriteAssetsName]); - const renderFavoriteBlok = favoriteAssets.map((element: IAsset) => { - return ( - - - -

{element.name}

-
-

${element.price_usd.toFixed(4)}

-

${element.volume_1hrs_usd}

-
-
- - Hello + const filterFavoriteArray = favoriteAssets.filter( + (value, index, self) => + index === self.findIndex(t => t.name === value.name), + ); + const filterHistoryPrice: IAssetPriceResponses[] = historyPrice.filter( + (value, index, self) => + index === self.findIndex(t => t.name === value.name), + ); + const renderFavoriteBlok = filterFavoriteArray.map( + (element: IAssetFavoriteResponses) => { + const { ID, NAME, PRICE_USD, CIRCULATING_MKT_CAP_USD } = + element.data.Data; + const history: IAssetPriceResponses[] = filterHistoryPrice.filter( + elem => elem.name === element.name, + ); + + return ( + + + +

{NAME}

+
+

${PRICE_USD.toFixed(4)}

+

+ ${CIRCULATING_MKT_CAP_USD.toFixed(0)} +

+
+
+ + {history.length !== 0 && ( + + )} +
-
- ); - }); + ); + }, + ); return ( diff --git a/frontend/src/store/slice/assets/index.ts b/frontend/src/store/slice/assets/index.ts index aa5b2a8a..1670d850 100644 --- a/frontend/src/store/slice/assets/index.ts +++ b/frontend/src/store/slice/assets/index.ts @@ -1,10 +1,11 @@ import { createSlice } from '@reduxjs/toolkit'; -import { getFavoriteAssets } from '../../thunks/assets'; +import { getFavoriteAssets, getPricePeriod } from '../../thunks/assets'; import { IAssetsState } from '../../../common/types/assets'; const initialState: IAssetsState = { assets: [], favoriteAssets: [], + historyPrice: [], }; const assetsSlice = createSlice({ @@ -12,9 +13,13 @@ const assetsSlice = createSlice({ initialState, reducers: {}, extraReducers: builder => { - builder.addCase(getFavoriteAssets.fulfilled, (state, action) => { - state.favoriteAssets = action.payload; - }); + builder + .addCase(getFavoriteAssets.fulfilled, (state, action) => { + state.favoriteAssets.push(action.payload); + }) + .addCase(getPricePeriod.fulfilled, (state, action) => { + state.historyPrice.push(action.payload); + }); }, }); diff --git a/frontend/src/store/thunks/assets/index.ts b/frontend/src/store/thunks/assets/index.ts index 12d88d40..f2678d75 100644 --- a/frontend/src/store/thunks/assets/index.ts +++ b/frontend/src/store/thunks/assets/index.ts @@ -1,20 +1,46 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { getDataCoinMarket } from '../../../utils/axios'; +import { instanceAssets, instanceHistory } from '../../../utils/axios'; import { IError } from '../../../common/types/errors'; -import { IAsset } from '../../../common/types/assets'; +import { + IAssetFavoriteResponses, + IAssetPriceResponses, +} from '../../../common/types/assets'; -export const getFavoriteAssets = createAsyncThunk( - 'assets/getFavoriteAssets', +export const getFavoriteAssets = createAsyncThunk< + IAssetFavoriteResponses, + string +>('assets/getFavoriteAssets', async (data: string, { rejectWithValue }) => { + try { + const response = await instanceAssets.get('/asset/v1/data/by/symbol', { + params: { + asset_symbol: data, + }, + }); + return { name: data, data: response.data }; + } catch (error) { + const typedError = error as IError; + if (typedError.response && typedError.response.data?.message) { + return rejectWithValue(typedError.response.data.message); + } else { + return rejectWithValue(typedError.message); + } + } +}); + +export const getPricePeriod = createAsyncThunk( + 'assets/getPricePeriod', async (data: string, { rejectWithValue }) => { try { - const response = await getDataCoinMarket.get('/v1/assets', { + const response = await instanceHistory.get(`data/v2/histohour`, { params: { - filter_asset_id: data, - // include_supply: true, - // filter_asset_id: data, + fsym: data, + tsym: 'USD', + limit: 10, + aggregate: 24, }, }); - return response.data; + + return { name: data, data: response.data.Data }; } catch (error) { const typedError = error as IError; if (typedError.response && typedError.response.data?.message) { diff --git a/frontend/src/utils/axios/index.ts b/frontend/src/utils/axios/index.ts index a2dbfb04..38057c75 100644 --- a/frontend/src/utils/axios/index.ts +++ b/frontend/src/utils/axios/index.ts @@ -5,11 +5,21 @@ export const instance = axios.create({ timeout: 1000, headers: { 'X-Custom-Header': 'foobar' }, }); -export const getDataCoinMarket = axios.create({ - baseURL: 'https://rest.coinapi.io', +export const instanceAssets = axios.create({ + baseURL: 'https://data-api.cryptocompare.com', timeout: 5000, headers: { - 'X-CoinAPI-Key': '904F960C-9C32-4B2E-B643-1F461BFDF54F', + authorization: + 'c486f48338229247d2c810410a4021ed92f17525f0d01ebc450f04230160f7be', + Accept: 'application/json', + }, +}); +export const instanceHistory = axios.create({ + baseURL: 'https://min-api.cryptocompare.com', + timeout: 5000, + headers: { + authorization: + 'c486f48338229247d2c810410a4021ed92f17525f0d01ebc450f04230160f7be', Accept: 'application/json', }, }); From f2381de53fee4dc4ae5c4c07d486f6ecbc8082d8 Mon Sep 17 00:00:00 2001 From: DenysShchypt Date: Wed, 5 Jun 2024 19:00:20 +0300 Subject: [PATCH 2/2] add chart data --- frontend/package-lock.json | 9 +++++++ frontend/package.json | 1 + frontend/src/components/TopBar/index.tsx | 4 ++- .../src/components/charts/areaChart/index.tsx | 25 ++++++++++++++++--- frontend/src/pages/Home/index.tsx | 3 +-- frontend/src/store/thunks/auth/index.ts | 3 +++ 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 833338cb..da19b871 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "@types/node": "^16.18.97", "axios": "^1.7.2", "chart.js": "^4.4.3", + "moment": "^2.30.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", @@ -14229,6 +14230,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index ec990b3c..96afbe4b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "@types/node": "^16.18.97", "axios": "^1.7.2", "chart.js": "^4.4.3", + "moment": "^2.30.1", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", diff --git a/frontend/src/components/TopBar/index.tsx b/frontend/src/components/TopBar/index.tsx index 3dc61934..8dc74156 100644 --- a/frontend/src/components/TopBar/index.tsx +++ b/frontend/src/components/TopBar/index.tsx @@ -37,7 +37,9 @@ const TopBarComponent: FC = ( className="menuIcon" onClick={() => setIsOpen(!isOpen)} /> - Welcome {user.firstName} + + Welcome {sessionStorage.getItem('name')} + diff --git a/frontend/src/components/charts/areaChart/index.tsx b/frontend/src/components/charts/areaChart/index.tsx index e60c1568..f589692b 100644 --- a/frontend/src/components/charts/areaChart/index.tsx +++ b/frontend/src/components/charts/areaChart/index.tsx @@ -27,10 +27,12 @@ ChartJS.register( interface IAreaChartProps { data: IAssetPriceData[]; } + const options: ChartOptions<'line'> = { responsive: true, scales: { x: { + display: false, grid: { display: false, }, @@ -46,13 +48,28 @@ const options: ChartOptions<'line'> = { legend: { display: false, }, + title: { + display: true, + text: new Date().toLocaleDateString('en-us', { + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + }), + }, }, }; const AreaChart: FC = ({ data }) => { - console.log(data); const values: ChartData<'line'> = { - labels: data.map(el => new Date(el.time).getUTCDate()), + labels: data.map(el => { + const timeDay = Number(`${el.time}000`); + return new Date(timeDay).toLocaleDateString('en-us', { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + }), datasets: [ { label: 'Price', @@ -60,7 +77,7 @@ const AreaChart: FC = ({ data }) => { fill: 'start', backgroundColor: (context: ScriptableContext<'line'>) => { const ctx = context.chart.ctx; - const gradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height); + const gradient = ctx.createLinearGradient(0, 50, 0, 180); gradient.addColorStop(0, '#C1EF00'); gradient.addColorStop(1, '#232323'); return gradient; @@ -68,7 +85,7 @@ const AreaChart: FC = ({ data }) => { }, ], }; - return ; + return ; }; export default AreaChart; diff --git a/frontend/src/pages/Home/index.tsx b/frontend/src/pages/Home/index.tsx index ae33e84d..f23a67a1 100644 --- a/frontend/src/pages/Home/index.tsx +++ b/frontend/src/pages/Home/index.tsx @@ -4,7 +4,6 @@ import { useAppDispatch, useAppSelector } from '../../utils/hook'; import { getFavoriteAssets, getPricePeriod } from '../../store/thunks/assets'; import { IAssetFavoriteResponses, - IAssetPriceData, IAssetPriceResponses, } from '../../common/types/assets'; import { BoxStyled } from './styles'; @@ -38,7 +37,7 @@ const Home: FC = (): JSX.Element => { fetchDataAsset(favoriteAssetsName); }, [dispatch, favoriteAssetsName]); - const filterFavoriteArray = favoriteAssets.filter( + const filterFavoriteArray: IAssetFavoriteResponses[] = favoriteAssets.filter( (value, index, self) => index === self.findIndex(t => t.name === value.name), ); diff --git a/frontend/src/store/thunks/auth/index.ts b/frontend/src/store/thunks/auth/index.ts index d9f24899..685ba793 100644 --- a/frontend/src/store/thunks/auth/index.ts +++ b/frontend/src/store/thunks/auth/index.ts @@ -11,6 +11,8 @@ export const loginUsers = createAsyncThunk< try { const user = await instance.post('auth/login', data); sessionStorage.setItem('token', user.data.token); + sessionStorage.setItem('name', user.data.user.firstName); + console.log(user.data); return user.data; } catch (error) { const typedError = error as IError; @@ -29,6 +31,7 @@ export const registerUsers = createAsyncThunk< try { const newUser = await instance.post('auth/register', data); sessionStorage.setItem('token', newUser.data.token); + sessionStorage.setItem('name', newUser.data.user.firstName); return newUser.data; } catch (error) { const typedError = error as IError;