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 00000000..85969e44 Binary files /dev/null and b/Frontend/public/assets/images/chatbot.png differ diff --git a/Frontend/src/benchi/ActionProvider.js b/Frontend/src/benchi/ActionProvider.js new file mode 100644 index 00000000..0172474f --- /dev/null +++ b/Frontend/src/benchi/ActionProvider.js @@ -0,0 +1,186 @@ +class ActionProvider { + + strategies = { + 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 : '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 = + [ + `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: `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']; + + 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, userPreferences) { + if (questionNr === undefined) { + 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); + 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 (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.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + 'questionNr': questionNr + 1, + 'userPreferences': {...userPreferences, [this.preferencesArray[questionNr-1]]: answer} + })); + } else { + if (questionNr === 0 && answer === 'false') { + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer], + 'questionNr': -1, + })); + return; + } + this.setState((prev) => ({ + ...prev, + messages: [...prev.messages, botAnswer, botNewQuestion], + questionNr: questionNr + 1 + })); + } + } + + 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() { + 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..b4a018c2 --- /dev/null +++ b/Frontend/src/benchi/MessageParser.js @@ -0,0 +1,52 @@ +class MessageParser { + constructor(actionProvider, state) { + this.actionProvider = actionProvider; + this.state = {...state}; + } + + parse(message) { + console.log(this.state) + 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, this.state.userPreferences); + return; + } + if (chitChatAnswer === 'true' || chitChatAnswer === 'false' || chitChatAnswer === 'medium') { + this.actionProvider.handleAnswer(chitChatAnswer, this.state.questionNr, this.state.userPreferences); + 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/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, 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 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/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 ( - - + + diff --git a/Frontend/src/components/screens/ErrorPage.jsx b/Frontend/src/components/screens/ErrorPage.jsx new file mode 100644 index 00000000..ddce41aa --- /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 321e1d84..f07e81a0 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, @@ -488,24 +489,6 @@ const AppRoutes = () => { setMessageType={setMessageType} />} /> - } - /> { setMessageType={setMessageType} />} /> - } - /> }/> }/> }/> }/> + }/> ); }