admin/account: better ux by selecting once and for all what type of account you are adding
Massimo Melina committed
Mar 12, 2022 at 20:29 UTC
dfa796260d2b32df8d26340d20a7e583297a2937
2 files changed
+32
-13
admin/src/AccountsPage.ts
+30
-12
@@ -1,15 +1,17 @@
1
// This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
-import { isValidElement, createElement as h, useState, useEffect, Fragment } from "react"
3
+import { isValidElement, createElement as h, useState, useEffect, Fragment, useRef } from "react"
4
import { apiCall, useApiComp } from './api'
5
import { Box, Button, Card, CardContent, Grid, List, ListItem, ListItemText, Typography } from '@mui/material'
6
-import { Delete, Group, MilitaryTech, Person, PersonAdd, Refresh } from '@mui/icons-material'
6
+import { Add, Delete, Group, MilitaryTech, Person, PersonAdd, Refresh } from '@mui/icons-material'
7
import { BoolField, Form, MultiSelectField, SelectField, StringField } from './Form'
8
import { alertDialog, confirmDialog } from './dialog'
9
import { iconTooltip, isEqualLax, onlyTruthy } from './misc'
10
import { TreeItem, TreeView } from '@mui/lab'
11
import { makeStyles } from '@mui/styles'
12
import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
13
+import MenuButton from './MenuButton'
14
+import addFiles, { addVirtual } from './addFiles'
15
16
const useStyles = makeStyles({
17
label: {
@@ -32,7 +34,7 @@ interface Account {
34
export default function AccountsPage() {
35
const [res, reload] = useApiComp('get_accounts')
36
const [sel, setSel] = useState<string[]>([])
35
- const [add, setAdd] = useState(false)
37
+ const [addGroup, setAddGroup] = useState<boolean|null>(null)
38
const styles = useStyles()
39
useEffect(() => { // if accounts are reloaded, review the selection to remove elements that don't exist anymore
40
if (isValidElement(res) || !Array.isArray(res?.list)) return
@@ -41,7 +43,8 @@ export default function AccountsPage() {
43
if (isValidElement(res))
44
return res
45
const { list }: { list: Account[] } = res
44
- const account = add ? { username: '', hasPassword:true } : sel.length === 1 && list.find(x => x.username === sel[0])
46
+ const account = addGroup !== null ? { username: '', hasPassword: !addGroup }
47
+ : sel.length === 1 && list.find(x => x.username === sel[0])
48
return h(Grid, { container: true, maxWidth: '50em' },
49
h(Grid, { item: true, xs: 12 },
50
h(Box, {
@@ -56,11 +59,14 @@ export default function AccountsPage() {
59
width: 'fit-content',
60
},
61
},
59
- h(Button, {
62
+ h(MenuButton, {
63
variant: 'contained',
64
startIcon: h(PersonAdd),
62
- onClick(){ setAdd(true) }
63
- }, "Add"),
65
+ items: [
66
+ { children: "user", onClick: () => switchTo(false) },
67
+ { children: "group", onClick: () => switchTo(true) }
68
+ ]
69
+ }, 'Add'),
70
h(Button, {
71
disabled: !sel.length,
72
startIcon: h(Delete),
@@ -83,8 +89,7 @@ export default function AccountsPage() {
89
sx: { pr: 4, minWidth: '15em' },
90
selected: sel,
91
onNodeSelect(ev, ids) {
86
- setAdd(false)
87
- setSel(ids)
92
+ switchTo(ids)
93
}
94
},
95
list.map((ac: Account) =>
@@ -102,14 +107,13 @@ export default function AccountsPage() {
107
)
108
)
109
),
105
- (add || sel.length > 0) && h(Grid, { item: true, md: 6 },
110
+ (addGroup !== null || sel.length > 0) && h(Grid, { item: true, md: 6 },
111
h(Card, {},
112
h(CardContent, {},
113
account ? h(AccountForm, {
114
account,
115
groups: list.filter(x => !x.hasPassword).map( x => x.username ),
116
done(username) {
112
- setAdd(false)
117
setSel([username])
118
reload()
119
}
@@ -122,6 +126,17 @@ export default function AccountsPage() {
126
)
127
)))
128
)
129
+
130
+ function switchTo(what: boolean | string[]) {
131
+ if (Array.isArray(what)) {
132
+ setAddGroup(null)
133
+ setSel(what)
134
+ }
135
+ else {
136
+ setSel([])
137
+ setAddGroup(what)
138
+ }
139
+ }
140
}
141
142
function hList(heading: string, list: any[]) {
@@ -140,10 +155,14 @@ function AccountForm({ account, done, groups }: { account: Account, groups: stri
155
useEffect(() => {
156
setValues(account)
157
setBelongOptions(groups.filter(x => x !== account.username ))
158
+ //@ts-ignore
159
+ ref.current?.querySelector('input')?.focus()
160
}, [JSON.stringify(account)]) //eslint-disable-line
161
const add = !account.username
162
const group = !values.hasPassword
163
+ const ref = useRef()
164
return h(Form, {
165
+ formRef: ref,
166
values,
167
set(v, { k }) {
168
setValues({ ...values, [k]: v })
@@ -151,7 +170,6 @@ function AccountForm({ account, done, groups }: { account: Account, groups: stri
170
barSx: { width: 'initial', justifyContent: 'space-between' },
171
addToBar: [ account2icon(values, { fontSize: 'large', sx: { p: 1 }}) ],
172
fields: [
154
- add && { k: 'hasPassword', comp: SelectField, label: 'Account type', options: [{ value: true, label: 'Simple' }, { value: false, label: 'Group' }] },
173
{ k: 'username', label: group ? 'Group name' : undefined, autoComplete: 'off' },
174
!group && { k: 'password', comp: StringField, md: 6, type: 'password', autoComplete: 'new-password', label: add ? 'Password' : 'Change password' },
175
!group && { k: 'password2', comp: StringField, md: 6, type: 'password', autoComplete: 'off', label: 'Repeat password' },
admin/src/Form.ts
+2
-1
@@ -33,7 +33,7 @@ interface FormProps {
33
barSx?: Dict
34
[rest:string]: any
35
}
36
-export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=[], barSx, ...rest }: FormProps) {
36
+export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=[], barSx, formRef, ...rest }: FormProps) {
37
const [loading, setLoading] = useStateMounted(false)
38
const onClick = save?.onClick
39
if (onClick)
@@ -68,6 +68,7 @@ export function Form({ fields, values, set, defaults, save, stickyBar, addToBar=
68
)
69
70
return h('form', {
71
+ ref: formRef,
72
onSubmit(ev) {
73
ev.preventDefault()
74
},