Skip to content

Commit 9107241

Browse files
committed
feat: form to manage accounts
1 parent 8a9d6dd commit 9107241

File tree

1 file changed

+151
-39
lines changed

1 file changed

+151
-39
lines changed

electron/renderer/components/sidebar/sidebar-item-accounts.tsx

+151-39
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,100 @@ import {
55
EuiCallOut,
66
EuiCode,
77
EuiConfirmModal,
8+
EuiFieldPassword,
9+
EuiFieldText,
810
EuiFlexGroup,
911
EuiFlexItem,
12+
EuiForm,
13+
EuiFormRow,
1014
EuiInMemoryTable,
1115
EuiPanel,
1216
EuiSpacer,
1317
EuiToolTip,
1418
} from '@elastic/eui';
15-
import {
16-
type ReactNode,
17-
useCallback,
18-
useEffect,
19-
useMemo,
20-
useState,
21-
} from 'react';
22-
import type { Maybe } from '../../../common/types.js';
19+
import type { ReactNode } from 'react';
20+
import type React from 'react';
21+
import { useCallback, useEffect, useMemo, useState } from 'react';
22+
import { isBlank } from '../../../common/string/is-blank.js';
2323
import { runInBackground } from '../../lib/async/run-in-background.js';
2424

2525
interface TableRowItem {
2626
accountName: string;
2727
}
2828

29-
export const SidebarItemAccounts: React.FC = (): ReactNode => {
30-
// When displaying models to add, edit, or remove an account,
31-
// this record indicates the contextual record from the table.
32-
const [record, setRecord] = useState<Maybe<TableRowItem>>(undefined);
29+
interface FormRecord {
30+
accountName?: string;
31+
accountPassword?: string;
32+
}
33+
34+
interface FormErrors {
35+
accountName?: string;
36+
accountPassword?: string;
37+
}
3338

39+
export const SidebarItemAccounts: React.FC = (): ReactNode => {
3440
const [showAddAccountModal, setShowAddAccountModal] = useState(false);
3541
const [showEditAccountModal, setShowEditAccountModal] = useState(false);
3642
const [showRemoveAccountModal, setShowRemoveAccountModal] = useState(false);
3743

44+
// All the table row items (accounts) to display.
3845
const [tableRowItems, setTableRowItems] = useState<Array<TableRowItem>>([]);
3946

47+
// The contextual form record (account) for the current action.
48+
const [formRecord, setFormRecord] = useState<FormRecord>({});
49+
const [formErrors, setFormErrors] = useState<FormErrors>({});
50+
51+
const validateAccountName = useCallback(() => {
52+
if (isBlank(formRecord.accountName)) {
53+
setFormErrors({
54+
...formErrors,
55+
accountName: 'Name is required.',
56+
});
57+
} else {
58+
setFormErrors({
59+
...formErrors,
60+
accountName: undefined,
61+
});
62+
}
63+
}, [formRecord, formErrors]);
64+
65+
const validateAccountPassword = useCallback(() => {
66+
if (isBlank(formRecord.accountPassword)) {
67+
setFormErrors({
68+
...formErrors,
69+
accountPassword: 'Password is required.',
70+
});
71+
} else {
72+
setFormErrors({
73+
...formErrors,
74+
accountPassword: undefined,
75+
});
76+
}
77+
}, [formRecord, formErrors]);
78+
4079
const loadAccounts = useCallback(async () => {
4180
const accounts = await window.api.listAccounts();
42-
43-
setTableRowItems(
44-
accounts.map((account) => {
45-
return {
46-
accountName: account.accountName,
47-
};
48-
})
49-
);
81+
setTableRowItems(accounts);
5082
}, []);
5183

5284
const closeModals = useCallback(() => {
5385
setShowAddAccountModal(false);
5486
setShowEditAccountModal(false);
5587
setShowRemoveAccountModal(false);
56-
setRecord(undefined);
88+
setFormRecord({});
89+
setFormErrors({});
5790
}, []);
5891

5992
const onAddAccountClick = useCallback(() => {
60-
// TODO show prompt to enter account name and password
6193
closeModals();
62-
setRecord({ accountName: '' });
6394
setShowAddAccountModal(true);
6495
}, [closeModals]);
6596

6697
const onEditAccountClick = useCallback(
6798
(tableRowItem: TableRowItem) => {
68-
// TODO show prompt to edit account name and password
6999
closeModals();
70-
setRecord(tableRowItem);
100+
setFormRecord(tableRowItem);
101+
setFormErrors({});
71102
setShowEditAccountModal(true);
72103
},
73104
[closeModals]
@@ -76,28 +107,107 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
76107
const onRemoveAccountClick = useCallback(
77108
(tableRowItem: TableRowItem) => {
78109
closeModals();
79-
setRecord(tableRowItem);
110+
setFormRecord(tableRowItem);
111+
setFormErrors({});
80112
setShowRemoveAccountModal(true);
81113
},
82114
[closeModals]
83115
);
84116

117+
const onAccountSaveConfirm = useCallback(() => {
118+
runInBackground(async () => {
119+
validateAccountName();
120+
validateAccountPassword();
121+
if (formErrors.accountName || formErrors.accountPassword) {
122+
return;
123+
}
124+
await window.api.saveAccount({
125+
accountName: formRecord.accountName!,
126+
accountPassword: formRecord.accountPassword!,
127+
});
128+
await loadAccounts();
129+
closeModals();
130+
});
131+
}, [
132+
formRecord,
133+
formErrors,
134+
validateAccountName,
135+
validateAccountPassword,
136+
loadAccounts,
137+
closeModals,
138+
]);
139+
85140
const onAccountRemoveConfirm = useCallback(() => {
86-
if (!record) {
87-
return;
88-
}
89141
runInBackground(async () => {
142+
if (isBlank(formRecord.accountName)) {
143+
return;
144+
}
90145
await window.api.removeAccount({
91-
accountName: record.accountName,
146+
accountName: formRecord.accountName,
92147
});
93148
await loadAccounts();
94149
closeModals();
95150
});
96-
}, [record, loadAccounts, closeModals]);
151+
}, [formRecord, loadAccounts, closeModals]);
97152

98153
const accountAddModal = useMemo(() => {
99-
return <>add</>;
100-
}, []);
154+
return (
155+
<EuiConfirmModal
156+
title="Add Account"
157+
onCancel={closeModals}
158+
onConfirm={onAccountSaveConfirm}
159+
cancelButtonText="Cancel"
160+
confirmButtonText="Save"
161+
buttonColor="primary"
162+
defaultFocusedButton="cancel"
163+
>
164+
<EuiForm component="form">
165+
<EuiFormRow
166+
label="Name"
167+
isInvalid={!!formErrors.accountName?.length}
168+
error={formErrors.accountName}
169+
>
170+
<EuiFieldText
171+
name="accountName"
172+
onChange={(event) => {
173+
setFormRecord({
174+
...formRecord,
175+
accountName: event.target.value,
176+
});
177+
validateAccountName();
178+
}}
179+
isInvalid={!!formErrors.accountName?.length}
180+
/>
181+
</EuiFormRow>
182+
<EuiFormRow
183+
label="Password"
184+
isInvalid={!!formErrors.accountPassword?.length}
185+
error={formErrors.accountPassword}
186+
>
187+
<EuiFieldPassword
188+
name="accountPassword"
189+
onChange={(event) => {
190+
setFormRecord({
191+
...formRecord,
192+
accountPassword: event.target.value,
193+
});
194+
validateAccountPassword();
195+
}}
196+
isInvalid={!!formErrors.accountPassword?.length}
197+
type="dual"
198+
/>
199+
</EuiFormRow>
200+
</EuiForm>
201+
</EuiConfirmModal>
202+
);
203+
}, [
204+
formRecord,
205+
formErrors,
206+
validateAccountName,
207+
validateAccountPassword,
208+
onAccountSaveConfirm,
209+
closeModals,
210+
]);
101211

