settings frontend bugfix + refactoring
This commit is contained in:
parent
3888e7fcde
commit
b274cd4ad2
@ -21,7 +21,7 @@ const LogView = lazy(() => import("./views/log-view"));
|
|||||||
const AccessControlList = lazy(() => import("./views/access-control-list"));
|
const AccessControlList = lazy(() => import("./views/access-control-list"));
|
||||||
const RouteListView = lazy(() => import("./views/route/route-list"));
|
const RouteListView = lazy(() => import("./views/route/route-list"));
|
||||||
const RouteEditView = lazy(() => import("./views/route/route-edit"));
|
const RouteEditView = lazy(() => import("./views/route/route-edit"));
|
||||||
const SettingsView = lazy(() => import("./views/settings"));
|
const SettingsView = lazy(() => import("./views/settings/settings"));
|
||||||
const ProfileView = lazy(() => import("./views/profile/profile"));
|
const ProfileView = lazy(() => import("./views/profile/profile"));
|
||||||
|
|
||||||
export default function AdminDashboard(props) {
|
export default function AdminDashboard(props) {
|
||||||
|
7
react/admin-panel/src/elements/form-group.js
Normal file
7
react/admin-panel/src/elements/form-group.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {FormGroup, styled} from "@mui/material";
|
||||||
|
|
||||||
|
const SpacedFormGroup = styled(FormGroup)((props) => ({
|
||||||
|
marginBottom: props.theme.spacing(2)
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default SpacedFormGroup;
|
@ -27,6 +27,7 @@ import MfaTotp from "./mfa-totp";
|
|||||||
import MfaFido from "./mfa-fido";
|
import MfaFido from "./mfa-fido";
|
||||||
import Dialog from "shared/elements/dialog";
|
import Dialog from "shared/elements/dialog";
|
||||||
import PasswordStrength from "shared/elements/password-strength";
|
import PasswordStrength from "shared/elements/password-strength";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
|
||||||
const GpgKeyField = styled(TextField)((props) => ({
|
const GpgKeyField = styled(TextField)((props) => ({
|
||||||
"& > div": {
|
"& > div": {
|
||||||
@ -46,10 +47,6 @@ const GpgFingerprintBox = styled(Box)((props) => ({
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ProfileFormGroup = styled(FormGroup)((props) => ({
|
|
||||||
marginBottom: props.theme.spacing(2)
|
|
||||||
}));
|
|
||||||
|
|
||||||
const MFAOptions = styled(Box)((props) => ({
|
const MFAOptions = styled(Box)((props) => ({
|
||||||
"& > div": {
|
"& > div": {
|
||||||
borderColor: props.theme.palette.divider,
|
borderColor: props.theme.palette.divider,
|
||||||
@ -231,7 +228,7 @@ export default function ProfileView(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={"content"}>
|
<div className={"content"}>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.username")}</FormLabel>
|
<FormLabel>{L("account.username")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"}
|
<TextField variant={"outlined"}
|
||||||
@ -239,8 +236,8 @@ export default function ProfileView(props) {
|
|||||||
value={profile.name}
|
value={profile.name}
|
||||||
onChange={e => setProfile({...profile, name: e.target.value })} />
|
onChange={e => setProfile({...profile, name: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.full_name")}</FormLabel>
|
<FormLabel>{L("account.full_name")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"}
|
<TextField variant={"outlined"}
|
||||||
@ -248,12 +245,12 @@ export default function ProfileView(props) {
|
|||||||
value={profile.fullName ?? ""}
|
value={profile.fullName ?? ""}
|
||||||
onChange={e => setProfile({...profile, fullName: e.target.value })} />
|
onChange={e => setProfile({...profile, fullName: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
|
|
||||||
<CollapseBox title={L("account.change_password")} open={openedTab === "password"}
|
<CollapseBox title={L("account.change_password")} open={openedTab === "password"}
|
||||||
onToggle={() => setOpenedTab(openedTab === "password" ? "" : "password")}
|
onToggle={() => setOpenedTab(openedTab === "password" ? "" : "password")}
|
||||||
icon={<Password />}>
|
icon={<Password />}>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.password_old")}</FormLabel>
|
<FormLabel>{L("account.password_old")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"}
|
<TextField variant={"outlined"}
|
||||||
@ -263,8 +260,8 @@ export default function ProfileView(props) {
|
|||||||
value={changePassword.old}
|
value={changePassword.old}
|
||||||
onChange={e => setChangePassword({...changePassword, old: e.target.value })} />
|
onChange={e => setChangePassword({...changePassword, old: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.password_new")}</FormLabel>
|
<FormLabel>{L("account.password_new")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"}
|
<TextField variant={"outlined"}
|
||||||
@ -273,8 +270,8 @@ export default function ProfileView(props) {
|
|||||||
value={changePassword.new}
|
value={changePassword.new}
|
||||||
onChange={e => setChangePassword({...changePassword, new: e.target.value })} />
|
onChange={e => setChangePassword({...changePassword, new: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.password_confirm")}</FormLabel>
|
<FormLabel>{L("account.password_confirm")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"}
|
<TextField variant={"outlined"}
|
||||||
@ -283,7 +280,7 @@ export default function ProfileView(props) {
|
|||||||
value={changePassword.confirm}
|
value={changePassword.confirm}
|
||||||
onChange={e => setChangePassword({...changePassword, confirm: e.target.value })} />
|
onChange={e => setChangePassword({...changePassword, confirm: e.target.value })} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<Box className={"w-50"}>
|
<Box className={"w-50"}>
|
||||||
<PasswordStrength password={changePassword.new} minLength={6} />
|
<PasswordStrength password={changePassword.new} minLength={6} />
|
||||||
</Box>
|
</Box>
|
||||||
@ -303,7 +300,7 @@ export default function ProfileView(props) {
|
|||||||
{profile.gpgKey.fingerprint}
|
{profile.gpgKey.fingerprint}
|
||||||
</code>
|
</code>
|
||||||
</GpgFingerprintBox>
|
</GpgFingerprintBox>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.password")}</FormLabel>
|
<FormLabel>{L("account.password")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"} size={"small"}
|
<TextField variant={"outlined"} size={"small"}
|
||||||
@ -312,7 +309,7 @@ export default function ProfileView(props) {
|
|||||||
placeholder={L("account.password")}
|
placeholder={L("account.password")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<Button startIcon={isGpgKeyRemoving ? <CircularProgress size={12} /> : <Remove />}
|
<Button startIcon={isGpgKeyRemoving ? <CircularProgress size={12} /> : <Remove />}
|
||||||
color={"secondary"} onClick={onRemoveGpgKey}
|
color={"secondary"} onClick={onRemoveGpgKey}
|
||||||
variant={"outlined"} size={"small"}
|
variant={"outlined"} size={"small"}
|
||||||
@ -321,7 +318,7 @@ export default function ProfileView(props) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box> :
|
</Box> :
|
||||||
<Box>
|
<Box>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.gpg_key")}</FormLabel>
|
<FormLabel>{L("account.gpg_key")}</FormLabel>
|
||||||
<GpgKeyField value={gpgKey} multiline={true} rows={8}
|
<GpgKeyField value={gpgKey} multiline={true} rows={8}
|
||||||
disabled={isGpgKeyUploading || !api.hasPermission("user/importGPG")}
|
disabled={isGpgKeyUploading || !api.hasPermission("user/importGPG")}
|
||||||
@ -334,7 +331,7 @@ export default function ProfileView(props) {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}}/>
|
}}/>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<ButtonBar>
|
<ButtonBar>
|
||||||
<Button size={"small"}
|
<Button size={"small"}
|
||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
@ -372,7 +369,7 @@ export default function ProfileView(props) {
|
|||||||
}
|
}
|
||||||
{L("account.2fa_type_" + profile.twoFactorToken.type)}
|
{L("account.2fa_type_" + profile.twoFactorToken.type)}
|
||||||
</GpgFingerprintBox>
|
</GpgFingerprintBox>
|
||||||
<ProfileFormGroup>
|
<SpacedFormGroup>
|
||||||
<FormLabel>{L("account.password")}</FormLabel>
|
<FormLabel>{L("account.password")}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<TextField variant={"outlined"} size={"small"}
|
<TextField variant={"outlined"} size={"small"}
|
||||||
@ -381,7 +378,7 @@ export default function ProfileView(props) {
|
|||||||
placeholder={L("account.password")}
|
placeholder={L("account.password")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</ProfileFormGroup>
|
</SpacedFormGroup>
|
||||||
<Button startIcon={is2FARemoving ? <CircularProgress size={12} /> : <Remove />}
|
<Button startIcon={is2FARemoving ? <CircularProgress size={12} /> : <Remove />}
|
||||||
color={"secondary"} onClick={onRemove2FA}
|
color={"secondary"} onClick={onRemove2FA}
|
||||||
variant={"outlined"} size={"small"}
|
variant={"outlined"} size={"small"}
|
||||||
|
21
react/admin-panel/src/views/settings/input-check-box.js
Normal file
21
react/admin-panel/src/views/settings/input-check-box.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {Checkbox, FormControlLabel} from "@mui/material";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {parseBool} from "shared/util";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsCheckBox(props) {
|
||||||
|
|
||||||
|
const {key_name, value, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormControlLabel
|
||||||
|
disabled={disabled}
|
||||||
|
control={<Checkbox
|
||||||
|
disabled={disabled}
|
||||||
|
checked={parseBool(value)}
|
||||||
|
onChange={(e, v) => onChangeValue(v)} />}
|
||||||
|
label={L("settings." + key_name)} />
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
22
react/admin-panel/src/views/settings/input-number.js
Normal file
22
react/admin-panel/src/views/settings/input-number.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {FormControl, FormLabel, TextField} from "@mui/material";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsNumberInput(props) {
|
||||||
|
|
||||||
|
const {key_name, value, minValue, maxValue, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
type={"number"}
|
||||||
|
disabled={disabled}
|
||||||
|
inputProps={{min: minValue, max: maxValue}}
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChangeValue(e.target.value)} />
|
||||||
|
</FormControl>
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
22
react/admin-panel/src/views/settings/input-password.js
Normal file
22
react/admin-panel/src/views/settings/input-password.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {FormControl, FormLabel, TextField} from "@mui/material";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsPasswordInput(props) {
|
||||||
|
|
||||||
|
const {key_name, value, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
type={"password"}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder={"(" + L("general.unchanged") + ")"}
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChangeValue(e.target.value)} />
|
||||||
|
</FormControl>
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
25
react/admin-panel/src/views/settings/input-selection.js
Normal file
25
react/admin-panel/src/views/settings/input-selection.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {FormControl, FormLabel, Select} from "@mui/material";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsSelection(props) {
|
||||||
|
|
||||||
|
const {key_name, value, options, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Select native value={value}
|
||||||
|
disabled={disabled}
|
||||||
|
size={"small"} onChange={e => onChangeValue(e.target.value)}>
|
||||||
|
{options.map(option => <option
|
||||||
|
key={"option-" + option}
|
||||||
|
value={option}>
|
||||||
|
{option}
|
||||||
|
</option>)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
49
react/admin-panel/src/views/settings/input-text-values.js
Normal file
49
react/admin-panel/src/views/settings/input-text-values.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {Autocomplete, Chip, FormLabel, TextField} from "@mui/material";
|
||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {useCallback, useContext, useState} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsTextValues(props) {
|
||||||
|
|
||||||
|
const {key_name, value, options, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
const [textInput, setTextInput] = useState("");
|
||||||
|
|
||||||
|
const onFinishTyping = useCallback(() => {
|
||||||
|
setTextInput("");
|
||||||
|
const newValue = textInput?.trim();
|
||||||
|
if (newValue) {
|
||||||
|
onChangeValue(value ? [...value, newValue] : [newValue]);
|
||||||
|
}
|
||||||
|
}, [textInput, value]);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
||||||
|
<Autocomplete
|
||||||
|
clearIcon={false}
|
||||||
|
options={[]}
|
||||||
|
freeSolo
|
||||||
|
multiple
|
||||||
|
value={value || []}
|
||||||
|
inputValue={textInput}
|
||||||
|
onChange={(e, v) => onChangeValue(v)}
|
||||||
|
onInputChange={e => setTextInput(e.target.value.trim())}
|
||||||
|
renderTags={(values, props) =>
|
||||||
|
values.map((option, index) => (
|
||||||
|
<Chip label={option} {...props({ index })} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
renderInput={(params) => <TextField
|
||||||
|
{...params}
|
||||||
|
onKeyDown={e => {
|
||||||
|
if (["Enter", "Tab", ",", " "].includes(e.key)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onFinishTyping();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={onFinishTyping} />}
|
||||||
|
/>
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
20
react/admin-panel/src/views/settings/input-text.js
Normal file
20
react/admin-panel/src/views/settings/input-text.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import SpacedFormGroup from "../../elements/form-group";
|
||||||
|
import {FormControl, FormLabel, TextField} from "@mui/material";
|
||||||
|
import {useContext} from "react";
|
||||||
|
import {LocaleContext} from "shared/locale";
|
||||||
|
|
||||||
|
export default function SettingsTextInput(props) {
|
||||||
|
|
||||||
|
const {key_name, value, onChangeValue, disabled, ...other} = props;
|
||||||
|
const {translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
|
return <SpacedFormGroup {...other}>
|
||||||
|
<FormLabel disabled={!!disabled}>{L("settings." + key_name)}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<TextField size={"small"} variant={"outlined"}
|
||||||
|
disabled={!!disabled}
|
||||||
|
value={value}
|
||||||
|
onChange={e => onChangeValue(e.target.value)} />
|
||||||
|
</FormControl>
|
||||||
|
</SpacedFormGroup>
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
import {useCallback, useContext, useEffect, useState} from "react";
|
import {useCallback, useContext, useEffect, useState} from "react";
|
||||||
import {LocaleContext} from "shared/locale";
|
import {LocaleContext} from "shared/locale";
|
||||||
import {
|
import {
|
||||||
Box, Button, Checkbox,
|
Box, Button,
|
||||||
CircularProgress, FormControl, FormControlLabel,
|
CircularProgress, FormControl,
|
||||||
FormGroup, FormLabel, Grid, IconButton,
|
FormGroup, FormLabel, Grid, IconButton,
|
||||||
Paper, Select, styled,
|
Paper,
|
||||||
Tab,
|
Tab,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@ -13,8 +13,6 @@ import {
|
|||||||
TableContainer,
|
TableContainer,
|
||||||
TableRow,
|
TableRow,
|
||||||
Tabs, TextField,
|
Tabs, TextField,
|
||||||
Autocomplete,
|
|
||||||
Chip
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
@ -29,11 +27,14 @@ import {
|
|||||||
SettingsApplications
|
SettingsApplications
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import TIME_ZONES from "shared/time-zones";
|
import TIME_ZONES from "shared/time-zones";
|
||||||
import ButtonBar from "../elements/button-bar";
|
import ButtonBar from "../../elements/button-bar";
|
||||||
|
import {parseBool} from "shared/util";
|
||||||
const SettingsFormGroup = styled(FormGroup)((props) => ({
|
import SettingsTextValues from "./input-text-values";
|
||||||
marginBottom: props.theme.spacing(1),
|
import SettingsCheckBox from "./input-check-box";
|
||||||
}));
|
import SettingsNumberInput from "./input-number";
|
||||||
|
import SettingsPasswordInput from "./input-password";
|
||||||
|
import SettingsTextInput from "./input-text";
|
||||||
|
import SettingsSelection from "./input-selection";
|
||||||
|
|
||||||
export default function SettingsView(props) {
|
export default function SettingsView(props) {
|
||||||
|
|
||||||
@ -71,7 +72,6 @@ export default function SettingsView(props) {
|
|||||||
// data
|
// data
|
||||||
const [fetchSettings, setFetchSettings] = useState(true);
|
const [fetchSettings, setFetchSettings] = useState(true);
|
||||||
const [settings, setSettings] = useState(null);
|
const [settings, setSettings] = useState(null);
|
||||||
const [extra, setExtra] = useState({});
|
|
||||||
const [uncategorizedKeys, setUncategorizedKeys] = useState([]);
|
const [uncategorizedKeys, setUncategorizedKeys] = useState([]);
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
@ -197,118 +197,41 @@ export default function SettingsView(props) {
|
|||||||
setFetchSettings(true);
|
setFetchSettings(true);
|
||||||
setNewKey("");
|
setNewKey("");
|
||||||
setChanged(false);
|
setChanged(false);
|
||||||
setExtra({});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const parseBool = (v) => v !== undefined && (v === true || v === 1 || ["true", "1", "yes"].includes(v.toString().toLowerCase()));
|
const getInputProps = (key_name, disabled = false, props = {}) => {
|
||||||
|
return {
|
||||||
|
key: "form-" + key_name,
|
||||||
|
key_name: key_name,
|
||||||
|
value: settings[key_name],
|
||||||
|
disabled: disabled,
|
||||||
|
onChangeValue: v => setSettings({...settings, [key_name]: v}),
|
||||||
|
...props
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const renderTextInput = (key_name, disabled=false, props={}) => {
|
const renderTextInput = (key_name, disabled=false, props={}) => {
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
return <SettingsTextInput {...getInputProps(key_name, disabled, props)} />
|
||||||
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<TextField size={"small"} variant={"outlined"}
|
|
||||||
disabled={disabled}
|
|
||||||
value={settings[key_name]}
|
|
||||||
onChange={e => onChangeValue(key_name, e.target.value)} />
|
|
||||||
</FormControl>
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderPasswordInput = (key_name, disabled=false, props={}) => {
|
const renderPasswordInput = (key_name, disabled=false, props={}) => {
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
return <SettingsPasswordInput {...getInputProps(key_name, disabled, props)} />
|
||||||
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<TextField size={"small"} variant={"outlined"}
|
|
||||||
type={"password"}
|
|
||||||
disabled={disabled}
|
|
||||||
placeholder={"(" + L("general.unchanged") + ")"}
|
|
||||||
value={settings[key_name]}
|
|
||||||
onChange={e => onChangeValue(key_name, e.target.value)} />
|
|
||||||
</FormControl>
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderNumberInput = (key_name, minValue, maxValue, disabled=false, props={}) => {
|
const renderNumberInput = (key_name, minValue, maxValue, disabled=false, props={}) => {
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
return <SettingsNumberInput minValue={minValue} maxValue={maxValue} {...getInputProps(key_name, disabled, props)} />
|
||||||
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<TextField size={"small"} variant={"outlined"}
|
|
||||||
type={"number"}
|
|
||||||
disabled={disabled}
|
|
||||||
inputProps={{min: minValue, max: maxValue}}
|
|
||||||
value={settings[key_name]}
|
|
||||||
onChange={e => onChangeValue(key_name, e.target.value)} />
|
|
||||||
</FormControl>
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderCheckBox = (key_name, disabled=false, props={}) => {
|
const renderCheckBox = (key_name, disabled=false, props={}) => {
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
return <SettingsCheckBox {...getInputProps(key_name, disabled, props)} />
|
||||||
<FormControlLabel
|
|
||||||
disabled={disabled}
|
|
||||||
control={<Checkbox
|
|
||||||
disabled={disabled}
|
|
||||||
checked={parseBool(settings[key_name])}
|
|
||||||
onChange={(e, v) => onChangeValue(key_name, v)} />}
|
|
||||||
label={L("settings." + key_name)} />
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderSelection = (key_name, options, disabled=false, props={}) => {
|
const renderSelection = (key_name, options, disabled=false, props={}) => {
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
return <SettingsSelection options={options} {...getInputProps(key_name, disabled, props)} />
|
||||||
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Select native value={settings[key_name]}
|
|
||||||
disabled={disabled}
|
|
||||||
size={"small"} onChange={e => onChangeValue(key_name, e.target.value)}>
|
|
||||||
{options.map(option => <option
|
|
||||||
key={"option-" + option}
|
|
||||||
value={option}>
|
|
||||||
{option}
|
|
||||||
</option>)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderTextValuesInput = (key_name, disabled=false, props={}) => {
|
const renderTextValuesInput = (key_name, disabled=false, props={}) => {
|
||||||
|
return <SettingsTextValues {...getInputProps(key_name, disabled, props)} />
|
||||||
const finishTyping = () => {
|
|
||||||
console.log("finishTyping", key_name);
|
|
||||||
setExtra({...extra, [key_name]: ""});
|
|
||||||
if (extra[key_name]) {
|
|
||||||
setSettings({...settings, [key_name]: [...settings[key_name], extra[key_name]]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <SettingsFormGroup key={"form-" + key_name} {...props}>
|
|
||||||
<FormLabel disabled={disabled}>{L("settings." + key_name)}</FormLabel>
|
|
||||||
<Autocomplete
|
|
||||||
clearIcon={false}
|
|
||||||
options={[]}
|
|
||||||
freeSolo
|
|
||||||
multiple
|
|
||||||
value={settings[key_name]}
|
|
||||||
onChange={(e, v) => setSettings({...settings, [key_name]: v})}
|
|
||||||
renderTags={(values, props) =>
|
|
||||||
values.map((option, index) => (
|
|
||||||
<Chip label={option} {...props({ index })} />
|
|
||||||
))
|
|
||||||
}
|
|
||||||
renderInput={(params) => <TextField
|
|
||||||
{...params}
|
|
||||||
value={extra[key_name] ?? ""}
|
|
||||||
onChange={e => setExtra({...extra, [key_name]: e.target.value.trim()})}
|
|
||||||
onKeyDown={e => {
|
|
||||||
if (["Enter", "Tab", " "].includes(e.key)) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
finishTyping();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onBlur={finishTyping} />}
|
|
||||||
/>
|
|
||||||
</SettingsFormGroup>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderTab = () => {
|
const renderTab = () => {
|
@ -109,7 +109,12 @@ const isInt = (value) => {
|
|||||||
!isNaN(parseInt(value, 10));
|
!isNaN(parseInt(value, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const parseBool = (v) => v !== undefined &&
|
||||||
|
(v === true || v === 1 || ["true", "1", "yes"].includes(v.toString().toLowerCase()));
|
||||||
|
|
||||||
|
|
||||||
export { humanReadableSize, removeParameter, getParameter, getCookie,
|
export { humanReadableSize, removeParameter, getParameter, getCookie,
|
||||||
encodeText, decodeText, getBaseUrl,
|
encodeText, decodeText, getBaseUrl,
|
||||||
formatDate, formatDateTime, formatDistance,
|
formatDate, formatDateTime, formatDistance,
|
||||||
upperFirstChars, isInt, createDownload };
|
upperFirstChars, isInt, parseBool, createDownload };
|
Loading…
Reference in New Issue
Block a user