Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Summary report #3792

Merged
merged 35 commits into from
Nov 21, 2024
Merged
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
56f601f
Summary card report
lelemm Nov 4, 2024
a6db37e
Apply suggestions from code rabbit
lelemm Nov 12, 2024
b80b336
Apply suggestions from code review
lelemm Nov 12, 2024
aa5fb0b
MORE CODE RABBIT SUGGESTIONS
lelemm Nov 12, 2024
f180300
typecheck fix
lelemm Nov 12, 2024
969e958
change view form the details page
lelemm Nov 13, 2024
ab241e4
added privacy filter
lelemm Nov 13, 2024
f8a9540
Apply suggestions from code review
lelemm Nov 13, 2024
e056775
debounce
lelemm Nov 13, 2024
bc4b5da
Merge branch 'summary_card' of https://github.com/lelemm/actual into …
lelemm Nov 13, 2024
c46db16
removed binary search and changed the summary page to not use the car…
lelemm Nov 13, 2024
d0de526
Update packages/desktop-client/src/components/reports/spreadsheets/su…
lelemm Nov 13, 2024
1e6dbc1
fix on recommended code rabbit commit
lelemm Nov 13, 2024
97f525d
added some padding to number so it fits the window better for big num…
lelemm Nov 13, 2024
23cbf39
accept infinite
lelemm Nov 14, 2024
a92cd68
feedback fixes
lelemm Nov 14, 2024
5d802f1
Update packages/desktop-client/src/components/reports/reports/Summary…
lelemm Nov 14, 2024
1ff4c41
translations
lelemm Nov 14, 2024
f34268d
Merge branch 'master' into summary_card
lelemm Nov 14, 2024
3173541
fix on the save, linter and changed "include summary date range" to "…
lelemm Nov 14, 2024
f927f69
Merge branch 'summary_card' of https://github.com/lelemm/actual into …
lelemm Nov 14, 2024
bb21b07
changed MD from enhancements to feature
lelemm Nov 14, 2024
2188cfa
typo
lelemm Nov 14, 2024
b4b4c2b
change card
lelemm Nov 15, 2024
2bcd3ab
typecheck
lelemm Nov 15, 2024
110244b
Update packages/desktop-client/src/components/reports/SummaryNumber.tsx
lelemm Nov 15, 2024
297be16
typecheck
lelemm Nov 15, 2024
72cdf6d
Merge branch 'summary_card' of https://github.com/lelemm/actual into …
lelemm Nov 15, 2024
b4d54c1
changes to fit the number better
lelemm Nov 18, 2024
4c23c6f
small fix
lelemm Nov 18, 2024
db9cae4
fix on filters
lelemm Nov 18, 2024
a6c5263
Merge branch 'master' into summary_card
lelemm Nov 18, 2024
4ec2703
code review
lelemm Nov 21, 2024
7f89401
Merge remote-tracking branch 'org/master' into summary_card
lelemm Nov 21, 2024
a8ebb5a
revert code to check for height
lelemm Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/desktop-client/src/components/reports/Overview.tsx
Original file line number Diff line number Diff line change
@@ -39,8 +39,8 @@ import { CustomReportListCards } from './reports/CustomReportListCards';
import { MarkdownCard } from './reports/MarkdownCard';
import { NetWorthCard } from './reports/NetWorthCard';
import { SpendingCard } from './reports/SpendingCard';

import './overview.scss';
import { SummaryCard } from './reports/SummaryCard';

const ResponsiveGridLayout = WidthProvider(Responsive);

@@ -396,6 +396,10 @@ export function Overview() {
name: 'markdown-card' as const,
text: t('Text widget'),
},
{
name: 'summary-card' as const,
text: t('Summary card'),
},
lelemm marked this conversation as resolved.
Show resolved Hide resolved
{
name: 'custom-report' as const,
text: t('New custom report'),
@@ -551,6 +555,14 @@ export function Overview() {
report={customReportMap.get(item.meta.id)}
onRemove={() => onRemoveWidget(item.i)}
/>
) : item.type === 'summary-card' ? (
<SummaryCard
widgetId={item.i}
isEditing={isEditing}
meta={item.meta}
onMetaChange={newMeta => onMetaChange(item, newMeta)}
onRemove={() => onRemoveWidget(item.i)}
/>
) : null}
</div>
))}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { CashFlow } from './reports/CashFlow';
import { CustomReport } from './reports/CustomReport';
import { NetWorth } from './reports/NetWorth';
import { Spending } from './reports/Spending';
import { Summary } from './reports/Summary';

