diff --git a/packages/desktop-client/src/components/reports/ReportOptions.ts b/packages/desktop-client/src/components/reports/ReportOptions.ts index e8c7bd8becf915c40fc00e470fdc52c76ecb38b1..0ae9c6b0a5222ec7cd688d8a3ba9914c1c41c742 100644 --- a/packages/desktop-client/src/components/reports/ReportOptions.ts +++ b/packages/desktop-client/src/components/reports/ReportOptions.ts @@ -226,7 +226,10 @@ export type QueryDataEntity = { amount: number; }; -export type UncategorizedEntity = Pick<CategoryEntity, 'name' | 'hidden'> & { +export type UncategorizedEntity = Pick< + CategoryEntity, + 'id' | 'name' | 'hidden' +> & { /* When looking at uncategorized and hidden transactions we need a way to group them. To do this we give them a unique @@ -241,6 +244,7 @@ export type UncategorizedEntity = Pick<CategoryEntity, 'name' | 'hidden'> & { }; const uncategorizedCategory: UncategorizedEntity = { + id: '', name: 'Uncategorized', uncategorized_id: '1', hidden: false, @@ -249,6 +253,7 @@ const uncategorizedCategory: UncategorizedEntity = { has_category: false, }; const transferCategory: UncategorizedEntity = { + id: '', name: 'Transfers', uncategorized_id: '2', hidden: false, @@ -257,6 +262,7 @@ const transferCategory: UncategorizedEntity = { has_category: false, }; const offBudgetCategory: UncategorizedEntity = { + id: '', name: 'Off Budget', uncategorized_id: '3', hidden: false, @@ -283,7 +289,7 @@ export const categoryLists = (categories: { list: CategoryEntity[]; grouped: CategoryGroupEntity[]; }) => { - const categoryList = [ + const categoryList: UncategorizedEntity[] = [ ...categories.list.sort((a, b) => { //The point of this sorting is to make the graphs match the "budget" page const catGroupA = categories.grouped.find(f => f.id === a.cat_group); @@ -304,34 +310,46 @@ export const categoryLists = (categories: { transferCategory, ]; - const categoryGroup = [...categories.grouped, uncategorizedGroup]; + const categoryGroup: UncategorizedGroupEntity[] = [ + ...categories.grouped, + uncategorizedGroup, + ]; return [categoryList, categoryGroup.filter(group => group !== null)] as const; }; export const groupBySelections = ( groupBy: string, - categoryList: CategoryEntity[], + categoryList: UncategorizedEntity[], categoryGroup: CategoryGroupEntity[], payees: PayeeEntity[], accounts: AccountEntity[], -) => { - let groupByList; - let groupByLabel; +): [ + UncategorizedEntity[], + 'category' | 'categoryGroup' | 'payee' | 'account', +] => { + let groupByList: UncategorizedEntity[]; + let groupByLabel: 'category' | 'categoryGroup' | 'payee' | 'account'; switch (groupBy) { case 'Category': groupByList = categoryList; groupByLabel = 'category'; break; case 'Group': - groupByList = categoryGroup; + groupByList = categoryGroup.map(group => { + return { id: group.id, name: group.name, hidden: group.hidden }; + }); groupByLabel = 'categoryGroup'; break; case 'Payee': - groupByList = payees; + groupByList = payees.map(payee => { + return { id: payee.id, name: payee.name, hidden: false }; + }); groupByLabel = 'payee'; break; case 'Account': - groupByList = accounts; + groupByList = accounts.map(account => { + return { id: account.id, name: account.name, hidden: false }; + }); groupByLabel = 'account'; break; case 'Interval': diff --git a/packages/desktop-client/src/components/reports/SaveReport.tsx b/packages/desktop-client/src/components/reports/SaveReport.tsx index af27b31e11992914cbf81c919dea6779ce69d305..d5edb18d123d32729b4adf6ba50438ef3bb55ff1 100644 --- a/packages/desktop-client/src/components/reports/SaveReport.tsx +++ b/packages/desktop-client/src/components/reports/SaveReport.tsx @@ -40,14 +40,14 @@ export function SaveReport({ const [chooseMenuOpen, setChooseMenuOpen] = useState(false); const [menuItem, setMenuItem] = useState(''); const [err, setErr] = useState(''); - const [name, setName] = useState(report.name ?? ''); + const [newName, setNewName] = useState(report.name ?? ''); const inputRef = createRef<HTMLInputElement>(); async function onApply(cond: string) { const chooseSavedReport = listReports.find(r => cond === r.id); onReportChange({ savedReport: chooseSavedReport, type: 'choose' }); setChooseMenuOpen(false); - setName(chooseSavedReport === undefined ? '' : chooseSavedReport.name); + setNewName(chooseSavedReport === undefined ? '' : chooseSavedReport.name); } const onAddUpdate = async ({ menuChoice }: { menuChoice?: string }) => { @@ -58,7 +58,7 @@ export function SaveReport({ const newSavedReport = { ...report, ...customReportItems, - name, + name: newName, }; const response = await sendCatch('report/create', newSavedReport); @@ -80,9 +80,11 @@ export function SaveReport({ return; } + const { name, id, ...props } = customReportItems; + const updatedReport = { ...report, - ...(menuChoice === 'rename-report' ? { name } : customReportItems), + ...(menuChoice === 'rename-report' ? { name: newName } : props), }; const response = await sendCatch('report/update', updatedReport); @@ -100,7 +102,7 @@ export function SaveReport({ }; const onDelete = async () => { - setName(''); + setNewName(''); await send('report/delete', report.id); onReportChange({ type: 'reset' }); setDeleteMenuOpen(false); @@ -134,7 +136,7 @@ export function SaveReport({ break; case 'reset-report': setMenuOpen(false); - setName(''); + setNewName(''); onReportChange({ type: 'reset' }); break; case 'choose-report': @@ -185,8 +187,8 @@ export function SaveReport({ <SaveReportName onClose={() => setNameMenuOpen(false)} menuItem={menuItem} - name={name} - setName={setName} + name={newName} + setName={setNewName} inputRef={inputRef} onAddUpdate={onAddUpdate} err={err} 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 149c5678577d372931f49aed3d5cf1e04e75c3a5..ad734c270394efe28e6f0b84aa084ee7eac2b98d 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts @@ -1,4 +1,3 @@ -// @ts-strict-ignore import * as d from 'date-fns'; import { runQuery } from 'loot-core/src/client/query-helpers'; @@ -13,13 +12,19 @@ import { type RuleConditionEntity, type CategoryGroupEntity, } from 'loot-core/src/types/models'; -import { type DataEntity } from 'loot-core/src/types/models/reports'; +import { + type DataEntity, + type GroupedEntity, + type IntervalEntity, +} from 'loot-core/src/types/models/reports'; import { type LocalPrefs } from 'loot-core/types/prefs'; import { categoryLists, groupBySelections, + type QueryDataEntity, ReportOptions, + type UncategorizedEntity, } from '../ReportOptions'; import { calculateLegend } from './calculateLegend'; @@ -61,10 +66,10 @@ export function createCustomSpreadsheet({ showOffBudget, showHiddenCategories, showUncategorized, - groupBy, + groupBy = '', balanceTypeOp = 'totalDebts', - payees, - accounts, + payees = [], + accounts = [], graphType, firstDayOfWeekIdx, setDataCheck, @@ -79,13 +84,10 @@ export function createCustomSpreadsheet({ ), ); - const [groupByList, groupByLabel] = groupBySelections( - groupBy, - categoryList, - categoryGroup, - payees, - accounts, - ); + const [groupByList, groupByLabel]: [ + groupByList: UncategorizedEntity[], + groupByLabel: 'category' | 'categoryGroup' | 'payee' | 'account', + ] = groupBySelections(groupBy, categoryList, categoryGroup, payees, accounts); return async ( spreadsheet: ReturnType<typeof useSpreadsheet>, @@ -100,7 +102,9 @@ export function createCustomSpreadsheet({ }); const conditionsOpKey = conditionsOp === 'or' ? '$or' : '$and'; - let [assets, debts] = await Promise.all([ + let assets: QueryDataEntity[]; + let debts: QueryDataEntity[]; + [assets, debts] = await Promise.all([ runQuery( makeQuery( 'assets', @@ -143,83 +147,85 @@ export function createCustomSpreadsheet({ const intervals = interval === 'Weekly' ? monthUtils.weekRangeInclusive(startDate, endDate, firstDayOfWeekIdx) - : monthUtils[ReportOptions.intervalRange.get(interval)]( - startDate, - endDate, - ); + : monthUtils[ + ReportOptions.intervalRange.get(interval) || 'rangeInclusive' + ](startDate, endDate); let totalAssets = 0; let totalDebts = 0; - const intervalData = intervals.reduce((arr, intervalItem) => { - let perIntervalAssets = 0; - let perIntervalDebts = 0; - const stacked = {}; + const intervalData = intervals.reduce( + (arr: IntervalEntity[], intervalItem) => { + let perIntervalAssets = 0; + let perIntervalDebts = 0; + const stacked: Record<string, number> = {}; - groupByList.map(item => { - let stackAmounts = 0; + groupByList.map(item => { + let stackAmounts = 0; - const intervalAssets = filterHiddenItems( - item, - assets, - showOffBudget, - showHiddenCategories, - showUncategorized, - ) - .filter( - asset => - asset.date === intervalItem && - asset[groupByLabel] === (item.id ?? null), + const intervalAssets = filterHiddenItems( + item, + assets, + showOffBudget, + showHiddenCategories, + showUncategorized, ) - .reduce((a, v) => (a = a + v.amount), 0); - perIntervalAssets += intervalAssets; + .filter( + asset => + asset.date === intervalItem && + asset[groupByLabel] === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + perIntervalAssets += intervalAssets; - const intervalDebts = filterHiddenItems( - item, - debts, - showOffBudget, - showHiddenCategories, - showUncategorized, - ) - .filter( - debt => - debt.date === intervalItem && - debt[groupByLabel] === (item.id ?? null), + const intervalDebts = filterHiddenItems( + item, + debts, + showOffBudget, + showHiddenCategories, + showUncategorized, ) - .reduce((a, v) => (a = a + v.amount), 0); - perIntervalDebts += intervalDebts; + .filter( + debt => + debt.date === intervalItem && + debt[groupByLabel] === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + perIntervalDebts += intervalDebts; - if (balanceTypeOp === 'totalAssets') { - stackAmounts += intervalAssets; - } - if (balanceTypeOp === 'totalDebts') { - stackAmounts += intervalDebts; - } - if (stackAmounts !== 0) { - stacked[item.name] = integerToAmount(Math.abs(stackAmounts)); - } + if (balanceTypeOp === 'totalAssets') { + stackAmounts += intervalAssets; + } + if (balanceTypeOp === 'totalDebts') { + stackAmounts += intervalDebts; + } + if (stackAmounts !== 0) { + stacked[item.name] = integerToAmount(Math.abs(stackAmounts)); + } - return null; - }); - totalAssets += perIntervalAssets; - totalDebts += perIntervalDebts; + return null; + }); + totalAssets += perIntervalAssets; + totalDebts += perIntervalDebts; - arr.push({ - date: d.format( - d.parseISO(intervalItem), - ReportOptions.intervalFormat.get(interval), - ), - ...stacked, - dateStart: intervalItem, - totalDebts: integerToAmount(perIntervalDebts), - totalAssets: integerToAmount(perIntervalAssets), - totalTotals: integerToAmount(perIntervalDebts + perIntervalAssets), - }); + arr.push({ + date: d.format( + d.parseISO(intervalItem), + ReportOptions.intervalFormat.get(interval) || '', + ), + ...stacked, + dateStart: intervalItem, + totalDebts: integerToAmount(perIntervalDebts), + totalAssets: integerToAmount(perIntervalAssets), + totalTotals: integerToAmount(perIntervalDebts + perIntervalAssets), + }); - return arr; - }, []); + return arr; + }, + [], + ); - const calcData = groupByList.map(item => { + const calcData: GroupedEntity[] = groupByList.map(item => { const calc = recalculate({ item, intervals, @@ -233,7 +239,7 @@ export function createCustomSpreadsheet({ return { ...calc }; }); const calcDataFiltered = calcData.filter(i => - filterEmptyRows(showEmpty, i, balanceTypeOp), + filterEmptyRows({ showEmpty, data: i, balanceTypeOp }), ); const legend = calculateLegend( diff --git a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts index 2e1d9ca985b6434b4a915da488b751b0603c1ea4..02a48e459b74e292278d2f9405e14d8de3cd8e97 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts @@ -1,10 +1,14 @@ -import { type DataEntity } from 'loot-core/src/types/models/reports'; +import { type GroupedEntity } from 'loot-core/src/types/models/reports'; -export function filterEmptyRows( - showEmpty: boolean, - data: DataEntity, - balanceTypeOp: keyof DataEntity, -): boolean { +export function filterEmptyRows({ + showEmpty, + data, + balanceTypeOp = 'totalDebts', +}: { + showEmpty: boolean; + data: GroupedEntity; + balanceTypeOp?: keyof GroupedEntity; +}): boolean { let showHide: boolean; if (balanceTypeOp === 'totalTotals') { showHide = 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 9bd7b45d9aacf5e0a3774dfe70e8207650622271..0995a14fc64615891b116723d80a00f34d2db8a3 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts @@ -176,14 +176,16 @@ export function createGroupedSpreadsheet({ totalTotals: integerToAmount(totalAssets + totalDebts), intervalData, categories: stackedCategories.filter(i => - filterEmptyRows(showEmpty, i, balanceTypeOp), + filterEmptyRows({ showEmpty, data: i, balanceTypeOp }), ), }; }, [startDate, endDate], ); setData( - groupedData.filter(i => filterEmptyRows(showEmpty, i, balanceTypeOp)), + groupedData.filter(i => + filterEmptyRows({ showEmpty, data: i, balanceTypeOp }), + ), ); }; } diff --git a/upcoming-release-notes/2727.md b/upcoming-release-notes/2727.md new file mode 100644 index 0000000000000000000000000000000000000000..cc69a354fb7ffaebe5d1df1ab1411f0d6ebd6569 --- /dev/null +++ b/upcoming-release-notes/2727.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [carkom] +--- + +Making files in custom reports to comply with TS strict - stage #2