feat(transactions): refine new transaction form layout and balance toggle (#597)

## What

Three tweaks to the **Add Transaction** form:

1. **Account selector moved to the top** of the create form, above the
date field.
2. **"Update account balance" checkbox hidden for connected accounts**
(accounts with a `banking_connection_id`). Their balance is kept in sync
by the banking connection, so the submit also skips the balance update
for them.
3. **Amount placeholder** now shows a negative example `-25.00` instead
of `0.00`.

## Testing

- Added a unit test covering that the checkbox is hidden for a connected
account.
- `bun run test edit-transaction-dialog` — 5 passed.
- `bun run format` / `bun run lint` clean (only a pre-existing warning
in `chart.tsx`).
This commit is contained in:
Víctor Falcón 2026-06-26 19:59:44 +02:00 committed by GitHub
parent 934d834ab3
commit d6ec9830df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 88 additions and 55 deletions

View File

@ -222,9 +222,35 @@ describe('EditTransactionDialog', () => {
onOpenChange={vi.fn()}
onSuccess={vi.fn()}
mode="create"
initialAccountId="account-1"
/>,
);
expect(screen.getByRole('checkbox')).toBeChecked();
});
it('hides "update account balance" for a connected account', () => {
const connectedAccount = {
...checkingAccount,
id: 'account-connected',
banking_connection_id: 'connection-1',
};
render(
<EditTransactionDialog
transaction={null}
categories={[]}
accounts={[connectedAccount]}
banks={[]}
labels={[]}
open
onOpenChange={vi.fn()}
onSuccess={vi.fn()}
mode="create"
initialAccountId="account-connected"
/>,
);
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
});
});

View File

@ -354,7 +354,11 @@ export function EditTransactionDialog({
? finalLabelIds
: undefined,
},
{ updateBalance: updateAccountBalance },
{
updateBalance: selectedAccount.banking_connection_id
? false
: updateAccountBalance,
},
);
const updatedCategory = finalCategoryId
@ -523,6 +527,42 @@ export function EditTransactionDialog({
<form onSubmit={handleSubmit}>
<div className="space-y-4 py-4">
{mode === 'create' && (
<div className="space-y-2">
<FormLabel htmlFor="account">
{__('Account')}
</FormLabel>
<Select
value={accountId}
onValueChange={setAccountId}
disabled={isSubmitting}
>
<SelectTrigger
id="account"
data-testid="account-select"
>
<SelectValue
placeholder={__('Select account')}
/>
</SelectTrigger>
<SelectContent>
{transactionalAccounts.map(
(account) => (
<SelectItem
key={account.id}
value={String(account.id)}
>
{decryptedAccountNames.get(
account.id,
) || __('[Loading...]')}
</SelectItem>
),
)}
</SelectContent>
</Select>
</div>
)}
<div className="space-y-2">
<FormLabel
htmlFor="date"
@ -683,29 +723,32 @@ export function EditTransactionDialog({
selectedAccount?.currency_code ||
'USD'
}
placeholder="-25.00"
disabled={isSubmitting}
required
/>
<div className="flex items-center gap-2">
<Checkbox
id="update-balance"
checked={updateAccountBalance}
onCheckedChange={(checked) =>
handleUpdateBalanceChange(
checked === true,
)
}
disabled={isSubmitting}
/>
{!selectedAccount?.banking_connection_id && (
<div className="flex items-center gap-2">
<Checkbox
id="update-balance"
checked={updateAccountBalance}
onCheckedChange={(checked) =>
handleUpdateBalanceChange(
checked === true,
)
}
disabled={isSubmitting}
/>
<FormLabel
htmlFor="update-balance"
className="cursor-pointer font-normal"
>
{__('Update account balance')}
</FormLabel>
</div>
<FormLabel
htmlFor="update-balance"
className="cursor-pointer font-normal"
>
{__('Update account balance')}
</FormLabel>
</div>
)}
</>
) : (
<div className="text-sm font-medium">
@ -720,42 +763,6 @@ export function EditTransactionDialog({
)}
</div>
{mode === 'create' && (
<div className="space-y-2">
<FormLabel htmlFor="account">
{__('Account')}
</FormLabel>
<Select
value={accountId}
onValueChange={setAccountId}
disabled={isSubmitting}
>
<SelectTrigger
id="account"
data-testid="account-select"
>
<SelectValue
placeholder={__('Select account')}
/>
</SelectTrigger>
<SelectContent>
{transactionalAccounts.map(
(account) => (
<SelectItem
key={account.id}
value={String(account.id)}
>
{decryptedAccountNames.get(
account.id,
) || __('[Loading...]')}
</SelectItem>
),
)}
</SelectContent>
</Select>
</div>
)}
<div className="space-y-2">
<FormLabel htmlFor="category">
{__('Category')}