102212
const accountEditModal = useMemo(() => {
103213
return <>edit</>;
@@ -108,7 +218,7 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
108218
<EuiConfirmModal
109219
title={
110220
<>
111-
Remove account <EuiCode>{record?.accountName}</EuiCode>?
221+
Remove account <EuiCode>{formRecord.accountName}</EuiCode>?
112222
</>
113223
}
114224
onCancel={closeModals}
@@ -118,10 +228,10 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
118228
buttonColor="danger"
119229
defaultFocusedButton="cancel"
120230
>
121-
Any associated characters will also be removed.
231+
Associated characters will also be removed.
122232
</EuiConfirmModal>
123233
);
124-
}, [record, closeModals, onAccountRemoveConfirm]);
234+
}, [formRecord, onAccountRemoveConfirm, closeModals]);
125235

126236
useEffect(() => {
127237
runInBackground(async () => {
@@ -132,7 +242,7 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
132242
const columns: Array<EuiBasicTableColumn<TableRowItem>> = [
133243
{
134244
field: 'accountName',
135-
name: 'Account',
245+
name: 'Name',
136246
dataType: 'string',
137247
},
138248
{
@@ -142,8 +252,9 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
142252
return (
143253
<EuiFlexGroup responsive={true} gutterSize="s" alignItems="center">
144254
<EuiFlexItem grow={false}>
145-
<EuiToolTip content="Edit account" position="bottom">
255+
<EuiToolTip content="Edit Account" position="bottom">
146256
<EuiButtonIcon
257+
aria-label="Edit Account"
147258
iconType="pencil"
148259
display="base"
149260
color="warning"
@@ -152,8 +263,9 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
152263
</EuiToolTip>
153264
</EuiFlexItem>
154265
<EuiFlexItem grow={false}>
155-
<EuiToolTip content="Remove account" position="bottom">
266+
<EuiToolTip content="Remove Account" position="bottom">
156267
<EuiButtonIcon
268+
aria-label="Remove Account"
157269
iconType="cross"
158270
display="base"
159271
color="danger"

0 commit comments

Comments
 (0)