diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-1-chromium-linux.png index d52d2ef3024..c2ed7f61e9b 100644 Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-2-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-2-chromium-linux.png index 139319f84e7..c4ee6bae769 100644 Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-2-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-cash-flow-graph-and-checks-visuals-2-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png index 6ad0736f6f5..633364db146 100644 Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png differ diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-2-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-2-chromium-linux.png index 4052e48c433..21fd182d2dd 100644 Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-2-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-2-chromium-linux.png differ diff --git a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx index 5defc3c3941..d1b82ccaf22 100644 --- a/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/CashFlowGraph.tsx @@ -1,66 +1,164 @@ -// @ts-strict-ignore -import React from 'react'; +import React, { useState } from 'react'; import * as d from 'date-fns'; +import { css } from 'glamor'; import { - VictoryChart, - VictoryBar, - VictoryLine, - VictoryAxis, - VictoryVoronoiContainer, - VictoryGroup, -} from 'victory'; + Bar, + CartesianGrid, + ComposedChart, + Line, + ReferenceLine, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, + type TooltipProps, +} from 'recharts'; + +import { usePrivacyMode } from 'loot-core/src/client/privacy'; +import { + amountToCurrency, + amountToCurrencyNoDecimal, +} from 'loot-core/src/shared/util'; import { theme } from '../../../style'; +import { AlignedText } from '../../common/AlignedText'; import { chartTheme } from '../chart-theme'; -import { Container } from '../Container'; -import { Tooltip } from '../Tooltip'; -type CashFlowGraphProps = { - graphData: { expenses; income; balances }; +const MAX_BAR_SIZE = 50; +const ANIMATION_DURATION = 1000; // in ms + +type CustomTooltipProps = TooltipProps & { isConcise: boolean; }; -export function CashFlowGraph({ graphData, isConcise }: CashFlowGraphProps) { + +function CustomTooltip({ active, payload, isConcise }: CustomTooltipProps) { + if (!active || !payload) { + return null; + } + + const [{ payload: data }] = payload; + return ( - - {(width, height, portalHost) => - graphData && ( - +
+
+
+ + {d.format(data.date, isConcise ? 'MMMM yyyy' : 'MMMM dd, yyyy')} + +
+
+ + + {amountToCurrency(data.income + data.expenses)} } - > - - - - - } - labels={x => x.premadeLabel} - style={{ - data: { stroke: theme.pageTextLight }, - }} + /> + {data.transfers !== 0 && ( + - d.format(x, isConcise ? "MMM ''yy" : 'MMM d')} - tickValues={graphData.balances.map(item => item.x)} - tickCount={Math.min(5, graphData.balances.length)} - offsetY={50} - /> - - - ) - } - + )} + +
+
+
+ ); +} + +type CashFlowGraphProps = { + graphData: { + expenses: { x: Date; y: number }[]; + income: { x: Date; y: number }[]; + balances: { x: Date; y: number }[]; + transfers: { x: Date; y: number }[]; + }; + isConcise: boolean; +}; +export function CashFlowGraph({ graphData, isConcise }: CashFlowGraphProps) { + const privacyMode = usePrivacyMode(); + const [yAxisIsHovered, setYAxisIsHovered] = useState(false); + + const data = graphData.expenses.map((row, idx) => ({ + date: row.x, + expenses: row.y, + income: graphData.income[idx].y, + balance: graphData.balances[idx].y, + transfers: graphData.transfers[idx].y, + })); + + return ( + + + + { + // eslint-disable-next-line rulesdir/typography + return d.format(x, isConcise ? "MMM ''yy" : 'MMM d'); + }} + minTickGap={50} + /> + + privacyMode && !yAxisIsHovered + ? '...' + : amountToCurrencyNoDecimal(value) + } + onMouseEnter={() => setYAxisIsHovered(true)} + onMouseLeave={() => setYAxisIsHovered(false)} + /> + { + // eslint-disable-next-line rulesdir/typography + return d.format(x, isConcise ? "MMM ''yy" : 'MMM d'); + }} + content={} + isAnimationActive={false} + /> + + + + + + + ); } diff --git a/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx b/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx index 30df9368c5f..07759a4ece3 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useCallback } from 'react'; -import { VictoryBar, VictoryGroup, VictoryVoronoiContainer } from 'victory'; +import { Bar, BarChart, ResponsiveContainer } from 'recharts'; import * as monthUtils from 'loot-core/src/shared/months'; import { integerToCurrency } from 'loot-core/src/shared/util'; @@ -11,14 +11,37 @@ import { View } from '../../common/View'; import { PrivacyFilter } from '../../PrivacyFilter'; import { Change } from '../Change'; import { chartTheme } from '../chart-theme'; -import { Container } from '../Container'; import { DateRange } from '../DateRange'; import { LoadingIndicator } from '../LoadingIndicator'; import { ReportCard } from '../ReportCard'; import { simpleCashFlow } from '../spreadsheets/cash-flow-spreadsheet'; -import { Tooltip } from '../Tooltip'; import { useReport } from '../useReport'; +function CustomLabel({ value, name, position, ...props }) { + return ( + <> + + {name} + + + {integerToCurrency(value)} + + + ); +} + export function CashFlowCard() { const end = monthUtils.currentDay(); const start = monthUtils.currentMonth() + '-01'; @@ -31,7 +54,7 @@ export function CashFlowCard() { const onCardHoverEnd = useCallback(() => setIsCardHovered(false)); const { graphData } = data || {}; - const expense = -(graphData?.expense || 0); + const expenses = -(graphData?.expense || 0); const income = graphData?.income || 0; return ( @@ -55,7 +78,7 @@ export function CashFlowCard() { @@ -64,84 +87,33 @@ export function CashFlowCard() { {data ? ( - - {(width, height, portalHost) => ( - - } - labelComponent={ - (y + 40 > height ? height - 40 : y)} - light={true} - forceActive={true} - style={{ - padding: 0, - }} - /> - } - padding={{ - top: 0, - bottom: 0, - left: 0, - right: 0, - }} - > - - Income - - - {integerToCurrency(income)} - - - - ), - labelPosition: 'left', - }, - ]} - labels={d => d.premadeLabel} - /> - - Expenses - - - {integerToCurrency(expense)} - - - - ), - labelPosition: 'right', - }, - ]} - labels={d => d.premadeLabel} - /> - - )} - + + + } + /> + } + /> + + ) : ( )} diff --git a/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx b/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx index 7048e0a1d6d..f13f6105659 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx +++ b/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx @@ -180,6 +180,10 @@ function recalculate(data, start, end, isConcise) { res.income.push({ x, y: integerToAmount(income) }); res.expenses.push({ x, y: integerToAmount(expense) }); + res.transfers.push({ + x, + y: integerToAmount(creditTransfers + debitTransfers), + }); res.balances.push({ x, y: integerToAmount(balance), @@ -188,7 +192,7 @@ function recalculate(data, start, end, isConcise) { }); return res; }, - { expenses: [], income: [], balances: [] }, + { expenses: [], income: [], transfers: [], balances: [] }, ); const { balances } = graphData; diff --git a/upcoming-release-notes/2260.md b/upcoming-release-notes/2260.md new file mode 100644 index 00000000000..8f4a3b44571 --- /dev/null +++ b/upcoming-release-notes/2260.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Refactored cash flow report from `victory` to `recharts`