diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx
index 97c2c2acafb..a680453607d 100644
--- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx
+++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx
@@ -132,7 +132,18 @@ export function ChooseGraph({
);
}
if (graphType === 'LineGraph') {
- return ;
+ return (
+
+ );
}
if (graphType === 'StackedBarGraph') {
return (
diff --git a/packages/desktop-client/src/components/reports/ReportTopbar.jsx b/packages/desktop-client/src/components/reports/ReportTopbar.jsx
index 6a78e7d8d9a..0bb9c45f99e 100644
--- a/packages/desktop-client/src/components/reports/ReportTopbar.jsx
+++ b/packages/desktop-client/src/components/reports/ReportTopbar.jsx
@@ -83,9 +83,7 @@ export function ReportTopbar({
title="Line Graph"
selected={customReportItems.graphType === 'LineGraph'}
onSelect={() => {
- onReportChange({ type: 'modify' });
- setGraphType('LineGraph');
- defaultItems('LineGraph');
+ onChangeGraph('LineGraph');
}}
style={{ marginRight: 15 }}
disabled={disabledItems('LineGraph')}
diff --git a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx
index 2fe7f59bc98..3b69d289411 100644
--- a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx
@@ -1,5 +1,5 @@
// @ts-strict-ignore
-import React from 'react';
+import React, { useState } from 'react';
import { css } from 'glamor';
import {
@@ -12,13 +12,22 @@ import {
ResponsiveContainer,
} from 'recharts';
-import { amountToCurrency } from 'loot-core/src/shared/util';
+import {
+ amountToCurrency,
+ amountToCurrencyNoDecimal,
+} from 'loot-core/src/shared/util';
+import { type GroupedEntity } from 'loot-core/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';
import { AlignedText } from '../../common/AlignedText';
-import { PrivacyFilter } from '../../PrivacyFilter';
import { Container } from '../Container';
+import { getCustomTick } from '../getCustomTick';
import { numberFormatterTooltip } from '../numberFormatter';
type PayloadItem = {
@@ -32,12 +41,20 @@ type PayloadItem = {
};
type CustomTooltipProps = {
+ compact: boolean;
+ tooltip: string;
active?: boolean;
payload?: PayloadItem[];
};
-const CustomTooltip = ({ active, payload }: CustomTooltipProps) => {
+const CustomTooltip = ({
+ compact,
+ tooltip,
+ active,
+ payload,
+}: CustomTooltipProps) => {
if (active && payload && payload.length) {
+ let sumTotals = 0;
return (
{
{payload[0].payload.date}
-
- {payload
- .sort((p1: PayloadItem, p2: PayloadItem) => p2.value - p1.value)
- .map((p: PayloadItem, index: number) => (
-
- ))}
-
+ {payload
+ .sort((p1: PayloadItem, p2: PayloadItem) => p2.value - p1.value)
+ .map((p: PayloadItem, index: number) => {
+ sumTotals += p.value;
+ return (
+ (compact ? index < 4 : true) && (
+
+ )
+ );
+ })}
+ {payload.length > 5 && compact && '...'}
+
@@ -76,13 +108,83 @@ const CustomTooltip = ({ active, payload }: CustomTooltipProps) => {
type LineGraphProps = {
style?: CSSProperties;
- data;
+ data: GroupedEntity;
+ filters: RuleConditionEntity[];
+ groupBy: string;
compact?: boolean;
+ balanceTypeOp: string;
+ showHiddenCategories?: boolean;
+ showOffBudget?: boolean;
};
-export function LineGraph({ style, data, compact }: LineGraphProps) {
- const tickFormatter = tick => {
- return `${Math.round(tick).toLocaleString()}`; // Formats the tick values as strings with commas
+export function LineGraph({
+ style,
+ data,
+ filters,
+ groupBy,
+ compact,
+ balanceTypeOp,
+ showHiddenCategories,
+ showOffBudget,
+}: LineGraphProps) {
+ 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 : 5;
+
+ const onShowActivity = (item, id, payload) => {
+ 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: payload.payload.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 (
@@ -101,18 +203,35 @@ export function LineGraph({ style, data, compact }: LineGraphProps) {
width={width}
height={height}
data={data.intervalData}
- margin={{ top: 10, right: 10, left: 10, bottom: 10 }}
+ margin={{ top: 10, right: 10, left: leftMargin, bottom: 10 }}
+ style={{ cursor: pointer }}
>
}
+ content={
+
+ }
formatter={numberFormatterTooltip}
isAnimationActive={false}
/>
{!compact && (
<>
-
-
+
+
+ getCustomTick(
+ amountToCurrencyNoDecimal(value),
+ privacyMode,
+ )
+ }
+ tick={{ fill: theme.pageText }}
+ tickLine={{ stroke: theme.pageText }}
+ tickSize={0}
+ />
>
)}
{data.legend.map((entry, index) => {
@@ -123,6 +242,22 @@ export function LineGraph({ style, data, compact }: LineGraphProps) {
type="monotone"
dataKey={entry.name}
stroke={entry.color}
+ activeDot={{
+ r: entry.name === tooltip && !compact ? 8 : 3,
+ onMouseEnter: () => {
+ setTooltip(entry.name);
+ if (!['Group', 'Interval'].includes(groupBy)) {
+ setPointer('pointer');
+ }
+ },
+ onMouseLeave: () => {
+ setPointer('');
+ setTooltip('');
+ },
+ onClick: (e, payload) =>
+ !['Group', 'Interval'].includes(groupBy) &&
+ onShowActivity(e, entry.id, payload),
+ }}
/>
);
})}
diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
index 7264e94fdfe..227319d2dea 100644
--- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
@@ -245,13 +245,13 @@ export function StackedBarGraph({
isAnimationActive={false}
cursor={{ fill: 'transparent' }}
/>
-
{!compact && (
<>
+
diff --git a/upcoming-release-notes/2636.md b/upcoming-release-notes/2636.md
new file mode 100644
index 00000000000..8f411a8b0ad
--- /dev/null
+++ b/upcoming-release-notes/2636.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [carkom]
+---
+
+Enables the ability to show transactions when LineGraph is clicked. Also adds missing formatting to lineGraph.