From d0a72f10b6cdf7275982e7d922ef51a32d935c70 Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Wed, 21 Jan 2026 19:01:23 +0100 Subject: [PATCH] Fix reports to correctly render categories with identical names (#6733) * Fix reports to correctly render categories with identical names Use category id instead of name as the data key for chart lookups to prevent collisions when multiple categories share the same display name. * Enhance CustomTooltip in LineGraph and StackedBarGraph to utilize legend data for improved display names. This change introduces a new LegendEntity type and updates the tooltip rendering logic to map data keys to their corresponding names, ensuring clarity when displaying chart data. --- .../components/reports/graphs/LineGraph.tsx | 18 +++++++++++++---- .../reports/graphs/StackedBarGraph.tsx | 20 ++++++++++++++----- .../reports/spreadsheets/calculateLegend.ts | 1 + .../spreadsheets/custom-spreadsheet.ts | 3 ++- .../loot-core/src/types/models/reports.ts | 1 + upcoming-release-notes/6733.md | 6 ++++++ 6 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 upcoming-release-notes/6733.md diff --git a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx index f428593663..becece5b60 100644 --- a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx @@ -17,6 +17,7 @@ import { import { type balanceTypeOpType, type DataEntity, + type LegendEntity, type RuleConditionEntity, } from 'loot-core/types/models'; @@ -46,6 +47,7 @@ type PayloadItem = { type CustomTooltipProps = { compact: boolean; tooltip: string; + legend: LegendEntity[]; active?: boolean; payload?: PayloadItem[]; format: (value: unknown, type: FormatType) => string; @@ -54,11 +56,17 @@ type CustomTooltipProps = { const CustomTooltip = ({ compact, tooltip, + legend, active, payload, format, }: CustomTooltipProps) => { const { t } = useTranslation(); + + const dataKeyToName = useMemo(() => { + return new Map(legend.map(entry => [entry.dataKey, entry.name])); + }, [legend]); + const { sumTotals, items } = useMemo(() => { return (payload ?? []) .sort((p1: PayloadItem, p2: PayloadItem) => p2.value - p1.value) @@ -94,11 +102,12 @@ const CustomTooltip = ({
{items.map((p: PayloadItem, index: number) => { + const displayName = dataKeyToName.get(p.dataKey) ?? p.dataKey; return ( (compact ? index < 4 : true) && ( {format(p.value, 'financial')} @@ -214,6 +223,7 @@ export function LineGraph({ } @@ -248,13 +258,13 @@ export function LineGraph({ key={index} strokeWidth={2} type="monotone" - dataKey={entry.name} + dataKey={entry.dataKey} stroke={entry.color} {...animationProps} activeDot={{ - r: entry.name === tooltip && !compact ? 8 : 3, + r: entry.dataKey === tooltip && !compact ? 8 : 3, onMouseEnter: () => { - setTooltip(entry.name); + setTooltip(entry.dataKey); if (!['Group', 'Interval'].includes(groupBy)) { setPointer('pointer'); } diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx index dbf3cd01ae..2f001c4572 100644 --- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx @@ -18,6 +18,7 @@ import { import { type balanceTypeOpType, type DataEntity, + type LegendEntity, type RuleConditionEntity, } from 'loot-core/types/models'; @@ -48,6 +49,7 @@ type PayloadItem = { type CustomTooltipProps = { compact: boolean; tooltip: string; + legend: LegendEntity[]; active?: boolean; payload?: PayloadItem[]; label?: string; @@ -57,12 +59,18 @@ type CustomTooltipProps = { const CustomTooltip = ({ compact, tooltip, + legend, active, payload, label, format, }: CustomTooltipProps) => { const { t } = useTranslation(); + + const dataKeyToName = useMemo(() => { + return new Map(legend.map(entry => [entry.dataKey, entry.name])); + }, [legend]); + const { sumTotals, items } = useMemo(() => { return (payload ?? []) .slice(0) @@ -99,12 +107,13 @@ const CustomTooltip = ({
{items.map((pay, i) => { + const displayName = dataKeyToName.get(pay.name) ?? pay.name; return ( pay.value !== 0 && (compact ? i < 5 : true) && ( {format(pay.value, 'financial')} @@ -229,6 +238,7 @@ export function StackedBarGraph({ } @@ -261,8 +271,8 @@ export function StackedBarGraph({ .reverse() .map(entry => ( { - setTooltip(entry.name); + setTooltip(entry.dataKey); if (!['Group', 'Interval'].includes(groupBy)) { setPointer('pointer'); } @@ -298,7 +308,7 @@ export function StackedBarGraph({ > {viewLabels && !compact && ( )} diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts index 869c0b8d6d..fed7221d7f 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts @@ -55,6 +55,7 @@ export function calculateLegend( id: item.id || '', name: item.name || '', color: getColor(item.data, index), + dataKey: item.id || item.name || '', // Use id for unique data lookup }; }); return legend; 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 3f4e68fbce..06d846bdf9 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts @@ -220,7 +220,8 @@ export function createCustomSpreadsheet({ stackAmounts += netAmounts; } - stacked[item.name] = stackAmounts; + // Use id as key to prevent collisions when categories have the same name + stacked[item.id || item.name] = stackAmounts; perIntervalTotals += netAmounts; diff --git a/packages/loot-core/src/types/models/reports.ts b/packages/loot-core/src/types/models/reports.ts index 4d90957962..796e922256 100644 --- a/packages/loot-core/src/types/models/reports.ts +++ b/packages/loot-core/src/types/models/reports.ts @@ -86,6 +86,7 @@ export type LegendEntity = { name: string; id: string | null; color: string; + dataKey: string; // Uses id for unique data lookup when categories have same name }; export type IntervalEntity = { diff --git a/upcoming-release-notes/6733.md b/upcoming-release-notes/6733.md new file mode 100644 index 0000000000..ddf6f8b109 --- /dev/null +++ b/upcoming-release-notes/6733.md @@ -0,0 +1,6 @@ +--- +category: Bugfixes +authors: [MatissJanis] +--- + +Reports: correctly render categories with identical names