From e62e8ca24eb932d4e7004327650eb0d0c0f24b23 Mon Sep 17 00:00:00 2001
From: Neil <55785687+carkom@users.noreply.github.com>
Date: Sat, 16 Dec 2023 21:38:45 +0000
Subject: [PATCH] Custom Reports Enable Legend (#2078)

* enable Legend

* notes

* adding type

* overhaul

* calculateLegend
---
 .../src/components/reports/ReportSidebar.js   |  2 +-
 .../src/components/reports/ReportTopbar.js    |  7 ++-
 .../src/components/reports/entities.d.ts      | 10 +++-
 .../components/reports/graphs/BarGraph.tsx    | 38 ++-------------
 .../components/reports/graphs/DonutGraph.tsx  | 42 ++---------------
 .../reports/graphs/StackedBarGraph.tsx        | 46 +++++--------------
 .../reports/reports/CustomReport.js           |  7 +--
 .../reports/spreadsheets/calculateLegend.ts   | 31 +++++++++++++
 .../spreadsheets/default-spreadsheet.tsx      | 17 ++++++-
 upcoming-release-notes/2078.md                |  6 +++
 10 files changed, 86 insertions(+), 120 deletions(-)
 create mode 100644 packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
 create mode 100644 upcoming-release-notes/2078.md

diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.js b/packages/desktop-client/src/components/reports/ReportSidebar.js
index 347f3184f..d3bad3e9a 100644
--- a/packages/desktop-client/src/components/reports/ReportSidebar.js
+++ b/packages/desktop-client/src/components/reports/ReportSidebar.js
@@ -99,7 +99,7 @@ export function ReportSidebar({
       }
       if (['AreaGraph', 'DonutGraph'].includes(graphType)) {
         setGraphType('TableGraph');
-        //setViewLegend(false);
+        setViewLegend(false);
       }
       if (['Month', 'Year'].includes(groupBy)) {
         setGroupBy('Category');
diff --git a/packages/desktop-client/src/components/reports/ReportTopbar.js b/packages/desktop-client/src/components/reports/ReportTopbar.js
index c623a5203..353f9ddcb 100644
--- a/packages/desktop-client/src/components/reports/ReportTopbar.js
+++ b/packages/desktop-client/src/components/reports/ReportTopbar.js
@@ -45,7 +45,7 @@ export function ReportTopbar({
         title="Data Table"
         onSelect={() => {
           setGraphType('TableGraph');
-          //setViewLegend(false);
+          setViewLegend(false);
           setTypeDisabled([]);
         }}
         style={{ marginRight: 15 }}
@@ -78,7 +78,7 @@ export function ReportTopbar({
         onSelect={() => {
           setGraphType('AreaGraph');
           setGroupBy('Month');
-          //setViewLegend(false);
+          setViewLegend(false);
           setTypeDisabled([]);
         }}
         style={{ marginRight: 15 }}
@@ -116,8 +116,7 @@ export function ReportTopbar({
         style={{ marginRight: 15 }}
         title="Show Legend"
         disabled={
-          true //descoping for future PR
-          //graphType === 'TableGraph' || graphType === 'AreaGraph' ? true : false
+          graphType === 'TableGraph' || graphType === 'AreaGraph' ? true : false
         }
       >
         <ListBullet width={15} height={15} />
diff --git a/packages/desktop-client/src/components/reports/entities.d.ts b/packages/desktop-client/src/components/reports/entities.d.ts
index cbd01affb..d58698837 100644
--- a/packages/desktop-client/src/components/reports/entities.d.ts
+++ b/packages/desktop-client/src/components/reports/entities.d.ts
@@ -2,6 +2,7 @@ export type DataEntity = {
   data: Array<ItemEntity>;
   monthData: Array<MonthData>;
   groupedData: Array<GroupedEntity>;
+  legend: LegendEntity[];
   startDate: string;
   endDate: string;
   totalDebts: number;
@@ -9,7 +10,12 @@ export type DataEntity = {
   totalTotals: number;
 };
 
-type ItemEntity = {
+type LegendEntity = {
+  name: string;
+  color: string;
+};
+
+export type ItemEntity = {
   id: string;
   name: string;
   monthData: MonthData[];
@@ -18,7 +24,7 @@ type ItemEntity = {
   totalTotals: number;
 };
 
-type MonthData = {
+export type MonthData = {
   date: string;
   totalAssets: number;
   totalDebts: number;
diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
index ca7c4d042..9a44dfd90 100644
--- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
@@ -5,7 +5,6 @@ import {
   BarChart,
   Bar,
   CartesianGrid,
-  //Legend,
   Cell,
   ReferenceLine,
   XAxis,
@@ -21,7 +20,6 @@ import { theme } from '../../../style';
 import { type CSSProperties } from '../../../style';
 import AlignedText from '../../common/AlignedText';
 import PrivacyFilter from '../../PrivacyFilter';
-import { getColorScale } from '../chart-theme';
 import Container from '../Container';
 import { type DataEntity } from '../entities';
 import getCustomTick from '../getCustomTick';
@@ -107,26 +105,6 @@ const CustomTooltip = ({
     );
   }
 };
-/* Descoped for future PR
-type CustomLegendProps = {
-  active?: boolean;
-  payload?: PayloadItem[];
-  label?: string;
-};
-
-const CustomLegend = ({ active, payload, label }: CustomLegendProps) => {
-  const agg = payload[0].payload.children.map(leg => {
-    return {
-      name: leg.props.name,
-      color: leg.props.fill,
-    };
-  });
-
-  OnChangeLegend(agg);
-
-  return <div />;
-};
-*/
 
 type BarGraphProps = {
   style?: CSSProperties;
@@ -145,7 +123,6 @@ function BarGraph({
 }: BarGraphProps) {
   const privacyMode = usePrivacyMode();
 
-  const colorScale = getColorScale('qualitative');
   const yAxis = ['Month', 'Year'].includes(groupBy) ? 'date' : 'name';
   const splitData = ['Month', 'Year'].includes(groupBy) ? 'monthData' : 'data';
 
@@ -180,9 +157,6 @@ function BarGraph({
                 data={data[splitData]}
                 margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
               >
-                {
-                  //!compact && <Legend content={<CustomLegend />} />
-                }
                 <Tooltip
                   content={
                     <CustomTooltip
@@ -215,17 +189,11 @@ function BarGraph({
                   <ReferenceLine y={0} stroke={theme.pageTextLight} />
                 )}
                 <Bar dataKey={val => getVal(val)} stackId="a">
-                  {data[splitData].map((entry, index) => (
+                  {data.legend.map((entry, index) => (
                     <Cell
                       key={`cell-${index}`}
-                      fill={
-                        yAxis === 'date'
-                          ? balanceTypeOp === 'totalDebts'
-                            ? theme.reportsRed
-                            : theme.reportsBlue
-                          : colorScale[index % colorScale.length]
-                      }
-                      name={entry[yAxis]}
+                      fill={entry.color}
+                      name={entry.name}
                     />
                   ))}
                 </Bar>
diff --git a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
index 87b42a9d5..f377a48e9 100644
--- a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
@@ -1,14 +1,7 @@
 import React from 'react';
 
 import { css } from 'glamor';
-import {
-  PieChart,
-  Pie,
-  Cell,
-  //Legend,
-  Tooltip,
-  ResponsiveContainer,
-} from 'recharts';
+import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts';
 
 import { amountToCurrency } from 'loot-core/src/shared/util';
 
@@ -16,7 +9,6 @@ import { theme } from '../../../style';
 import { type CSSProperties } from '../../../style';
 import Text from '../../common/Text';
 import PrivacyFilter from '../../PrivacyFilter';
-import { getColorScale } from '../chart-theme';
 import Container from '../Container';
 import { type DataEntity } from '../entities';
 import numberFormatterTooltip from '../numberFormatter';
@@ -72,27 +64,6 @@ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
   }
 };
 
-/* Descoped for future PR
-type CustomLegendProps = {
-  active?: boolean;
-  payload?: PayloadItem[];
-  label?: string;
-};
-
-const CustomLegend = ({ active, payload, label }: CustomLegendProps) => {
-  const agg = payload.map(leg => {
-    return {
-      name: leg.value,
-      color: leg.color,
-    };
-  });
-
-  OnChangeLegend(agg);
-
-  return <div />;
-};
-*/
-
 type DonutGraphProps = {
   style?: CSSProperties;
   data: DataEntity;
@@ -108,7 +79,6 @@ function DonutGraph({
   balanceTypeOp,
   compact,
 }: DonutGraphProps) {
-  const colorScale = getColorScale('qualitative');
   const yAxis = ['Month', 'Year'].includes(groupBy) ? 'date' : 'name';
   const splitData = ['Month', 'Year'].includes(groupBy) ? 'monthData' : 'data';
 
@@ -133,9 +103,6 @@ function DonutGraph({
             <div>
               {!compact && <div style={{ marginTop: '15px' }} />}
               <PieChart width={width} height={height}>
-                {
-                  //<Legend content={<CustomLegend />} />
-                }
                 <Tooltip
                   content={<CustomTooltip />}
                   formatter={numberFormatterTooltip}
@@ -149,11 +116,8 @@ function DonutGraph({
                   innerRadius={Math.min(width, height) * 0.2}
                   fill="#8884d8"
                 >
-                  {data[splitData].map((entry, index) => (
-                    <Cell
-                      key={`cell-${index}`}
-                      fill={colorScale[index % colorScale.length]}
-                    />
+                  {data.legend.map((entry, index) => (
+                    <Cell key={`cell-${index}`} fill={entry.color} />
                   ))}
                 </Pie>
               </PieChart>
diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
index bb080e448..4eb60cfdb 100644
--- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
@@ -5,7 +5,6 @@ import {
   BarChart,
   Bar,
   CartesianGrid,
-  //Legend,
   XAxis,
   YAxis,
   Tooltip,
@@ -19,7 +18,6 @@ import { theme } from '../../../style';
 import { type CSSProperties } from '../../../style';
 import AlignedText from '../../common/AlignedText';
 import PrivacyFilter from '../../PrivacyFilter';
-import { getColorScale } from '../chart-theme';
 import Container from '../Container';
 import { type DataEntity } from '../entities';
 import getCustomTick from '../getCustomTick';
@@ -93,27 +91,6 @@ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
   }
 };
 
-/* Descoped for future PR
-type CustomLegendProps = {
-  active?: boolean;
-  payload?: PayloadItem[];
-  label?: string;
-};
-
-const CustomLegend = ({ active, payload, label }: CustomLegendProps) => {
-  const agg = payload.map(leg => {
-    return {
-      name: leg.value,
-      color: leg.color,
-    };
-  });
-
-  OnChangeLegend(agg.slice(0).reverse());
-
-  return <div />;
-};
-*/
-
 type StackedBarGraphProps = {
   style?: CSSProperties;
   data: DataEntity;
@@ -122,7 +99,6 @@ type StackedBarGraphProps = {
 
 function StackedBarGraph({ style, data, compact }: StackedBarGraphProps) {
   const privacyMode = usePrivacyMode();
-  const colorScale = getColorScale('qualitative');
 
   return (
     <Container
@@ -142,9 +118,6 @@ function StackedBarGraph({ style, data, compact }: StackedBarGraphProps) {
                 data={data.monthData}
                 margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
               >
-                {
-                  //<Legend content={<CustomLegend />} />
-                }
                 <Tooltip
                   content={<CustomTooltip />}
                   formatter={numberFormatterTooltip}
@@ -163,14 +136,17 @@ function StackedBarGraph({ style, data, compact }: StackedBarGraphProps) {
                     tickLine={{ stroke: theme.pageText }}
                   />
                 )}
-                {data.data.reverse().map((c, index) => (
-                  <Bar
-                    key={c.name}
-                    dataKey={c.name}
-                    stackId="a"
-                    fill={colorScale[index % colorScale.length]}
-                  />
-                ))}
+                {data.legend
+                  .slice(0)
+                  .reverse()
+                  .map(entry => (
+                    <Bar
+                      key={entry.name}
+                      dataKey={entry.name}
+                      stackId="a"
+                      fill={entry.color}
+                    />
+                  ))}
               </BarChart>
             </div>
           </ResponsiveContainer>
diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.js b/packages/desktop-client/src/components/reports/reports/CustomReport.js
index 01103a485..edd208d9a 100644
--- a/packages/desktop-client/src/components/reports/reports/CustomReport.js
+++ b/packages/desktop-client/src/components/reports/reports/CustomReport.js
@@ -64,8 +64,6 @@ export default function CustomReport() {
   const [viewLegend, setViewLegend] = useState(false);
   const [viewSummary, setViewSummary] = useState(false);
   const [viewLabels, setViewLabels] = useState(false);
-  //const [legend, setLegend] = useState([]);
-  const legend = [];
   const dateRangeLine = ReportOptions.dateRange.length - 3;
   const months = monthUtils.rangeInclusive(startDate, endDate);
 
@@ -103,6 +101,7 @@ export default function CustomReport() {
     }
     run();
   }, []);
+
   const balanceTypeOp = ReportOptions.balanceTypeMap.get(balanceType);
   const payees = useCachedPayees();
   const accounts = useCachedAccounts();
@@ -149,6 +148,7 @@ export default function CustomReport() {
       payees,
       accounts,
       setDataCheck,
+      graphType,
     });
   }, [
     startDate,
@@ -164,6 +164,7 @@ export default function CustomReport() {
     showEmpty,
     showOffBudgetHidden,
     showUncategorized,
+    graphType,
   ]);
   const graphData = useReport('default', getGraphData);
   const groupedData = useReport('grouped', getGroupData);
@@ -348,7 +349,7 @@ export default function CustomReport() {
                     />
                   )}
                   {viewLegend && (
-                    <ReportLegend legend={legend} groupBy={groupBy} />
+                    <ReportLegend legend={data.legend} groupBy={groupBy} />
                   )}
                 </View>
               )}
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
new file mode 100644
index 000000000..7ff756f4f
--- /dev/null
+++ b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
@@ -0,0 +1,31 @@
+import { theme } from '../../../style';
+import { getColorScale } from '../chart-theme';
+import { type ItemEntity, type MonthData } from '../entities';
+
+function calculateLegend(
+  monthData: MonthData[],
+  calcDataFiltered: ItemEntity[],
+  groupBy: string,
+  graphType: string,
+  balanceTypeOp: string,
+) {
+  const colorScale = getColorScale('qualitative');
+  const chooseData = ['Month', 'Year'].includes(groupBy)
+    ? monthData
+    : calcDataFiltered;
+  return chooseData.map((c, index) => {
+    return {
+      name: ['Month', 'Year'].includes(groupBy) ? c.date : c.name,
+      color:
+        graphType === 'DonutGraph'
+          ? colorScale[index % colorScale.length]
+          : ['Month', 'Year'].includes(groupBy)
+          ? balanceTypeOp === 'totalDebts'
+            ? theme.reportsRed
+            : theme.reportsBlue
+          : colorScale[index % colorScale.length],
+    };
+  });
+}
+
+export default calculateLegend;
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx b/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx
index 23fcf52c9..e26e17360 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx
+++ b/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx
@@ -14,6 +14,7 @@ import {
 
 import { categoryLists, groupBySelections } from '../ReportOptions';
 
+import calculateLegend from './calculateLegend';
 import filterHiddenItems from './filterHiddenItems';
 import makeQuery from './makeQuery';
 import recalculate from './recalculate';
@@ -33,6 +34,7 @@ export type createSpreadsheetProps = {
   payees?: PayeeEntity[];
   accounts?: AccountEntity[];
   setDataCheck?: (value: boolean) => void;
+  graphType: string;
 };
 
 export default function createSpreadsheet({
@@ -50,6 +52,7 @@ export default function createSpreadsheet({
   payees,
   accounts,
   setDataCheck,
+  graphType,
 }: createSpreadsheetProps) {
   const [categoryList, categoryGroup] = categoryLists(
     showOffBudgetHidden,
@@ -167,10 +170,22 @@ export default function createSpreadsheet({
       const calc = recalculate({ item, months, assets, debts, groupByLabel });
       return { ...calc };
     });
+    const calcDataFiltered = calcData.filter(i =>
+      !showEmpty ? i[balanceTypeOp] !== 0 : true,
+    );
+
+    const legend = calculateLegend(
+      monthData,
+      calcDataFiltered,
+      groupBy,
+      graphType,
+      balanceTypeOp,
+    );
 
     setData({
-      data: calcData.filter(i => (!showEmpty ? i[balanceTypeOp] !== 0 : true)),
+      data: calcDataFiltered,
       monthData,
+      legend,
       startDate,
       endDate,
       totalDebts: integerToAmount(totalDebts),
diff --git a/upcoming-release-notes/2078.md b/upcoming-release-notes/2078.md
new file mode 100644
index 000000000..56a60e0b5
--- /dev/null
+++ b/upcoming-release-notes/2078.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [carkom]
+---
+
+Enable Legend for custom reports.
-- 
GitLab