diff --git a/Backend/data/shareSymbols.txt b/Backend/data/shareSymbols.txt index 6753ef8d..279cf524 100644 --- a/Backend/data/shareSymbols.txt +++ b/Backend/data/shareSymbols.txt @@ -1 +1,7 @@ -IBM \ No newline at end of file +IBM +A +JPM-P-M +AMZN +AADI +K +FB \ No newline at end of file diff --git a/Frontend/src/Example DataJSON.jsonc b/Frontend/src/Example DataJSON.jsonc deleted file mode 100644 index aa3cd0e5..00000000 --- a/Frontend/src/Example DataJSON.jsonc +++ /dev/null @@ -1,107 +0,0 @@ -{ -"portfolios": { - "Portfolio1": { - "name": "Portfolio1", - "value": 1203.19, - "gains": 401.33, - "realisedGains": 0, - "performance": 0.05, - "shares": [ - { - "symbol": "IBM", - "quantity": 1.6 - }, - { - "symbol": "XXX", - "quantity": 1 - } - ], - "crypto": [ - - ], - "cash": [ - { - "symbol": "" //wird selbst angelegt oder irgendwie gehasht - } - ], - "activities": [ - { - "assettype": "share", //,crypto oder cash - "asset": "XXX", //symbol des assets - "type": "buy", //mögliche Werte: buy (nicht für cash), sell (nicht für cash), dividend (nur für share), deposit (nur für cash), payout (nur für cash) - "date": "DD:MM:YYYY", - "time": "hh:mm", //optional, wenn nicht angegeben wurde auf irgendeinen default wert und nicht anzeigen - "quantity": 2, - "sum": 60.24, - "tax": 0, - "fee": 0 - }, - { - "assettype": "crypto", //,crypto oder cash - "asset": "XXX", //symbol des assets - "type": "sell", //nur für assets die man davor im besitz hatte - "date": "DD:MM:YYYY", - "time": "hh:mm", //optional, wenn nicht angegeben wurde auf irgendeinen default wert und nicht anzeigen - "quantity": 2, - "sum": 60.24, - "fee": 0, - "tax": 0 - }, - { - "assettype": "share", //nur für share - "asset": "XXX", //symbol des assets - "type": "dividend", //nur für assets die man davor im besitz hatte - "date": "DD:MM:YYYY", - "time": "hh:mm", //optional, wenn nicht angegeben wurde auf irgendeinen default wert und nicht anzeigen - "quantity": 2, - "sum": 20.24, - "tax": 0, - "fee": 0 - }, - { - "assettype": "cash", //nur für cash - "asset": "XXX", //symbol des assets - "type": "deposit", - "date": "DD:MM:YYYY", - "time": "hh:mm", //optional, wenn nicht angegeben wurde auf irgendeinen default wert und nicht anzeigen - "quantity": 1, - "sum": 1000.23, - "tax": 0, - "fee": 0 - }, - { - "assettype": "cash", //nur für cash - "asset": "XXX", //symbol des assets - "type": "payout", - "date": "DD:MM:YYYY", - "time": "hh:mm", //optional, wenn nicht angegeben wurde auf irgendeinen default wert und nicht anzeigen - "quantity": 1, - "sum": 1000.23, - "tax": 0, - "fee": 0 - } - ], - "updated": "timestamp" - }, - "Portfolio2": { - "name": "Portfolio2" - //restliche daten... - } -}, -"watchlists": [ - { - "name": "x", - "assets": { - "shares": ["IBM", "ACN"], - "crypto": ["BTC", "BIT"] - } - }, - { - "name": "watchlist2", - "assets": { - "shares": [ "IBM", "ACN"], - "crypto": ["BTC", "BIT"] - } - } -] -} \ No newline at end of file diff --git a/Frontend/src/components/common/SearchField.jsx b/Frontend/src/components/common/SearchField.jsx index e8bd913a..18b539a1 100644 --- a/Frontend/src/components/common/SearchField.jsx +++ b/Frontend/src/components/common/SearchField.jsx @@ -38,6 +38,7 @@ const SearchField = props => { return ( { return isAssetInWatchList; } + let navigate = useNavigate(); + const routeChange = path => { + navigate(path); + } + /** * Renders each item if the search results table * @param element @@ -88,17 +93,23 @@ const SearchResultsTable = props => { 0 ? 'flex-xl-row flex-grow-1 col-1 pe-4' : 'col-9 col-sm-10'}`}> - 0 && 'col-xl-8'}`} - fontSize={{ - lg: 16, - md: 15, - xs: 14 - }} + - {element.name ? element.name : element.symbol} - + 0 && 'col-xl-8'}`} + fontSize={{ + lg: 16, + md: 15, + xs: 14 + }} + > + {element.name ? element.name : element.symbol} + + 0 && 'ms-xl-3 text-xl-center'} col-lg-4`} @@ -112,7 +123,39 @@ const SearchResultsTable = props => { {element.assetType} - + {!props.watchListsArray && + + {routeChange(`../activities/addActivity/${element.assetType}/${element.symbol}/${element.name}`)}} + > + + + + + + + + } {props.watchListsArray && props.watchListsArray.length > 0 && { { - }} + onClick={() => {routeChange(`../activities/addActivity/${element.assetType}/${element.symbol}/${element.name}`)}} > { 0 ? 'col-12 col-sm-10 col-lg-9 col-xl-11' : 'col-12 col-sm-11 col-md-10 col-lg-11 col-xl-9'}`}> {props.searchResult.map((element, index) => { - return props.watchListsArray && props.watchListsArray.length > 0 ? - renderSearchResultList(element, index) - : ( - - {renderSearchResultList(element, index)} - - ) + return renderSearchResultList(element, index) })} - + + Activities + + + { if (values.assetType === 'cash') props.addActivity(values.assetType, values.asset, values.typeCash, values.date, '1', values.sumCash, values.sumCash, values.tax, values.fee); if (!addAnother) { routeChange('../activities'); - } else { - alert('Activity saved!'); } } } @@ -412,6 +410,8 @@ const AddActivityForm = props => { setValues={setValues} initialLoading={initialLoading} setInitialLoading={setInitialLoading} + setStatusMessage={props.setStatusMessage} + setMessageType={props.setMessageType} /> } @@ -699,7 +699,11 @@ const AddActivityForm = props => { AddActivityForm.propTypes = { addActivity: PropTypes.func, portfolioData: PropTypes.object, - initialAssetObj: PropTypes.object + initialAssetObj: PropTypes.object, + statusMessage: PropTypes.string, + setStatusMessage: PropTypes.func, + messageType: PropTypes.string, + setMessageType: PropTypes.func }; export default AddActivityForm; diff --git a/Frontend/src/components/screens/Activities/AddActivity/AddActivityScreen.jsx b/Frontend/src/components/screens/Activities/AddActivity/AddActivityScreen.jsx index fde06a5b..b613525e 100644 --- a/Frontend/src/components/screens/Activities/AddActivity/AddActivityScreen.jsx +++ b/Frontend/src/components/screens/Activities/AddActivity/AddActivityScreen.jsx @@ -35,6 +35,8 @@ const AddActivityScreen = props => { addActivity={props.addActivity} portfolioData={portfolioData} initialAssetObj={initialAssetObj} + setStatusMessage={props.setStatusMessage} + setMessageType={props.setMessageType} /> @@ -44,10 +46,14 @@ const AddActivityScreen = props => { ); @@ -61,7 +67,10 @@ AddActivityScreen.propTypes = { portfolioData: PropTypes.object, getAllAssets: PropTypes.func, assetsListArray: PropTypes.array, - addActivity: PropTypes.func + addActivity: PropTypes.func, + setStatusMessage: PropTypes.func, + setMessageType: PropTypes.func + }; export default AddActivityScreen; diff --git a/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx b/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx index b2e099ca..9360ed40 100644 --- a/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx +++ b/Frontend/src/components/screens/Activities/AddActivity/SearchAssetsInput.jsx @@ -156,7 +156,9 @@ const SearchAssetInput = props => { return results; } catch (e) { - console.log('fetching failed === ', e); + props.setStatusMessage('Lost connection to server. Please try again later'); + props.setMessageType('error'); + console.log('fetching failed === ', e); }; } @@ -177,7 +179,9 @@ const fetchCryptoOptions = async (query) => { return results; } catch (e) { - console.log('fetching failed === ', e); + props.setStatusMessage('Lost connection to server. Please try again later'); + props.setMessageType('error'); + console.log('fetching failed === ', e); }; } @@ -274,7 +278,9 @@ SearchAssetInput.propTypes = { setValues: PropTypes.func, initialAssetObj: PropTypes.object, initialLoading: PropTypes.bool, - setInitialLoading: PropTypes.func + setInitialLoading: PropTypes.func, + setStatusMessage: PropTypes.func, + setMessageType: PropTypes.func }; export default SearchAssetInput; \ No newline at end of file diff --git a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx index 3d0a87a9..eea9aeb6 100644 --- a/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx +++ b/Frontend/src/components/screens/Dashboard/DashboardScreen.jsx @@ -1,6 +1,6 @@ import React, {useState} from 'react'; import ScreensTemplate from '../../ScreensTemplate'; -import {Grid, Box} from '@mui/material'; +import {Grid, Typography, Box, Button} from '@mui/material'; import PropTypes from 'prop-types'; import AllocationGraph from './AllocationGraph'; import PortfolioOverview from './PortfolioOverview'; @@ -16,6 +16,36 @@ import ChartButtons from '../AssetDetails/ChartButtons'; const DashboardScreen = props => { const [view, setView] = useState('month'); + const dummyCash = () => { + const cash = [{ + firstActivity: '2900-01-01', + id: 0, + symbol: 'ING', + name: 'ING Konto', + assetTypeForDisplay: 'Cash', + value: 0, + quantity: 1, + gains: 0, + realisedGains: 0, + totalGains: 0, + performanceWithRealisedGains: 0, + performanceWithOutRealisedGains: 0, + taxes: 0, + fees: 0, + dailyDataForPerformanceGraph: [], + dailyDataForValueDevelopment: [], + stateChanges: [], + deposits: [], + analysisInfo: undefined + }] + + props.setPortfolioData(prevData => { + const portfolioData = {...prevData}; + portfolioData[props.activePortfolio]['cash'] = cash; + return portfolioData; + }); + } + const renderBody = () => ( { /> - - - { setActivePortfolio={props.setActivePortfolio} /> + + + - + {Object.keys(props.portfolioData[props.activePortfolio]['dailyDataForValueDevelopment']).length === 0 ? + + : + + } + ); diff --git a/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx b/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx index 167a81bf..1be6538e 100644 --- a/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx +++ b/Frontend/src/components/screens/Dashboard/PortfolioOverview.jsx @@ -5,7 +5,8 @@ import { Select, MenuItem, FormControl, - Typography + Typography, + IconButton } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; import SettingsIcon from '@mui/icons-material/Settings'; @@ -57,7 +58,13 @@ const PortfolioOverview = props => { - + {}} + > + + @@ -74,11 +81,11 @@ const PortfolioOverview = props => { Realised Gains - {portfolio['realisedGains']} + {new Intl.NumberFormat('de-DE', {style: 'currency', currency: 'EUR',}).format(portfolio['realisedGains'])} Dividends - 0,00€ + {new Intl.NumberFormat('de-DE', {style: 'currency', currency: 'EUR',}).format(portfolio['dividends'])} diff --git a/Frontend/src/components/screens/Settings/SettingsScreen.jsx b/Frontend/src/components/screens/Settings/SettingsScreen.jsx index 9b8da453..eccd424d 100644 --- a/Frontend/src/components/screens/Settings/SettingsScreen.jsx +++ b/Frontend/src/components/screens/Settings/SettingsScreen.jsx @@ -22,17 +22,17 @@ const SettingsScreen = props => { - Settings + Settings @@ -41,12 +41,12 @@ const SettingsScreen = props => { sx={{ borderRadius: '1rem', border: '1px solid black', - padding: '20px', + padding: '1rem', '@media screen and (max-width: 768px)': { background: 'linear-gradient(180deg, rgb(78 185 111) 55%, #FFFFFF 0%);' }, background: 'linear-gradient(90deg, rgb(78 185 111) 72%, #FFFFFF 50%);', - marginBottom: '40px' + marginBottom: '2rem' }} > { sx={{ borderRadius: '1rem', border: '1px solid black', - padding: '20px', + padding: '1rem', '@media screen and (max-width: 768px)': { background: 'linear-gradient(180deg, rgb(59 151 210) 55%, #FFFFFF 0%);' }, background: 'linear-gradient(90deg, rgb(59 151 210) 72%, #FFFFFF 50%);', - marginBottom: '40px' + marginBottom: '2rem' }} > { sx={{ borderRadius: '1rem', border: '1px solid black', - padding: '20px', + padding: '1rem', '@media screen and (max-width: 768px)': { background: 'linear-gradient(180deg, rgb(228 126 37) 55%, #FFFFFF 0%);' }, background: 'linear-gradient(90deg, rgb(228 126 37) 72%, #FFFFFF 50%);', - marginBottom: '40px' + marginBottom: '2rem' }} > { tempPortfolioData[activePortfolio]['updated'] = new Date(activityObj.date).addDays(-1).getFormattedString(); //update Portfolio Data automatically recalculates the dailyValues for the whole portfolio from this date on return tempPortfolioData; }); + + setStatusMessage('Activity saved'); + setMessageType('success'); } /** @@ -359,6 +362,8 @@ const AppRoutes = () => { const response = await fetch(`${process.env.REACT_APP_BASEURL}/getShareInformationsForAnalyse?symbol=${symbol}`, {mode: 'cors'}) return await response.json(); } catch (e) { + setStatusMessage('Lost connection to server. Please try again later'); + setMessageType('error'); console.log('fetching failed === ', e); } } @@ -376,7 +381,7 @@ const AppRoutes = () => { tempPortfolioData[activePortfolio]['gains'] = dailyDataForValueDevelopment[latestDateWithData]['gains']; tempPortfolioData[activePortfolio]['realisedGains'] = dailyDataForValueDevelopment[latestDateWithData]['realisedGains']; tempPortfolioData[activePortfolio]['totalGains'] = dailyDataForValueDevelopment[latestDateWithData]['totalGains']; - tempPortfolioData[activePortfolio]['dividens'] = dailyDataForValueDevelopment[latestDateWithData]['dividens']; + tempPortfolioData[activePortfolio]['dividends'] = dailyDataForValueDevelopment[latestDateWithData]['dividends']; tempPortfolioData[activePortfolio]['fees'] = dailyDataForValueDevelopment[latestDateWithData]['fees']; tempPortfolioData[activePortfolio]['taxes'] = dailyDataForValueDevelopment[latestDateWithData]['taxes']; tempPortfolioData[activePortfolio]['performanceWithRealisedGains'] = dailyDataForPerformanceGraph[latestDateWithData]['performanceWithRealisedGains']; @@ -396,7 +401,7 @@ const AppRoutes = () => { if (assetDailyDataForValueDevelopment[dateKey] === undefined) return; //return equals continue in a forEach loop const portfolioDateData = dailyDataForValueDevelopment[dateKey] ? dailyDataForValueDevelopment[dateKey] : - {value: 0, invested: 0, gains: 0, realisedGains: 0, totalGains: 0, dividens: 0, taxes: 0, fees: 0}; + {value: 0, invested: 0, gains: 0, realisedGains: 0, totalGains: 0, dividends: 0, taxes: 0, fees: 0}; let assetDateData = assetDailyDataForValueDevelopment[dateKey]; dailyDataForValueDevelopment[dateKey] = {}; Object.keys(assetDateData).forEach(attribute => {