From c98a21b030d1af94dd87f8c7733b643307874dee Mon Sep 17 00:00:00 2001 From: Neil <55785687+carkom@users.noreply.github.com> Date: Sun, 21 Apr 2024 18:26:32 +0100 Subject: [PATCH] Custom Reports StackedBar show activity (#2606) * StackedBar Activity * LineGraph and some session updates * notes * table graphs * Revert "table graphs" This reverts commit 69b5a441261a7e257fb84f353907d5d101c0fa48. * revert Line * notes update * adust session code and add filters * review fixes --- .../src/components/reports/ChooseGraph.tsx | 4 + .../reports/graphs/StackedBarGraph.tsx | 94 ++++++++++++++++++- .../reports/reports/CustomReport.jsx | 17 +++- .../reports/spreadsheets/calculateLegend.ts | 13 ++- .../spreadsheets/custom-spreadsheet.ts | 1 + .../loot-core/src/types/models/reports.d.ts | 1 + upcoming-release-notes/2606.md | 6 ++ 7 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 upcoming-release-notes/2606.md diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx index 3e294fbff..97c2c2aca 100644 --- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx +++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx @@ -140,8 +140,12 @@ export function ChooseGraph({ style={graphStyle} compact={compact} data={data} + filters={filters} viewLabels={viewLabels} balanceTypeOp={balanceTypeOp} + groupBy={groupBy} + showHiddenCategories={showHiddenCategories} + showOffBudget={showOffBudget} /> ); } diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx index 2a3136fe6..7264e94fd 100644 --- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx @@ -1,5 +1,5 @@ // @ts-strict-ignore -import React from 'react'; +import React, { useState } from 'react'; import { css } from 'glamor'; import { @@ -18,7 +18,11 @@ import { amountToCurrencyNoDecimal, } from 'loot-core/src/shared/util'; import { type GroupedEntity } from 'loot-core/src/types/models/reports'; +import { type RuleConditionEntity } from 'loot-core/types/models/rule'; +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'; @@ -41,6 +45,7 @@ type PayloadItem = { type CustomTooltipProps = { compact: boolean; + tooltip: string; active?: boolean; payload?: PayloadItem[]; label?: string; @@ -48,6 +53,7 @@ type CustomTooltipProps = { const CustomTooltip = ({ compact, + tooltip, active, payload, label, @@ -83,7 +89,11 @@ const CustomTooltip = ({ key={pay.name} left={pay.name} right={amountToCurrency(pay.value)} - style={{ color: pay.color }} + style={{ + color: pay.color, + textDecoration: + tooltip === pay.name ? 'underline' : 'inherit', + }} /> ) ); @@ -128,25 +138,86 @@ const customLabel = props => { type StackedBarGraphProps = { style?: CSSProperties; data: GroupedEntity; + filters: RuleConditionEntity[]; + groupBy: string; compact?: boolean; viewLabels: boolean; balanceTypeOp: string; + showHiddenCategories?: boolean; + showOffBudget?: boolean; }; export function StackedBarGraph({ style, data, + filters, + groupBy, compact, viewLabels, balanceTypeOp, + showHiddenCategories, + showOffBudget, }: StackedBarGraphProps) { + const navigate = useNavigate(); + const categories = useCategories(); + const accounts = useAccounts(); const privacyMode = usePrivacyMode(); + const [pointer, setPointer] = useState(''); + const [tooltip, setTooltip] = useState(''); const largestValue = data.intervalData .map(c => c[balanceTypeOp]) .reduce((acc, cur) => (Math.abs(cur) > Math.abs(acc) ? cur : acc), 0); const leftMargin = Math.abs(largestValue) > 1000000 ? 20 : 0; + + const onShowActivity = (item, id) => { + 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 = [ + ...filters, + { field, op: 'is', value: id, type: 'id' }, + { + field: 'date', + op: 'is', + value: item.dateStart, + options: { date: true }, + }, + 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={{ @@ -164,9 +235,12 @@ export function StackedBarGraph({ height={height} data={data.intervalData} margin={{ top: 0, right: 0, left: leftMargin, bottom: 0 }} + style={{ cursor: pointer }} > <Tooltip - content={<CustomTooltip compact={compact} />} + content={ + <CustomTooltip compact={compact} tooltip={tooltip} /> + } formatter={numberFormatterTooltip} isAnimationActive={false} cursor={{ fill: 'transparent' }} @@ -201,6 +275,20 @@ export function StackedBarGraph({ dataKey={entry.name} stackId="a" fill={entry.color} + onMouseLeave={() => { + setPointer(''); + setTooltip(''); + }} + onMouseEnter={() => { + setTooltip(entry.name); + if (!['Group', 'Interval'].includes(groupBy)) { + setPointer('pointer'); + } + }} + onClick={e => + !['Group', 'Interval'].includes(groupBy) && + onShowActivity(e, entry.id) + } > {viewLabels && !compact && ( <LabelList dataKey={entry.name} content={customLabel} /> diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx index 81292f60e..d69f94675 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx @@ -293,17 +293,22 @@ export function CustomReport() { ? defaultsList.modeGraphsMap.get(item) : chooseGraph; if (disabledList.modeGraphsMap.get(item).includes(graphType)) { + setSessionReport('graphType', newGraph); setGraphType(newGraph); } if (disabledList.graphSplitMap.get(item).get(newGraph).includes(groupBy)) { - setGroupBy(defaultsList.graphSplitMap.get(item).get(newGraph)); + const cond = defaultsList.graphSplitMap.get(item).get(newGraph); + setSessionReport('groupBy', cond); + setGroupBy(cond); } if ( disabledList.graphTypeMap.get(item).get(newGraph).includes(balanceType) ) { - setBalanceType(defaultsList.graphTypeMap.get(item).get(newGraph)); + const cond = defaultsList.graphTypeMap.get(item).get(newGraph); + setSessionReport('balanceType', cond); + setBalanceType(cond); } }; @@ -312,12 +317,16 @@ export function CustomReport() { if ( disabledList.graphSplitMap.get(mode).get(chooseGraph).includes(groupBy) ) { - setGroupBy(defaultsList.graphSplitMap.get(mode).get(chooseGraph)); + const cond = defaultsList.graphSplitMap.get(mode).get(chooseGraph); + setSessionReport('groupBy', cond); + setGroupBy(cond); } if ( disabledList.graphTypeMap.get(mode).get(chooseGraph).includes(balanceType) ) { - setBalanceType(defaultsList.graphTypeMap.get(mode).get(chooseGraph)); + const cond = defaultsList.graphTypeMap.get(mode).get(chooseGraph); + setSessionReport('balanceType', cond); + setBalanceType(cond); } }; diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts index db3976bd9..f19bc6904 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts @@ -16,11 +16,16 @@ export function calculateLegend( const colorScale = getColorScale('qualitative'); const chooseData = groupBy === 'Interval' - ? intervalData.map(c => c.date) - : calcDataFiltered.map(c => c.name); - return chooseData.map((name, index) => { + ? intervalData.map(c => { + return { name: c.date, id: null }; + }) + : calcDataFiltered.map(c => { + return { name: c.name, id: c.id }; + }); + return chooseData.map((item, index) => { return { - name, + id: item.id, + name: item.name, color: graphType === 'DonutGraph' ? colorScale[index % colorScale.length] diff --git a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts index d950c615e..d79175b2e 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts @@ -221,6 +221,7 @@ export function createCustomSpreadsheet({ d.format(d.parseISO(`${intervalItem}-01`), "MMM ''yy") : intervalItem, ...stacked, + dateStart: intervalItem, totalDebts: integerToAmount(perIntervalDebts), totalAssets: integerToAmount(perIntervalAssets), totalTotals: integerToAmount(perIntervalDebts + perIntervalAssets), diff --git a/packages/loot-core/src/types/models/reports.d.ts b/packages/loot-core/src/types/models/reports.d.ts index 0b1af87a7..19789df76 100644 --- a/packages/loot-core/src/types/models/reports.d.ts +++ b/packages/loot-core/src/types/models/reports.d.ts @@ -38,6 +38,7 @@ export interface GroupedEntity { type LegendEntity = { name: string; + id: string | null; color: string; }; diff --git a/upcoming-release-notes/2606.md b/upcoming-release-notes/2606.md new file mode 100644 index 000000000..0e8c6321b --- /dev/null +++ b/upcoming-release-notes/2606.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [carkom] +--- + +Enables the ability to show transactions when StackedBarGraph is clicked. -- GitLab