export function ReportRouter() {
return (
@@ -19,6 +20,8 @@ export function ReportRouter() {
<Route path="/custom/:id" element={<CustomReport />} />
<Route path="/spending" element={<Spending />} />
<Route path="/spending/:id" element={<Spending />} />
<Route path="/summary" element={<Summary />} />
<Route path="/summary/:id" element={<Summary />} />
</Routes>
);
}
145 changes: 145 additions & 0 deletions packages/desktop-client/src/components/reports/SummaryNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, { type Ref, useRef, useState } from 'react';

import { debounce } from 'debounce';

import { amountToCurrency } from 'loot-core/shared/util';

import { useMergedRefs } from '../../hooks/useMergedRefs';
import { useResizeObserver } from '../../hooks/useResizeObserver';
import { View } from '../common/View';
import { PrivacyFilter } from '../PrivacyFilter';

import { chartTheme } from './chart-theme';
import { LoadingIndicator } from './LoadingIndicator';

type AnimatedNumberProps = {
lelemm marked this conversation as resolved.
Show resolved Hide resolved
value: number;
animate?: boolean;
suffix?: string;
loading?: boolean;
initialFontSize?: number;
fontSizeChanged?: (fontSize: number) => void;
};

export function SummaryNumber({
value,
animate = false,
suffix = '',
loading = true,
initialFontSize = 14,
fontSizeChanged,
}: AnimatedNumberProps) {
const [fontSize, setFontSize] = useState<number>(0);
lelemm marked this conversation as resolved.
Show resolved Hide resolved
const refDiv = useRef<HTMLDivElement>(null);
const offScreenRef = useRef<HTMLDivElement>(null);

const FONT_SIZE_SCALE_FACTOR = 0.9;
lelemm marked this conversation as resolved.
Show resolved Hide resolved
const MAX_RECURSION_DEPTH = 10;

const adjustFontSizeBinary = (minFontSize: number, maxFontSize: number) => {
if (!offScreenRef.current || !refDiv.current) return;

const offScreenDiv = offScreenRef.current;
const refDivCurrent = refDiv.current;

const binarySearchFontSize = (
min: number,
max: number,
depth: number = 0,
) => {
if (depth >= MAX_RECURSION_DEPTH) {
setFontSize(min);
return;
}

const testFontSize = (min + max) / 2;
offScreenDiv.style.fontSize = `${testFontSize}px`;

requestAnimationFrame(() => {
const isOverflowing =
offScreenDiv.scrollWidth > refDivCurrent.clientWidth;

if (isOverflowing) {
binarySearchFontSize(min, testFontSize, depth + 1);
} else {
const isUnderflowing =
offScreenDiv.scrollWidth <=
refDivCurrent.clientWidth * FONT_SIZE_SCALE_FACTOR;

if (isUnderflowing && testFontSize < max) {
binarySearchFontSize(testFontSize, max, depth + 1);
} else {
setFontSize(testFontSize);
if (initialFontSize !== testFontSize && fontSizeChanged) {
fontSizeChanged(testFontSize);
}
}
}
});
};

binarySearchFontSize(minFontSize, maxFontSize);
};

const handleResize = debounce(() => {
adjustFontSizeBinary(14, 200);
}, 250);

const ref = useResizeObserver(handleResize);
const mergedRef = useMergedRefs(ref, refDiv);
lelemm marked this conversation as resolved.
Show resolved Hide resolved

return (
<>
{loading && <LoadingIndicator />}
{!loading && (
<>
<div
ref={offScreenRef}
style={{
position: 'fixed',
left: '-999px',
top: '-999px',
fontSize: `${initialFontSize}px`,
lineHeight: 1,
visibility: 'hidden',
whiteSpace: 'nowrap',
padding: 8,
}}
>
<PrivacyFilter>
{amountToCurrency(Math.abs(value))}
{suffix}
</PrivacyFilter>
</div>

<View
ref={mergedRef as Ref<HTMLDivElement>}
role="text"
aria-label={`${value < 0 ? 'Negative' : 'Positive'} amount: ${amountToCurrency(Math.abs(value))}${suffix}`}
style={{
alignItems: 'center',
flexGrow: 1,
flexShrink: 1,
width: '100%',
height: '100%',
maxWidth: '100%',
fontSize: `${fontSize}px`,
lineHeight: 1,
padding: 8,
justifyContent: 'center',
transition: animate ? 'font-size 0.3s ease' : '',
color: value < 0 ? chartTheme.colors.red : chartTheme.colors.blue,
}}
>
<span aria-hidden="true">
<PrivacyFilter>
{amountToCurrency(Math.abs(value))}
{suffix}
</PrivacyFilter>
</span>
</View>
</>
)}
</>
);
}
Loading