From 1ee2cbec1c8a10441d9a652e27dc56bd03c9f4ea Mon Sep 17 00:00:00 2001 From: Neil <55785687+carkom@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:37:07 +0100 Subject: [PATCH] Custom Reports: Show Activity onClick (#2522) * barGraph * notes * sessionStorage * sessionStorage Complete * Click for details * mode click graphType * compact * hidden and offbudget working * add pointer * typecheck fix --- .../src/components/reports/ChooseGraph.tsx | 6 ++ .../src/components/reports/Overview.jsx | 4 + .../src/components/reports/ReportSidebar.jsx | 71 ++++++++++++++++ .../src/components/reports/ReportTopbar.jsx | 29 +++---- .../components/reports/graphs/BarGraph.tsx | 84 ++++++++++++++++++- .../reports/reports/CustomReport.jsx | 26 +++++- upcoming-release-notes/2522.md | 6 ++ 7 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 upcoming-release-notes/2522.md diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx index 1e47c0c61..e68b99ef3 100644 --- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx +++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx @@ -32,6 +32,8 @@ type ChooseGraphProps = { viewLabels?: boolean; compact?: boolean; style?: CSSProperties; + showHiddenCategories?: boolean; + showOffBudget?: boolean; }; export function ChooseGraph({ @@ -47,6 +49,8 @@ export function ChooseGraph({ viewLabels, compact, style, + showHiddenCategories, + showOffBudget, }: ChooseGraphProps) { const intervals: string[] = monthUtils.rangeInclusive(startDate, endDate); const graphStyle = compact ? { ...style } : { flexGrow: 1 }; @@ -104,6 +108,8 @@ export function ChooseGraph({ groupBy={groupBy} balanceTypeOp={balanceTypeOp} viewLabels={viewLabels} + showHiddenCategories={showHiddenCategories} + showOffBudget={showOffBudget} /> ); } diff --git a/packages/desktop-client/src/components/reports/Overview.jsx b/packages/desktop-client/src/components/reports/Overview.jsx index 939421c30..e6a5e00d6 100644 --- a/packages/desktop-client/src/components/reports/Overview.jsx +++ b/packages/desktop-client/src/components/reports/Overview.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useLocation } from 'react-router-dom'; import { useReports } from 'loot-core/src/client/data-hooks/reports'; @@ -17,6 +18,9 @@ import { NetWorthCard } from './reports/NetWorthCard'; export function Overview() { const customReports = useReports(); + const location = useLocation(); + sessionStorage.setItem('url', location.pathname); + const customReportsFeatureFlag = useFeatureFlag('customReports'); const accounts = useAccounts(); diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.jsx b/packages/desktop-client/src/components/reports/ReportSidebar.jsx index 3b2585eba..b537911bf 100644 --- a/packages/desktop-client/src/components/reports/ReportSidebar.jsx +++ b/packages/desktop-client/src/components/reports/ReportSidebar.jsx @@ -42,22 +42,40 @@ export function ReportSidebar({ }) { const [menuOpen, setMenuOpen] = useState(false); const onSelectRange = cond => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, dateRange: cond }), + ); onReportChange({ type: 'modify' }); setDateRange(cond); onChangeDates(...getLiveRange(cond, earliestTransaction)); }; const onChangeMode = cond => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, mode: cond }), + ); onReportChange({ type: 'modify' }); setMode(cond); let graph; if (cond === 'time') { if (customReportItems.graphType === 'BarGraph') { + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, graphType: 'StackedBarGraph' }), + ); setGraphType('StackedBarGraph'); graph = 'StackedBarGraph'; } } else { if (customReportItems.graphType === 'StackedBarGraph') { + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, graphType: 'BarGraph' }), + ); setGraphType('BarGraph'); graph = 'BarGraph'; } @@ -66,12 +84,22 @@ export function ReportSidebar({ }; const onChangeSplit = cond => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, groupBy: cond }), + ); onReportChange({ type: 'modify' }); setGroupBy(cond); defaultItems(cond); }; const onChangeBalanceType = cond => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, balanceType: cond }), + ); onReportChange({ type: 'modify' }); setBalanceType(cond); }; @@ -217,17 +245,50 @@ export function ReportSidebar({ > <Menu onMenuSelect={type => { + const storedReport = JSON.parse( + sessionStorage.getItem('report'), + ); onReportChange({ type: 'modify' }); if (type === 'show-hidden-categories') { + sessionStorage.setItem( + 'report', + JSON.stringify({ + ...storedReport, + showHiddenCategories: + !customReportItems.showHiddenCategories, + }), + ); setShowHiddenCategories( !customReportItems.showHiddenCategories, ); } else if (type === 'show-off-budget') { + sessionStorage.setItem( + 'report', + JSON.stringify({ + ...storedReport, + showOffBudget: !customReportItems.showOffBudget, + }), + ); setShowOffBudget(!customReportItems.showOffBudget); } else if (type === 'show-empty-items') { + sessionStorage.setItem( + 'report', + JSON.stringify({ + ...storedReport, + showEmpty: !customReportItems.showEmpty, + }), + ); setShowEmpty(!customReportItems.showEmpty); } else if (type === 'show-uncategorized') { + sessionStorage.setItem( + 'report', + JSON.stringify({ + ...storedReport, + showUncategorized: + !customReportItems.showUncategorized, + }), + ); setShowUncategorized( !customReportItems.showUncategorized, ); @@ -287,6 +348,11 @@ export function ReportSidebar({ <ModeButton selected={!customReportItems.isDateStatic} onSelect={() => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, isDateStatic: false }), + ); setIsDateStatic(false); onSelectRange(customReportItems.dateRange); }} @@ -296,6 +362,11 @@ export function ReportSidebar({ <ModeButton selected={customReportItems.isDateStatic} onSelect={() => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, isDateStatic: true }), + ); setIsDateStatic(true); onChangeDates( customReportItems.startDate, diff --git a/packages/desktop-client/src/components/reports/ReportTopbar.jsx b/packages/desktop-client/src/components/reports/ReportTopbar.jsx index ab23885b0..3a98f00ee 100644 --- a/packages/desktop-client/src/components/reports/ReportTopbar.jsx +++ b/packages/desktop-client/src/components/reports/ReportTopbar.jsx @@ -31,6 +31,17 @@ export function ReportTopbar({ disabledItems, defaultItems, }) { + const onChangeGraph = cond => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ ...storedReport, graphType: cond }), + ); + onReportChange({ type: 'modify' }); + setGraphType(cond); + defaultItems(cond); + }; + return ( <View style={{ @@ -44,9 +55,7 @@ export function ReportTopbar({ selected={customReportItems.graphType === 'TableGraph'} title="Data Table" onSelect={() => { - onReportChange({ type: 'modify' }); - setGraphType('TableGraph'); - defaultItems('TableGraph'); + onChangeGraph('TableGraph'); }} style={{ marginRight: 15 }} disabled={disabledItems('TableGraph')} @@ -62,11 +71,7 @@ export function ReportTopbar({ customReportItems.graphType === 'StackedBarGraph' } onSelect={() => { - onReportChange({ type: 'modify' }); - setGraphType( - customReportItems.mode === 'total' ? 'BarGraph' : 'StackedBarGraph', - ); - defaultItems( + onChangeGraph( customReportItems.mode === 'total' ? 'BarGraph' : 'StackedBarGraph', ); }} @@ -94,9 +99,7 @@ export function ReportTopbar({ title="Area Graph" selected={customReportItems.graphType === 'AreaGraph'} onSelect={() => { - onReportChange({ type: 'modify' }); - setGraphType('AreaGraph'); - defaultItems('AreaGraph'); + onChangeGraph('AreaGraph'); }} style={{ marginRight: 15 }} disabled={disabledItems('AreaGraph')} @@ -107,9 +110,7 @@ export function ReportTopbar({ title="Donut Graph" selected={customReportItems.graphType === 'DonutGraph'} onSelect={() => { - onReportChange({ type: 'modify' }); - setGraphType('DonutGraph'); - defaultItems('DonutGraph'); + onChangeGraph('DonutGraph'); }} style={{ marginRight: 15 }} disabled={disabledItems('DonutGraph')} diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx index f85572d8c..e9d5252b4 100644 --- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx @@ -1,5 +1,5 @@ // @ts-strict-ignore -import React from 'react'; +import React, { useState } from 'react'; import { css } from 'glamor'; import { @@ -21,9 +21,12 @@ import { } from 'loot-core/src/shared/util'; import { type GroupedEntity } from 'loot-core/src/types/models/reports'; +import { useAccounts } from '../../../hooks/useAccounts'; +import { useCategories } from '../../../hooks/useCategories'; +import { useNavigate } from '../../../hooks/useNavigate'; import { usePrivacyMode } from '../../../hooks/usePrivacyMode'; -import { theme } from '../../../style'; import { type CSSProperties } from '../../../style'; +import { theme } from '../../../style/index'; import { AlignedText } from '../../common/AlignedText'; import { Container } from '../Container'; import { getCustomTick } from '../getCustomTick'; @@ -133,6 +136,8 @@ type BarGraphProps = { balanceTypeOp: string; compact?: boolean; viewLabels: boolean; + showHiddenCategories?: boolean; + showOffBudget?: boolean; }; export function BarGraph({ @@ -142,8 +147,14 @@ export function BarGraph({ balanceTypeOp, compact, viewLabels, + showHiddenCategories, + showOffBudget, }: BarGraphProps) { + const navigate = useNavigate(); + const categories = useCategories(); + const accounts = useAccounts(); const privacyMode = usePrivacyMode(); + const [pointer, setPointer] = useState(''); const yAxis = groupBy === 'Interval' ? 'date' : 'name'; const splitData = groupBy === 'Interval' ? 'intervalData' : 'data'; @@ -166,6 +177,61 @@ export function BarGraph({ .reduce((acc, cur) => (Math.abs(cur) > Math.abs(acc) ? cur : acc), 0); const leftMargin = Math.abs(largestValue) > 1000000 ? 20 : 0; + + const onShowActivity = item => { + const amount = balanceTypeOp === 'totalDebts' ? 'lte' : 'gte'; + const field = groupBy === 'Interval' ? null : groupBy.toLowerCase(); + const hiddenCategories = categories.list + .filter(f => f.hidden) + .map(e => e.id); + const offBudgetAccounts = accounts.filter(f => f.offbudget).map(e => e.id); + + const conditions = [ + { field, op: 'is', value: item.id, type: 'id' }, + { + field: 'date', + op: 'gte', + value: data.startDate, + options: { date: true }, + type: 'date', + }, + { + field: 'date', + op: 'lte', + value: data.endDate, + options: { date: true }, + type: 'date', + }, + balanceTypeOp !== 'totalTotals' && { + field: 'amount', + op: amount, + value: 0, + type: 'number', + }, + hiddenCategories.length > 0 && + !showHiddenCategories && { + field: 'category', + op: 'notOneOf', + value: hiddenCategories, + type: 'id', + }, + offBudgetAccounts.length > 0 && + !showOffBudget && { + field: 'account', + op: 'notOneOf', + value: offBudgetAccounts, + type: 'id', + }, + ].filter(f => f); + navigate('/accounts', { + state: { + goBack: true, + conditions, + categoryId: item.id, + }, + }); + }; + return ( <Container style={{ @@ -183,6 +249,7 @@ export function BarGraph({ height={height} stackOffset="sign" data={data[splitData]} + style={{ cursor: pointer }} margin={{ top: labelsMargin, right: 0, @@ -226,7 +293,18 @@ export function BarGraph({ <ReferenceLine y={0} stroke={theme.pageTextLight} /> </> )} - <Bar dataKey={val => getVal(val)} stackId="a"> + <Bar + dataKey={val => getVal(val)} + stackId="a" + onMouseLeave={() => setPointer('')} + onMouseEnter={() => + !['Group', 'Interval'].includes(groupBy) && + setPointer('pointer') + } + onClick={ + !['Group', 'Interval'].includes(groupBy) && onShowActivity + } + > {viewLabels && !compact && ( <LabelList dataKey={val => getVal(val)} diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx index c6da3eb0a..db792e71e 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx @@ -54,9 +54,19 @@ export function CustomReport() { } = useFilters(); const location = useLocation(); - const loadReport = location.state + + const prevUrl = sessionStorage.getItem('url'); + + sessionStorage.setItem('prevUrl', prevUrl); + sessionStorage.setItem('url', location.pathname); + + if (['/reports'].includes(prevUrl)) sessionStorage.clear(); + + const session = JSON.parse(sessionStorage.getItem('report')); + const combine = location.state ? location.state.report ?? defaultReport : defaultReport; + const loadReport = { ...combine, ...session }; const [allIntervals, setAllIntervals] = useState(null); @@ -319,6 +329,15 @@ export function CustomReport() { }; const onChangeDates = (dateStart, dateEnd) => { + const storedReport = JSON.parse(sessionStorage.getItem('report')); + sessionStorage.setItem( + 'report', + JSON.stringify({ + ...storedReport, + startDate: dateStart, + endDate: dateEnd, + }), + ); setStartDate(dateStart); setEndDate(dateEnd); onReportChange({ type: 'modify' }); @@ -381,15 +400,18 @@ export function CustomReport() { } break; case 'reload': + sessionStorage.clear(); setSavedStatus('saved'); setReportData(report); break; case 'reset': + sessionStorage.clear(); setSavedStatus('new'); setReport(defaultReport); setReportData(defaultReport); break; case 'choose': + sessionStorage.clear(); setSavedStatus('saved'); setReport(savedReport); setReportData(savedReport); @@ -554,6 +576,8 @@ export function CustomReport() { setScrollWidth={setScrollWidth} viewLabels={viewLabels} compact={false} + showHiddenCategories={showHiddenCategories} + showOffBudget={showOffBudget} /> ) : ( <LoadingIndicator message="Loading report..." /> diff --git a/upcoming-release-notes/2522.md b/upcoming-release-notes/2522.md new file mode 100644 index 000000000..be098155e --- /dev/null +++ b/upcoming-release-notes/2522.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [carkom] +--- + +Custom reports so transactions activity on accounts page for graphs when clicked. -- GitLab