diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx index 8e7b1605ae8fb78a691c5ce5da97ea897a96d8a9..a8c0691a14e278506515bf12818ce0a0dd592968 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx @@ -183,8 +183,8 @@ export function CustomReport() { balanceTypeOp, payees, accounts, - setDataCheck, graphType, + setDataCheck, }); }, [ startDate, @@ -225,7 +225,7 @@ export function CustomReport() { graphType, conditions: filters, conditionsOp, - data, + data: {}, }; const [scrollWidth, setScrollWidth] = useState(0); diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx index 0aeacba2fced778f4e4100eb1d4de724fd40c1b0..9fabce52401672674aac2d5bb8a1bdb18808d827 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx @@ -1,9 +1,11 @@ import React, { createRef, useMemo, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { send, sendCatch } from 'loot-core/platform/client/fetch/index'; import { type CustomReportEntity } from 'loot-core/types/models/reports'; +import { useAccounts } from '../../../hooks/useAccounts'; +import { useCategories } from '../../../hooks/useCategories'; +import { usePayees } from '../../../hooks/usePayees'; import { styles } from '../../../style/index'; import { theme } from '../../../style/theme'; import { Block } from '../../common/Block'; @@ -12,12 +14,12 @@ import { MenuButton } from '../../common/MenuButton'; import { MenuTooltip } from '../../common/MenuTooltip'; import { Text } from '../../common/Text'; import { View } from '../../common/View'; -import { ChooseGraph } from '../ChooseGraph'; import { DateRange } from '../DateRange'; -import { LoadingIndicator } from '../LoadingIndicator'; import { ReportCard } from '../ReportCard'; import { SaveReportName } from '../SaveReportName'; +import { GetCardData } from './GetCardData'; + type CardMenuProps = { onClose: () => void; onMenuSelect: (item: string, report: CustomReportEntity) => void; @@ -57,19 +59,6 @@ function index(data: CustomReportEntity[]): { [key: string]: boolean }[] { }, []); } -function ErrorFallback() { - return ( - <> - <div> - <br /> - </div> - <Text style={{ ...styles.mediumText, color: theme.errorText }}> - There was a problem loading your report - </Text> - </> - ); -} - export function CustomReportListCards({ reports, }: { @@ -82,6 +71,10 @@ export function CustomReportListCards({ const [name, setName] = useState(''); const inputRef = createRef<HTMLInputElement>(); + const payees = usePayees(); + const accounts = useAccounts(); + const categories = useCategories(); + const [isCardHovered, setIsCardHovered] = useState(''); const onAddUpdate = async ({ @@ -205,25 +198,12 @@ export function CustomReportListCards({ )} </View> </View> - - {report.data ? ( - <ErrorBoundary FallbackComponent={ErrorFallback}> - <ChooseGraph - startDate={report.startDate} - endDate={report.endDate} - data={report.data} - mode={report.mode} - graphType={report.graphType} - balanceType={report.balanceType} - groupBy={report.groupBy} - interval={report.interval} - compact={true} - style={{ height: 'auto', flex: 1 }} - /> - </ErrorBoundary> - ) : ( - <LoadingIndicator /> - )} + <GetCardData + report={report} + payees={payees} + accounts={accounts} + categories={categories} + /> </View> </ReportCard> </View> diff --git a/packages/desktop-client/src/components/reports/reports/GetCardData.tsx b/packages/desktop-client/src/components/reports/reports/GetCardData.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ade04b9cc4ff6ed8de64e50f43152814779f2872 --- /dev/null +++ b/packages/desktop-client/src/components/reports/reports/GetCardData.tsx @@ -0,0 +1,104 @@ +import React, { useMemo } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; + +import { type AccountEntity } from 'loot-core/types/models/account'; +import { type CategoryEntity } from 'loot-core/types/models/category'; +import { type CategoryGroupEntity } from 'loot-core/types/models/category-group'; +import { type PayeeEntity } from 'loot-core/types/models/payee'; +import { type CustomReportEntity } from 'loot-core/types/models/reports'; + +import { styles } from '../../../style/styles'; +import { theme } from '../../../style/theme'; +import { Text } from '../../common/Text'; +import { ChooseGraph } from '../ChooseGraph'; +import { LoadingIndicator } from '../LoadingIndicator'; +import { ReportOptions } from '../ReportOptions'; +import { createCustomSpreadsheet } from '../spreadsheets/custom-spreadsheet'; +import { createGroupedSpreadsheet } from '../spreadsheets/grouped-spreadsheet'; +import { useReport } from '../useReport'; + +function ErrorFallback() { + return ( + <> + <div> + <br /> + </div> + <Text style={{ ...styles.mediumText, color: theme.errorText }}> + There was a problem loading your report + </Text> + </> + ); +} + +export function GetCardData({ + report, + payees, + accounts, + categories, +}: { + report: CustomReportEntity; + payees: PayeeEntity[]; + accounts: AccountEntity[]; + categories: { list: CategoryEntity[]; grouped: CategoryGroupEntity[] }; +}) { + const getGroupData = useMemo(() => { + return createGroupedSpreadsheet({ + startDate: report.startDate, + endDate: report.endDate, + interval: report.interval, + categories, + selectedCategories: report.selectedCategories ?? categories.list, + conditions: report.conditions ?? [], + conditionsOp: report.conditionsOp, + showEmpty: report.showEmpty, + showOffBudget: report.showOffBudget, + showHiddenCategories: report.showHiddenCategories, + showUncategorized: report.showUncategorized, + balanceTypeOp: ReportOptions.balanceTypeMap.get(report.balanceType), + }); + }, [report, categories]); + const getGraphData = useMemo(() => { + return createCustomSpreadsheet({ + startDate: report.startDate, + endDate: report.endDate, + interval: report.interval, + categories, + selectedCategories: report.selectedCategories ?? categories.list, + conditions: report.conditions ?? [], + conditionsOp: report.conditionsOp, + showEmpty: report.showEmpty, + showOffBudget: report.showOffBudget, + showHiddenCategories: report.showHiddenCategories, + showUncategorized: report.showUncategorized, + groupBy: report.groupBy, + balanceTypeOp: ReportOptions.balanceTypeMap.get(report.balanceType), + payees, + accounts, + graphType: report.graphType, + }); + }, [report, categories, payees, accounts]); + const graphData = useReport('default' + report.name, getGraphData); + const groupedData = useReport('grouped' + report.name, getGroupData); + + const data = + graphData && groupedData ? { ...graphData, groupedData } : graphData; + + return data?.data ? ( + <ErrorBoundary FallbackComponent={ErrorFallback}> + <ChooseGraph + startDate={report.startDate} + endDate={report.endDate} + data={data} + mode={report.mode} + graphType={report.graphType} + balanceType={report.balanceType} + groupBy={report.groupBy} + interval={report.interval} + compact={true} + style={{ height: 'auto', flex: 1 }} + /> + </ErrorBoundary> + ) : ( + <LoadingIndicator /> + ); +} diff --git a/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx b/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx index ff94d1cb7fe000c2ffaf9ab911287a7514f0d917..b0ea66d48ed31d979d86af7af88e3f3a9067fbe0 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx +++ b/packages/desktop-client/src/components/reports/spreadsheets/cash-flow-spreadsheet.tsx @@ -3,6 +3,7 @@ import React from 'react'; import * as d from 'date-fns'; +import { type useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider'; import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { q } from 'loot-core/src/shared/query'; @@ -48,7 +49,10 @@ export function cashFlowByDate( conditions: RuleConditionEntity[] = [], conditionsOp: 'and' | 'or', ) { - return async (spreadsheet, setData) => { + return async ( + spreadsheet: ReturnType<typeof useSpreadsheet>, + setData: (data: ReturnType<typeof recalculate>) => void, + ) => { const { filters } = await send('make-filters-from-conditions', { conditions: conditions.filter(cond => !cond.customName), }); 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 9d03d9ccf55267403076ddd09247b0cf22b339a1..82de8084a1a17cc4f1e9193cc9a55087eb04f630 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts @@ -2,6 +2,7 @@ import * as d from 'date-fns'; import { runQuery } from 'loot-core/src/client/query-helpers'; +import { type useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider'; import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { integerToAmount } from 'loot-core/src/shared/util'; @@ -12,6 +13,10 @@ import { type RuleConditionEntity, type CategoryGroupEntity, } from 'loot-core/src/types/models'; +import { + type DataEntity, + type GroupedEntity, +} from 'loot-core/src/types/models/reports'; import { categoryLists, groupBySelections } from '../ReportOptions'; @@ -34,11 +39,11 @@ export type createCustomSpreadsheetProps = { showHiddenCategories: boolean; showUncategorized: boolean; groupBy?: string; - balanceTypeOp?: string; + balanceTypeOp?: keyof DataEntity; payees?: PayeeEntity[]; accounts?: AccountEntity[]; - setDataCheck?: (value: boolean) => void; graphType?: string; + setDataCheck?: (value: boolean) => void; }; export function createCustomSpreadsheet({ @@ -57,8 +62,8 @@ export function createCustomSpreadsheet({ balanceTypeOp, payees, accounts, - setDataCheck, graphType, + setDataCheck, }: createCustomSpreadsheetProps) { const [categoryList, categoryGroup] = categoryLists(categories); @@ -78,9 +83,12 @@ export function createCustomSpreadsheet({ accounts, ); - return async (spreadsheet, setData) => { + return async ( + spreadsheet: ReturnType<typeof useSpreadsheet>, + setData: (data: GroupedEntity) => void, + ) => { if (groupByList.length === 0) { - return null; + return; } const { filters } = await send('make-filters-from-conditions', { diff --git a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts index c0b6e8f3443f004dda1b964f276305bf1695fecb..2e1d9ca985b6434b4a915da488b751b0603c1ea4 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts @@ -1,12 +1,11 @@ -// @ts-strict-ignore -import { type GroupedEntity } from 'loot-core/src/types/models/reports'; +import { type DataEntity } from 'loot-core/src/types/models/reports'; export function filterEmptyRows( showEmpty: boolean, - data: GroupedEntity, - balanceTypeOp: string, + data: DataEntity, + balanceTypeOp: keyof DataEntity, ): boolean { - let showHide; + let showHide: boolean; if (balanceTypeOp === 'totalTotals') { showHide = data['totalDebts'] !== 0 || diff --git a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts index 57baa600986453d9cf4d6bcae5c4ac0e54faa94d..822b53f1c85cfa11a42e8a5d8ba5949b3c85eefe 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts @@ -1,9 +1,10 @@ // @ts-strict-ignore import { runQuery } from 'loot-core/src/client/query-helpers'; +import { type useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider'; import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { integerToAmount } from 'loot-core/src/shared/util'; -import { type GroupedEntity } from 'loot-core/src/types/models/reports'; +import { type DataEntity } from 'loot-core/src/types/models/reports'; import { categoryLists } from '../ReportOptions'; @@ -37,9 +38,12 @@ export function createGroupedSpreadsheet({ ), ); - return async (spreadsheet, setData) => { + return async ( + spreadsheet: ReturnType<typeof useSpreadsheet>, + setData: (data: DataEntity[]) => void, + ) => { if (categoryList.length === 0) { - return null; + return; } const { filters } = await send('make-filters-from-conditions', { @@ -82,7 +86,7 @@ export function createGroupedSpreadsheet({ monthUtils[format](endDate), ); - const groupedData: GroupedEntity[] = categoryGroup.map( + const groupedData: DataEntity[] = categoryGroup.map( group => { let totalAssets = 0; let totalDebts = 0; diff --git a/packages/desktop-client/src/components/reports/useReport.ts b/packages/desktop-client/src/components/reports/useReport.ts index d9f41719673b6a5dd81122e76e672c58100d77e1..102b94c2bdd9f54a4a3b7b291dfc5982eaaacb02 100644 --- a/packages/desktop-client/src/components/reports/useReport.ts +++ b/packages/desktop-client/src/components/reports/useReport.ts @@ -1,26 +1,19 @@ -// @ts-strict-ignore -import { useState, useEffect, type SetStateAction } from 'react'; +import { useState, useEffect } from 'react'; import { useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider'; -export function useReport( +export function useReport<T>( sheetName: string, getData: ( spreadsheet: ReturnType<typeof useSpreadsheet>, - setData: (results: unknown) => SetStateAction<unknown>, + setData: (results: T) => void, ) => Promise<void>, -) { +): T | null { const spreadsheet = useSpreadsheet(); - const [results, setResults] = useState(null); + const [results, setResults] = useState<T | null>(null); useEffect(() => { - let cleanup; - getData(spreadsheet, results => setResults(results)).then(c => { - cleanup = c; - }); - return () => { - cleanup?.(); - }; + getData(spreadsheet, results => setResults(results)); }, [getData]); return results; } diff --git a/upcoming-release-notes/2505.md b/upcoming-release-notes/2505.md new file mode 100644 index 0000000000000000000000000000000000000000..41efa5d28894d7e996d84bb8f19a63ccb46ef521 --- /dev/null +++ b/upcoming-release-notes/2505.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [carkom] +--- + +Change custom reports overview cards to use live data. Also, stops saving data query in saved report db table.