diff --git a/Backend/.env b/Backend/.env new file mode 100644 index 00000000..b1fe361e --- /dev/null +++ b/Backend/.env @@ -0,0 +1,7 @@ +#API Keys +APIKEY1 = ZSQ57OXG4YKUA0B8 #API Key Hakan; +APIKEY2 = MB6DE4CNFFYGP4M7 #API Key Steffen; +APIKEY3 = NOGQ7D1A1RHDGMU4 #API Key Cedrik +APIKEY4 = ZSQ57OXG4YKUA0B8 #API Key Hakan; #RESERVIERT FÜR MONATLICH +APIKEY5 = MB6DE4CNFFYGP4M7 #API Key Steffen; +APIKEY6 = NOGQ7D1A1RHDGMU4 #API Key Cedrik; \ No newline at end of file diff --git a/Backend/.gitignore b/Backend/.gitignore index 6a7d6d8e..fa6a20f5 100644 --- a/Backend/.gitignore +++ b/Backend/.gitignore @@ -73,7 +73,6 @@ web_modules/ .yarn-integrity # dotenv environment variable files -.env .env.development.local .env.test.local .env.production.local diff --git a/Backend/package-lock.json b/Backend/package-lock.json index 304db1a9..f1b1cc96 100644 --- a/Backend/package-lock.json +++ b/Backend/package-lock.json @@ -1,17 +1,17 @@ { - "name": "package.json", + "name": "benchmarket-backend", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "package.json", + "name": "benchmarket-backend", "version": "1.0.0", - "license": "ISC", "dependencies": { "axios": "^0.26.1", "body-parser": "^1.19.2", "convert-csv-to-json": "^1.3.3", + "dotenv": "^16.0.1", "express": "^4.17.3", "linebyline": "^1.3.0", "match-sorter": "^6.3.1", @@ -643,6 +643,14 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -2839,6 +2847,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", diff --git a/Backend/package.json b/Backend/package.json index 3aa56854..26762213 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -1,5 +1,5 @@ { - "name": "package.json", + "name": "benchmarket-backend", "version": "1.0.0", "description": "BenchmarketBackendServer", "main": "server.js", @@ -8,11 +8,11 @@ "start": "node server.js" }, "author": "Steffen Kruschina @fefifef", - "license": "ISC", "dependencies": { "axios": "^0.26.1", "body-parser": "^1.19.2", "convert-csv-to-json": "^1.3.3", + "dotenv": "^16.0.1", "express": "^4.17.3", "linebyline": "^1.3.0", "match-sorter": "^6.3.1", diff --git a/Backend/server.js b/Backend/server.js index f6b467f6..4af2afc4 100644 --- a/Backend/server.js +++ b/Backend/server.js @@ -1,3 +1,4 @@ +require('dotenv').config(); const express = require('express'); const bodyParser = require('body-parser'); const fs = require('fs'); @@ -17,12 +18,12 @@ let accessURL = '*'; const apiKeys =[ - 'ZSQ57OXG4YKUA0B8', //API Key Hakan; - 'MB6DE4CNFFYGP4M7', //API Key Steffen; - 'NOGQ7D1A1RHDGMU4', //API Key Cedrik - 'ZSQ57OXG4YKUA0B8', //API Key Hakan; //RESERVIERT FÜR MONATLICH - 'MB6DE4CNFFYGP4M7', //API Key Steffen; - 'NOGQ7D1A1RHDGMU4' //API Key Cedrik + process.env.APIKEY1, + process.env.APIKEY2, + process.env.APIKEY3, + process.env.APIKEY4, + process.env.APIKEY5, + process.env.APIKEY6 ] let apiKeyIndex = 0; // let apiKey; diff --git a/Frontend/.env b/Frontend/.env index 665e1871..d2fe3ef3 100644 --- a/Frontend/.env +++ b/Frontend/.env @@ -1 +1,2 @@ -REACT_APP_BASEURL = http://localhost:3001 \ No newline at end of file +REACT_APP_BASEURL = http://localhost:3001 +REACT_APP_BOTBACKENDURL = http://benchmarket.germanywestcentral.cloudapp.azure.com:1880 \ No newline at end of file diff --git a/Frontend/.env.production b/Frontend/.env.production index b0ce0d55..78643652 100644 --- a/Frontend/.env.production +++ b/Frontend/.env.production @@ -1 +1,2 @@ -REACT_APP_BASEURL = http://benchmarket.germanywestcentral.cloudapp.azure.com:3001 \ No newline at end of file +REACT_APP_BASEURL = http://benchmarket.germanywestcentral.cloudapp.azure.com:3001 +REACT_APP_BOTBACKENDURL = http://benchmarket.germanywestcentral.cloudapp.azure.com:1880 \ No newline at end of file diff --git a/Frontend/README.md b/Frontend/README.md index d3b3ef60..f38f96b5 100644 --- a/Frontend/README.md +++ b/Frontend/README.md @@ -10,7 +10,7 @@ To avoid incompatibility's issues it is recommended to [install](https://nodejs. - Node (version 16.14.2) - npm (version 8.5.0) -Once the installation is finished, for load the used dependencies it is required to run: +Once the installation is finished, for loading the used dependencies it is required to run: ####`npm install` ## Available Scripts diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 82ae76b0..b7e14b36 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -1,11 +1,11 @@ { - "name": "finanzen-projekt", + "name": "bench-market", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "finanzen-projekt", + "name": "bench-market", "version": "0.1.0", "dependencies": { "@date-io/date-fns": "^2.13.1", @@ -14473,14 +14473,6 @@ "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", @@ -27723,11 +27715,6 @@ "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", diff --git a/Frontend/public/favicon.ico b/Frontend/public/favicon.ico index a11777cc..ee93e25f 100644 Binary files a/Frontend/public/favicon.ico and b/Frontend/public/favicon.ico differ diff --git a/Frontend/public/index.html b/Frontend/public/index.html index aa069f27..81bf65eb 100644 --- a/Frontend/public/index.html +++ b/Frontend/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + Bench:market diff --git a/Frontend/public/logo192.png b/Frontend/public/logo192.png index fc44b0a3..2ecb5d4a 100644 Binary files a/Frontend/public/logo192.png and b/Frontend/public/logo192.png differ diff --git a/Frontend/public/logo512.png b/Frontend/public/logo512.png index a4e47a65..e7f9526f 100644 Binary files a/Frontend/public/logo512.png and b/Frontend/public/logo512.png differ diff --git a/Frontend/public/manifest.json b/Frontend/public/manifest.json index 080d6c77..fb7615ec 100644 --- a/Frontend/public/manifest.json +++ b/Frontend/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Bench:market", + "name": "Bench:market | SWE Finanzen", "icons": [ { "src": "favicon.ico", diff --git a/Frontend/src/App.css b/Frontend/src/App.css index 31aceeee..1eb69a87 100644 --- a/Frontend/src/App.css +++ b/Frontend/src/App.css @@ -1,5 +1,6 @@ .App { text-align: center; + color: #30302f } .App-logo { @@ -48,3 +49,12 @@ .hovered { color: green; } + +@keyframes spin { + from { + transform:rotate(0deg); + } + to { + transform:rotate(360deg); + } +} diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx index 6383d045..34da9136 100644 --- a/Frontend/src/App.jsx +++ b/Frontend/src/App.jsx @@ -3,6 +3,7 @@ import {CircularProgress} from '@mui/material'; import {BrowserRouter as Router} from 'react-router-dom'; import AppRoutes from './routes'; import './App.css'; +import { Loading } from './components/common'; /** * App root component with link navigations and routes @@ -17,7 +18,7 @@ const App = () => ( transform: 'translate(-50%, -50%)' }} > - + }> diff --git a/Frontend/src/benchi-chatbot/MessageParser.js b/Frontend/src/benchi-chatbot/MessageParser.js index 13f9747a..0417357b 100644 --- a/Frontend/src/benchi-chatbot/MessageParser.js +++ b/Frontend/src/benchi-chatbot/MessageParser.js @@ -32,7 +32,7 @@ class MessageParser { async fetchAnswer(message) { try { - const response = await fetch(`http://benchmarket.germanywestcentral.cloudapp.azure.com:1880/interpretMessage?message=${message}`, {mode:'cors'}) + const response = await fetch(`${process.env.REACT_APP_BOTBACKENDURL}/interpretMessage?message=${message}`, {mode:'cors'}) const json = await response.json(); return json.answers; } catch (e) { diff --git a/Frontend/src/benchi-chatbot/TextToSpeech.js b/Frontend/src/benchi-chatbot/TextToSpeech.js index a148ddb7..22952a56 100644 --- a/Frontend/src/benchi-chatbot/TextToSpeech.js +++ b/Frontend/src/benchi-chatbot/TextToSpeech.js @@ -6,7 +6,7 @@ class TextToSpeech { let audio; try { - const response = await fetch(`http://benchmarket.germanywestcentral.cloudapp.azure.com:1880/textToSpeech?text=${answer}`, {mode:'cors'}) + const response = await fetch(`${process.env.REACT_APP_BOTBACKENDURL}/textToSpeech?text=${answer}`, {mode:'cors'}) const arrayBuffer = await response.arrayBuffer(); TextToSpeech.ctx.close(); TextToSpeech.ctx = new AudioContext(); diff --git a/Frontend/src/benchi-chatbot/config.js b/Frontend/src/benchi-chatbot/config.js index 63d3dd93..1dd6e70a 100644 --- a/Frontend/src/benchi-chatbot/config.js +++ b/Frontend/src/benchi-chatbot/config.js @@ -12,7 +12,6 @@ const config = { state: { questionNr: 0, userPreferences: {experience: '', risk: '', active: false, effort: '', duration: ''}, - textToSpeech: new TextToSpeech() }, customComponents: { // Replaces the default header diff --git a/Frontend/src/components/common/AssetDetailItem.jsx b/Frontend/src/components/common/AssetDetailItem.jsx index 15388c3b..88b0362f 100644 --- a/Frontend/src/components/common/AssetDetailItem.jsx +++ b/Frontend/src/components/common/AssetDetailItem.jsx @@ -4,6 +4,8 @@ import {Avatar, Box, Container, ListItem, Typography} from '@mui/material'; import DropdownMenu from '../screens/WatchLists/DropdownMenu'; import PropTypes from 'prop-types'; +import Colors from './Colors'; + /** * Formats the date (day and month) * @param activityDate @@ -51,7 +53,7 @@ const AssetDetailItem = props => ( borderBottomLeftRadius: props.itemsArray && props.index === props.itemsArray.length - 1 && '0.5rem', borderBottomRightRadius: props.itemsArray && props.index === props.itemsArray.length - 1 && '0.5rem', backgroundColor: 'white', - borderLeftColor: props.activities ? props.colorsArray[4][props.row.type][1] : props.colorsArray[props.row.symbol.hashCode() % 4], + borderLeftColor: props.activities ? props.colorsArray[0][props.row.type][1] : Colors.COLORPALETTE[props.row.symbol.hashCode() % 10], boxShadow: props.index === 0 ? 'rgb(0 0 0 / 15%) 0px -6px 6px -6px' : props.itemsArray && props.index === props.itemsArray.length - 1 ? @@ -76,8 +78,8 @@ const AssetDetailItem = props => ( xs: 12 }} sx={{ - color: props.colorsArray[4][props.row.type][0], - backgroundColor: props.colorsArray[4][props.row.type][1], + color: props.colorsArray[0][props.row.type][0], + backgroundColor: props.colorsArray[0][props.row.type][1], }} > {`${props.row.type}`} @@ -101,7 +103,7 @@ const AssetDetailItem = props => ( alt={`${props.activities ? props.row.assetName : props.row.name}-logo`} //src={`${process.env.PUBLIC_URL}/assets/images/allianz-logo.jpeg`} //TODO: put icon if exists sx={{ - backgroundColor: props.activities ? props.colorsArray[props.row.asset.hashCode() % 4] : props.colorsArray[props.row.symbol.hashCode() % 4], + backgroundColor: props.activities ? Colors.COLORPALETTE[props.row.asset.hashCode() % 10] : Colors.COLORPALETTE[props.row.symbol.hashCode() % 10], width: { xs: '2.5rem', md: '2.8rem' diff --git a/Frontend/src/components/common/Benchi.jsx b/Frontend/src/components/common/Benchi.jsx index de65ab01..4042d448 100644 --- a/Frontend/src/components/common/Benchi.jsx +++ b/Frontend/src/components/common/Benchi.jsx @@ -15,7 +15,7 @@ import ActionProvider from '../../benchi-chatbot/ActionProvider.js'; import TextToSpeech from '../../benchi-chatbot/TextToSpeech.js'; /** - * Template to show modals throughout the app + * Renders the Benchi Icon and Chat * @param props * @returns {JSX.Element} * @constructor diff --git a/Frontend/src/components/common/Colors.jsx b/Frontend/src/components/common/Colors.jsx index ad382cb8..fa31fd50 100644 --- a/Frontend/src/components/common/Colors.jsx +++ b/Frontend/src/components/common/Colors.jsx @@ -1,11 +1,25 @@ const Colors = { BROWN: '#493f35', LIGHTBROWN: '#eacfb4', + TEXTBROWN: '#30302f', GREY: '#f3f4f6', + DARKGREY: '#bdbab7', GREEN: '#4eb96f', DARKGREEN: '#068930', - - + DARKORANGE: '#e47e25', + DARKERDARKORANGE: '#c96208', + COLORPALETTE: [ + '#e57e24ff', //cadmium-orange + '#eb8e1fff', //carrot-orange + '#f19b1fff', //orange-peel + '#b69994ff', //tuscany + '#3a97d2ff', //carolina-blue + '#3d9bcaff', //carolina-blue-2 + '#4eb96fff', //medium-sea-green + '#c1be49ff', // olive-green + '#efc317ff', //jonquil + '#ecb81aff', //orange-yellow + ] } export default Colors; \ No newline at end of file diff --git a/Frontend/src/components/common/DoughnutChart.jsx b/Frontend/src/components/common/DoughnutChart.jsx index 88df38c6..20e3a106 100644 --- a/Frontend/src/components/common/DoughnutChart.jsx +++ b/Frontend/src/components/common/DoughnutChart.jsx @@ -4,6 +4,24 @@ import {Chart as ChartJS, ArcElement, Tooltip, Legend} from 'chart.js'; import {Doughnut} from 'react-chartjs-2'; import {Typography, Grid, Paper} from '@mui/material'; +import Colors from './Colors'; + +/** + * Creates a hashCode from a String + * @param String + * @returns {interger} + */ + String.prototype.hashCode = function() { + let hash = 0, i, chr; + if (this.length === 0) return hash; + for (i = 0; i < this.length; i++) { + chr = this.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; + /** * Shows a Custom DoughnutChart * @param props @@ -29,16 +47,19 @@ const DoughnutChart = props => { setMiddleDisplayValue(defaultMiddleDisplayValue); } + const getColors = (symbols) => { + let colors = []; + symbols.forEach((symbol, index) => { + const symbolColor = Colors.COLORPALETTE[symbol ? symbol.hashCode() % 10 : index*8 % 10]; + colors.push(symbolColor); + }); + return colors; + } + const labels = props.labels; const valueData = props.data; - 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 colors = getColors(props.symbols); const data = { labels: labels, @@ -133,7 +154,8 @@ DoughnutChart.propTypes = { defaultMiddleDisplayLabel: PropTypes.string, defaultMiddleDisplayValue: PropTypes.string, data: PropTypes.array, - labels: PropTypes.array + labels: PropTypes.array, + symbols: PropTypes.array }; export default DoughnutChart; diff --git a/Frontend/src/components/common/Loading.jsx b/Frontend/src/components/common/Loading.jsx new file mode 100644 index 00000000..f054091a --- /dev/null +++ b/Frontend/src/components/common/Loading.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * Spinning Benchmarket Logo for displaying while loading + * @returns {JSX.Element} + * @constructor + */ + const Loading = props => { + + return ( + + ); + } + + +Loading.propTypes = { + size: PropTypes.number +}; + +export default Loading; diff --git a/Frontend/src/components/common/SearchResultsTable.jsx b/Frontend/src/components/common/SearchResultsTable.jsx index 1829ac42..3bf0988c 100644 --- a/Frontend/src/components/common/SearchResultsTable.jsx +++ b/Frontend/src/components/common/SearchResultsTable.jsx @@ -6,6 +6,8 @@ import BookmarkBorderIcon from '@mui/icons-material/BookmarkBorder'; import AddIcon from '@mui/icons-material/Add'; import PropTypes from 'prop-types'; +import Colors from './Colors'; + /** * Component related to the list of search results * @param props @@ -13,7 +15,6 @@ import PropTypes from 'prop-types'; * @constructor */ const SearchResultsTable = props => { - const colorsArray = ['rgb(59 151 210)', 'rgb(78 185 111)', 'rgb(228 126 37)', 'rgb(239 195 21)']; /** * Checks whether asset is already in watchlist @@ -71,7 +72,7 @@ const SearchResultsTable = props => { { sx={{ color: 'white', width: '5rem', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', } }} > diff --git a/Frontend/src/components/common/StyledTextField.jsx b/Frontend/src/components/common/StyledTextField.jsx new file mode 100644 index 00000000..f6d4bc28 --- /dev/null +++ b/Frontend/src/components/common/StyledTextField.jsx @@ -0,0 +1,27 @@ +import {TextField, styled} from '@mui/material'; + +const StyledTextField = styled(TextField)({ +//Label color when focused +'& label.Mui-focused': { + color: '#493f35', +}, +'& .MuiInput-underline:after': { + borderBottomColor: '#493f35', +}, +'& .MuiOutlinedInput-root': { + //Standard border color + '& fieldset': { + borderColor: '#c4b8ac', + }, + //Border color on hover + '&:hover fieldset': { + borderColor: '#493f35', + }, + //Border color when focused + '&.Mui-focused fieldset': { + borderColor: '#493f35', + }, +}, +}); + +export default StyledTextField; \ No newline at end of file diff --git a/Frontend/src/components/common/index.jsx b/Frontend/src/components/common/index.jsx index 6b34f1a6..fb5004cc 100644 --- a/Frontend/src/components/common/index.jsx +++ b/Frontend/src/components/common/index.jsx @@ -5,7 +5,9 @@ import AssetDetailItem from './AssetDetailItem'; import DoughnutChart from './DoughnutChart'; import Benchi from './Benchi'; import Footer from './Footer'; +import Loading from './Loading'; import Colors from './Colors'; +import StyledTextField from './StyledTextField'; export { SideNavLeft, @@ -15,5 +17,7 @@ export { DoughnutChart, Footer, Benchi, + Loading, + StyledTextField, Colors } \ No newline at end of file diff --git a/Frontend/src/components/screens/Activities/ActivitiesList.jsx b/Frontend/src/components/screens/Activities/ActivitiesList.jsx index 58bdcb27..5ec2127a 100644 --- a/Frontend/src/components/screens/Activities/ActivitiesList.jsx +++ b/Frontend/src/components/screens/Activities/ActivitiesList.jsx @@ -17,7 +17,7 @@ import {AssetDetailItem} from '../../common'; */ const ActivitiesList = (props) => { const [, setListDropdownIndex] = useState(0); - const colorsArray = ['rgb(59 151 210)', 'rgb(78 185 111)', 'rgb(228 126 37)', 'rgb(239 195 21)',{ + const colorsArray = [{ 'buy': ['blue', 'rgb(59, 151, 210, .2)'], 'deposit': ['green', 'rgb(78, 185, 111, .2)'], 'sell': ['brown', 'rgb(228, 126, 37, .2)'], @@ -77,7 +77,7 @@ const ActivitiesList = (props) => { - {activitiesForEachYearArray[index].length} total ·  + {activitiesForEachYearArray[index].length} total ·  {activitiesForEachYearArray[index].filter(activity => activity.type === 'buy').length} buys ·  {activitiesForEachYearArray[index].filter(activity => activity.type === 'sell').length} sells ·  {activitiesForEachYearArray[index].filter(activity => activity.type === 'dividend').length} dividends ·  diff --git a/Frontend/src/components/screens/Activities/ActivitiesScreen.jsx b/Frontend/src/components/screens/Activities/ActivitiesScreen.jsx index ad290cd3..576c619f 100644 --- a/Frontend/src/components/screens/Activities/ActivitiesScreen.jsx +++ b/Frontend/src/components/screens/Activities/ActivitiesScreen.jsx @@ -156,13 +156,51 @@ const ActivitiesScreen = props => { Add Activity - + {props.portfolioData[props.activePortfolio]['activities'].length === 0 ? + + + Start off by adding an Activity + + + Activities are the base for all data in Bench:market! They track changes in your portfolio and allow us to create detailed Charts about your Portfolio. + + + As soon as you add an activity our algorithms will automatically calculated all important numbers related to your portfolio. If your need any help you can always ask our Chatbot Benchi! + + + + : + + } ); diff --git a/Frontend/src/components/screens/Activities/AddActivity/AddActivityForm.jsx b/Frontend/src/components/screens/Activities/AddActivity/AddActivityForm.jsx index c75d310b..8f4987a6 100644 --- a/Frontend/src/components/screens/Activities/AddActivity/AddActivityForm.jsx +++ b/Frontend/src/components/screens/Activities/AddActivity/AddActivityForm.jsx @@ -1,6 +1,6 @@ import React, {useState, useEffect} from 'react'; import {useNavigate} from 'react-router-dom'; -import {Grid, Button, Box, TextField, MenuItem, styled, InputAdornment} from '@mui/material'; +import {Grid, Button, Box, TextField, MenuItem, InputAdornment} from '@mui/material'; import {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider'; import {AdapterDateFns} from '@mui/x-date-pickers/AdapterDateFns'; import {DatePicker} from '@mui/x-date-pickers/DatePicker'; @@ -9,30 +9,7 @@ import ClearIcon from '@mui/icons-material/Clear'; import DragHandleIcon from '@mui/icons-material/DragHandle'; import PropTypes from 'prop-types'; import SearchAssetInput from './SearchAssetsInput'; - -const StyledTextField = styled(TextField)({ - //Label color when focused - '& label.Mui-focused': { - color: '#493f35', - }, - '& .MuiInput-underline:after': { - borderBottomColor: '#493f35', - }, - '& .MuiOutlinedInput-root': { - //Standard border color - '& fieldset': { - borderColor: '#c4b8ac', - }, - //Border color on hover - '&:hover fieldset': { - borderColor: '#493f35', - }, - //Border color when focused - '&.Mui-focused fieldset': { - borderColor: '#493f35', - }, - }, -}); +import { StyledTextField } from '../../../common'; /** * Form for adding an activity @@ -43,7 +20,7 @@ const StyledTextField = styled(TextField)({ const AddActivityForm = props => { const initialValues = { - assetType: 'share', + assetType: props.initialAssetObj ? (props.initialAssetObj.assetType === 'Crypto' ? 'crypto' : props.initialAssetObj.assetType === 'Cash' ? 'cash' : 'share') : 'share', asset: props.initialAssetObj, assetInput: '', typeShare: 'buy', diff --git a/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx b/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx index 9360ed40..968d854a 100644 --- a/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx +++ b/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx @@ -3,6 +3,7 @@ import {Box} from '@mui/material'; import Autocomplete from '@mui/material/Autocomplete'; import CircularProgress from '@mui/material/CircularProgress'; import PropTypes from 'prop-types'; +import { Loading } from '../../../common'; /** * Form for adding an activity @@ -56,7 +57,7 @@ const SearchAssetInput = props => { const [open, setOpen] = useState(false); const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); - const [cash, setCash] = useState(false); + const [cash, setCash] = useState(props.initialAssetObj ? (props.initialAssetObj.assetType === 'Cash' ? true : false) : false); const sharesInPortfolioOptions = getSharesInPortfolioOptions(); const cryptoInPortfolioOptions = getCryptoInPortfolioOptions(); @@ -258,7 +259,7 @@ const fetchCryptoOptions = async (query) => { autoComplete: 'new-password', // disable autocomplete and autofill endAdornment: ( - {loading ? : null} + {loading ? : null} {params.InputProps.endAdornment} ), diff --git a/Frontend/src/components/screens/Activities/Modals/activityModals.jsx b/Frontend/src/components/screens/Activities/Modals/activityModals.jsx index 723a739d..509b6b35 100644 --- a/Frontend/src/components/screens/Activities/Modals/activityModals.jsx +++ b/Frontend/src/components/screens/Activities/Modals/activityModals.jsx @@ -19,10 +19,11 @@ export const renderRemoveActivityModal = (open, onClick, handleClose) => ( sx={{ color: 'white', width: '5rem', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', } }} > diff --git a/Frontend/src/components/screens/Analysis/AnalysisDetailitem.jsx b/Frontend/src/components/screens/Analysis/AnalysisDetailItem.jsx similarity index 98% rename from Frontend/src/components/screens/Analysis/AnalysisDetailitem.jsx rename to Frontend/src/components/screens/Analysis/AnalysisDetailItem.jsx index 3a008c8a..8752b5de 100644 --- a/Frontend/src/components/screens/Analysis/AnalysisDetailitem.jsx +++ b/Frontend/src/components/screens/Analysis/AnalysisDetailItem.jsx @@ -68,4 +68,4 @@ AnalysisDetailItem.propTypes = { percentage: PropTypes.string, }; -export default AnalysisDetailItem; \ No newline at end of file +export default AnalysisDetailItem; diff --git a/Frontend/src/components/screens/Analysis/AnalysisList.jsx b/Frontend/src/components/screens/Analysis/AnalysisList.jsx index c9f2ea7d..bc96b625 100644 --- a/Frontend/src/components/screens/Analysis/AnalysisList.jsx +++ b/Frontend/src/components/screens/Analysis/AnalysisList.jsx @@ -4,29 +4,7 @@ import {List, MenuItem, styled, TextField} from '@mui/material'; import AnalysisDetailItem from './AnalysisDetailItem'; import PropTypes from 'prop-types'; -const StyledTextField = styled(TextField)({ - //Label color when focused - '& label.Mui-focused': { - color: '#493f35', - }, - '& .MuiInput-underline:after': { - borderBottomColor: '#493f35', - }, - '& .MuiOutlinedInput-root': { - //Standard border color - '& fieldset': { - borderColor: '#c4b8ac', - }, - //Border color on hover - '&:hover fieldset': { - borderColor: '#493f35', - }, - //Border color when focused - '&.Mui-focused fieldset': { - borderColor: '#493f35', - }, - }, -}); +import { StyledTextField } from '../../common'; /** * Component to render the selected portfolio allocation list diff --git a/Frontend/src/components/screens/Analysis/AnalysisScreen.jsx b/Frontend/src/components/screens/Analysis/AnalysisScreen.jsx index 2daaed8e..bcf810e2 100644 --- a/Frontend/src/components/screens/Analysis/AnalysisScreen.jsx +++ b/Frontend/src/components/screens/Analysis/AnalysisScreen.jsx @@ -52,7 +52,8 @@ const AnalysisScreen = props => { stockArray.push({ asset: element.name, - percentage: parseFloat(percentage.toFixed(2)) + percentage: parseFloat(percentage.toFixed(2)), + symbol: element.symbol }) }); @@ -116,8 +117,6 @@ const AnalysisScreen = props => { } }) - - if(stockValue){ percentage = stockValue / value * 100; @@ -145,7 +144,7 @@ const AnalysisScreen = props => { }) } - if(portfolioData.cashValue ){ + if(portfolioData.cashValue){ percentage = portfolioData.cashValue / value * 100; stockArray.push({ @@ -161,15 +160,18 @@ const AnalysisScreen = props => { const getDoughnutChartData = (splitArray) => { //Perpare data for the doughnut chart let labelArray = []; let dataArray = []; + let symbolsArray = []; splitArray.forEach(arrayElement => { labelArray.push(arrayElement.asset) dataArray.push(arrayElement.percentage) + symbolsArray.push(arrayElement.symbol) }); return { 'labels': labelArray, - 'data': dataArray + 'data': dataArray, + 'symbols': symbolsArray } } @@ -206,6 +208,7 @@ const AnalysisScreen = props => { analysis data={doughnutChartData['data']} labels={doughnutChartData['labels']} + symbols={doughnutChartData['symbols']} defaultMiddleDisplayLabel={analysisTypes[analysisType]} defaultMiddleDisplayValue={''} /> @@ -274,7 +277,7 @@ const AnalysisScreen = props => { xs: 18 }} > - Start off by adding a Activity + Start off by adding an Activity { xs: 16 }} > - With activities you can fill your portfolio with your diffrent types of assets. + With activities you can fill your portfolio with your different types of assets. { xs: 16 }} > - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + Come back after adding one and see how your Portfolio is allocated! diff --git a/Frontend/src/components/screens/Analysis/__test__/AnalysisScreen.test.jsx b/Frontend/src/components/screens/Analysis/__test__/AnalysisScreen.test.jsx index 05d1c717..f327c036 100644 --- a/Frontend/src/components/screens/Analysis/__test__/AnalysisScreen.test.jsx +++ b/Frontend/src/components/screens/Analysis/__test__/AnalysisScreen.test.jsx @@ -8,6 +8,10 @@ import toJson from 'enzyme-to-json'; configure({adapter: new Adapter()}); +jest.mock('../../../../benchi-chatbot/TextToSpeech', () => ({ + ctx: {} +})); + it('AnalysisScreen renders without crashing', () => { const setSearchResult = jest.fn(); const setPortfolioData = jest.fn(); diff --git a/Frontend/src/components/screens/AssetDetails/AssetCard.jsx b/Frontend/src/components/screens/AssetDetails/AssetCard.jsx index 76e27e67..b135e1c3 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetCard.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetCard.jsx @@ -1,8 +1,9 @@ import React, {useEffect, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; import AssetChart from './AssetChart'; import ChartButtons from './ChartButtons'; import Masterdata from './Masterdata'; -import {Container, Card, Box} from '@mui/material'; +import {Container, Card, Box, Button} from '@mui/material'; import PropTypes from 'prop-types'; import SwitchButtons from './SwitchButtons'; import AssetPerformance from './AssetPerformance'; @@ -49,13 +50,36 @@ const AssetCard = props => { } } + const navigate = useNavigate(); + const routeChange = path => { + navigate(path); + } + return ( - + + {renderContent()} diff --git a/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx b/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx index 3bcc44af..e7398169 100644 --- a/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx +++ b/Frontend/src/components/screens/AssetDetails/AssetPerformance.jsx @@ -130,7 +130,7 @@ export const AssetPerformance = props => { { setRealised(!realised) - }}/>} label='Realised Performance'/> + }}/>} label='Include realised Gains'/> }/> diff --git a/Frontend/src/components/screens/AssetDetails/__test__/AssetDetails.test.jsx b/Frontend/src/components/screens/AssetDetails/__test__/AssetDetails.test.jsx index b0ba5b11..ecedff77 100644 --- a/Frontend/src/components/screens/AssetDetails/__test__/AssetDetails.test.jsx +++ b/Frontend/src/components/screens/AssetDetails/__test__/AssetDetails.test.jsx @@ -6,6 +6,10 @@ import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; Enzyme.configure({adapter: new Adapter()}) +jest.mock('../../../../benchi-chatbot/TextToSpeech', () => ({ + ctx: {} +})); + it('AssetDetailsScreen renders without crashing', () => { const portfolioData = { 'Portfolio': { diff --git a/Frontend/src/components/screens/Dashboard/AllocationGraph.jsx b/Frontend/src/components/screens/Dashboard/AllocationGraph.jsx index e0ecc3c6..534b168c 100644 --- a/Frontend/src/components/screens/Dashboard/AllocationGraph.jsx +++ b/Frontend/src/components/screens/Dashboard/AllocationGraph.jsx @@ -28,6 +28,15 @@ const AllocationGraph = props => { return labels; })(); + const symbols = (() => { + let symbols = []; + assets.forEach(element => { + let symbol = element['symbol']; + symbols.push(symbol); + }); + return symbols; + })(); + const valueData = (() => { let valueData = []; assets.forEach(element => { @@ -40,6 +49,7 @@ const AllocationGraph = props => { diff --git a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx index c954f7ae..7bb4649c 100644 --- a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx +++ b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx @@ -7,6 +7,7 @@ import AllocationGraph from './AllocationGraph'; import PortfolioOverview from './PortfolioOverview'; import PortfolioCharts from './PortfolioCharts'; import ChartButtons from '../AssetDetails/ChartButtons'; +import {RenderAddPortfolioModal} from './Modals/dashboardModals' /** * Component related to the dashboard screen @@ -15,8 +16,10 @@ import ChartButtons from '../AssetDetails/ChartButtons'; * @constructor */ const DashboardScreen = props => { - const [view, setView] = useState('month'); + const [view, setView] = useState('all'); + const [addPortfolioModal, setAddPortfolioModal] = useState(false); + //adds a dummyCash account to the data const dummyCash = () => { const cash = [{ firstActivity: '2900-01-01', @@ -46,6 +49,42 @@ const DashboardScreen = props => { return portfolioData; }); } + + //adds a new Portfolio to the data + const addPortfolio = (name) => { + props.setPortfolioData(prevData => { + const portfolioData = {...prevData, + [name]: { + 'name': name, + 'value': 0, + 'gains': 0, + 'realisedGains': 0, + 'totalGains': 0, + 'dividends': 0, + 'performanceWithRealisedGains': 0, + 'performanceWithoutRealisedGains': 0, + 'shares': [], + 'crypto': [], + 'cash': [], + 'activities': [], + 'activitiesLastId': -1, + 'dailyDataForValueDevelopment': {}, + 'dailyDataForPerformanceGraph': {}, + 'updated': '1970-01-01', + 'shareValue': 0, + 'cryptoValue': 0, + 'cashValue': 0 + } + }; + return portfolioData; + }); + props.setActivePortfolio(name); + } + + // Function to close the modals + const handleClose = () => { + setAddPortfolioModal(false); + } const navigate = useNavigate(); const routeChange = path => { @@ -56,7 +95,7 @@ const DashboardScreen = props => { { portfolioData={props.portfolioData} activePortfolio={props.activePortfolio} setActivePortfolio={props.setActivePortfolio} + setAddPortfolioModal={setAddPortfolioModal} /> @@ -79,81 +119,81 @@ const DashboardScreen = props => { {props.portfolioData[props.activePortfolio]['dailyDataForValueDevelopment'] && Object.keys(props.portfolioData[props.activePortfolio]['dailyDataForValueDevelopment']).length === 0 ? - - - - - - - - Start off by adding a Activity - - - Activities are the base for all data in Bench:market! They track changes in your portfolio and - - - As soon as you add an activity our algorithms will automatically calculated all important numbers related to your portfolio. If your need any help you can always ask our Chatbot Benchi! - - - - + marginTop: '30px' + }} + > + + + + + + Start off by adding an Activity + + + Activities are the base for all data in Bench:market! They track changes in your portfolio and allow us to create detailed Charts about your Portfolio. + + + As soon as you add an activity our algorithms will automatically calculated all important numbers related to your portfolio. If your need any help you can always ask our Chatbot Benchi! + + + : } @@ -163,6 +203,7 @@ const DashboardScreen = props => { onClick={() => dummyCash()} sx={{ margin: '1rem', + marginTop: '3rem', color: '#4eb96f', borderColor: '#4eb96f', backgroundColor: 'white', @@ -191,6 +232,12 @@ const DashboardScreen = props => { messageType={props.messageType} setMessageType={props.setMessageType} /> + handleClose()} + onClick={(newPortfolioName) => addPortfolio(newPortfolioName)} + /> ); diff --git a/Frontend/src/components/screens/Dashboard/Modals/dashboardModals.jsx b/Frontend/src/components/screens/Dashboard/Modals/dashboardModals.jsx new file mode 100644 index 00000000..fb8e5b9f --- /dev/null +++ b/Frontend/src/components/screens/Dashboard/Modals/dashboardModals.jsx @@ -0,0 +1,90 @@ +import React, {useEffect, useState} from 'react'; +import {Button, FormControl, FormHelperText, InputLabel} from '@mui/material'; +import {CustomModal, StyledTextField} from '../../../common'; + +export const RenderAddPortfolioModal = ({ + open, + portfolioData, + handleClose, + onClick + }) => { + const [error, setError] = useState(false); + const [errorDescripton, setErrorDescripton] = useState(''); + const [newPortfolioName, setNewPortfolioName] = useState(''); + + useEffect(() => { + if (open) { + if (newPortfolioName === '') { + setError(true); + setErrorDescripton('Can not be empty'); + return; + } + const existingPortfolios = Object.keys(portfolioData); + for (let index = 0; index < existingPortfolios.length; index++) { + const element = existingPortfolios[index]; + if (element === newPortfolioName) { + setError(true); + setErrorDescripton('Name is already used'); + return; + } + } + setError(false); + } + }, [open, newPortfolioName]); + + return ( + { + handleClose(); + setError(false); + }} + labelledby='add_portfolio-modal-title' + describedby='add_portfolio-modal-description' + modalTitle='Add a new portfolio:' + modalBody={() => ( + + { + setNewPortfolioName(event.target.value); + }} + /> + {error && *{errorDescripton}} + + )} + modalButton={() => ( + + )} + /> + ); + } \ No newline at end of file diff --git a/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx b/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx index 7f9382a9..11debdcd 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioCharts.jsx @@ -1,18 +1,21 @@ import React from 'react'; -import {Container, Box} from '@mui/material'; +import {Grid, Box} from '@mui/material'; import PortfolioPerformance from './PortfolioPerformance'; import PortfolioValueChart from './PortfolioValuechart'; export const PortfolioCharts = props => { return ( - - + + - + - - + + ) } diff --git a/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx b/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx index 1be6538e..94781793 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx @@ -61,7 +61,7 @@ const PortfolioOverview = props => { {}} + onClick={() => props.setAddPortfolioModal(true)} > @@ -96,6 +96,7 @@ PortfolioOverview.propTypes = { activePortfolio: PropTypes.any, setActivePortfolio: PropTypes.func, portfolioData: PropTypes.object, + setAddPortfolioModal: PropTypes.func }; export default PortfolioOverview; \ No newline at end of file diff --git a/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx b/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx index 7983b225..1c80ff1f 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioPerformance.jsx @@ -134,7 +134,7 @@ export const PortfolioPerformance = props => { control={ { setRealised(!realised) }}/>} - label='Realised Performance' + label='Include realised Gains' /> }/> diff --git a/Frontend/src/components/screens/Dashboard/__test__/DashboardScreen.test.jsx b/Frontend/src/components/screens/Dashboard/__test__/DashboardScreen.test.jsx index b1c93b92..659e8948 100644 --- a/Frontend/src/components/screens/Dashboard/__test__/DashboardScreen.test.jsx +++ b/Frontend/src/components/screens/Dashboard/__test__/DashboardScreen.test.jsx @@ -3,9 +3,13 @@ import DashboardScreen from '../DashboardScreen'; import toJson from 'enzyme-to-json'; import Enzyme, {shallow} from 'enzyme'; import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; +import {BrowserRouter} from 'react-router-dom'; Enzyme.configure({adapter: new Adapter()}) +jest.mock('../../../../benchi-chatbot/TextToSpeech', () => ({ + ctx: {} +})); it('DashboardScreen renders without crashing', () => { const portfolioData = { @@ -29,8 +33,9 @@ it('DashboardScreen renders without crashing', () => { const getAllAssets = jest.fn(); const setStatusMessage = jest.fn(); const setMessageType = jest.fn(); + const addPortfolio = jest.fn(); - const wrapper = shallow( + const wrapper = shallow( { setPortfolioData={setPortfolioData} setStatusMessage={setStatusMessage} setMessageType={setMessageType} - />); + /> + ); expect(toJson(wrapper)).toMatchSnapshot(); }) \ No newline at end of file diff --git a/Frontend/src/components/screens/Dashboard/__test__/__snapshots__/DashboardScreen.test.jsx.snap b/Frontend/src/components/screens/Dashboard/__test__/__snapshots__/DashboardScreen.test.jsx.snap index a5d5ceea..6b5c5ec1 100644 --- a/Frontend/src/components/screens/Dashboard/__test__/__snapshots__/DashboardScreen.test.jsx.snap +++ b/Frontend/src/components/screens/Dashboard/__test__/__snapshots__/DashboardScreen.test.jsx.snap @@ -1,19 +1,69 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DashboardScreen renders without crashing 1`] = ` - - + - + `; diff --git a/Frontend/src/components/screens/ErrorPage.jsx b/Frontend/src/components/screens/ErrorPage.jsx index ddce41aa..1c51718d 100644 --- a/Frontend/src/components/screens/ErrorPage.jsx +++ b/Frontend/src/components/screens/ErrorPage.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Typography, Container, List, ListItem} from '@mui/material'; +import {Typography, Container } from '@mui/material'; import {Link} from 'react-router-dom'; const ErrorPage = () => ( diff --git a/Frontend/src/components/screens/Settings/Modals/settingsModals.jsx b/Frontend/src/components/screens/Settings/Modals/settingsModals.jsx index 8349f6fc..7ad91d43 100644 --- a/Frontend/src/components/screens/Settings/Modals/settingsModals.jsx +++ b/Frontend/src/components/screens/Settings/Modals/settingsModals.jsx @@ -16,10 +16,11 @@ export const renderDeleteDataModal = (open, handleClose, onClick) => ( sx={{ color: 'white', width: '5rem', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', } }} > diff --git a/Frontend/src/components/screens/Settings/SettingsScreen.jsx b/Frontend/src/components/screens/Settings/SettingsScreen.jsx index 203ecaf5..0931dcb8 100644 --- a/Frontend/src/components/screens/Settings/SettingsScreen.jsx +++ b/Frontend/src/components/screens/Settings/SettingsScreen.jsx @@ -4,7 +4,6 @@ import ScreensTemplate from '../../ScreensTemplate'; import {Container, Box, Button} from '@mui/material'; import {renderDeleteDataModal} from './Modals/settingsModals' import PropTypes from 'prop-types'; -import {margin} from '@mui/system'; import {Grid} from '@mui/material'; import Typography from '@mui/material/Typography'; import SettingsIcon from '@mui/icons-material/Settings'; @@ -41,7 +40,7 @@ const SettingsScreen = props => { { { xs: 12 }} sx={{ + color: 'white', '@media screen and (max-width: 768px)': { marginBottom: '40px' } @@ -188,7 +188,7 @@ const SettingsScreen = props => { { xs: 12 }} sx={{ + color: 'white', '@media screen and (max-width: 768px)': { marginBottom: '40px' } }} > - Delete your data to get an empty portfolio. Your watchlist will be reset as well. Be careful that we can't restore your data, so download it first if you still need it! + Delete your data to get an empty portfolio. Your watchlist will be reset as well. Be careful because we can't restore your data, so download it first if you still need it! { onClick={() => setDeleteDataModal(true)} sx={{ color: 'white', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', }, margin: 'auto !important', display: 'block' diff --git a/Frontend/src/components/screens/Settings/__test__/SettingsScreen.test.jsx b/Frontend/src/components/screens/Settings/__test__/SettingsScreen.test.jsx index 92964aa9..e795f837 100644 --- a/Frontend/src/components/screens/Settings/__test__/SettingsScreen.test.jsx +++ b/Frontend/src/components/screens/Settings/__test__/SettingsScreen.test.jsx @@ -7,6 +7,10 @@ import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; Enzyme.configure({adapter: new Adapter()}) +jest.mock('../../../../benchi-chatbot/TextToSpeech', () => ({ + ctx: {} +})); + it('SettingsScreen renders without crashing', () => { const emptyPortfolioData = { 'Portfolio': { diff --git a/Frontend/src/components/screens/WatchLists/AssetListItem.jsx b/Frontend/src/components/screens/WatchLists/AssetListItem.jsx index d782bbf6..24dbe1fb 100644 --- a/Frontend/src/components/screens/WatchLists/AssetListItem.jsx +++ b/Frontend/src/components/screens/WatchLists/AssetListItem.jsx @@ -13,7 +13,6 @@ import {AssetDetailItem} from '../../common'; */ const AssetListItem = props => { const [, setListDropdownIndex] = useState(0); - const colorsArray = ['rgb(59 151 210)', 'rgb(78 185 111)', 'rgb(228 126 37)', 'rgb(239 195 21)']; return ( @@ -32,7 +31,6 @@ const AssetListItem = props => { key={`asset_${index}`} row={row} index={index} - colorsArray={colorsArray} itemsArray={props.assetsListArray[props.selectedListIndex]} listName={props.watchListsArray[index]} selectedListIndex={props.selectedListIndex} diff --git a/Frontend/src/components/screens/WatchLists/Modals/assetModals.jsx b/Frontend/src/components/screens/WatchLists/Modals/assetModals.jsx index e17dd092..827e5f57 100644 --- a/Frontend/src/components/screens/WatchLists/Modals/assetModals.jsx +++ b/Frontend/src/components/screens/WatchLists/Modals/assetModals.jsx @@ -1,6 +1,6 @@ import React from 'react'; import {Button, TextField} from '@mui/material'; -import {CustomModal} from '../../../common'; +import {CustomModal, StyledTextField} from '../../../common'; export const renderRemoveAssetModal = (open, handleClose, onClick) => ( ( sx={{ color: 'white', width: '5rem', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', } }} > @@ -37,7 +38,7 @@ export const renderAddAssetModal = (open, handleClose, errorModal, onChange, onC describedby='add_asset-modal-description' modalTitle='New asset' modalBody={() => ( - ( ( - ( - ( sx={{ color: 'white', width: '5rem', - borderColor: 'rgb(228 126 37)', - backgroundColor: 'rgb(228 126 37)', + borderColor: '#e47e25', + backgroundColor: '#e47e25', '&:hover': { - backgroundColor: 'rgb(228 126 37)', + borderColor: '#c96208', + backgroundColor: '#e47e25', } }} > diff --git a/Frontend/src/components/screens/WatchLists/WatchLists.jsx b/Frontend/src/components/screens/WatchLists/WatchLists.jsx index 1f588435..c192b6b9 100644 --- a/Frontend/src/components/screens/WatchLists/WatchLists.jsx +++ b/Frontend/src/components/screens/WatchLists/WatchLists.jsx @@ -25,29 +25,23 @@ import DropdownMenu from './DropdownMenu'; import CustomSelectField from './CustomSelectField'; import {renderAddWatchlistModal, renderEditListModal, renderRemoveListModal} from './Modals/watchlistModals'; -const StyledTextField = styled(TextField)({ - //Label color when focused - '& label.Mui-focused': { - color: '#493f35', - }, - '& .MuiInput-underline:after': { - borderBottomColor: '#493f35', - }, - '& .MuiOutlinedInput-root': { - //Standard border color - '& fieldset': { - borderColor: '#c4b8ac', - }, - //Border color on hover - '&:hover fieldset': { - borderColor: '#493f35', - }, - //Border color when focused - '&.Mui-focused fieldset': { - borderColor: '#493f35', - }, - }, -}); +import { StyledTextField, Colors } from '../../common'; + +/** + * Creates a hashCode from a String + * @param String + * @returns {interger} + */ + String.prototype.hashCode = function() { + let hash = 0, i, chr; + if (this.length === 0) return hash; + for (i = 0; i < this.length; i++) { + chr = this.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; /** * Show all the watchLists @@ -62,7 +56,7 @@ const WatchLists = props => { const [watchlist, setWatchlist] = useState(undefined); const [errorModal, setErrorModal] = useState(false); const [listDropdownIndex, setListDropdownIndex] = useState(0); - const avatarGroupColors = ['rgb(59 151 210)', 'rgb(78 185 111)', 'rgb(228 126 37)']; + const avatarGroupColors = Colors.COLORPALETTE; const handleWatchListItemClick = (event, index) => { props.setSelectedListIndex(index); @@ -298,7 +292,7 @@ const WatchLists = props => { '&.MuiAvatar-circular': { borderColor: props.selectedListIndex === index ? 'white' : 'black' }, - backgroundColor: avatarGroupColors[assetIndex], + backgroundColor: avatarGroupColors[asset.symbol.hashCode() % 10], width: { md: '1.2rem', xl: '1.5rem' diff --git a/Frontend/src/components/screens/WatchLists/__test__/WatchListsScreen.test.jsx b/Frontend/src/components/screens/WatchLists/__test__/WatchListsScreen.test.jsx index 8df3abf4..002598ce 100644 --- a/Frontend/src/components/screens/WatchLists/__test__/WatchListsScreen.test.jsx +++ b/Frontend/src/components/screens/WatchLists/__test__/WatchListsScreen.test.jsx @@ -25,6 +25,10 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockedNavigator })); +jest.mock('../../../../benchi-chatbot/TextToSpeech', () => ({ + ctx: {} +})); + describe('Tests regarding WatchList screen', () => { const addToWatchList = jest.fn(); const setWatchListsArray = jest.fn();