From bc04a8cbec0a0a5ec8a50abb016e20fe049fbd45 Mon Sep 17 00:00:00 2001
From: Matiss Janis Aboltins <matiss@mja.lv>
Date: Wed, 4 Sep 2024 20:15:27 +0100
Subject: [PATCH] :recycle: (typescript) migrate reconcile file to TS (#3355)

---
 .../accounts/{Reconcile.jsx => Reconcile.tsx} | 57 +++++++++++--------
 .../src/components/spreadsheet/index.ts       |  9 +++
 packages/loot-core/src/client/queries.ts      | 43 +++++---------
 upcoming-release-notes/3355.md                |  6 ++
 4 files changed, 63 insertions(+), 52 deletions(-)
 rename packages/desktop-client/src/components/accounts/{Reconcile.jsx => Reconcile.tsx} (72%)
 create mode 100644 upcoming-release-notes/3355.md

diff --git a/packages/desktop-client/src/components/accounts/Reconcile.jsx b/packages/desktop-client/src/components/accounts/Reconcile.tsx
similarity index 72%
rename from packages/desktop-client/src/components/accounts/Reconcile.jsx
rename to packages/desktop-client/src/components/accounts/Reconcile.tsx
index 0a4ac13b1..77e0dc145 100644
--- a/packages/desktop-client/src/components/accounts/Reconcile.jsx
+++ b/packages/desktop-client/src/components/accounts/Reconcile.tsx
@@ -2,7 +2,9 @@ import React, { useState } from 'react';
 import { Trans } from 'react-i18next';
 
 import * as queries from 'loot-core/src/client/queries';
+import { type Query } from 'loot-core/src/shared/query';
 import { currencyToInteger } from 'loot-core/src/shared/util';
+import { type AccountEntity } from 'loot-core/types/models';
 
 import { SvgCheckCircle1 } from '../../icons/v2';
 import { styles, theme } from '../../style';
@@ -14,20 +16,32 @@ import { View } from '../common/View';
 import { useFormat } from '../spreadsheet/useFormat';
 import { useSheetValue } from '../spreadsheet/useSheetValue';
 
+type ReconcilingMessageProps = {
+  balanceQuery: { name: `balance-query-${string}`; query: Query };
+  targetBalance: number;
+  onDone: () => void;
+  onCreateTransaction: (targetDiff: number) => void;
+};
+
 export function ReconcilingMessage({
   balanceQuery,
   targetBalance,
   onDone,
   onCreateTransaction,
-}) {
-  const cleared = useSheetValue({
-    name: balanceQuery.name + '-cleared',
+}: ReconcilingMessageProps) {
+  const cleared = useSheetValue<'balance', `balance-query-${string}-cleared`>({
+    name: (balanceQuery.name + '-cleared') as `balance-query-${string}-cleared`,
     value: 0,
     query: balanceQuery.query.filter({ cleared: true }),
   });
   const format = useFormat();
   const targetDiff = targetBalance - cleared;
 
+  const clearedBalance = format(cleared, 'financial');
+  const bankBalance = format(targetBalance, 'financial');
+  const difference =
+    (targetDiff > 0 ? '+' : '') + format(targetDiff, 'financial');
+
   return (
     <View
       style={{
@@ -66,23 +80,10 @@ export function ReconcilingMessage({
           <View style={{ color: theme.tableText }}>
             <Text style={{ fontStyle: 'italic', textAlign: 'center' }}>
               <Trans>
-                Your cleared balance{' '}
-                <strong>
-                  {{ clearedBalance: format(cleared, 'financial') }}
-                </strong>{' '}
-                needs{' '}
-                <strong>
-                  {{
-                    difference:
-                      (targetDiff > 0 ? '+' : '') +
-                      format(targetDiff, 'financial'),
-                  }}
-                </strong>{' '}
-                to match
+                Your cleared balance <strong>{clearedBalance}</strong> needs{' '}
+                <strong>{difference}</strong> to match
                 <br /> your bank&apos;s balance of{' '}
-                <Text style={{ fontWeight: 700 }}>
-                  {{ bankBalance: format(targetBalance, 'financial') }}
-                </Text>
+                <Text style={{ fontWeight: 700 }}>{bankBalance}</Text>
               </Trans>
             </Text>
           </View>
@@ -104,15 +105,25 @@ export function ReconcilingMessage({
   );
 }
 
-export function ReconcileMenu({ account, onReconcile, onClose }) {
+type ReconcileMenuProps = {
+  account: AccountEntity;
+  onReconcile: (amount: number | null) => void;
+  onClose: () => void;
+};
+
+export function ReconcileMenu({
+  account,
+  onReconcile,
+  onClose,
+}: ReconcileMenuProps) {
   const balanceQuery = queries.accountBalance(account);
-  const clearedBalance = useSheetValue({
-    name: balanceQuery.name + '-cleared',
+  const clearedBalance = useSheetValue<'account', `balance-${string}-cleared`>({
+    name: (balanceQuery.name + '-cleared') as `balance-${string}-cleared`,
     value: null,
     query: balanceQuery.query.filter({ cleared: true }),
   });
   const format = useFormat();
-  const [inputValue, setInputValue] = useState(null);
+  const [inputValue, setInputValue] = useState<string | null>(null);
   const [inputFocused, setInputFocused] = useState(false);
 
   function onSubmit() {
diff --git a/packages/desktop-client/src/components/spreadsheet/index.ts b/packages/desktop-client/src/components/spreadsheet/index.ts
index e9ff3d7aa..38b6b27af 100644
--- a/packages/desktop-client/src/components/spreadsheet/index.ts
+++ b/packages/desktop-client/src/components/spreadsheet/index.ts
@@ -8,6 +8,7 @@ export type Spreadsheets = {
 
     // Account fields
     balance: number;
+    [key: `balance-${string}-cleared`]: number | null;
     'accounts-balance': number;
     'budgeted-accounts-balance': number;
     'offbudget-accounts-balance': number;
@@ -62,6 +63,14 @@ export type Spreadsheets = {
     goal: number;
     'long-goal': number;
   };
+  [`balance`]: {
+    // Common fields
+    'uncategorized-amount': number;
+    'uncategorized-balance': number;
+
+    // Balance fields
+    [key: `balance-query-${string}-cleared`]: number;
+  };
 };
 
 export type SheetNames = keyof Spreadsheets & string;
diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts
index 807ee0eca..590e5e9a6 100644
--- a/packages/loot-core/src/client/queries.ts
+++ b/packages/loot-core/src/client/queries.ts
@@ -115,73 +115,61 @@ export function makeTransactionSearchQuery(
   });
 }
 
-export function accountBalance(
-  acct: AccountEntity,
-): Binding<'account', 'balance'> {
+export function accountBalance(acct: AccountEntity) {
   return {
     name: accountParametrizedField('balance')(acct.id),
     query: q('transactions')
       .filter({ account: acct.id })
       .options({ splits: 'none' })
       .calculate({ $sum: '$amount' }),
-  };
+  } satisfies Binding<'account', 'balance'>;
 }
 
-export function accountBalanceCleared(
-  acct: AccountEntity,
-): Binding<'account', 'balanceCleared'> {
+export function accountBalanceCleared(acct: AccountEntity) {
   return {
     name: accountParametrizedField('balanceCleared')(acct.id),
     query: q('transactions')
       .filter({ account: acct.id, cleared: true })
       .options({ splits: 'none' })
       .calculate({ $sum: '$amount' }),
-  };
+  } satisfies Binding<'account', 'balanceCleared'>;
 }
 
-export function accountBalanceUncleared(
-  acct: AccountEntity,
-): Binding<'account', 'balanceUncleared'> {
+export function accountBalanceUncleared(acct: AccountEntity) {
   return {
     name: accountParametrizedField('balanceUncleared')(acct.id),
     query: q('transactions')
       .filter({ account: acct.id, cleared: false })
       .options({ splits: 'none' })
       .calculate({ $sum: '$amount' }),
-  };
+  } satisfies Binding<'account', 'balanceUncleared'>;
 }
 
-export function allAccountBalance(): Binding<'account', 'accounts-balance'> {
+export function allAccountBalance() {
   return {
     query: q('transactions')
       .filter({ 'account.closed': false })
       .calculate({ $sum: '$amount' }),
     name: 'accounts-balance',
-  };
+  } satisfies Binding<'account', 'accounts-balance'>;
 }
 
-export function budgetedAccountBalance(): Binding<
-  'account',
-  'budgeted-accounts-balance'
-> {
+export function budgetedAccountBalance() {
   return {
     name: `budgeted-accounts-balance`,
     query: q('transactions')
       .filter({ 'account.offbudget': false, 'account.closed': false })
       .calculate({ $sum: '$amount' }),
-  };
+  } satisfies Binding<'account', 'budgeted-accounts-balance'>;
 }
 
-export function offbudgetAccountBalance(): Binding<
-  'account',
-  'offbudget-accounts-balance'
-> {
+export function offbudgetAccountBalance() {
   return {
     name: `offbudget-accounts-balance`,
     query: q('transactions')
       .filter({ 'account.offbudget': true, 'account.closed': false })
       .calculate({ $sum: '$amount' }),
-  };
+  } satisfies Binding<'account', 'offbudget-accounts-balance'>;
 }
 
 export function categoryBalance(category: CategoryEntity, month: string) {
@@ -249,14 +237,11 @@ export function uncategorizedBalance() {
   };
 }
 
-export function uncategorizedCount<SheetName extends SheetNames>(): Binding<
-  SheetName,
-  'uncategorized-amount'
-> {
+export function uncategorizedCount<SheetName extends SheetNames>() {
   return {
     name: 'uncategorized-amount',
     query: uncategorizedQuery.calculate({ $count: '$id' }),
-  };
+  } satisfies Binding<SheetName, 'uncategorized-amount'>;
 }
 
 export const rolloverBudget = {
diff --git a/upcoming-release-notes/3355.md b/upcoming-release-notes/3355.md
new file mode 100644
index 000000000..d74e4039f
--- /dev/null
+++ b/upcoming-release-notes/3355.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+TypeScript: migrate Reconcile file.
-- 
GitLab