diff --git a/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx b/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx similarity index 85% rename from packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx rename to packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx index 0c13c2251184052c3522d2e0ae3648bfde54be47..e1aa13f1a9162235071184002e5bfe977af75055 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx +++ b/packages/desktop-client/src/components/reports/reports/CashFlowCard.tsx @@ -1,4 +1,5 @@ import React, { useState, useMemo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { Bar, BarChart, LabelList, ResponsiveContainer } from 'recharts'; @@ -18,15 +19,25 @@ import { ReportCard } from '../ReportCard'; import { simpleCashFlow } from '../spreadsheets/cash-flow-spreadsheet'; import { useReport } from '../useReport'; +type CustomLabelProps = { + value?: number; + name: string; + position?: 'left' | 'right'; + x?: number; + y?: number; + width?: number; + height?: number; +}; + function CustomLabel({ - value, + value = 0, name, - position, - x, - y, - width: barWidth, - height: barHeight, -}) { + position = 'left', + x = 0, + y = 0, + width: barWidth = 0, + height: barHeight = 0, +}: CustomLabelProps) { const valueLengthOffset = 20; const yOffset = barHeight < 25 ? 105 : y; @@ -68,7 +79,13 @@ function CustomLabel({ ); } -export function CashFlowCard({ isEditing, onRemove }) { +type CashFlowCardProps = { + isEditing?: boolean; + onRemove: () => void; +}; + +export function CashFlowCard({ isEditing, onRemove }: CashFlowCardProps) { + const { t } = useTranslation(); const end = monthUtils.currentDay(); const start = monthUtils.currentMonth() + '-01'; @@ -76,8 +93,8 @@ export function CashFlowCard({ isEditing, onRemove }) { const data = useReport('cash_flow_simple', params); const [isCardHovered, setIsCardHovered] = useState(false); - const onCardHover = useCallback(() => setIsCardHovered(true)); - const onCardHoverEnd = useCallback(() => setIsCardHovered(false)); + const onCardHover = useCallback(() => setIsCardHovered(true), []); + const onCardHoverEnd = useCallback(() => setIsCardHovered(false), []); const { graphData } = data || {}; const expenses = -(graphData?.expense || 0); @@ -90,7 +107,7 @@ export function CashFlowCard({ isEditing, onRemove }) { menuItems={[ { name: 'remove', - text: 'Remove', + text: t('Remove'), }, ]} onMenuSelect={item => { @@ -153,7 +170,7 @@ export function CashFlowCard({ isEditing, onRemove }) { <LabelList dataKey="income" position="left" - content={<CustomLabel name="Income" />} + content={<CustomLabel name={t('Income')} />} /> </Bar> @@ -165,7 +182,7 @@ export function CashFlowCard({ isEditing, onRemove }) { <LabelList dataKey="expenses" position="right" - content={<CustomLabel name="Expenses" />} + content={<CustomLabel name={t('Expenses')} />} /> </Bar> </BarChart> diff --git a/packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx similarity index 87% rename from packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx rename to packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx index aab03f09920817c04ee5ddb68281446cadf635f5..877312728e7053d2eb4d59e6f752db1dafe2e91c 100644 --- a/packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx +++ b/packages/desktop-client/src/components/reports/reports/NetWorthCard.tsx @@ -1,7 +1,9 @@ import React, { useState, useMemo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import * as monthUtils from 'loot-core/src/shared/months'; import { integerToCurrency } from 'loot-core/src/shared/util'; +import { type AccountEntity } from 'loot-core/src/types/models'; import { useResponsive } from '../../../ResponsiveProvider'; import { styles } from '../../../style'; @@ -16,14 +18,25 @@ import { ReportCard } from '../ReportCard'; import { createSpreadsheet as netWorthSpreadsheet } from '../spreadsheets/net-worth-spreadsheet'; import { useReport } from '../useReport'; -export function NetWorthCard({ isEditing, accounts, onRemove }) { +type NetWorthCardProps = { + isEditing?: boolean; + accounts: AccountEntity[]; + onRemove: () => void; +}; + +export function NetWorthCard({ + isEditing, + accounts, + onRemove, +}: NetWorthCardProps) { + const { t } = useTranslation(); const { isNarrowWidth } = useResponsive(); const end = monthUtils.currentMonth(); const start = monthUtils.subMonths(end, 5); const [isCardHovered, setIsCardHovered] = useState(false); - const onCardHover = useCallback(() => setIsCardHovered(true)); - const onCardHoverEnd = useCallback(() => setIsCardHovered(false)); + const onCardHover = useCallback(() => setIsCardHovered(true), []); + const onCardHoverEnd = useCallback(() => setIsCardHovered(false), []); const params = useMemo( () => netWorthSpreadsheet(start, end, accounts), @@ -38,7 +51,7 @@ export function NetWorthCard({ isEditing, accounts, onRemove }) { menuItems={[ { name: 'remove', - text: 'Remove', + text: t('Remove'), }, ]} onMenuSelect={item => { @@ -88,8 +101,6 @@ export function NetWorthCard({ isEditing, accounts, onRemove }) { {data ? ( <NetWorthGraph - start={start} - end={end} graphData={data.graphData} compact={true} showTooltip={!isEditing && !isNarrowWidth} diff --git a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx index 9127e3040d2d9ed08acfc838ae6f51e7cf10d549..0f1f01406523bbb6a3f752127957fdb54258d4f7 100644 --- a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx @@ -1,4 +1,5 @@ import React, { useState, useMemo } from 'react'; +import { Trans, useTranslation } from 'react-i18next'; import * as monthUtils from 'loot-core/src/shared/months'; import { amountToCurrency } from 'loot-core/src/shared/util'; @@ -25,6 +26,8 @@ type SpendingCardProps = { }; export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) { + const { t } = useTranslation(); + const [isCardHovered, setIsCardHovered] = useState(false); const [spendingReportFilter = ''] = useLocalPref('spendingReportFilter'); const [spendingReportTime = 'lastMonth'] = useLocalPref('spendingReportTime'); @@ -59,7 +62,9 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) { if (!spendingReportFeatureFlag) { return ( <MissingReportCard isEditing={isEditing} onRemove={onRemove}> - The experimental spending report feature has not been enabled. + <Trans> + The experimental spending report feature has not been enabled. + </Trans> </MissingReportCard> ); } @@ -71,7 +76,7 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) { menuItems={[ { name: 'remove', - text: 'Remove', + text: t('Remove'), }, ]} onMenuSelect={item => { @@ -128,7 +133,7 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) { {!showLastMonth ? ( <View style={{ padding: 5 }}> <p style={{ margin: 0, textAlign: 'center' }}> - Additional data required to generate graph + <Trans>Additional data required to generate graph</Trans> </p> </View> ) : data ? ( @@ -140,7 +145,7 @@ export function SpendingCard({ isEditing, onRemove }: SpendingCardProps) { compare={spendingReportCompare} /> ) : ( - <LoadingIndicator message="Loading report..." /> + <LoadingIndicator message={t('Loading report...')} /> )} </View> </ReportCard> 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 b0ea66d48ed31d979d86af7af88e3f3a9067fbe0..04a7967d09b94ac11dc14799f785b3a40a327d92 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 @@ -1,4 +1,3 @@ -// @ts-strict-ignore import React from 'react'; import * as d from 'date-fns'; @@ -13,8 +12,11 @@ import { type RuleConditionEntity } from 'loot-core/types/models'; import { AlignedText } from '../../common/AlignedText'; import { runAll, indexCashFlow } from '../util'; -export function simpleCashFlow(start, end) { - return async (spreadsheet, setData) => { +export function simpleCashFlow(start: string, end: string) { + return async ( + spreadsheet: ReturnType<typeof useSpreadsheet>, + setData: (data: { graphData: { income: number; expense: number } }) => void, + ) => { function makeQuery() { return q('transactions') .filter({ @@ -109,7 +111,16 @@ export function cashFlowByDate( }; } -function recalculate(data, start, end, isConcise) { +function recalculate( + data: [ + number, + Array<{ date: string; isTransfer: string | null; amount: number }>, + Array<{ date: string; isTransfer: string | null; amount: number }>, + ], + start: string, + end: string, + isConcise: boolean, +) { const [startingBalance, income, expense] = data; const convIncome = income.map(t => { return { ...t, isTransfer: t.isTransfer !== null }; @@ -123,15 +134,25 @@ function recalculate(data, start, end, isConcise) { monthUtils.getMonth(end), ) : monthUtils.dayRangeInclusive(start, end); - const incomes = indexCashFlow(convIncome, 'date', 'isTransfer'); - const expenses = indexCashFlow(convExpense, 'date', 'isTransfer'); + const incomes = indexCashFlow(convIncome); + const expenses = indexCashFlow(convExpense); let balance = startingBalance; let totalExpenses = 0; let totalIncome = 0; let totalTransfers = 0; - const graphData = dates.reduce( + const graphData = dates.reduce<{ + expenses: Array<{ x: Date; y: number }>; + income: Array<{ x: Date; y: number }>; + transfers: Array<{ x: Date; y: number }>; + balances: Array<{ + x: Date; + y: number; + premadeLabel: JSX.Element; + amount: number; + }>; + }>( (res, date) => { let income = 0; let expense = 0; diff --git a/packages/desktop-client/src/components/reports/spreadsheets/net-worth-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/net-worth-spreadsheet.ts index 2fc4c1ae272ceece19f729659f649801b397ed0f..49c01d96d6c7cf48ff24fa250c47a123928974fb 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/net-worth-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/net-worth-spreadsheet.ts @@ -26,7 +26,7 @@ export function createSpreadsheet( end: string, accounts: AccountEntity[], conditions: RuleConditionEntity[] = [], - conditionsOp: 'and' | 'or', + conditionsOp: 'and' | 'or' = 'and', ) { return async ( spreadsheet: ReturnType<typeof useSpreadsheet>, diff --git a/packages/desktop-client/src/components/reports/util.ts b/packages/desktop-client/src/components/reports/util.ts index ab45b9bb550ae16011081a77af5be4e809bc2f7b..2495a1c09528c26997d2b37026b041bcc63b83c6 100644 --- a/packages/desktop-client/src/components/reports/util.ts +++ b/packages/desktop-client/src/components/reports/util.ts @@ -20,16 +20,12 @@ export async function runAll( export function indexCashFlow< T extends { date: string; isTransfer: boolean; amount: number }, ->(data: T[], date: string, isTransfer: string) { - const results = {}; +>(data: T[]): Record<string, Record<'true' | 'false', number>> { + const results: Record<string, Record<'true' | 'false', number>> = {}; data.forEach(item => { - const findExisting = results[item.date] - ? results[item.date][item.isTransfer] - ? results[item.date][item.isTransfer] - : 0 - : 0; - const result = { [item[isTransfer]]: item.amount + findExisting }; - results[item[date]] = { ...results[item[date]], ...result }; + const findExisting = results?.[item.date]?.[String(item.isTransfer)] ?? 0; + const result = { [String(item.isTransfer)]: item.amount + findExisting }; + results[item.date] = { ...results[item.date], ...result }; }); return results; } diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts index f455b2bbb0eda466b9a2d251d47e7e0e54dbd2ef..51967882f0e59e0d7cf4e30b2112e37d5ddc32b4 100644 --- a/packages/loot-core/src/types/server-handlers.d.ts +++ b/packages/loot-core/src/types/server-handlers.d.ts @@ -18,6 +18,7 @@ import { PayeeEntity, } from './models'; import { GlobalPrefs, LocalPrefs } from './prefs'; +import { Query } from './query'; import { EmptyObject } from './util'; export interface ServerHandlers { @@ -137,7 +138,7 @@ export interface ServerHandlers { 'create-query': (arg: { sheetName; name; query }) => Promise<unknown>; - query: (query) => Promise<{ data; dependencies }>; + query: (query: Query) => Promise<{ data: unknown; dependencies }>; 'account-update': (arg: { id; name }) => Promise<unknown>; diff --git a/upcoming-release-notes/3285.md b/upcoming-release-notes/3285.md new file mode 100644 index 0000000000000000000000000000000000000000..fa0c39c09978c968a0310d7889def3142b3c796e --- /dev/null +++ b/upcoming-release-notes/3285.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [Matissjanis] +--- + +TypeScript: migrate report cards to TS.