mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 03:32:54 -05:00
Custom Reports Enable Legend (#2078)
* enable Legend * notes * adding type * overhaul * calculateLegend
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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;
|
||||
@@ -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),
|
||||
|
||||
6
upcoming-release-notes/2078.md
Normal file
6
upcoming-release-notes/2078.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [carkom]
|
||||
---
|
||||
|
||||
Enable Legend for custom reports.
|
||||
Reference in New Issue
Block a user