-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert SelectLinkedAccountsModal to TypeScript #3984
Changes from all commits
d3de35f
1c51a5c
2bf3b70
126c4bd
7bb4289
2a270c8
c1d7672
3a893fe
71c6d07
a8446f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,5 +1,5 @@ | ||||||
import React, { useState } from 'react'; | ||||||
import { useTranslation } from 'react-i18next'; | ||||||
import { Trans, useTranslation } from 'react-i18next'; | ||||||
import { useDispatch } from 'react-redux'; | ||||||
|
||||||
import { | ||||||
|
@@ -8,6 +8,10 @@ import { | |||||
linkAccountSimpleFin, | ||||||
unlinkAccount, | ||||||
} from 'loot-core/client/actions'; | ||||||
import { | ||||||
type AccountEntity, | ||||||
type AccountSyncSource, | ||||||
} from 'loot-core/types/models/account'; | ||||||
|
||||||
import { useAccounts } from '../../hooks/useAccounts'; | ||||||
import { theme } from '../../style'; | ||||||
|
@@ -17,24 +21,51 @@ import { Modal, ModalCloseButton, ModalHeader } from '../common/Modal'; | |||||
import { Text } from '../common/Text'; | ||||||
import { View } from '../common/View'; | ||||||
import { PrivacyFilter } from '../PrivacyFilter'; | ||||||
import { TableHeader, Table, Row, Field } from '../table'; | ||||||
import { Field, Row, Table, TableHeader } from '../table'; | ||||||
|
||||||
import { type NormalizedAccount } from './CreateAccountModal'; | ||||||
|
||||||
type LinkAccountOption = { | ||||||
id: string; | ||||||
name: string; | ||||||
}; | ||||||
|
||||||
const addOnBudgetAccountOption = { id: 'new-on', name: 'Create new account' }; | ||||||
const addOffBudgetAccountOption = { | ||||||
const addOnBudgetAccountOption: LinkAccountOption = { | ||||||
id: 'new-on', | ||||||
name: 'Create new account', | ||||||
}; | ||||||
|
||||||
const addOffBudgetAccountOption: LinkAccountOption = { | ||||||
id: 'new-off', | ||||||
name: 'Create new account (off budget)', | ||||||
}; | ||||||
|
||||||
type SelectLinkedAccountsModalProps = { | ||||||
requisitionId: string; | ||||||
externalAccounts: NormalizedAccount[]; | ||||||
syncSource: AccountSyncSource; | ||||||
}; | ||||||
|
||||||
type LinkedAccountIds = { [key: string]: string }; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be done with AccountEntity instead of string? |
||||||
type LinkedAccountIdsSetter = ( | ||||||
fn: (value: LinkedAccountIds) => LinkedAccountIds, | ||||||
) => void; | ||||||
|
||||||
export function SelectLinkedAccountsModal({ | ||||||
requisitionId, | ||||||
externalAccounts, | ||||||
syncSource, | ||||||
}) { | ||||||
}: SelectLinkedAccountsModalProps) { | ||||||
externalAccounts.sort((a, b) => a.name.localeCompare(b.name)); | ||||||
const { t } = useTranslation(); | ||||||
const dispatch = useDispatch(); | ||||||
const localAccounts = useAccounts().filter(a => a.closed === 0); | ||||||
const [chosenAccounts, setChosenAccounts] = useState(() => { | ||||||
const localAccounts: AccountEntity[] = useAccounts().filter( | ||||||
a => a.closed === 0, | ||||||
); | ||||||
const [chosenAccounts, setChosenAccounts]: [ | ||||||
LinkedAccountIds, | ||||||
LinkedAccountIdsSetter, | ||||||
] = useState(() => { | ||||||
dkhalife marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return Object.fromEntries( | ||||||
localAccounts | ||||||
.filter(acc => acc.account_id) | ||||||
|
@@ -43,7 +74,7 @@ export function SelectLinkedAccountsModal({ | |||||
}); | ||||||
|
||||||
async function onNext() { | ||||||
const chosenLocalAccountIds = Object.values(chosenAccounts); | ||||||
const chosenLocalAccountIds: string[] = Object.values(chosenAccounts); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you type this using AccountEntity?
Suggested change
|
||||||
|
||||||
// Unlink accounts that were previously linked, but the user | ||||||
// chose to remove the bank-sync | ||||||
|
@@ -56,7 +87,7 @@ export function SelectLinkedAccountsModal({ | |||||
Object.entries(chosenAccounts).forEach( | ||||||
([chosenExternalAccountId, chosenLocalAccountId]) => { | ||||||
const externalAccount = externalAccounts.find( | ||||||
account => account.account_id === chosenExternalAccountId, | ||||||
account => account.id === chosenExternalAccountId, | ||||||
); | ||||||
const offBudget = chosenLocalAccountId === addOffBudgetAccountOption.id; | ||||||
|
||||||
|
@@ -101,14 +132,17 @@ export function SelectLinkedAccountsModal({ | |||||
account => !Object.values(chosenAccounts).includes(account.id), | ||||||
); | ||||||
|
||||||
function onSetLinkedAccount(externalAccount, localAccountId) { | ||||||
setChosenAccounts(accounts => { | ||||||
const updatedAccounts = { ...accounts }; | ||||||
function onSetLinkedAccount( | ||||||
externalAccount: NormalizedAccount, | ||||||
localAccountId: string | undefined, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be optional instead?
Suggested change
|
||||||
) { | ||||||
setChosenAccounts((accounts: LinkedAccountIds): LinkedAccountIds => { | ||||||
const updatedAccounts: LinkedAccountIds = { ...accounts }; | ||||||
|
||||||
if (localAccountId) { | ||||||
updatedAccounts[externalAccount.account_id] = localAccountId; | ||||||
if (localAccountId !== undefined) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use if(variable) for presence checks elsewhere in the code
Suggested change
|
||||||
updatedAccounts[externalAccount.id] = localAccountId; | ||||||
} else { | ||||||
delete updatedAccounts[externalAccount.account_id]; | ||||||
delete updatedAccounts[externalAccount.id]; | ||||||
} | ||||||
|
||||||
return updatedAccounts; | ||||||
|
@@ -138,32 +172,36 @@ export function SelectLinkedAccountsModal({ | |||||
border: '1px solid ' + theme.tableBorder, | ||||||
}} | ||||||
> | ||||||
<TableHeader | ||||||
headers={[ | ||||||
{ name: t('Bank Account To Sync'), width: 200 }, | ||||||
{ name: t('Balance'), width: 80 }, | ||||||
{ name: t('Account in Actual'), width: 'flex' }, | ||||||
{ name: t('Actions'), width: 'flex' }, | ||||||
]} | ||||||
/> | ||||||
|
||||||
<TableHeader> | ||||||
<Field width="200"> | ||||||
<Trans>Bank Account To Sync</Trans> | ||||||
</Field> | ||||||
<Field width="80"> | ||||||
<Trans>Balance</Trans> | ||||||
</Field> | ||||||
<Field width="flex"> | ||||||
<Trans>Account in Actual</Trans> | ||||||
</Field> | ||||||
<Field width="flex"> | ||||||
<Trans>Actions</Trans> | ||||||
</Field> | ||||||
</TableHeader> | ||||||
<Table | ||||||
items={externalAccounts} | ||||||
style={{ backgroundColor: theme.tableHeaderBackground }} | ||||||
getItemKey={index => index} | ||||||
renderItem={({ key, item }) => ( | ||||||
<View key={key}> | ||||||
getItemKey={index => externalAccounts[index].id} | ||||||
renderItem={({ item }) => ( | ||||||
<View key={item.id}> | ||||||
<TableRow | ||||||
externalAccount={item} | ||||||
chosenAccount={ | ||||||
chosenAccounts[item.account_id] === | ||||||
addOnBudgetAccountOption.id | ||||||
chosenAccounts[item.id] === addOnBudgetAccountOption.id | ||||||
? addOnBudgetAccountOption | ||||||
: chosenAccounts[item.account_id] === | ||||||
: chosenAccounts[item.id] === | ||||||
addOffBudgetAccountOption.id | ||||||
? addOffBudgetAccountOption | ||||||
: localAccounts.find( | ||||||
acc => chosenAccounts[item.account_id] === acc.id, | ||||||
acc => chosenAccounts[item.id] === acc.id, | ||||||
) | ||||||
} | ||||||
unlinkedAccounts={unlinkedAccounts} | ||||||
|
@@ -195,22 +233,39 @@ export function SelectLinkedAccountsModal({ | |||||
); | ||||||
} | ||||||
|
||||||
type TableRowProps = { | ||||||
externalAccount: NormalizedAccount; | ||||||
chosenAccount: LinkAccountOption | undefined; | ||||||
unlinkedAccounts: LinkAccountOption[]; | ||||||
onSetLinkedAccount: ( | ||||||
account: NormalizedAccount, | ||||||
localAccountId: string | undefined, | ||||||
) => void; | ||||||
}; | ||||||
|
||||||
function TableRow({ | ||||||
externalAccount, | ||||||
chosenAccount, | ||||||
unlinkedAccounts, | ||||||
onSetLinkedAccount, | ||||||
}) { | ||||||
}: TableRowProps) { | ||||||
const { t } = useTranslation(); | ||||||
const [focusedField, setFocusedField] = useState(null); | ||||||
const [focusedField, setFocusedField] = useState<string | null>(null); | ||||||
|
||||||
const availableAccountOptions = [ | ||||||
const availableAccountOptions: LinkAccountOption[] = [ | ||||||
...unlinkedAccounts, | ||||||
chosenAccount?.id !== addOnBudgetAccountOption.id && chosenAccount, | ||||||
addOnBudgetAccountOption, | ||||||
addOffBudgetAccountOption, | ||||||
].filter(Boolean); | ||||||
|
||||||
if ( | ||||||
chosenAccount && | ||||||
chosenAccount.id !== addOnBudgetAccountOption.id && | ||||||
chosenAccount.id !== addOffBudgetAccountOption.id | ||||||
) { | ||||||
availableAccountOptions.push(chosenAccount); | ||||||
} | ||||||
|
||||||
return ( | ||||||
<Row style={{ backgroundColor: theme.tableBackground }}> | ||||||
<Field width={200}>{externalAccount.name}</Field> | ||||||
|
@@ -228,7 +283,7 @@ function TableRow({ | |||||
strict | ||||||
highlightFirst | ||||||
suggestions={availableAccountOptions} | ||||||
onSelect={value => { | ||||||
onSelect={(value: string | undefined) => { | ||||||
onSetLinkedAccount(externalAccount, value); | ||||||
}} | ||||||
inputProps={{ | ||||||
|
@@ -244,7 +299,7 @@ function TableRow({ | |||||
{chosenAccount ? ( | ||||||
<Button | ||||||
onPress={() => { | ||||||
onSetLinkedAccount(externalAccount, null); | ||||||
onSetLinkedAccount(externalAccount, undefined); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the other change to make the second argument optional
Suggested change
|
||||||
}} | ||||||
style={{ float: 'right' }} | ||||||
> | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
category: Maintenance | ||
authors: [dkhalife] | ||
--- | ||
|
||
Migrate SelectLinkedAccountsModal to TypeScript |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this use types from AccountEntity?