diff --git a/src/App.tsx b/src/App.tsx index ccc07a3f..a0a32137 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,6 +22,7 @@ import TwoFactorCard from "./security/two-factor.card"; import SecurityRepository from "./core/repositories/security-repository"; import RestAPI from "./core/repositories/rest-api"; import { AxiosError } from "axios"; +import { CurrencyRepository } from "./core/RestAPI"; const LoginCard = lazy(() => import("./security/login-card")); const RegisterCard = lazy(() => import("./security/RegisterCard")); @@ -45,9 +46,13 @@ function App() { const authenticated = () => { RestAPI.profile() .then(() => { - console.log('Profile loaded') setAuthenticate(true) setTwoFactor(false) + CurrencyRepository.list().then(() => { + const profile: any = RestAPI.user() + profile.defaultCurrency = CurrencyRepository.cached(profile.currency) + }) + }) .catch((ex: AxiosError) => { if (ex.response?.status === 403) { @@ -104,6 +109,12 @@ function App() { ); } + if (sessionStorage.getItem('token')) { + return
+ +
+ } + return ( diff --git a/src/account/liability/liability-detail-page.tsx b/src/account/liability/liability-detail-page.tsx index 346a2404..8dda8f4b 100644 --- a/src/account/liability/liability-detail-page.tsx +++ b/src/account/liability/liability-detail-page.tsx @@ -51,7 +51,7 @@ const LiabilityDetailView = () => { -
+

{ account.name }

diff --git a/src/config/global-chart-config.js b/src/config/global-chart-config.js index c969d5c8..9cabfe4c 100644 --- a/src/config/global-chart-config.js +++ b/src/config/global-chart-config.js @@ -66,7 +66,17 @@ const GlobalChartConfig = { enabled: true, usePointStyle: true, position: 'nearest', + backgroundColor: 'white', + bodyColor: 'black', + titleColor: 'black', + borderColor: 'black', + borderWidth: .5, callbacks: { + title: (context) => context.label, + label: (context) => { + const value = context.parsed?.y ?? context.parsed.value; + return `${value}` + }, labelPointStyle: (_) => { return { pointStyle: 'triangle', @@ -189,28 +199,17 @@ const Service = { } export const defaultGraphColors = [ - '#E0FFFF', - '#00CED1', - '#40E0D0', - '#48D1CC', - '#AFEEEE', - '#7FFFD4', - '#B0E0E6', - '#5F9EA0', - '#66CDAA', - '#3CB371', - '#20B2AA', - '#2F4F4F', - '#008080', - '#008B8B', - '#32CD32', - '#90EE90', - '#ADFF2F', - '#90EE90', - '#ADFF2F', - '#7FFF00', - '#7FFF00', - '#6B8E23', + 'rgba(25, 25, 112, 0.8)', // Midnight Blue + 'rgba(0, 0, 128, 0.8)', // Navy Blue + 'rgba(0, 0, 139, 0.8)', // Dark Blue + 'rgba(0, 0, 156, 0.8)', // Medium Blue + 'rgba(0, 0, 205, 0.8)', // Medium Blue + 'rgba(0, 0, 255, 0.8)', // Blue + 'rgba(70, 130, 180, 0.8)', // Steel Blue + 'rgba(100, 149, 237, 0.8)', // Cornflower Blue + 'rgba(135, 206, 235, 0.8)', // Sky Blue + 'rgba(135, 206, 250, 0.8)', // Light Sky Blue + 'rgba(240, 248, 255, 0.8)', // Alice Blue ] export { diff --git a/src/core/RestAPI.js b/src/core/RestAPI.js index 7c963a5c..01bd7b54 100644 --- a/src/core/RestAPI.js +++ b/src/core/RestAPI.js @@ -42,10 +42,20 @@ const TransactionRepository = (api => { })(RestApi) const CurrencyRepository = (api => { + let knownCurrencies = [] return { - list: () => api.get('settings/currencies'), + list: () => api.get('settings/currencies').then(currencies => { + knownCurrencies = currencies + return currencies + }), get: code => api.get(`settings/currencies/${code}`), change: (code, enabled) => api.patch(`settings/currencies/${code}`, { enabled: enabled }) + .then(response => { + const currency = knownCurrencies.find(currency => currency.code === code) + currency.enabled = enabled + return response + }), + cached: (code) => knownCurrencies.find(currency => currency.code === code) } })(RestApi) diff --git a/src/core/graphs/categorized-pie-chart.tsx b/src/core/graphs/categorized-pie-chart.tsx index 5a999327..38277c59 100644 --- a/src/core/graphs/categorized-pie-chart.tsx +++ b/src/core/graphs/categorized-pie-chart.tsx @@ -5,7 +5,7 @@ import { isArray } from "chart.js/helpers"; import { Account } from "../types"; import StatisticalRepository from "../repositories/statistical-repository"; import { Chart } from "react-chartjs-2"; -import { ChartData } from "chart.js"; +import { ChartData, Tooltip, TooltipPosition } from "chart.js"; import { defaultGraphColors } from "../../config/global-chart-config"; type CategorizedPieChartProps = { @@ -15,8 +15,17 @@ type CategorizedPieChartProps = { accounts?: Account[] | Account } +let lastPosition: TooltipPosition +(Tooltip.positioners as any)['center'] = (_: any[], eventPosition: any) => { + const chartArea = eventPosition.chart?.chartArea + if (!chartArea) return lastPosition + + lastPosition = { x: chartArea.right / 2 - 40, y: chartArea.bottom / 2 } + return lastPosition +} + const CategorizedPieChart: FC = ({ id, split, incomeOnly, accounts = [] }) => { - const [pieSeries, setPieSeries] = useState(undefined) + const [pieSeries, setPieSeries] = useState | undefined>(undefined) const [range] = useDateRange() useEffect(() => { @@ -44,39 +53,46 @@ const CategorizedPieChart: FC = ({ id, split, incomeOn }) }, [split, incomeOnly, range]) // eslint-disable-line react-hooks/exhaustive-deps + const currency = accounts && !isArray(accounts) ? accounts.account.currency : '€' + if (!pieSeries) return return <> defaultGraphColors[context.dataIndex] } }, plugins: { legend: { - display: true, - position: 'right' + display: false, }, tooltip: { + backgroundColor: 'white', + bodyColor: 'black', + titleColor: 'black', + cornerRadius: 0, + caretSize: 0, + position: 'center', + bodyAlign: 'left', callbacks: { title: (context : any) => context.label, label: (context: any) => { - if (accounts && !isArray(accounts)) { - return `${context.raw} ${accounts?.account?.currency}` - } - - return context.raw + return ` ${currency}${context.raw}` } } } }, maintainAspectRatio: false - }}/> + } as any }/> } diff --git a/src/core/layout/Card.tsx b/src/core/layout/Card.tsx index 6786ef71..5aeff60d 100644 --- a/src/core/layout/Card.tsx +++ b/src/core/layout/Card.tsx @@ -32,7 +32,7 @@ const Card: FC = ({ title, actions, buttons, children, className = '' {actions &&
{ actions }
} )} -
+
{children}
{buttons && diff --git a/src/reports/budget-monthly/budget-chart.tsx b/src/reports/budget-monthly/budget-chart.tsx new file mode 100644 index 00000000..d09aa799 --- /dev/null +++ b/src/reports/budget-monthly/budget-chart.tsx @@ -0,0 +1,31 @@ +import { Chart } from "react-chartjs-2"; +import { DefaultChartConfig, Service } from "../../config/global-chart-config"; +import React from "react"; +import { ChartData } from "chart.js"; + +const BudgetChart = ({ dataSet, currencySymbol = '' } : { dataSet: ChartData, currencySymbol: string }) => { + return `${currencySymbol}${value}` + } + } + }, + plugins: { + legend: { + display: true + } + } + }) } + data={ dataSet } /> +} + +export default BudgetChart \ No newline at end of file diff --git a/src/reports/budget-monthly/budget-yearly-expense.tsx b/src/reports/budget-monthly/budget-yearly-expense.tsx index 350deee9..ecd06f25 100644 --- a/src/reports/budget-monthly/budget-yearly-expense.tsx +++ b/src/reports/budget-monthly/budget-yearly-expense.tsx @@ -3,20 +3,19 @@ import { Dates, Layout, Translations } from "../../core"; import { ChartData } from "chart.js"; import { Budget, BudgetExpense } from "../../core/types"; import StatisticalRepository from "../../core/repositories/statistical-repository"; -import { Chart } from "react-chartjs-2"; -import { DefaultChartConfig, Service } from "../../config/global-chart-config"; +import BudgetChart from "./budget-chart"; type BudgetYearlyExpenseProps = { year: number, - budgets: Budget[] + budgets: Budget[], + currencySymbol: string } -const BudgetYearlyExpense = ({ year, budgets } : BudgetYearlyExpenseProps) => { +const BudgetYearlyExpense = ({ year, budgets, currencySymbol } : BudgetYearlyExpenseProps) => { const [chartData, setChartData] = useState() useEffect(() => { if (budgets.length === 0) return - console.info(`BudgetYearlyExpense: ${year} ${budgets.length}`) setChartData(undefined) const uniqueExpenses = budgets.reduce((left, right) => [...left, ...right.expenses], new Array()) @@ -54,25 +53,7 @@ const BudgetYearlyExpense = ({ year, budgets } : BudgetYearlyExpenseProps) => { return <> { !chartData && } - { chartData && <> - - } + { chartData && } } diff --git a/src/reports/budget-monthly/budget-yearly-income.tsx b/src/reports/budget-monthly/budget-yearly-income.tsx index ad0d7cac..24a9789c 100644 --- a/src/reports/budget-monthly/budget-yearly-income.tsx +++ b/src/reports/budget-monthly/budget-yearly-income.tsx @@ -2,15 +2,15 @@ import React, { useEffect, useState } from "react"; import { Dates, Layout, Statistical, Translations } from "../../core"; import { Budget } from "../../core/types"; import { ChartData } from "chart.js"; -import { Chart } from "react-chartjs-2"; -import { DefaultChartConfig, Service } from "../../config/global-chart-config"; +import BudgetChart from "./budget-chart"; type BudgetYearlyExpenseProps = { year: number, - budgets: Budget[] + budgets: Budget[], + currencySymbol: string } -const YearlyIncomeGraphComponent = ({ year = 1970, budgets = [] } : BudgetYearlyExpenseProps) => { +const YearlyIncomeGraphComponent = ({ year = 1970, budgets = [], currencySymbol = '' } : BudgetYearlyExpenseProps) => { const [chartData, setChartData] = useState() useEffect(() => { @@ -46,25 +46,7 @@ const YearlyIncomeGraphComponent = ({ year = 1970, budgets = [] } : BudgetYearly return <> { !chartData && } - { chartData && <> - - } + { chartData && } } diff --git a/src/reports/budget-monthly/index.js b/src/reports/budget-monthly/index.js index 86c31bb9..adf4291e 100644 --- a/src/reports/budget-monthly/index.js +++ b/src/reports/budget-monthly/index.js @@ -18,8 +18,10 @@ import BudgetYearlyExpense from "./budget-yearly-expense"; import '../../assets/css/BudgetReportView.scss' import BudgetRepository from "../../core/repositories/budget.repository"; +import { CurrencyRepository } from "../../core/RestAPI"; export const BudgetReportView = () => { + const [currencySymbol, setCurrencySymbol] = useState('') const [range, setRange] = useState(() => Dates.Ranges.currentYear()) const { currency = 'EUR', year = new Date().getFullYear() } = useParams() const [budgets, setBudgets] = useState([]) @@ -36,6 +38,10 @@ export const BudgetReportView = () => { .catch(console.error) } }, [year]) + useEffect(() => { + CurrencyRepository.get(currency) + .then((c) => setCurrencySymbol(c.symbol)) + }, [currency]) const onDateChanged = ({ newYear = year, @@ -60,8 +66,8 @@ export const BudgetReportView = () => { - - + + diff --git a/src/reports/dashboard/balance-chart.tsx b/src/reports/dashboard/balance-chart.tsx index f8fe530a..ef9bb421 100644 --- a/src/reports/dashboard/balance-chart.tsx +++ b/src/reports/dashboard/balance-chart.tsx @@ -4,11 +4,13 @@ import { Range } from "../../core/Dates"; import { BalanceSeries } from "../../core/graphs/balance-series"; import { ChartData } from "chart.js"; import { Chart } from "react-chartjs-2"; -import { DefaultChartConfig } from "../../config/global-chart-config"; +import { DefaultChartConfig, Service as ChartService } from "../../config/global-chart-config"; +import RestAPI from "../../core/repositories/rest-api"; const BalanceChart = ({ range } : { range: Range }) => { const [balanceSeries, setBalanceSeries] = useState() + useEffect(() => { BalanceSeries({ title: 'graph.series.balance', @@ -19,6 +21,21 @@ const BalanceChart = ({ range } : { range: Range }) => { })) }, [range]) + const config = ChartService.mergeOptions( + DefaultChartConfig.line, + { + scales: { + y: { + ticks: { + callback: (value: number) => { + return `${(RestAPI.user() as any).defaultCurrency?.symbol}${value.toFixed(2)}` + } + } + } + } + } + ) + return <> { !balanceSeries && } @@ -26,7 +43,7 @@ const BalanceChart = ({ range } : { range: Range }) => { { balanceSeries &&
}
diff --git a/src/reports/dashboard/budget-balance.tsx b/src/reports/dashboard/budget-balance.tsx index 8c063529..3084037a 100644 --- a/src/reports/dashboard/budget-balance.tsx +++ b/src/reports/dashboard/budget-balance.tsx @@ -6,8 +6,9 @@ import { Range } from "../../core/Dates"; import { ChartData } from "chart.js"; import { Budget, BudgetExpense } from "../../core/types"; import StatisticalRepository from "../../core/repositories/statistical-repository"; -import { DefaultChartConfig, Service } from "../../config/global-chart-config"; +import { DefaultChartConfig, Service as ChartService } from "../../config/global-chart-config"; import BudgetRepository from "../../core/repositories/budget.repository"; +import RestAPI from "../../core/repositories/rest-api"; const percentageOfYear = 90 / 365 @@ -44,6 +45,27 @@ function BudgetBalance({ range } : { range : Range }) { .catch(_ => setBudgetSeries({ labels: [], datasets: [] })) }, [range]) + const config = ChartService.mergeOptions( + DefaultChartConfig.bar, + { + scales: { + y: { + ticks: { + callback: (value: number) => { + return `${(RestAPI.user() as any).defaultCurrency?.symbol}${value.toFixed(2)}` + } + } + } + }, + plugins: { + legend: { + position: 'bottom', + display: true + } + } + } + ) + return <> { !budgetSeries && } @@ -51,14 +73,7 @@ function BudgetBalance({ range } : { range : Range }) { } diff --git a/src/reports/dashboard/categories-balance.tsx b/src/reports/dashboard/categories-balance.tsx index 625b6b68..6c84d761 100644 --- a/src/reports/dashboard/categories-balance.tsx +++ b/src/reports/dashboard/categories-balance.tsx @@ -7,7 +7,8 @@ import { ChartData } from "chart.js"; import { Category } from "../../core/types"; import StatisticalRepository from "../../core/repositories/statistical-repository"; import { Chart } from "react-chartjs-2"; -import { DefaultChartConfig } from "../../config/global-chart-config"; +import { DefaultChartConfig, Service as ChartService } from "../../config/global-chart-config"; +import RestAPI from "../../core/repositories/rest-api"; const CategoriesBalance = ({ range } : { range: Range }) => { const [categorySeries, setCategorySeries] = useState() @@ -34,13 +35,28 @@ const CategoriesBalance = ({ range } : { range: Range }) => { .catch(_ => setCategorySeries({ labels: [], datasets: [] })) }, [range]) + const config = ChartService.mergeOptions( + DefaultChartConfig.bar, + { + scales: { + y: { + ticks: { + callback: (value: number) => { + return `${(RestAPI.user() as any).defaultCurrency?.symbol}${value.toFixed(2)}` + } + } + } + } + } + ) + return <> { !categorySeries && } { categorySeries && } diff --git a/src/reports/dashboard/summary-component.tsx b/src/reports/dashboard/summary-component.tsx index 72874f5c..1639b6da 100644 --- a/src/reports/dashboard/summary-component.tsx +++ b/src/reports/dashboard/summary-component.tsx @@ -34,7 +34,7 @@ const SummaryComponent: FC = ({ title, icon, currency, cu }, [current, previous]) return <> - +

@@ -45,10 +45,10 @@ const SummaryComponent: FC = ({ title, icon, currency, cu { previous !== null && (
- + Comparison ${comparisonClass}`} style={ { lineHeight: '1.5rem' } }> +
)}

diff --git a/src/reports/dashboard/summary.js b/src/reports/dashboard/summary.js index 6768107f..0861ca2d 100644 --- a/src/reports/dashboard/summary.js +++ b/src/reports/dashboard/summary.js @@ -18,50 +18,54 @@ const Summary = ({ range, compareRange }) => { } return <> -
- balance) - } - previousPromise={ - Statistical.Service.balance({ ...compareBaseCommand, onlyIncome: true }) - .then(({ balance }) => balance) - } - currency='EUR' /> +
+
+ balance) + } + previousPromise={ + Statistical.Service.balance({ ...compareBaseCommand, onlyIncome: true }) + .then(({ balance }) => balance) + } + currency='EUR' /> - Math.abs(balance)) - } - previousPromise={ - Statistical.Service.balance({ ...compareBaseCommand, onlyIncome: false }) - .then(({ balance }) => Math.abs(balance)) - } - currency='EUR' /> + Math.abs(balance)) + } + previousPromise={ + Statistical.Service.balance({ ...compareBaseCommand, onlyIncome: false }) + .then(({ balance }) => Math.abs(balance)) + } + currency='EUR' /> +
- balance) - } - previousPromise={ - Statistical.Service.balance({ dateRange: { start: '1970-01-01', end: compareRange.endString() }, allMoney: true }) - .then(({ balance }) => balance) - } - currency='EUR' /> +
+ balance) + } + previousPromise={ + Statistical.Service.balance({ dateRange: { start: '1970-01-01', end: compareRange.endString() }, allMoney: true }) + .then(({ balance }) => balance) + } + currency='EUR' /> - + +
} diff --git a/src/transactions/transaction-item.tsx b/src/transactions/transaction-item.tsx index 8bca8847..f68f5e3e 100644 --- a/src/transactions/transaction-item.tsx +++ b/src/transactions/transaction-item.tsx @@ -49,7 +49,7 @@ const TransactionItem: FC = ({ transaction, className = '' if (deleted) return null return
- + { transaction.metadata.budget &&
{ transaction.metadata.budget } @@ -63,7 +63,7 @@ const TransactionItem: FC = ({ transaction, className = '' } - { transaction.description } + { transaction.description } { transaction.metadata.tags &&
{ transaction.metadata.tags.map(t => ) }
}