From 3738bde9c3d0e811ae5a3b7007ee2c0e4773dec9 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 11:55:53 -0400
Subject: [PATCH 01/10] add duplicate name validation to sidebar, and both
narrow/wide Account pages
---
.../src/components/accounts/Account.tsx | 20 +-
.../src/components/accounts/Header.jsx | 235 ++++++++++--------
.../mobile/accounts/AccountTransactions.tsx | 1 +
.../components/modals/AccountMenuModal.tsx | 39 ++-
.../modals/CreateLocalAccountModal.tsx | 43 +++-
.../src/components/sidebar/Account.tsx | 10 -
.../src/components/util/accountValidation.ts | 23 ++
7 files changed, 238 insertions(+), 133 deletions(-)
create mode 100644 packages/desktop-client/src/components/util/accountValidation.ts
diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx
index 05e30623b77..1c8d20bdbbb 100644
--- a/packages/desktop-client/src/components/accounts/Account.tsx
+++ b/packages/desktop-client/src/components/accounts/Account.tsx
@@ -69,6 +69,7 @@ import { Button } from '../common/Button2';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { TransactionList } from '../transactions/TransactionList';
+import { validateAccountName } from '../util/accountValidation';
import { AccountHeader } from './Header';
@@ -296,6 +297,7 @@ type AccountInternalState = {
prevShowCleared?: boolean;
showReconciled: boolean;
editingName: boolean;
+ nameError: string;
isAdding: boolean;
modalShowing?: boolean;
sort: {
@@ -343,6 +345,7 @@ class AccountInternal extends PureComponent<
showCleared: props.showCleared,
showReconciled: props.showReconciled,
editingName: false,
+ nameError: null,
isAdding: false,
sort: null,
filteredAmount: null,
@@ -703,13 +706,19 @@ class AccountInternal extends PureComponent<
};
onSaveName = (name: string) => {
- if (name.trim().length) {
- const accountId = this.props.accountId;
+ const accountNameError = validateAccountName(
+ name,
+ this.props.accountId,
+ this.props.accounts,
+ );
+ if (accountNameError) {
+ this.setState({ nameError: accountNameError });
+ } else {
const account = this.props.accounts.find(
- account => account.id === accountId,
- )!;
+ account => account.id === this.props.accountId,
+ );
this.props.updateAccount({ ...account, name });
- this.setState({ editingName: false });
+ this.setState({ editingName: false, nameError: null });
}
};
@@ -1704,6 +1713,7 @@ class AccountInternal extends PureComponent<
onAddTransaction={this.onAddTransaction}
onToggleExtraBalances={this.onToggleExtraBalances}
onSaveName={this.onSaveName}
+ saveNameError={this.state.nameError}
onExposeName={this.onExposeName}
onReconcile={this.onReconcile}
onDoneReconciling={this.onDoneReconciling}
diff --git a/packages/desktop-client/src/components/accounts/Header.jsx b/packages/desktop-client/src/components/accounts/Header.jsx
index f8aa6b492d8..950d29f1b3a 100644
--- a/packages/desktop-client/src/components/accounts/Header.jsx
+++ b/packages/desktop-client/src/components/accounts/Header.jsx
@@ -1,6 +1,8 @@
-import React, { useState, useRef } from 'react';
+import React, { useState, useRef, Fragment } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
-import { Trans, useTranslation } from 'react-i18next';
+import { Trans } from 'react-i18next';
+
+import { t } from 'i18next';
import { useLocalPref } from '../../hooks/useLocalPref';
import { useSplitsExpanded } from '../../hooks/useSplitsExpanded';
@@ -14,6 +16,7 @@ import {
SvgPencil1,
} from '../../icons/v2';
import { theme, styles } from '../../style';
+import { Warning } from '../alerts';
import { AnimatedRefresh } from '../AnimatedRefresh';
import { Button } from '../common/Button2';
import { InitialFocus } from '../common/InitialFocus';
@@ -67,6 +70,7 @@ export function AccountHeader({
onCreateReconciliationTransaction,
onToggleExtraBalances,
onSaveName,
+ saveNameError,
onExposeName,
onSync,
onImport,
@@ -89,8 +93,6 @@ export function AccountHeader({
onMakeAsSplitTransaction,
onMakeAsNonSplitTransactions,
}) {
- const { t } = useTranslation();
-
const [menuOpen, setMenuOpen] = useState(false);
const searchInput = useRef(null);
const triggerRef = useRef(null);
@@ -176,99 +178,21 @@ export function AccountHeader({
}}
>
{!!account?.bank && (
-
)}
- {editingName ? (
-
- onSaveName(e.target.value)}
- onBlur={e => onSaveName(e.target.value)}
- onEscape={() => onExposeName(false)}
- style={{
- fontSize: 25,
- fontWeight: 500,
- marginTop: -3,
- marginBottom: -4,
- marginLeft: -6,
- paddingTop: 2,
- paddingBottom: 2,
- width: Math.max(20, accountName.length) + 'ch',
- }}
- />
-
- ) : isNameEditable ? (
-
-
- {account && account.closed
- ? t('Closed: {{ accountName }}', { accountName })
- : accountName}
-
-
- {account && (
-
- )}
-
-
- ) : (
-
- {account && account.closed
- ? t('Closed: {{ accountName }}', { accountName })
- : accountName}
-
- )}
+
@@ -471,6 +395,125 @@ export function AccountHeader({
);
}
+function AccountSyncSidebar({ account, failedAccounts, accountsSyncing }) {
+ return (
+
+ );
+}
+
+function AccountNameField({
+ account,
+ accountName,
+ isNameEditable,
+ editingName,
+ saveNameError,
+ onSaveName,
+ onExposeName,
+}) {
+ if (editingName) {
+ return (
+
+
+ onSaveName(e.target.value)}
+ onBlur={e => onSaveName(e.target.value)}
+ onEscape={() => onExposeName(false)}
+ style={{
+ fontSize: 25,
+ fontWeight: 500,
+ marginTop: -3,
+ marginBottom: -4,
+ marginLeft: -6,
+ paddingTop: 2,
+ paddingBottom: 2,
+ width: Math.max(20, accountName.length) + 'ch',
+ }}
+ />
+
+ {saveNameError && {saveNameError}}
+
+ );
+ } else {
+ if (isNameEditable) {
+ return (
+
+
+ {account && account.closed
+ ? t('Closed: {{ accountName }}', { accountName })
+ : accountName}
+
+
+ {account && (
+
+ )}
+
+
+ );
+ } else {
+ return (
+
+ {account && account.closed
+ ? t('Closed: {{ accountName }}', { accountName })
+ : accountName}
+
+ );
+ }
+ }
+}
+
function AccountMenu({
account,
canSync,
@@ -483,8 +526,6 @@ function AccountMenu({
onReconcile,
onMenuSelect,
}) {
- const { t } = useTranslation();
-
const [tooltip, setTooltip] = useState('default');
const syncServerStatus = useSyncServerStatus();
diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
index eaf49332fb9..453212917d7 100644
--- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
+++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
@@ -43,6 +43,7 @@ import { Button } from '../../common/Button2';
import { Text } from '../../common/Text';
import { View } from '../../common/View';
import { MobilePageHeader, Page } from '../../Page';
+import { validateAccountName } from '../../util/accountValidation';
import { MobileBackButton } from '../MobileBackButton';
import { AddTransactionButton } from '../transactions/AddTransactionButton';
import { TransactionListWithBalances } from '../transactions/TransactionListWithBalances';
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index f07a6915c44..7106dc5bc7f 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -1,8 +1,9 @@
-import React, { type ComponentProps, useRef, useState } from 'react';
+import React, { type ComponentProps, Fragment, useRef, useState } from 'react';
import { type AccountEntity } from 'loot-core/types/models';
import { useAccount } from '../../hooks/useAccount';
+import { useAccounts } from '../../hooks/useAccounts';
import { useNotes } from '../../hooks/useNotes';
import { SvgClose, SvgDotsHorizontalTriple, SvgLockOpen } from '../../icons/v1';
import { SvgNotesPaper } from '../../icons/v2';
@@ -18,6 +19,7 @@ import {
import { Popover } from '../common/Popover';
import { View } from '../common/View';
import { Notes } from '../Notes';
+import { validateAccountName } from '../util/accountValidation';
type AccountMenuModalProps = {
accountId: string;
@@ -37,7 +39,9 @@ export function AccountMenuModal({
onClose,
}: AccountMenuModalProps) {
const account = useAccount(accountId);
+ const accounts = useAccounts();
const originalNotes = useNotes(`account-${accountId}`);
+ const [accountNameError, setAccountNameError] = useState(null);
const onRename = (newName: string) => {
if (!account) {
@@ -45,10 +49,20 @@ export function AccountMenuModal({
}
if (newName !== account.name) {
- onSave?.({
- ...account,
- name: newName,
- });
+ const renameAccountError = validateAccountName(
+ newName,
+ accountId,
+ accounts,
+ );
+ if (renameAccountError) {
+ setAccountNameError(renameAccountError);
+ } else {
+ setAccountNameError(null);
+ onSave?.({
+ ...account,
+ name: newName,
+ });
+ }
}
};
@@ -93,11 +107,16 @@ export function AccountMenuModal({
/>
}
title={
-
+
+
+
+ {accountNameError}
+
+
}
rightContent={}
/>
diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
index 7c737d30615..2f46b080091 100644
--- a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
+++ b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
@@ -1,11 +1,14 @@
// @ts-strict-ignore
-import React, { type FormEvent, useState } from 'react';
+import { type FormEvent, useState } from 'react';
import { Form } from 'react-aria-components';
+import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { closeModal, createAccount } from 'loot-core/client/actions';
import { toRelaxedNumber } from 'loot-core/src/shared/util';
+import { type AccountEntity } from 'loot-core/types/models';
+import * as useAccounts from '../../hooks/useAccounts';
import { useNavigate } from '../../hooks/useNavigate';
import { theme } from '../../style';
import { Button } from '../common/Button2';
@@ -25,23 +28,46 @@ import { Text } from '../common/Text';
import { View } from '../common/View';
import { Checkbox } from '../forms';
+function accountNameIsInUse(accountName: string, accounts: AccountEntity[]) {
+ return accounts.map(a => a.name).includes(accountName);
+}
+
export function CreateLocalAccountModal() {
const navigate = useNavigate();
const dispatch = useDispatch();
+ const accounts = useAccounts.useAccounts();
const [name, setName] = useState('');
const [offbudget, setOffbudget] = useState(false);
const [balance, setBalance] = useState('0');
- const [nameError, setNameError] = useState(false);
+ const [nameError, setNameError] = useState(null);
const [balanceError, setBalanceError] = useState(false);
const validateBalance = balance => !isNaN(parseFloat(balance));
+ const { t } = useTranslation();
+
+ const validateName = (name: string): boolean => {
+ if (!name) {
+ setNameError(t('Name is required'));
+ return false;
+ } else if (accountNameIsInUse(name, accounts)) {
+ setNameError(t('Name {{ name }} must be unique.', { name }));
+ return false;
+ }
+ return true;
+ };
+
+ const validateAndSetName = (name: string) => {
+ if (validateName(name)) {
+ setName(name);
+ setNameError(null);
+ }
+ };
const onSubmit = async (event: FormEvent) => {
event.preventDefault();
- const nameError = !name;
- setNameError(nameError);
+ validateName(name);
const balanceError = !validateBalance(balance);
setBalanceError(balanceError);
@@ -72,19 +98,14 @@ export function CreateLocalAccountModal() {
onChange={event => setName(event.target.value)}
onBlur={event => {
const name = event.target.value.trim();
- setName(name);
- if (name && nameError) {
- setNameError(false);
- }
+ validateAndSetName(name);
}}
style={{ flex: 1 }}
/>
{nameError && (
-
- Name is required
-
+ {nameError}
)}
>({
padding: 10,
}}
>
-
- {name}
-
{accountNote && (
({
diff --git a/packages/desktop-client/src/components/util/accountValidation.ts b/packages/desktop-client/src/components/util/accountValidation.ts
new file mode 100644
index 00000000000..ddbb25c8269
--- /dev/null
+++ b/packages/desktop-client/src/components/util/accountValidation.ts
@@ -0,0 +1,23 @@
+import { t } from 'i18next';
+
+import { type AccountEntity } from 'loot-core/types/models';
+
+export function validateAccountName(
+ newAccountName: string,
+ accountId: string,
+ accounts: AccountEntity[],
+) {
+ newAccountName = newAccountName.trim();
+ if (newAccountName.length) {
+ const duplicateNamedAccounts = accounts.filter(
+ account => account.name === newAccountName && account.id !== accountId,
+ );
+ if (duplicateNamedAccounts.length) {
+ return t('Name {{ newAccountName }} must be unique.', { newAccountName });
+ } else {
+ return null;
+ }
+ } else {
+ return t('Name cannot be blank.');
+ }
+}
From 8c8783716c5e228aa1c46aa1d4f3258b79185b88 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 12:31:49 -0400
Subject: [PATCH 02/10] unify error messages and styles
---
.../desktop-client/src/components/accounts/Header.jsx | 5 +++--
.../components/mobile/accounts/AccountTransactions.tsx | 1 -
.../src/components/modals/CreateLocalAccountModal.tsx | 10 ++++++++--
.../desktop-client/src/components/sidebar/Account.tsx | 10 ++++++++++
4 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/packages/desktop-client/src/components/accounts/Header.jsx b/packages/desktop-client/src/components/accounts/Header.jsx
index 950d29f1b3a..657a8a408bf 100644
--- a/packages/desktop-client/src/components/accounts/Header.jsx
+++ b/packages/desktop-client/src/components/accounts/Header.jsx
@@ -16,7 +16,6 @@ import {
SvgPencil1,
} from '../../icons/v2';
import { theme, styles } from '../../style';
-import { Warning } from '../alerts';
import { AnimatedRefresh } from '../AnimatedRefresh';
import { Button } from '../common/Button2';
import { InitialFocus } from '../common/InitialFocus';
@@ -443,7 +442,9 @@ function AccountNameField({
}}
/>
- {saveNameError && {saveNameError}}
+ {saveNameError && (
+ {saveNameError}
+ )}
);
} else {
diff --git a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
index 453212917d7..eaf49332fb9 100644
--- a/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
+++ b/packages/desktop-client/src/components/mobile/accounts/AccountTransactions.tsx
@@ -43,7 +43,6 @@ import { Button } from '../../common/Button2';
import { Text } from '../../common/Text';
import { View } from '../../common/View';
import { MobilePageHeader, Page } from '../../Page';
-import { validateAccountName } from '../../util/accountValidation';
import { MobileBackButton } from '../MobileBackButton';
import { AddTransactionButton } from '../transactions/AddTransactionButton';
import { TransactionListWithBalances } from '../transactions/TransactionListWithBalances';
diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
index 2f46b080091..0dafff3084c 100644
--- a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
+++ b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
@@ -27,6 +27,7 @@ import {
import { Text } from '../common/Text';
import { View } from '../common/View';
import { Checkbox } from '../forms';
+import { validateAccountName } from '../util/accountValidation';
function accountNameIsInUse(accountName: string, accounts: AccountEntity[]) {
return accounts.map(a => a.name).includes(accountName);
@@ -58,7 +59,10 @@ export function CreateLocalAccountModal() {
};
const validateAndSetName = (name: string) => {
- if (validateName(name)) {
+ const nameError = validateAccountName(name, '', accounts);
+ if (nameError) {
+ setNameError(nameError);
+ } else {
setName(name);
setNameError(null);
}
@@ -105,7 +109,9 @@ export function CreateLocalAccountModal() {
{nameError && (
- {nameError}
+
+ {nameError}
+
)}
>({
padding: 10,
}}
>
+
+ {name}
+
{accountNote && (
({
From a54c1c93670e2d8a0ec7965f8ed9fe67c3fac308 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 12:47:06 -0400
Subject: [PATCH 03/10] keep state for the current account name on the mobile
account modal
---
.../desktop-client/src/components/modals/AccountMenuModal.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index 7106dc5bc7f..afe68897663 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -42,8 +42,10 @@ export function AccountMenuModal({
const accounts = useAccounts();
const originalNotes = useNotes(`account-${accountId}`);
const [accountNameError, setAccountNameError] = useState(null);
+ const [currentAccountName, setCurrentAccountName] = useState(account.name);
const onRename = (newName: string) => {
+ setCurrentAccountName(newName);
if (!account) {
return;
}
@@ -110,7 +112,7 @@ export function AccountMenuModal({
From c1011f176d8998a0b23edc1782cb72c4a17b706b Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 12:52:41 -0400
Subject: [PATCH 04/10] add release notes
---
upcoming-release-notes/3527.md | 6 ++++++
1 file changed, 6 insertions(+)
create mode 100644 upcoming-release-notes/3527.md
diff --git a/upcoming-release-notes/3527.md b/upcoming-release-notes/3527.md
new file mode 100644
index 00000000000..6e476c8276d
--- /dev/null
+++ b/upcoming-release-notes/3527.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [qedi-r]
+---
+
+Updates UI to disallow non-unique account names.
From 8a6b13e65644f385681576d62f9a07733a6baafe Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 13:13:57 -0400
Subject: [PATCH 05/10] remove extra validation function
---
.../modals/CreateLocalAccountModal.tsx | 20 +------------------
1 file changed, 1 insertion(+), 19 deletions(-)
diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
index 0dafff3084c..9b93c54a37c 100644
--- a/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
+++ b/packages/desktop-client/src/components/modals/CreateLocalAccountModal.tsx
@@ -1,12 +1,10 @@
// @ts-strict-ignore
import { type FormEvent, useState } from 'react';
import { Form } from 'react-aria-components';
-import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { closeModal, createAccount } from 'loot-core/client/actions';
import { toRelaxedNumber } from 'loot-core/src/shared/util';
-import { type AccountEntity } from 'loot-core/types/models';
import * as useAccounts from '../../hooks/useAccounts';
import { useNavigate } from '../../hooks/useNavigate';
@@ -29,10 +27,6 @@ import { View } from '../common/View';
import { Checkbox } from '../forms';
import { validateAccountName } from '../util/accountValidation';
-function accountNameIsInUse(accountName: string, accounts: AccountEntity[]) {
- return accounts.map(a => a.name).includes(accountName);
-}
-
export function CreateLocalAccountModal() {
const navigate = useNavigate();
const dispatch = useDispatch();
@@ -45,18 +39,6 @@ export function CreateLocalAccountModal() {
const [balanceError, setBalanceError] = useState(false);
const validateBalance = balance => !isNaN(parseFloat(balance));
- const { t } = useTranslation();
-
- const validateName = (name: string): boolean => {
- if (!name) {
- setNameError(t('Name is required'));
- return false;
- } else if (accountNameIsInUse(name, accounts)) {
- setNameError(t('Name {{ name }} must be unique.', { name }));
- return false;
- }
- return true;
- };
const validateAndSetName = (name: string) => {
const nameError = validateAccountName(name, '', accounts);
@@ -71,7 +53,7 @@ export function CreateLocalAccountModal() {
const onSubmit = async (event: FormEvent) => {
event.preventDefault();
- validateName(name);
+ const nameError = validateAccountName(name, '', accounts);
const balanceError = !validateBalance(balance);
setBalanceError(balanceError);
From 4d7a0f770d2d5da3ff8b6c4f9c91f8900f866166 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 13:16:19 -0400
Subject: [PATCH 06/10] fix typo in AccountSyncSidebar params
---
packages/desktop-client/src/components/accounts/Header.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/desktop-client/src/components/accounts/Header.jsx b/packages/desktop-client/src/components/accounts/Header.jsx
index 657a8a408bf..ef8ce343621 100644
--- a/packages/desktop-client/src/components/accounts/Header.jsx
+++ b/packages/desktop-client/src/components/accounts/Header.jsx
@@ -179,7 +179,7 @@ export function AccountHeader({
{!!account?.bank && (
)}
From 7a3529d73d3c378417513677f4bbecca86b374df Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 13:33:12 -0400
Subject: [PATCH 07/10] don't set current account name to empty string and
prevent further changes on AccountMenuModal
---
.../src/components/accounts/Account.tsx | 4 ++--
.../components/modals/AccountMenuModal.tsx | 24 +++++++++++++------
.../src/components/util/accountValidation.ts | 4 ++--
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/packages/desktop-client/src/components/accounts/Account.tsx b/packages/desktop-client/src/components/accounts/Account.tsx
index 1c8d20bdbbb..f06f6fb83d0 100644
--- a/packages/desktop-client/src/components/accounts/Account.tsx
+++ b/packages/desktop-client/src/components/accounts/Account.tsx
@@ -345,7 +345,7 @@ class AccountInternal extends PureComponent<
showCleared: props.showCleared,
showReconciled: props.showReconciled,
editingName: false,
- nameError: null,
+ nameError: '',
isAdding: false,
sort: null,
filteredAmount: null,
@@ -718,7 +718,7 @@ class AccountInternal extends PureComponent<
account => account.id === this.props.accountId,
);
this.props.updateAccount({ ...account, name });
- this.setState({ editingName: false, nameError: null });
+ this.setState({ editingName: false, nameError: '' });
}
};
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index afe68897663..0737cf30b1f 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -20,6 +20,7 @@ import { Popover } from '../common/Popover';
import { View } from '../common/View';
import { Notes } from '../Notes';
import { validateAccountName } from '../util/accountValidation';
+import { t } from 'i18next';
type AccountMenuModalProps = {
accountId: string;
@@ -41,14 +42,21 @@ export function AccountMenuModal({
const account = useAccount(accountId);
const accounts = useAccounts();
const originalNotes = useNotes(`account-${accountId}`);
- const [accountNameError, setAccountNameError] = useState(null);
- const [currentAccountName, setCurrentAccountName] = useState(account.name);
+ const [accountNameError, setAccountNameError] = useState('');
+ const [currentAccountName, setCurrentAccountName] = useState(
+ account?.name || t('New Account'),
+ );
const onRename = (newName: string) => {
- setCurrentAccountName(newName);
+ newName = newName.trim();
if (!account) {
return;
}
+ if (!newName) {
+ setCurrentAccountName(t('Account'));
+ } else {
+ setCurrentAccountName(newName);
+ }
if (newName !== account.name) {
const renameAccountError = validateAccountName(
@@ -59,7 +67,7 @@ export function AccountMenuModal({
if (renameAccountError) {
setAccountNameError(renameAccountError);
} else {
- setAccountNameError(null);
+ setAccountNameError('');
onSave?.({
...account,
name: newName,
@@ -115,9 +123,11 @@ export function AccountMenuModal({
title={currentAccountName}
onTitleUpdate={onRename}
/>
-
- {accountNameError}
-
+ {accountNameError && (
+
+ {accountNameError}
+
+ )}
}
rightContent={}
diff --git a/packages/desktop-client/src/components/util/accountValidation.ts b/packages/desktop-client/src/components/util/accountValidation.ts
index ddbb25c8269..f825e4b796f 100644
--- a/packages/desktop-client/src/components/util/accountValidation.ts
+++ b/packages/desktop-client/src/components/util/accountValidation.ts
@@ -6,7 +6,7 @@ export function validateAccountName(
newAccountName: string,
accountId: string,
accounts: AccountEntity[],
-) {
+): string {
newAccountName = newAccountName.trim();
if (newAccountName.length) {
const duplicateNamedAccounts = accounts.filter(
@@ -15,7 +15,7 @@ export function validateAccountName(
if (duplicateNamedAccounts.length) {
return t('Name {{ newAccountName }} must be unique.', { newAccountName });
} else {
- return null;
+ return '';
}
} else {
return t('Name cannot be blank.');
From a52d1730a65c61ae6bea4ae10a20159e7c4fa4b4 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Sat, 28 Sep 2024 13:39:20 -0400
Subject: [PATCH 08/10] lint imports
---
.../desktop-client/src/components/modals/AccountMenuModal.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index 0737cf30b1f..8dbfa8b720d 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -1,5 +1,7 @@
import React, { type ComponentProps, Fragment, useRef, useState } from 'react';
+import { t } from 'i18next';
+
import { type AccountEntity } from 'loot-core/types/models';
import { useAccount } from '../../hooks/useAccount';
@@ -20,7 +22,6 @@ import { Popover } from '../common/Popover';
import { View } from '../common/View';
import { Notes } from '../Notes';
import { validateAccountName } from '../util/accountValidation';
-import { t } from 'i18next';
type AccountMenuModalProps = {
accountId: string;
From b6df9c57f756373f734ac62cc41d53e11c11a2f8 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Thu, 3 Oct 2024 08:23:56 +0200
Subject: [PATCH 09/10] update error message to Name x already exists
---
.../desktop-client/src/components/util/accountValidation.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/desktop-client/src/components/util/accountValidation.ts b/packages/desktop-client/src/components/util/accountValidation.ts
index f825e4b796f..86ec7fe94b6 100644
--- a/packages/desktop-client/src/components/util/accountValidation.ts
+++ b/packages/desktop-client/src/components/util/accountValidation.ts
@@ -13,7 +13,7 @@ export function validateAccountName(
account => account.name === newAccountName && account.id !== accountId,
);
if (duplicateNamedAccounts.length) {
- return t('Name {{ newAccountName }} must be unique.', { newAccountName });
+ return t('Name {{ newAccountName }} already exists.', { newAccountName });
} else {
return '';
}
From 67d17190888c2ff02c62cfb0f527a76fe013a156 Mon Sep 17 00:00:00 2001
From: qedi-r <1435081+qedi-r@users.noreply.github.com>
Date: Thu, 10 Oct 2024 18:01:16 -0400
Subject: [PATCH 10/10] use proper translation functions
---
.../desktop-client/src/components/accounts/Header.jsx | 8 +++++---
.../src/components/modals/AccountMenuModal.tsx | 6 +++---
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/packages/desktop-client/src/components/accounts/Header.jsx b/packages/desktop-client/src/components/accounts/Header.jsx
index ef8ce343621..24e9fc6dc66 100644
--- a/packages/desktop-client/src/components/accounts/Header.jsx
+++ b/packages/desktop-client/src/components/accounts/Header.jsx
@@ -1,8 +1,6 @@
import React, { useState, useRef, Fragment } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
-import { Trans } from 'react-i18next';
-
-import { t } from 'i18next';
+import { Trans, useTranslation } from 'react-i18next';
import { useLocalPref } from '../../hooks/useLocalPref';
import { useSplitsExpanded } from '../../hooks/useSplitsExpanded';
@@ -92,6 +90,7 @@ export function AccountHeader({
onMakeAsSplitTransaction,
onMakeAsNonSplitTransactions,
}) {
+ const { t } = useTranslation();
const [menuOpen, setMenuOpen] = useState(false);
const searchInput = useRef(null);
const triggerRef = useRef(null);
@@ -421,6 +420,8 @@ function AccountNameField({
onSaveName,
onExposeName,
}) {
+ const { t } = useTranslation();
+
if (editingName) {
return (
@@ -527,6 +528,7 @@ function AccountMenu({
onReconcile,
onMenuSelect,
}) {
+ const { t } = useTranslation();
const [tooltip, setTooltip] = useState('default');
const syncServerStatus = useSyncServerStatus();
diff --git a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
index 8dbfa8b720d..c65fea01f3b 100644
--- a/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
+++ b/packages/desktop-client/src/components/modals/AccountMenuModal.tsx
@@ -1,6 +1,5 @@
-import React, { type ComponentProps, Fragment, useRef, useState } from 'react';
-
-import { t } from 'i18next';
+import { type ComponentProps, Fragment, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import { type AccountEntity } from 'loot-core/types/models';
@@ -40,6 +39,7 @@ export function AccountMenuModal({
onEditNotes,
onClose,
}: AccountMenuModalProps) {
+ const { t } = useTranslation();
const account = useAccount(accountId);
const accounts = useAccounts();
const originalNotes = useNotes(`account-${accountId}`);