From ad3e9a23e59cac25b370aab8ca17df047bc8c80e Mon Sep 17 00:00:00 2001 From: Lukas Walter Date: Thu, 12 May 2022 18:48:32 +0200 Subject: [PATCH 1/8] Adds Error404 page, removes second analysis route from routes/index.jsx --- Frontend/src/components/screens/ErrorPage.jsx | 29 +++++++++++++++++++ Frontend/src/routes/index.jsx | 20 ++----------- 2 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 Frontend/src/components/screens/ErrorPage.jsx diff --git a/Frontend/src/components/screens/ErrorPage.jsx b/Frontend/src/components/screens/ErrorPage.jsx new file mode 100644 index 00000000..c1620470 --- /dev/null +++ b/Frontend/src/components/screens/ErrorPage.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import {Typography, Container, List, ListItem} from '@mui/material'; +import {Link} from 'react-router-dom'; + +const ErrorPage = () => ( + + Application logo + + + Error 404 + + + + Sorry we can't find that page! + + + + + + Click here to go back! + + +); + +export default ErrorPage; \ No newline at end of file diff --git a/Frontend/src/routes/index.jsx b/Frontend/src/routes/index.jsx index c6b7101d..dc701af5 100644 --- a/Frontend/src/routes/index.jsx +++ b/Frontend/src/routes/index.jsx @@ -3,6 +3,7 @@ import {Route, Routes} from 'react-router-dom'; import Impressum from '../components/screens/Impressum'; import AGB from '../components/screens/AGB'; import Privacy from '../components/screens/Privacy'; +import ErrorPage from '../components/screens/ErrorPage'; import { DailyDataArraysService, @@ -483,24 +484,6 @@ const AppRoutes = () => { setMessageType={setMessageType} />} /> - } - /> { }/> }/> }/> + }/> ); } From 37e8d4fc3c5be236713fc105068a4be130d85c1f Mon Sep 17 00:00:00 2001 From: Lukas Walter Date: Thu, 12 May 2022 18:52:12 +0200 Subject: [PATCH 2/8] Changes 404Page Link Size --- Frontend/src/components/screens/ErrorPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/src/components/screens/ErrorPage.jsx b/Frontend/src/components/screens/ErrorPage.jsx index c1620470..ddce41aa 100644 --- a/Frontend/src/components/screens/ErrorPage.jsx +++ b/Frontend/src/components/screens/ErrorPage.jsx @@ -20,7 +20,7 @@ const ErrorPage = () => ( - + Click here to go back! From 0d426c561ad3d1b14bcf78b111adab44cdaac726 Mon Sep 17 00:00:00 2001 From: juli-and Date: Fri, 13 May 2022 02:36:10 +0200 Subject: [PATCH 3/8] featuree: added benchi bot --- Frontend/package-lock.json | 338 ++++++++++++++++++++ Frontend/package.json | 1 + Frontend/public/assets/images/chatbot.png | Bin 0 -> 33366 bytes Frontend/src/benchi/ActionProvider.js | 124 +++++++ Frontend/src/benchi/MessageParser.js | 51 +++ Frontend/src/benchi/config.js | 22 ++ Frontend/src/components/ScreensTemplate.jsx | 12 +- Frontend/src/components/common/Benchi.jsx | 88 +++++ Frontend/src/components/common/index.jsx | 6 +- 9 files changed, 639 insertions(+), 3 deletions(-) create mode 100644 Frontend/public/assets/images/chatbot.png create mode 100644 Frontend/src/benchi/ActionProvider.js create mode 100644 Frontend/src/benchi/MessageParser.js create mode 100644 Frontend/src/benchi/config.js create mode 100644 Frontend/src/components/common/Benchi.jsx diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 42eab7c1..82ae76b0 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -30,6 +30,7 @@ "prop-types": "^15.8.1", "react": "^17.0.2", "react-chartjs-2": "^4.0.0", + "react-chatbot-kit": "^2.0.1", "react-dom": "^17.0.2", "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", @@ -5819,6 +5820,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "node_modules/clean-css": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", @@ -14300,6 +14306,194 @@ "react": "^16.8.0 || ^17.0.0" } }, + "node_modules/react-chatbot-kit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-chatbot-kit/-/react-chatbot-kit-2.0.1.tgz", + "integrity": "sha512-OnGmlBx5WHEz3T/WsX0d3ofC8TC/DsD9FTGs5crv+fpFyfsuL/Z91gUkXtSQ1+reml286Ei3Vh5mvM454k2+kA==", + "dependencies": { + "classnames": "^2.2.6", + "css-loader": "^3.5.1", + "react-conditionally-render": "^1.0.2" + } + }, + "node_modules/react-chatbot-kit/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-chatbot-kit/node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dependencies": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/react-chatbot-kit/node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-chatbot-kit/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/react-chatbot-kit/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/react-chatbot-kit/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/react-chatbot-kit/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/react-chatbot-kit/node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-chatbot-kit/node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dependencies": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-chatbot-kit/node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-chatbot-kit/node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "node_modules/react-chatbot-kit/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/react-chatbot-kit/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/react-chatbot-kit/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-conditionally-render": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-conditionally-render/-/react-conditionally-render-1.0.2.tgz", + "integrity": "sha512-CtjIgaLHVDSgHis3gv/PT/8EnD6GPUL8PrhUjh7DP6S5Y3p56dGu7y2nVg6pYv1kv+fGznRhRmX3assr/vRw3A==" + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -21386,6 +21580,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "clean-css": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", @@ -27402,6 +27601,145 @@ "integrity": "sha512-q8bgWzKoFvBvD7YcjT/hXG8jt55TaMAuJ1dmI3tKFJ7CijUWYz4pIfOhkTI6PBTwqu/pmeWsClBRd/7HiWzN1g==", "requires": {} }, + "react-chatbot-kit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-chatbot-kit/-/react-chatbot-kit-2.0.1.tgz", + "integrity": "sha512-OnGmlBx5WHEz3T/WsX0d3ofC8TC/DsD9FTGs5crv+fpFyfsuL/Z91gUkXtSQ1+reml286Ei3Vh5mvM454k2+kA==", + "requires": { + "classnames": "^2.2.6", + "css-loader": "^3.5.1", + "react-conditionally-render": "^1.0.2" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "requires": { + "postcss": "^7.0.14" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "react-conditionally-render": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-conditionally-render/-/react-conditionally-render-1.0.2.tgz", + "integrity": "sha512-CtjIgaLHVDSgHis3gv/PT/8EnD6GPUL8PrhUjh7DP6S5Y3p56dGu7y2nVg6pYv1kv+fGznRhRmX3assr/vRw3A==" + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", diff --git a/Frontend/package.json b/Frontend/package.json index 02d86049..2b25707d 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -31,6 +31,7 @@ "prop-types": "^15.8.1", "react": "^17.0.2", "react-chartjs-2": "^4.0.0", + "react-chatbot-kit": "^2.0.1", "react-dom": "^17.0.2", "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", diff --git a/Frontend/public/assets/images/chatbot.png b/Frontend/public/assets/images/chatbot.png new file mode 100644 index 0000000000000000000000000000000000000000..85969e446dcb58e28c4cd2c779ee5d373fa64939 GIT binary patch literal 33366 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 literal 0 HcmV?d00001 diff --git a/Frontend/src/benchi/ActionProvider.js b/Frontend/src/benchi/ActionProvider.js new file mode 100644 index 00000000..2116c4ae --- /dev/null +++ b/Frontend/src/benchi/ActionProvider.js @@ -0,0 +1,124 @@ +class ActionProvider { + + strategies = { + BuyAndHold : {experience: 'beginner', risk: 'low', active: true, effort: 'low', duration: 'longterm'}, + Index : {experience: 'beginner', risk: 'low', active: false, effort: 'low',duration: 'longterm'}, + Size : {experience: 'beginner', risk: 'medium', active: true, effort: 'low', duration: 'undefined'}, + Growth : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Value : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Long : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Short :{experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, + Trend : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'mediumterm'} , + Dividend : {experience: 'beginner', risk: 'medium', active: true, effort: 'medium', duration: 'undefined'}, + Anticyclical : {experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, + Cyclical : {experience: 'undefined', risk: 'high', active: true, effort: 'high', duration: 'undefined'} + }; + + strategieDescriptions = { + BuyAndHold : {experience: 'beginner', risk: 'low', active: true, effort: 'low', duration: 'longterm'}, + Index : {experience: 'beginner', risk: 'low', active: false, effort: 'low',duration: 'longterm'}, + Size : {experience: 'beginner', risk: 'medium', active: true, effort: 'low', duration: 'undefined'}, + Growth : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Value : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Long : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, + Short :{experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, + Trend : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'mediumterm'} , + Dividend : {experience: 'beginner', risk: 'medium', active: true, effort: 'medium', duration: 'undefined'}, + Anticyclical : {experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, + Cyclical : {experience: 'undefined', risk: 'high', active: true, effort: 'high', duration: 'undefined'} + }; + + questions = + [ + `Are you a beginner, intermediate or advanced investor?`, + `How much risk are you willing to take?`, + `Would you like to invest active?`, + `How much effort do you want to put into your investing?`, + `Do you want to invest shortterm, mediumterm or longterm?` + ]; + + answerAfterQuestion = + [ + {true: `Nice to hear, let's start!`, medium:`Let's give it a try anyway!`, false: `Okay, you can come back any time!`}, + {true:`So you alreay have a lot of experience, impressive!`, medium: `So you are an intermediate investor!`, false: `No problem, there are also great strategies for beginners!`}, + {true: `Okay so you are a risk taker!`, medium:`Medium risk, nice choice!`, false: `Just a tiny bit of risk, okay!`}, + {true: `Okay, so you are an active investor!`, medium:`Then let's go with investing passivly!`, false: `Passive investing, nice choice!`}, + {true: `So you don't mind getting your hands dirty!`, medium:`Medium effort it is!`, false: `So you would rather chill at the beach than look through company reports!`}, + {true: `Looks like you're looking for quick money!`, medium:`Okay, we're going with mediumterm investing!`, false: `All things come to him who waits!`}, + ]; + + preferencesArray = ['experience', 'risk', 'active', 'effort', 'duration']; + + constructor( + createChatBotMessage, + setStateFunc, + createClientMessage, + stateRef, + createCustomMessage, + ...rest + ) { + this.createChatBotMessage = createChatBotMessage; + this.setState = setStateFunc; + this.createClientMessage = createClientMessage; + this.stateRef = stateRef; + this.createCustomMessage = createCustomMessage; + } + + handleChitChat(answer) { + const botMessage = this.createChatBotMessage(answer); + + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botMessage], + })); + } + + handleAnswer(answer, questionNr) { + console.log(questionNr); + if (questionNr === -1) { + const botAnswer = this.createChatBotMessage(`Hi! I'm Benchi! Are you interested in finding the right investment strategy for you together?`); + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer], + })); + questionNr = questionNr + 1; + return; + } + if (questionNr > 0 && questionNr < 5) { + this.saveUserPreference(answer, ); + } else { + if (answer === 'false') { + questionNr = -2; + } + } + const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]); + questionNr = questionNr + 1; + if (preference === 'active') { + answer = answer === 'medium' ? false : answer; + } + const botNewQuestion = this.createChatBotMessage(this.questions[questionNr]); + + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + questionNr: questionNr, + userPreferences: {...userPreferences, [this.preferencesArray[questionNr-1]]: answer} + })); + } + + findStrategyForUser() { + + } + + handleNoAnswer() { + const botMessage = this.createChatBotMessage(`Sorry, but I didn't understand you.`); + + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botMessage], + })); + this.messageCnt = this.messageCnt + 1; + } +} + + export default ActionProvider; \ No newline at end of file diff --git a/Frontend/src/benchi/MessageParser.js b/Frontend/src/benchi/MessageParser.js new file mode 100644 index 00000000..118ada55 --- /dev/null +++ b/Frontend/src/benchi/MessageParser.js @@ -0,0 +1,51 @@ +class MessageParser { + constructor(actionProvider, state) { + this.actionProvider = actionProvider; + this.state = {...state, questionNr: 0}; + } + + parse(message) { + this.fetchAnswer(message); + } + + interpret(data) { + const chitChatAnswer = data[0].answer; + const answer = data[1] !== undefined ? data[1].answer : undefined; + if (answer !== undefined) { + this.actionProvider.handleAnswer(answer, this.state.questionNr); + return; + } + if (chitChatAnswer === 'true' || chitChatAnswer === 'false' || chitChatAnswer === 'medium') { + this.actionProvider.handleAnswer(chitChatAnswer, this.state.questionNr); + return; + } + if (chitChatAnswer === 'No answer found') { + this.actionProvider.handleNoAnswer(); + return; + } + this.actionProvider.handleChitChat(chitChatAnswer); + } + + fetchAnswer(message) { + var url = "https://westeurope.api.cognitive.microsoft.com/language/:query-knowledgebases?projectName=benchi-QnA&api-version=2021-10-01&deploymentName=production"; + + var xhr = new XMLHttpRequest(); + xhr.open("POST", url); + xhr.responseType = 'json'; + + xhr.setRequestHeader("Ocp-Apim-Subscription-Key", "6ee6c9b8f982470abc210317cbf8d89c"); + xhr.setRequestHeader("Content-Type", "application/json"); + + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + this.interpret(xhr.response.answers); + } + }; + + var data = `{"top":3,"question":"${message}","includeUnstructuredSources":true,"confidenceScoreThreshold":"0.5","answerSpanRequest":{"enable":true,"topAnswersWithSpan":1,"confidenceScoreThreshold":"0.5"}}`; + + xhr.send(data); + } +} + +export default MessageParser; \ No newline at end of file diff --git a/Frontend/src/benchi/config.js b/Frontend/src/benchi/config.js new file mode 100644 index 00000000..ffc389b9 --- /dev/null +++ b/Frontend/src/benchi/config.js @@ -0,0 +1,22 @@ +import { createChatBotMessage } from 'react-chatbot-kit'; + +const botName = 'Benchi'; + +const config = { + initialMessages: [createChatBotMessage(`Hi! I'm ${botName}! Are you interested in finding the right investment strategy for you together?`)], + customComponents: { + // Replaces the default header + header: () =>
Talk to Benchi!
+ }, + botName: botName, + customStyles: { + botMessageBox: { + backgroundColor: '#493f35', + }, + chatButton: { + backgroundColor: '#5ccc9d', + }, + }, +}; + +export default config; \ No newline at end of file diff --git a/Frontend/src/components/ScreensTemplate.jsx b/Frontend/src/components/ScreensTemplate.jsx index e52994ba..6123c3e8 100644 --- a/Frontend/src/components/ScreensTemplate.jsx +++ b/Frontend/src/components/ScreensTemplate.jsx @@ -4,7 +4,7 @@ import {AppBar, Box, CssBaseline, Grid, Toolbar, Alert} from '@mui/material'; import MenuIcon from '@mui/icons-material/Menu'; import IconButton from '@mui/material/IconButton'; import PropTypes from 'prop-types'; -import {SideNavLeft, Footer, SearchField} from './common/index'; +import {SideNavLeft, Footer, SearchField, Benchi} from './common/index'; import SearchResultsTable from './common/SearchResultsTable'; const drawerWidth = 14; // This is the value in rem units, for responsiveness @@ -18,6 +18,7 @@ const ScreensTemplate = props => { const location = useLocation() const [openInMobile, setOpenInMobile] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + // Resetting the search when changing routes useEffect(() => { @@ -146,6 +147,15 @@ const ScreensTemplate = props => { {props.messageType !== undefined && props.statusMessage !== undefined && showStatusMessage(props.messageType, props.statusMessage)} + + + { + const [showBot, toggleBot] = useState(false); + + const saveMessages = (messages, HTMLString) => { + console.log(messages) + localStorage.setItem('chatMessages', JSON.stringify(HTMLString)); + }; + + const loadMessages = () => { + const messages = JSON.parse(localStorage.getItem('chatMessages')); + return messages; + }; + + return ( + + + + {showBot && ( + + + + )} + + + toggleBot((prev) => !prev)} + sx={{ + + }} + > + + + + + + ); + } + +export default Benchi; \ No newline at end of file diff --git a/Frontend/src/components/common/index.jsx b/Frontend/src/components/common/index.jsx index 05856c7a..f24287f9 100644 --- a/Frontend/src/components/common/index.jsx +++ b/Frontend/src/components/common/index.jsx @@ -2,7 +2,8 @@ import SideNavLeft from './SideNavLeft'; import SearchField from './SearchField'; import CustomModal from './CustomModal'; import AssetDetailItem from './AssetDetailItem'; -import DoughnutChart from './DoughnutChart' +import DoughnutChart from './DoughnutChart'; +import Benchi from './Benchi'; import Footer from './Footer'; export { @@ -11,5 +12,6 @@ export { CustomModal, AssetDetailItem, DoughnutChart, - Footer + Footer, + Benchi } \ No newline at end of file From bbeb52f936452c8b5f5772a4ddd184b0d0c07b0c Mon Sep 17 00:00:00 2001 From: juli-and Date: Fri, 13 May 2022 03:36:52 +0200 Subject: [PATCH 4/8] feature: Benchi completed --- Frontend/src/benchi/ActionProvider.js | 150 ++++++++++++++++++-------- Frontend/src/benchi/MessageParser.js | 7 +- 2 files changed, 111 insertions(+), 46 deletions(-) diff --git a/Frontend/src/benchi/ActionProvider.js b/Frontend/src/benchi/ActionProvider.js index 2116c4ae..246896c0 100644 --- a/Frontend/src/benchi/ActionProvider.js +++ b/Frontend/src/benchi/ActionProvider.js @@ -1,31 +1,31 @@ class ActionProvider { strategies = { - BuyAndHold : {experience: 'beginner', risk: 'low', active: true, effort: 'low', duration: 'longterm'}, - Index : {experience: 'beginner', risk: 'low', active: false, effort: 'low',duration: 'longterm'}, - Size : {experience: 'beginner', risk: 'medium', active: true, effort: 'low', duration: 'undefined'}, - Growth : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Value : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Long : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Short :{experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, - Trend : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'mediumterm'} , - Dividend : {experience: 'beginner', risk: 'medium', active: true, effort: 'medium', duration: 'undefined'}, - Anticyclical : {experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, - Cyclical : {experience: 'undefined', risk: 'high', active: true, effort: 'high', duration: 'undefined'} + BuyAndHold : {experience: 'false', risk: 'false', active: 'true', effort: 'false', duration: 'true'}, + Index : {experience: 'false', risk: 'false', active: 'false', effort: 'false',duration: 'true'}, + Size : {experience: 'false', risk: 'medium', active: 'true', effort: 'false', duration: undefined}, + Growth : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'}, + Value : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'}, + Long : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'true'}, + Short :{experience: 'true', risk: 'true', active: 'true', effort: 'true', duration: 'false'}, + Trend : {experience: 'true', risk: 'medium', active: 'true', effort: 'true', duration: 'medium'} , + Dividend : {experience: 'false', risk: 'medium', active: 'true', effort: 'medium', duration: undefined}, + Anticyclical : {experience: 'true', risk: 'true', active: 'true', effort: 'true', duration: 'false'}, + Cyclical : {experience: undefined, risk: 'true', active: 'true', effort: 'true', duration: undefined} }; strategieDescriptions = { - BuyAndHold : {experience: 'beginner', risk: 'low', active: true, effort: 'low', duration: 'longterm'}, - Index : {experience: 'beginner', risk: 'low', active: false, effort: 'low',duration: 'longterm'}, - Size : {experience: 'beginner', risk: 'medium', active: true, effort: 'low', duration: 'undefined'}, - Growth : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Value : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Long : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'longterm'}, - Short :{experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, - Trend : {experience: 'advanced', risk: 'medium', active: true, effort: 'high', duration: 'mediumterm'} , - Dividend : {experience: 'beginner', risk: 'medium', active: true, effort: 'medium', duration: 'undefined'}, - Anticyclical : {experience: 'advanced', risk: 'high', active: true, effort: 'high', duration: 'shortterm'}, - Cyclical : {experience: 'undefined', risk: 'high', active: true, effort: 'high', duration: 'undefined'} + BuyAndHold : 'In this strategy, you buy individual stocks and hold them for at least five to 20 years. The hope is for long-term growth through diversified single stock purchases. Short-term fluctuations are avoided by the long investment period.', + Index : 'You do not invest in individual stocks, but in indices or so-called ETFs (Exchange Traded Fund). This strategy works in principle like the buy and hold method, but ETFs contain many different securities according to certain indices and are therefore already diversified. This saves you a lot of effort before and during your loading investment.', + Size : 'In the size strategy you go by the size of the company. It is assumed that the larger the company, the more secure the price gain and the smaller the expected fluctuations. Thus, the strategy is based on stability.', + Growth : 'In the growth strategy you invest in stocks that are likely to increase in price over the next few years. This is judged on the basis of stock market data.', + Value : 'With the Growth strategy you invest in stocks that are likely to rise in price in the next few years. This is judged on the basis of a detailed analysis of the company.', + Long : 'In the Go-Long strategy you buy a stock and hold it in the hope that it will rise in the long term.', + Short :'You sell a stock from your holdings and try to buy it again shortly after at the low. The difference can therefore be considered a profit.', + Trend : 'In the trend determined strategy you try to predict the development of the stock by drawing certain trend lines, average values or by market technical theories.', + Dividend : 'You bet on dividend payments and price gains.', + Anticyclical : 'When everyone is buying, you sell and vice versa. You try to buy at the low and sell at the high, against the current.', + Cyclical : 'You try to go with the flow and invest in securities that will predictably perform well.' }; questions = @@ -44,7 +44,7 @@ class ActionProvider { {true: `Okay so you are a risk taker!`, medium:`Medium risk, nice choice!`, false: `Just a tiny bit of risk, okay!`}, {true: `Okay, so you are an active investor!`, medium:`Then let's go with investing passivly!`, false: `Passive investing, nice choice!`}, {true: `So you don't mind getting your hands dirty!`, medium:`Medium effort it is!`, false: `So you would rather chill at the beach than look through company reports!`}, - {true: `Looks like you're looking for quick money!`, medium:`Okay, we're going with mediumterm investing!`, false: `All things come to him who waits!`}, + {true: `All things come to him who waits!`, medium:`Okay, we're going with mediumterm investing!`, false: `Looks like you're looking for quick money!`}, ]; preferencesArray = ['experience', 'risk', 'active', 'effort', 'duration']; @@ -73,41 +73,105 @@ class ActionProvider { })); } - handleAnswer(answer, questionNr) { - console.log(questionNr); + handleAnswer(answer, questionNr, userPreferences) { + if (questionNr === undefined) { + questionNr = 0; + userPreferences = {experience: '', risk: '', active: false, effort: '', duration: ''}; + } + if (questionNr === 5) { + userPreferences = {...userPreferences, duration: answer}; + const Strategy = this.findStrategyForUser(userPreferences); + const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]); + const botStrategy = this.createChatBotMessage(`The best strategy for you is ${Strategy}`); + const botStrategyExplanation = this.createChatBotMessage(this.strategieDescriptions[Strategy]); + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botStrategy, botStrategyExplanation], + questionNr: questionNr + 1 + })); + return; + } + if (questionNr > 5) { + userPreferences = {...userPreferences, duration: answer}; + const Strategy = this.findStrategyForUser(userPreferences); + const botStrategy = this.createChatBotMessage(`The best strategy for you is ${Strategy}`); + const botStrategyExplanation = this.createChatBotMessage(this.strategieDescriptions[Strategy]); + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botStrategy, botStrategyExplanation], + questionNr: questionNr + 1 + })); + return; + } + if (questionNr === -1) { const botAnswer = this.createChatBotMessage(`Hi! I'm Benchi! Are you interested in finding the right investment strategy for you together?`); this.setState((prev) => ({ ...prev, messages: [...prev.messages, botAnswer], + 'questionNr': (questionNr + 1) })); - questionNr = questionNr + 1; return; } + + if (this.preferencesArray[questionNr-1] === 'active') { + answer = answer === 'medium' ? false : answer; + } + const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]); + const botNewQuestion = this.createChatBotMessage(this.questions[questionNr]); + if (questionNr > 0 && questionNr < 5) { - this.saveUserPreference(answer, ); + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + 'questionNr': questionNr + 1, + 'userPreferences': {...userPreferences, [this.preferencesArray[questionNr-1]]: answer} + })); } else { - if (answer === 'false') { + if (questionNr === 0 && answer === 'false') { questionNr = -2; + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + 'questionNr': questionNr, + })); + return; } + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + questionNr: questionNr + 1 + })); } - const botAnswer = this.createChatBotMessage(this.answerAfterQuestion[questionNr][answer]); - questionNr = questionNr + 1; - if (preference === 'active') { - answer = answer === 'medium' ? false : answer; - } - const botNewQuestion = this.createChatBotMessage(this.questions[questionNr]); - - this.setState((prev) => ({ - ...prev, - messages: [...prev.messages, botAnswer, botNewQuestion], - questionNr: questionNr, - userPreferences: {...userPreferences, [this.preferencesArray[questionNr-1]]: answer} - })); } - findStrategyForUser() { - + findStrategyForUser(userPreferences) { + let strategiesFit = {}; + const strategiesKeys = Object.keys(this.strategies); + for (let index = 0; index < strategiesKeys.length; index++) { + const strategieKey = strategiesKeys[index]; + const strategy = this.strategies[strategieKey]; + let fit = 0; + for (let i = 0; i < this.preferencesArray.length; i++) { + const preference = this.preferencesArray[i]; + if (strategy[preference] === undefined || strategy[preference] === userPreferences[preference]) { + fit++; + } + } + strategiesFit[strategieKey] = fit; + } + console.log(strategiesFit); + let bestStrategy = ''; + let bestFit = 0; + for (let index = 0; index < strategiesKeys.length; index++) { + const strategieKey = strategiesKeys[index]; + if (bestFit < strategiesFit[strategieKey]) { + bestFit = strategiesFit[strategieKey]; + bestStrategy = strategieKey; + } + } + console.log(bestStrategy); + return bestStrategy; } handleNoAnswer() { diff --git a/Frontend/src/benchi/MessageParser.js b/Frontend/src/benchi/MessageParser.js index 118ada55..b4a018c2 100644 --- a/Frontend/src/benchi/MessageParser.js +++ b/Frontend/src/benchi/MessageParser.js @@ -1,10 +1,11 @@ class MessageParser { constructor(actionProvider, state) { this.actionProvider = actionProvider; - this.state = {...state, questionNr: 0}; + this.state = {...state}; } parse(message) { + console.log(this.state) this.fetchAnswer(message); } @@ -12,11 +13,11 @@ class MessageParser { const chitChatAnswer = data[0].answer; const answer = data[1] !== undefined ? data[1].answer : undefined; if (answer !== undefined) { - this.actionProvider.handleAnswer(answer, this.state.questionNr); + this.actionProvider.handleAnswer(answer, this.state.questionNr, this.state.userPreferences); return; } if (chitChatAnswer === 'true' || chitChatAnswer === 'false' || chitChatAnswer === 'medium') { - this.actionProvider.handleAnswer(chitChatAnswer, this.state.questionNr); + this.actionProvider.handleAnswer(chitChatAnswer, this.state.questionNr, this.state.userPreferences); return; } if (chitChatAnswer === 'No answer found') { From 52708fc8887c4d6d24f358035173b1cb2269b476 Mon Sep 17 00:00:00 2001 From: juli-and Date: Fri, 13 May 2022 03:37:49 +0200 Subject: [PATCH 5/8] refactor: colors --- .../components/screens/AssetDetails/AssetCard.jsx | 2 +- .../components/screens/AssetDetails/AssetChart.jsx | 10 +++++----- .../{AssetPerfofmance.jsx => AssetPerformance.jsx} | 6 +++--- .../components/screens/AssetDetails/AssetValue.jsx | 10 +++++----- .../components/screens/Dashboard/DashboardScreen.jsx | 2 +- .../components/screens/Dashboard/PortfolioCharts.jsx | 6 +----- .../screens/Dashboard/PortfolioPerformance.jsx | 6 +++--- .../screens/Dashboard/PortfolioValuechart.jsx | 12 ++++++------ 8 files changed, 25 insertions(+), 29 deletions(-) rename Frontend/src/components/screens/AssetDetails/{AssetPerfofmance.jsx => AssetPerformance.jsx} (95%) diff --git a/Frontend/src/components/screens/AssetDetails/AssetCard.jsx b/Frontend/src/components/screens/AssetDetails/AssetCard.jsx index ed8a9600..3f8f3fff 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetCard.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetCard.jsx @@ -5,7 +5,7 @@ import Masterdata from './Masterdata'; import {Container, Card, Grid} from '@mui/material'; import PropTypes from 'prop-types'; import SwitchButtons from './SwitchButtons'; -import AssetPerformance from './AssetPerfofmance'; +import AssetPerformance from './AssetPerformance'; import AssetValue from './AssetValue'; /** diff --git a/Frontend/src/components/screens/AssetDetails/AssetChart.jsx b/Frontend/src/components/screens/AssetDetails/AssetChart.jsx index 5dc2fb38..1ffaf25e 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetChart.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetChart.jsx @@ -120,7 +120,7 @@ export const AssetChart = props => { datasets: [{ label: props.symbol, data: data, - borderColor: 'rgb(59 151 210)', // light blue + borderColor: 'rgb(78, 185, 111)', // green backgroundColor: 'rgb(78 185 111)',// light green yAxisId: 'stockpriceAxis', pointRadius: 0 @@ -173,9 +173,9 @@ export const AssetChart = props => { return title; }, labelTextColor: function (context) { - let color = 'red'; + let color = 'rgb(211, 116, 34)'; if (context.dataset.data[context.dataIndex] > context.dataset.data[context.dataIndex + 1]) { - color = 'green'; + color = 'rgb(78, 185, 111)'; } return color; } @@ -185,7 +185,7 @@ export const AssetChart = props => { display: false }, crosshair: { - color: 'grey', + color: 'rgb(137,128,128)', //grey width: 2 } } @@ -198,7 +198,7 @@ export const AssetChart = props => { // Manipulate data according to view changeData(props.view, data, labels, options, setup); return ( - + diff --git a/Frontend/src/components/screens/AssetDetails/AssetPerfofmance.jsx b/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx similarity index 95% rename from Frontend/src/components/screens/AssetDetails/AssetPerfofmance.jsx rename to Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx index 4ac9a08e..3bcc44af 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetPerfofmance.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx @@ -66,8 +66,8 @@ export const AssetPerformance = props => { datasets: [{ label: 'Performance', data: perfData, - borderColor: 'rgb(153, 102, 51)', - backgroundColor: 'rgb(153, 102, 51)', + borderColor: ' rgb(78, 185, 111)', + backgroundColor: ' rgb(78, 185, 111)', pointRadius: 0 }] } @@ -125,7 +125,7 @@ export const AssetPerformance = props => { } changeData(props.view, perfData, labels, options, setup) return ( - + { diff --git a/Frontend/src/components/screens/AssetDetails/AssetValue.jsx b/Frontend/src/components/screens/AssetDetails/AssetValue.jsx index 5f85c19a..65bcbdad 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetValue.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetValue.jsx @@ -63,14 +63,14 @@ export const AssetValue = props => { datasets: [{ label: 'Value', data: valueData, - borderColor: 'rgb(153, 102, 51)', - backgroundColor: 'rgb(153, 102, 51)', + borderColor: ' rgb(78, 185, 111)', + backgroundColor: ' rgb(78, 185, 111)', pointRadius: 0 }, { label: 'Invested', data: investedData, - borderColor: 'rgb(0, 102, 51)', - backgroundColor: 'rgb(0, 102, 51)', + borderColor: 'rgb(59, 151, 210)', + backgroundColor: 'rgb(59, 151, 210)', pointRadius: 0 }] } @@ -135,7 +135,7 @@ export const AssetValue = props => { changeData(props.view, valueData, labels, options, setup, 0) changeData(props.view, investedData, labels, options, setup, 1) return ( - + diff --git a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx index acb90545..3d0a87a9 100644 --- a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx +++ b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx @@ -42,7 +42,7 @@ const DashboardScreen = props => { - + ); diff --git a/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx b/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx index 3ff7ae9f..7dad8d44 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx @@ -5,18 +5,14 @@ import PortfolioValueChart from './PortfolioValuechart'; export const PortfolioCharts = props => { return ( - - - + - - ) } diff --git a/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx b/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx index 60f8b43a..7983b225 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx @@ -66,8 +66,8 @@ export const PortfolioPerformance = props => { datasets: [{ label: 'Performance', data: perfData, - borderColor: 'rgb(153, 102, 51)', - backgroundColor: 'rgb(153, 102, 51)', + borderColor: 'rgb(78, 185, 111)', + backgroundColor: 'rgb(78, 185, 111)', pointRadius: 0 }] } @@ -125,7 +125,7 @@ export const PortfolioPerformance = props => { } changeData(props.view, perfData, labels, options, setup) return ( - + { datasets: [{ label: 'Value', data: valueData, - borderColor: 'rgb(153, 102, 51)', - backgroundColor: 'rgb(153, 102, 51)', + borderColor: ' rgb(78, 185, 111)', + backgroundColor: 'rgb(78, 185, 111)', pointRadius: 0 }, { label: 'Invested', data: investedData, - borderColor: 'rgb(0, 102, 51)', - backgroundColor: 'rgb(0, 102, 51)', + borderColor: 'rgb(59, 151, 210)', + backgroundColor: 'rgb(59, 151, 210)', pointRadius: 0 }] } @@ -137,8 +137,8 @@ export const PortfolioValueChart = props => { changeData(props.view, valueData, labels, options, setup, 0) changeData(props.view, investedData, labels, options, setup, 1) return ( - - + + From df8b28fa6b37b997fa03e3b692d698c9c2dfc43d Mon Sep 17 00:00:00 2001 From: juli-and Date: Fri, 13 May 2022 03:46:41 +0200 Subject: [PATCH 6/8] refactor: color --- Frontend/src/components/common/DoughnutChart.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Frontend/src/components/common/DoughnutChart.jsx b/Frontend/src/components/common/DoughnutChart.jsx index be370494..88df38c6 100644 --- a/Frontend/src/components/common/DoughnutChart.jsx +++ b/Frontend/src/components/common/DoughnutChart.jsx @@ -32,12 +32,13 @@ const DoughnutChart = props => { const labels = props.labels; const valueData = props.data; - const colorOffset = 50; - const colors = labels.map((_, index) => { - const hue = colorOffset + index * 137.503; // rotates for distinguishable colors - return `hsl(${hue},60%,60%)`; - } - ); + const colors = [ + 'rgba(59, 151, 210, 1)', + 'rgba(241, 155, 31, 1)', + 'rgba(229, 126, 37, 1)', + 'rgba(239, 195, 25, 1)', + 'rgba(78, 185, 111, 1)', + ] const data = { labels: labels, From 135b885f39555e2939c0b96d6b90d93d380f5e91 Mon Sep 17 00:00:00 2001 From: juli-and Date: Fri, 13 May 2022 03:56:11 +0200 Subject: [PATCH 7/8] refactor: chatbot answer --- Frontend/src/benchi/ActionProvider.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Frontend/src/benchi/ActionProvider.js b/Frontend/src/benchi/ActionProvider.js index 246896c0..0172474f 100644 --- a/Frontend/src/benchi/ActionProvider.js +++ b/Frontend/src/benchi/ActionProvider.js @@ -78,6 +78,15 @@ class ActionProvider { questionNr = 0; userPreferences = {experience: '', risk: '', active: false, effort: '', duration: ''}; } + if (questionNr === -1) { + const botAnswer = this.createChatBotMessage(`Hi! I'm Benchi! Are you interested in finding the right investment strategy for you together?`); + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer], + 'questionNr': (questionNr + 1) + })); + return; + } if (questionNr === 5) { userPreferences = {...userPreferences, duration: answer}; const Strategy = this.findStrategyForUser(userPreferences); @@ -103,16 +112,6 @@ class ActionProvider { })); return; } - - if (questionNr === -1) { - const botAnswer = this.createChatBotMessage(`Hi! I'm Benchi! Are you interested in finding the right investment strategy for you together?`); - this.setState((prev) => ({ - ...prev, - messages: [...prev.messages, botAnswer], - 'questionNr': (questionNr + 1) - })); - return; - } if (this.preferencesArray[questionNr-1] === 'active') { answer = answer === 'medium' ? false : answer; @@ -129,12 +128,11 @@ class ActionProvider { })); } else { if (questionNr === 0 && answer === 'false') { - questionNr = -2; this.setState((prev) => ({ ...prev, - messages: [...prev.messages, botAnswer, botNewQuestion], - 'questionNr': questionNr, - })); + messages: [...prev.messages, botAnswer], + 'questionNr': -1, + })); return; } this.setState((prev) => ({ From 4c6716dbeef889a6d834143965bc09f6f9a133f4 Mon Sep 17 00:00:00 2001 From: cedscho <93663333+cedscho@users.noreply.github.com> Date: Fri, 13 May 2022 04:04:55 +0200 Subject: [PATCH 8/8] Update index.jsx --- Frontend/src/routes/index.jsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Frontend/src/routes/index.jsx b/Frontend/src/routes/index.jsx index dc701af5..06c6b97b 100644 --- a/Frontend/src/routes/index.jsx +++ b/Frontend/src/routes/index.jsx @@ -611,24 +611,6 @@ const AppRoutes = () => { setMessageType={setMessageType} />} /> - } - /> }/> }/> }/>