diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx index 0e19a7c1467..6d2e557e1f1 100644 --- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx +++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx @@ -1,5 +1,4 @@ -// @ts-strict-ignore -import React, { useRef } from 'react'; +import React, { type UIEvent, useRef } from 'react'; import { type DataEntity } from 'loot-core/src/types/models/reports'; import { type RuleConditionEntity } from 'loot-core/types/models/rule'; @@ -29,55 +28,70 @@ type ChooseGraphProps = { interval: string; setScrollWidth?: (value: number) => void; viewLabels?: boolean; - compact?: boolean; + compact: boolean; style?: CSSProperties; showHiddenCategories?: boolean; showOffBudget?: boolean; - intervalsCount?: number; + intervalsCount: number; }; export function ChooseGraph({ data, - filters, + filters = [], mode, graphType, balanceType, groupBy, interval, setScrollWidth, - viewLabels, + viewLabels = false, compact, style, - showHiddenCategories, - showOffBudget, + showHiddenCategories = false, + showOffBudget = false, intervalsCount, }: ChooseGraphProps) { const graphStyle = compact ? { ...style } : { flexGrow: 1 }; - const balanceTypeOp = ReportOptions.balanceTypeMap.get(balanceType); + const balanceTypeOp = + ReportOptions.balanceTypeMap.get(balanceType) || 'totalDebts'; - const saveScrollWidth = value => { - setScrollWidth(!value ? 0 : value); + const saveScrollWidth = (value: number) => { + setScrollWidth?.(value || 0); }; - const rowStyle = compact && { flex: '0 0 20px', height: 20 }; - const compactStyle = compact && { ...styles.tinyText }; + const rowStyle: CSSProperties = compact + ? { flex: '0 0 20px', height: 20 } + : {}; + const compactStyle: CSSProperties = compact ? { ...styles.tinyText } : {}; const headerScrollRef = useRef(null); const listScrollRef = useRef(null); const totalScrollRef = useRef(null); - const handleScroll = scroll => { - if (scroll.target.id === 'header') { - totalScrollRef.current.scrollLeft = scroll.target.scrollLeft; - listScrollRef.current.scrollLeft = scroll.target.scrollLeft; + const handleScroll = (scroll: UIEvent) => { + if ( + scroll.currentTarget.id === 'header' && + totalScrollRef.current && + listScrollRef.current + ) { + totalScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; + listScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; } - if (scroll.target.id === 'total') { - headerScrollRef.current.scrollLeft = scroll.target.scrollLeft; - listScrollRef.current.scrollLeft = scroll.target.scrollLeft; + if ( + scroll.currentTarget.id === 'total' && + headerScrollRef.current && + listScrollRef.current + ) { + headerScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; + listScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; } - if (scroll.target.id === 'list') { - headerScrollRef.current.scrollLeft = scroll.target.scrollLeft; - totalScrollRef.current.scrollLeft = scroll.target.scrollLeft; + if ( + scroll.currentTarget.id === 'list' && + totalScrollRef.current && + headerScrollRef.current + ) { + headerScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; + totalScrollRef.current.scrollLeft = scroll.currentTarget.scrollLeft; } }; diff --git a/packages/desktop-client/src/components/reports/Container.tsx b/packages/desktop-client/src/components/reports/Container.tsx index 93b968fc1bc..e169cc28b22 100644 --- a/packages/desktop-client/src/components/reports/Container.tsx +++ b/packages/desktop-client/src/components/reports/Container.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import React, { useRef, type ReactNode } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; @@ -7,7 +6,11 @@ import { View } from '../common/View'; type ContainerProps = { style?: CSSProperties; - children: (width: number, height: number, host: HTMLDivElement) => ReactNode; + children: ( + width: number, + height: number, + host: HTMLDivElement | null, + ) => ReactNode; }; export function Container({ style, children }: ContainerProps) { const portalHost = useRef(null); @@ -18,7 +21,7 @@ export function Container({ style, children }: ContainerProps) { >
- {({ width, height }) => ( + {({ width, height }: { width: number; height: number }) => (
{children(width, height, portalHost.current)}
diff --git a/packages/desktop-client/src/components/reports/GraphButton.tsx b/packages/desktop-client/src/components/reports/GraphButton.tsx index 0c155d2166d..f648ca14e74 100644 --- a/packages/desktop-client/src/components/reports/GraphButton.tsx +++ b/packages/desktop-client/src/components/reports/GraphButton.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import React, { type HTMLProps } from 'react'; import { type CSSProperties, theme } from '../../style'; @@ -10,7 +9,7 @@ import { Tooltip } from '../tooltips'; type GraphButtonProps = HTMLProps & { selected?: boolean; style?: CSSProperties; - onSelect?: (newValue) => void; + onSelect?: (newValue: string) => void; title?: string; disabled?: boolean; }; diff --git a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx index 860d2c7fcdf..94999532993 100644 --- a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx @@ -1,4 +1,3 @@ -// @ts-strict-ignore import React from 'react'; import { css } from 'glamor'; @@ -25,7 +24,6 @@ import { theme } from '../../../style'; import { type CSSProperties } from '../../../style'; import { AlignedText } from '../../common/AlignedText'; import { Container } from '../Container'; -import { numberFormatterTooltip } from '../numberFormatter'; import { adjustTextSize } from './adjustTextSize'; import { renderCustomLabel } from './renderCustomLabel'; @@ -42,7 +40,7 @@ type PayloadItem = { type CustomTooltipProps = { active?: boolean; payload?: PayloadItem[]; - balanceTypeOp?: string; + balanceTypeOp: 'totalAssets' | 'totalTotals' | 'totalDebts'; }; const CustomTooltip = ({ @@ -95,16 +93,37 @@ const CustomTooltip = ({
); } + + return
; +}; + +type PropsItem = { + index?: number; + x?: string | number; + y?: string | number; + value?: string | number; + width?: string | number; }; -const customLabel = (props, width, end) => { +const customLabel = ({ + props, + width, + end, +}: { + props: PropsItem; + width: number; + end: number; +}) => { //Add margin to first and last object const calcX = - props.x + (props.index === end ? -10 : props.index === 0 ? 5 : 0); - const calcY = props.y - (props.value > 0 ? 10 : -10); + (typeof props.x === 'number' ? props.x : 0) + + (props.index === end ? -10 : props.index === 0 ? 5 : 0); + const calcY = + (typeof props.y === 'number' ? props.y : 0) - + ((typeof props.value === 'number' ? props.value : 0) > 0 ? 10 : -10); const textAnchor = props.index === 0 ? 'left' : 'middle'; const display = - props.value !== 0 && `${amountToCurrencyNoDecimal(props.value)}`; + props.value !== 0 ? `${amountToCurrencyNoDecimal(props.value)}` : ''; const textSize = adjustTextSize({ sized: width, type: 'area' }); return renderCustomLabel(calcX, calcY, textAnchor, display, textSize); @@ -113,7 +132,7 @@ const customLabel = (props, width, end) => { type AreaGraphProps = { style?: CSSProperties; data: DataEntity; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalTotals' | 'totalDebts'; compact?: boolean; viewLabels: boolean; }; @@ -148,7 +167,7 @@ export function AreaGraph({ : Math.ceil((dataMax + extendRangeAmount) / 100) * 100; const lastLabel = data.intervalData.length - 1; - const tickFormatter = tick => { + const tickFormatter = (tick: number) => { if (!privacyMode) return `${amountToCurrencyNoDecimal(tick)}`; // Formats the tick values as strings with commas return '...'; }; @@ -216,7 +235,6 @@ export function AreaGraph({ {(!isNarrowWidth || !compact) && ( } - formatter={numberFormatterTooltip} isAnimationActive={false} /> )} @@ -272,7 +290,9 @@ export function AreaGraph({ {viewLabels && !compact && ( customLabel(e, width, lastLabel)} + content={props => + customLabel({ props, width, end: lastLabel }) + } /> )} diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx index e8d1eb26b7a..6b455cf2346 100644 --- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx @@ -59,7 +59,7 @@ type PayloadItem = { type CustomTooltipProps = { active?: boolean; payload?: PayloadItem[]; - balanceTypeOp?: string; + balanceTypeOp?: 'totalAssets' | 'totalDebts' | 'totalTotals'; yAxis?: string; }; @@ -136,7 +136,7 @@ type BarGraphProps = { data: DataEntity; filters: RuleConditionEntity[]; groupBy: string; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals'; compact?: boolean; viewLabels: boolean; showHiddenCategories?: boolean; diff --git a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx index c1f50df27a3..7267f49b791 100644 --- a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx @@ -180,7 +180,7 @@ type DonutGraphProps = { data: DataEntity; filters: RuleConditionEntity[]; groupBy: string; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals'; compact?: boolean; viewLabels: boolean; showHiddenCategories?: boolean; diff --git a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx index 3cb78f3d4df..15577b4e952 100644 --- a/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/LineGraph.tsx @@ -113,7 +113,7 @@ type LineGraphProps = { filters: RuleConditionEntity[]; groupBy: string; compact?: boolean; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals'; showHiddenCategories?: boolean; showOffBudget?: boolean; }; diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx index 2df89692dac..f2c2807eb1d 100644 --- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx @@ -143,7 +143,7 @@ type StackedBarGraphProps = { groupBy: string; compact?: boolean; viewLabels: boolean; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals'; showHiddenCategories?: boolean; showOffBudget?: boolean; }; diff --git a/packages/desktop-client/src/components/reports/graphs/showActivity.ts b/packages/desktop-client/src/components/reports/graphs/showActivity.ts index f0f6673bd73..fdbe64f8268 100644 --- a/packages/desktop-client/src/components/reports/graphs/showActivity.ts +++ b/packages/desktop-client/src/components/reports/graphs/showActivity.ts @@ -9,7 +9,7 @@ type showActivityProps = { navigate: NavigateFunction; categories: { list: CategoryEntity[]; grouped: CategoryGroupEntity[] }; accounts: AccountEntity[]; - balanceTypeOp: string; + balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals'; filters: RuleConditionEntity[]; showHiddenCategories: boolean; showOffBudget: boolean; diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts index de1ed867585..7ada7dec8d9 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts @@ -12,7 +12,7 @@ export function calculateLegend( calcDataFiltered: GroupedEntity[], groupBy: string, graphType?: string, - balanceTypeOp?: keyof GroupedEntity, + balanceTypeOp?: 'totalAssets' | 'totalDebts' | 'totalTotals', ): LegendEntity[] { const colorScale = getColorScale('qualitative'); const chooseData = diff --git a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts index 02a48e459b7..b133a67da13 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/filterEmptyRows.ts @@ -7,7 +7,7 @@ export function filterEmptyRows({ }: { showEmpty: boolean; data: GroupedEntity; - balanceTypeOp?: keyof GroupedEntity; + balanceTypeOp?: 'totalAssets' | 'totalDebts' | 'totalTotals'; }): boolean { let showHide: boolean; if (balanceTypeOp === 'totalTotals') { 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 0995a14fc64..170cc421316 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts @@ -1,12 +1,18 @@ -// @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 IntervalEntity, + type GroupedEntity, +} from 'loot-core/src/types/models/reports'; -import { categoryLists, ReportOptions } from '../ReportOptions'; +import { + categoryLists, + type QueryDataEntity, + ReportOptions, +} from '../ReportOptions'; import { type createCustomSpreadsheetProps } from './custom-spreadsheet'; import { filterEmptyRows } from './filterEmptyRows'; @@ -52,7 +58,9 @@ export function createGroupedSpreadsheet({ }); 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', @@ -95,89 +103,99 @@ export function createGroupedSpreadsheet({ const intervals = interval === 'Weekly' ? monthUtils.weekRangeInclusive(startDate, endDate, firstDayOfWeekIdx) - : monthUtils[ReportOptions.intervalRange.get(interval)]( - startDate, - endDate, - ); + : monthUtils[ + ReportOptions.intervalRange.get(interval) || 'rangeInclusive' + ](startDate, endDate); const groupedData: GroupedEntity[] = categoryGroup.map( group => { let totalAssets = 0; let totalDebts = 0; - const intervalData = intervals.reduce((arr, intervalItem) => { - let groupedAssets = 0; - let groupedDebts = 0; - - group.categories.forEach(item => { - const intervalAssets = filterHiddenItems( - item, - assets, - showOffBudget, - showHiddenCategories, - showUncategorized, - ) - .filter( - asset => - asset.date === intervalItem && - asset.category === (item.id ?? null), + const intervalData = intervals.reduce( + (arr: IntervalEntity[], intervalItem) => { + let groupedAssets = 0; + let groupedDebts = 0; + + if (!group.categories) { + return []; + } + + group.categories.forEach(item => { + const intervalAssets = filterHiddenItems( + item, + assets, + showOffBudget, + showHiddenCategories, + showUncategorized, ) - .reduce((a, v) => (a = a + v.amount), 0); - groupedAssets += intervalAssets; - - const intervalDebts = filterHiddenItems( + .filter( + asset => + asset.date === intervalItem && + asset.category === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + groupedAssets += intervalAssets; + + const intervalDebts = filterHiddenItems( + item, + debts, + showOffBudget, + showHiddenCategories, + showUncategorized, + ) + .filter( + debts => + debts.date === intervalItem && + debts.category === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + groupedDebts += intervalDebts; + }); + + totalAssets += groupedAssets; + totalDebts += groupedDebts; + + arr.push({ + date: intervalItem, + totalAssets: integerToAmount(groupedAssets), + totalDebts: integerToAmount(groupedDebts), + totalTotals: integerToAmount(groupedDebts + groupedAssets), + }); + + return arr; + }, + [], + ); + + const stackedCategories = + group.categories && + group.categories.map(item => { + const calc = recalculate({ item, + intervals, + assets, debts, + groupByLabel: 'category', showOffBudget, showHiddenCategories, showUncategorized, - ) - .filter( - debts => - debts.date === intervalItem && - debts.category === (item.id ?? null), - ) - .reduce((a, v) => (a = a + v.amount), 0); - groupedDebts += intervalDebts; - }); - - totalAssets += groupedAssets; - totalDebts += groupedDebts; - - arr.push({ - date: intervalItem, - totalAssets: integerToAmount(groupedAssets), - totalDebts: integerToAmount(groupedDebts), - totalTotals: integerToAmount(groupedDebts + groupedAssets), - }); - - return arr; - }, []); - - const stackedCategories = group.categories.map(item => { - const calc = recalculate({ - item, - intervals, - assets, - debts, - groupByLabel: 'category', - showOffBudget, - showHiddenCategories, - showUncategorized, + }); + return { ...calc }; }); - return { ...calc }; - }); return { - id: group.id, + id: group.id || '', name: group.name, totalAssets: integerToAmount(totalAssets), totalDebts: integerToAmount(totalDebts), totalTotals: integerToAmount(totalAssets + totalDebts), intervalData, - categories: stackedCategories.filter(i => - filterEmptyRows({ showEmpty, data: i, balanceTypeOp }), - ), + categories: + stackedCategories && + stackedCategories.filter(i => + filterEmptyRows({ showEmpty, data: i, balanceTypeOp }), + ), }; }, [startDate, endDate], diff --git a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts index a19ed597310..a7262dde838 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts @@ -1,16 +1,22 @@ -// @ts-strict-ignore import { amountToInteger, integerToAmount } from 'loot-core/src/shared/util'; +import { + type GroupedEntity, + type IntervalEntity, +} from 'loot-core/types/models/reports'; -import { type QueryDataEntity } from '../ReportOptions'; +import { + type UncategorizedEntity, + type QueryDataEntity, +} from '../ReportOptions'; import { filterHiddenItems } from './filterHiddenItems'; type recalculateProps = { - item; + item: UncategorizedEntity; intervals: Array; assets: QueryDataEntity[]; debts: QueryDataEntity[]; - groupByLabel: string; + groupByLabel: 'category' | 'categoryGroup' | 'payee' | 'account'; showOffBudget?: boolean; showHiddenCategories?: boolean; showUncategorized?: boolean; @@ -25,59 +31,62 @@ export function recalculate({ showOffBudget, showHiddenCategories, showUncategorized, -}: recalculateProps) { +}: recalculateProps): GroupedEntity { let totalAssets = 0; let totalDebts = 0; - const intervalData = intervals.reduce((arr, intervalItem) => { - const last = arr.length === 0 ? null : arr[arr.length - 1]; + const intervalData = intervals.reduce( + (arr: IntervalEntity[], intervalItem) => { + const last = arr.length === 0 ? null : arr[arr.length - 1]; - 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); - totalAssets += intervalAssets; + .filter( + asset => + asset.date === intervalItem && + asset[groupByLabel] === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + totalAssets += 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); - totalDebts += intervalDebts; + .filter( + debt => + debt.date === intervalItem && + debt[groupByLabel] === (item.id ?? null), + ) + .reduce((a, v) => (a = a + v.amount), 0); + totalDebts += intervalDebts; - const change = last - ? intervalAssets + intervalDebts - amountToInteger(last.totalTotals) - : 0; + const change = last + ? intervalAssets + intervalDebts - amountToInteger(last.totalTotals) + : 0; - arr.push({ - totalAssets: integerToAmount(intervalAssets), - totalDebts: integerToAmount(intervalDebts), - totalTotals: integerToAmount(intervalAssets + intervalDebts), - change, - dateLookup: intervalItem, - }); + arr.push({ + totalAssets: integerToAmount(intervalAssets), + totalDebts: integerToAmount(intervalDebts), + totalTotals: integerToAmount(intervalAssets + intervalDebts), + change, + dateLookup: intervalItem, + }); - return arr; - }, []); + return arr; + }, + [], + ); return { - id: item.id, + id: item.id || '', name: item.name, totalAssets: integerToAmount(totalAssets), totalDebts: integerToAmount(totalDebts), diff --git a/upcoming-release-notes/2728.md b/upcoming-release-notes/2728.md new file mode 100644 index 00000000000..77a4068ca16 --- /dev/null +++ b/upcoming-release-notes/2728.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [carkom] +--- + +Making files in custom reports to comply with TS strict - stage #3.