@@ -5,69 +5,100 @@ import {
5
5
EuiCallOut ,
6
6
EuiCode ,
7
7
EuiConfirmModal ,
8
+ EuiFieldPassword ,
9
+ EuiFieldText ,
8
10
EuiFlexGroup ,
9
11
EuiFlexItem ,
12
+ EuiForm ,
13
+ EuiFormRow ,
10
14
EuiInMemoryTable ,
11
15
EuiPanel ,
12
16
EuiSpacer ,
13
17
EuiToolTip ,
14
18
} 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' ;
23
23
import { runInBackground } from '../../lib/async/run-in-background.js' ;
24
24
25
25
interface TableRowItem {
26
26
accountName : string ;
27
27
}
28
28
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
+ }
33
38
39
+ export const SidebarItemAccounts : React . FC = ( ) : ReactNode => {
34
40
const [ showAddAccountModal , setShowAddAccountModal ] = useState ( false ) ;
35
41
const [ showEditAccountModal , setShowEditAccountModal ] = useState ( false ) ;
36
42
const [ showRemoveAccountModal , setShowRemoveAccountModal ] = useState ( false ) ;
37
43
44
+ // All the table row items (accounts) to display.
38
45
const [ tableRowItems , setTableRowItems ] = useState < Array < TableRowItem > > ( [ ] ) ;
39
46
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
+
40
79
const loadAccounts = useCallback ( async ( ) => {
41
80
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 ) ;
50
82
} , [ ] ) ;
51
83
52
84
const closeModals = useCallback ( ( ) => {
53
85
setShowAddAccountModal ( false ) ;
54
86
setShowEditAccountModal ( false ) ;
55
87
setShowRemoveAccountModal ( false ) ;
56
- setRecord ( undefined ) ;
88
+ setFormRecord ( { } ) ;
89
+ setFormErrors ( { } ) ;
57
90
} , [ ] ) ;
58
91
59
92
const onAddAccountClick = useCallback ( ( ) => {
60
- // TODO show prompt to enter account name and password
61
93
closeModals ( ) ;
62
- setRecord ( { accountName : '' } ) ;
63
94
setShowAddAccountModal ( true ) ;
64
95
} , [ closeModals ] ) ;
65
96
66
97
const onEditAccountClick = useCallback (
67
98
( tableRowItem : TableRowItem ) => {
68
- // TODO show prompt to edit account name and password
69
99
closeModals ( ) ;
70
- setRecord ( tableRowItem ) ;
100
+ setFormRecord ( tableRowItem ) ;
101
+ setFormErrors ( { } ) ;
71
102
setShowEditAccountModal ( true ) ;
72
103
} ,
73
104
[ closeModals ]
@@ -76,28 +107,107 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
76
107
const onRemoveAccountClick = useCallback (
77
108
( tableRowItem : TableRowItem ) => {
78
109
closeModals ( ) ;
79
- setRecord ( tableRowItem ) ;
110
+ setFormRecord ( tableRowItem ) ;
111
+ setFormErrors ( { } ) ;
80
112
setShowRemoveAccountModal ( true ) ;
81
113
} ,
82
114
[ closeModals ]
83
115
) ;
84
116
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
+
85
140
const onAccountRemoveConfirm = useCallback ( ( ) => {
86
- if ( ! record ) {
87
- return ;
88
- }
89
141
runInBackground ( async ( ) => {
142
+ if ( isBlank ( formRecord . accountName ) ) {
143
+ return ;
144
+ }
90
145
await window . api . removeAccount ( {
91
- accountName : record . accountName ,
146
+ accountName : formRecord . accountName ,
92
147
} ) ;
93
148
await loadAccounts ( ) ;
94
149
closeModals ( ) ;
95
150
} ) ;
96
- } , [ record , loadAccounts , closeModals ] ) ;
151
+ } , [ formRecord , loadAccounts , closeModals ] ) ;
97
152
98
153
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
+ ] ) ;
101
211
102
212
const accountEditModal = useMemo ( ( ) => {
103
213
return < > edit</ > ;
@@ -108,7 +218,7 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
108
218
< EuiConfirmModal
109
219
title = {
110
220
< >
111
- Remove account < EuiCode > { record ? .accountName } </ EuiCode > ?
221
+ Remove account < EuiCode > { formRecord . accountName } </ EuiCode > ?
112
222
</ >
113
223
}
114
224
onCancel = { closeModals }
@@ -118,10 +228,10 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
118
228
buttonColor = "danger"
119
229
defaultFocusedButton = "cancel"
120
230
>
121
- Any associated characters will also be removed.
231
+ Associated characters will also be removed.
122
232
</ EuiConfirmModal >
123
233
) ;
124
- } , [ record , closeModals , onAccountRemoveConfirm ] ) ;
234
+ } , [ formRecord , onAccountRemoveConfirm , closeModals ] ) ;
125
235
126
236
useEffect ( ( ) => {
127
237
runInBackground ( async ( ) => {
@@ -132,7 +242,7 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
132
242
const columns : Array < EuiBasicTableColumn < TableRowItem > > = [
133
243
{
134
244
field : 'accountName' ,
135
- name : 'Account ' ,
245
+ name : 'Name ' ,
136
246
dataType : 'string' ,
137
247
} ,
138
248
{
@@ -142,8 +252,9 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
142
252
return (
143
253
< EuiFlexGroup responsive = { true } gutterSize = "s" alignItems = "center" >
144
254
< EuiFlexItem grow = { false } >
145
- < EuiToolTip content = "Edit account " position = "bottom" >
255
+ < EuiToolTip content = "Edit Account " position = "bottom" >
146
256
< EuiButtonIcon
257
+ aria-label = "Edit Account"
147
258
iconType = "pencil"
148
259
display = "base"
149
260
color = "warning"
@@ -152,8 +263,9 @@ export const SidebarItemAccounts: React.FC = (): ReactNode => {
152
263
</ EuiToolTip >
153
264
</ EuiFlexItem >
154
265
< EuiFlexItem grow = { false } >
155
- < EuiToolTip content = "Remove account " position = "bottom" >
266
+ < EuiToolTip content = "Remove Account " position = "bottom" >
156
267
< EuiButtonIcon
268
+ aria-label = "Remove Account"
157
269
iconType = "cross"
158
270
display = "base"
159
271
color = "danger"
0 commit comments