frontend fixes

This commit is contained in:
2024-03-29 18:44:31 +01:00
parent 755da257f8
commit 9fc0a19f59
15 changed files with 131 additions and 89 deletions

View File

@@ -37,8 +37,8 @@ export default function AdminDashboard(props) {
const showDialog = useCallback((message, title, options=["Close"], onOption = null) => {
setDialog({
show: true, message:
message,
show: true,
message: message,
title: title,
options: options,
onOption: onOption,
@@ -80,27 +80,12 @@ export default function AdminDashboard(props) {
<Route path={"/admin/groups"} element={<GroupListView {...controlObj} />}/>
<Route path={"/admin/group/:groupId"} element={<EditGroupView {...controlObj} />}/>
<Route path={"/admin/logs"} element={<LogView {...controlObj} />}/>
<Route path={"/admin/acl"} element={<AccessControlList {...controlObj} />}/>
<Route path={"/admin/permissions"} element={<AccessControlList {...controlObj} />}/>
<Route path={"/admin/routes"} element={<RouteListView {...controlObj} />}/>
<Route path={"/admin/routes/:routeId"} element={<RouteEditView {...controlObj} />}/>
<Route path={"*"} element={<View404 />} />
</Routes>
</Suspense>
{/*<Route exact={true} path={"/admin/users"}><UserOverview {...this.controlObj} /></Route>
<Route path={"/admin/user/add"}><CreateUser {...this.controlObj} /></Route>
<Route path={"/admin/user/edit/:userId"} render={(props) => {
let newProps = {...props, ...this.controlObj};
return <EditUser {...newProps} />
}}/>
<Route path={"/admin/user/permissions"}><PermissionSettings {...this.controlObj}/></Route>
<Route path={"/admin/group/add"}><CreateGroup {...this.controlObj} /></Route>
<Route exact={true} path={"/admin/contact/"}><ContactRequestOverview {...this.controlObj} /></Route>
<Route path={"/admin/visitors"}><Visitors {...this.controlObj} /></Route>
<Route path={"/admin/logs"}><Logs {...this.controlObj} notifications={this.state.notifications} /></Route>
<Route path={"/admin/settings"}><Settings {...this.controlObj} /></Route>
<Route path={"/admin/pages"}><PageOverview {...this.controlObj} /></Route>
<Route path={"/admin/help"}><HelpPage {...this.controlObj} /></Route>
<Route path={"*"}><View404 /></Route>*/}
<Dialog {...dialog}/>
</section>
</div>

View File

@@ -44,7 +44,7 @@ export default function Sidebar(props) {
"name": "admin.settings",
"icon": "tools"
},
"acl": {
"permissions": {
"name": "admin.acl",
"icon": "door-open"
},

View File

@@ -12,7 +12,7 @@ import {
TableContainer,
TableHead,
TableRow,
IconButton, styled
IconButton, styled, FormGroup, FormLabel, FormControl, Box
} from "@material-ui/core";
import {Add, Delete, Edit, Refresh} from "@material-ui/icons";
import {USER_GROUP_ADMIN} from "shared/constants";
@@ -224,39 +224,40 @@ export default function AccessControlList(props) {
</div>
</div>
<div className={"row"}>
<div className={"col-6"}>
<div className={"form-group"}>
<label>{L("permissions.query")}</label>
<TextField
className={"form-control"}
placeholder={L("permissions.query") + "…"}
value={query}
onChange={e => setQuery(e.target.value)}
variant={"outlined"}
size={"small"} />
</div>
</div>
<FormGroup className={"col-6"}>
<FormLabel>{L("permissions.search")}</FormLabel>
<TextField
placeholder={L("permissions.query") + "…"}
value={query}
onChange={e => setQuery(e.target.value)}
variant={"outlined"}
size={"small"} />
</FormGroup>
<div className={"col-6 text-right"}>
<label>{L("general.controls")}</label>
<div className={"form-group"}>
<Button variant={"outlined"} color={"primary"} className={"m-1"} startIcon={<Refresh />} onClick={() => onFetchACL(true)}>
<Box>
<FormLabel>{L("general.controls")}</FormLabel>
</Box>
<Box mb={2}>
<Button variant={"outlined"} color={"primary"} className={"mr-1"}
startIcon={<Refresh />} onClick={() => onFetchACL(true)}>
{L("general.reload")}
</Button>
<Button variant={"outlined"} className={"m-1"} startIcon={<Add />} disabled={!props.api.hasGroup(USER_GROUP_ADMIN)}
<Button variant={"outlined"} startIcon={<Add />}
disabled={!props.api.hasGroup(USER_GROUP_ADMIN)}
onClick={() => setDialogData({
open: true,
title: L("permissions.add_permission"),
inputs: [
{ type: "label", value: L("general.method") + ":" },
{ type: "text", name: "method", value: "", placeholder: L("general.method") },
{ type: "label", value: L("general.description") + ":" },
{ type: "text", name: "description", maxLength: 128, placeholder: L("general.description") }
{ type: "label", value: L("permissions.method") + ":" },
{ type: "text", name: "method", value: "", placeholder: L("permissions.method") },
{ type: "label", value: L("permissions.description") + ":" },
{ type: "text", name: "description", maxLength: 128, placeholder: L("permissions.description") }
],
onOption: (option, inputData) => option === 0 && onUpdatePermission(inputData, [])
})} >
{L("general.add")}
</Button>
</div>
</Box>
</div>
</div>
<TableContainer component={Paper} style={{overflowX: "initial"}}>

View File

@@ -62,7 +62,6 @@ export default function GroupListView(props) {
<div className={"container-fluid"}>
<div className={"row mb-2"}>
<div className={"col-sm-6"}>
<h1 className={"m-0 text-dark"}>Users</h1>
</div>
<div className={"col-sm-6"}>
<ol className={"breadcrumb float-sm-right"}>

View File

@@ -0,0 +1,4 @@
import EditGroupView from "./group-edit";
import GroupListView from "./group-list";
export default { EditGroupView, GroupListView };

View File

@@ -4,12 +4,12 @@ import {Link} from "react-router-dom";
import usePagination from "shared/hooks/pagination";
import {DataColumn, DataTable, DateTimeColumn, NumericColumn, StringColumn} from "shared/elements/data-table";
import {TextField} from "@mui/material";
import {DesktopDateTimePicker} from "@mui/x-date-pickers";
import {DateTimePicker} from "@mui/x-date-pickers";
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import {API_DATETIME_FORMAT} from "shared/constants";
import {format, toDate} from "date-fns";
import {Select} from "@material-ui/core";
import {FormControl, FormGroup, FormLabel, MenuItem, Select} from "@material-ui/core";
export default function LogView(props) {
@@ -30,7 +30,7 @@ export default function LogView(props) {
const [forceReload, setForceReload] = useState(0);
useEffect(() => {
requestModules(props.api, ["general"], currentLocale).then(data => {
requestModules(props.api, ["general", "logs"], currentLocale).then(data => {
if (!data.success) {
props.showDialog("Error fetching translations: " + data.msg);
}
@@ -38,16 +38,22 @@ export default function LogView(props) {
}, [currentLocale]);
const onFetchLogs = useCallback((page, count, orderBy, sortOrder) => {
let apiTimeStamp = null;
try {
if (timestamp) {
apiTimeStamp = format(timestamp, API_DATETIME_FORMAT);
}
} catch (e) {
apiTimeStamp = null;
}
api.fetchLogEntries(page, count, orderBy, sortOrder,
LOG_LEVELS[logLevel],
timestamp ? format(timestamp, API_DATETIME_FORMAT) : null,
query
).then((res) => {
LOG_LEVELS[logLevel], apiTimeStamp, query).then((res) => {
if (res.success) {
setLogEntries(res.logs);
pagination.update(res.pagination);
} else {
showDialog(res.msg, "Error fetching log entries");
showDialog(res.msg, L("logs.fetch_logs_error"));
return null;
}
});
@@ -59,7 +65,7 @@ export default function LogView(props) {
}, [query, timestamp, logLevel]);
const messageColumn = (() => {
let column = new DataColumn(L("message"), "message");
let column = new DataColumn(L("logs.message"), "message");
column.sortable = false;
column.renderData = (L, entry) => {
return <pre>{entry.message}</pre>
@@ -69,9 +75,9 @@ export default function LogView(props) {
const columnDefinitions = [
new NumericColumn(L("general.id"), "id"),
new StringColumn(L("module"), "module"),
new StringColumn(L("severity"), "severity"),
new DateTimeColumn(L("timestamp"), "timestamp", { precise: true }),
new StringColumn(L("logs.module"), "module"),
new StringColumn(L("logs.severity"), "severity"),
new DateTimeColumn(L("logs.timestamp"), "timestamp", { precise: true }),
messageColumn,
];
@@ -80,54 +86,55 @@ export default function LogView(props) {
<div className={"container-fluid"}>
<div className={"row mb-2"}>
<div className={"col-sm-6"}>
<h1 className={"m-0 text-dark"}>System Log</h1>
<h1 className={"m-0 text-dark"}>{L("logs.title")}</h1>
</div>
<div className={"col-sm-6"}>
<ol className={"breadcrumb float-sm-right"}>
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
<li className="breadcrumb-item active">System Log</li>
<li className="breadcrumb-item active">{L("logs.title")}</li>
</ol>
</div>
</div>
</div>
<div className={"content overflow-auto"}>
<div className={"row p-2"}>
<div className={"col-2"}>
<div className={"form-group"}>
<label>{L("log.severity")}</label>
<Select native className={"form-control"} value={logLevel} onChange={e => setLogLevel(parseInt(e.target.value))}>
<FormGroup className={"col-2"}>
<FormLabel>{L("logs.severity")}</FormLabel>
<FormControl>
<TextField select variant={"outlined"} size={"small"} value={logLevel}
onChange={e => setLogLevel(parseInt(e.target.value))}
inputProps={{ size: "small" }}>
{LOG_LEVELS.map((value, index) =>
<option key={"option-" + value} value={index}>{value}</option>)
<MenuItem key={"option-" + value} value={index}>{value}</MenuItem>)
}
</Select>
</div>
</div>
<div className={"col-4"}>
<div className={"form-group"}>
<label>{L("log.timestamp")}</label>
</TextField>
</FormControl>
</FormGroup>
<FormGroup className={"col-4"}>
<FormLabel>{L("logs.timestamp")}</FormLabel>
<FormControl>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DesktopDateTimePicker className={"form-control"}
label={L("Select date time to filter...")}
<DateTimePicker label={L("logs.timestamp_placeholder") + "…"}
value={timestamp ? toDate(new Date()) : null}
format={L("general.datefns_datetime_format_precise")}
onChange={(newValue) => setTimestamp(newValue)}
slotProps={{ textField: { } }}
slotProps={{ textField: { size:'small' } }}
sx={{"& .MuiInputBase-input": { height: "23px", padding: 1 }}}
/>
</LocalizationProvider>
</div>
</div>
<div className={"col-6"}>
<div className={"form-group"}>
<label>{L("log.query")}</label>
</FormControl>
</FormGroup>
<FormGroup className={"col-6"}>
<FormLabel>{L("logs.search")}</FormLabel>
<FormControl>
<TextField
className={"form-control"}
placeholder={L("log.search_query") + "…"}
placeholder={L("logs.search_query") + "…"}
value={query}
onChange={e => setQuery(e.target.value)}
variant={"outlined"}
size={"small"}/>
</div>
</div>
</FormControl>
</FormGroup>
</div>
<div className={"container-fluid"}>
<DataTable
@@ -138,7 +145,7 @@ export default function LogView(props) {
forceReload={forceReload}
defaultSortColumn={3}
defaultSortOrder={"desc"}
placeholder={"No log entries to display"}
placeholder={L("logs.no_entries_placeholder")}
columns={columnDefinitions} />
</div>
</div>

View File

@@ -0,0 +1,4 @@
import RouteEditView from "./route-edit";
import RouteListView from "./route-list";
export default { RouteEditView, RouteListView };

View File

@@ -1,7 +1,8 @@
import {Checkbox, FormControl, FormControlLabel, Select, styled, TextField} from "@material-ui/core";
import {Box, Checkbox, FormControl, FormControlLabel, Select, styled, TextField} from "@material-ui/core";
import * as React from "react";
import {useCallback, useContext, useEffect, useRef} from "react";
import {LocaleContext} from "shared/locale";
import {CheckCircle, ErrorRounded} from "@material-ui/icons";
const RouteFormControl = styled(FormControl)((props) => ({
"& > label": {
@@ -85,11 +86,12 @@ export default function RouteForm(props) {
);
if (route.type === "dynamic") {
let extraArgs, type;
let extraArgs, type, isValid = false;
try {
extraArgs = JSON.parse(route.extra);
type = typeof extraArgs;
extraArgs = JSON.stringify(extraArgs, null, 2);
isValid = type === "object";
} catch (e) {
extraArgs = null
}
@@ -97,14 +99,16 @@ export default function RouteForm(props) {
<RouteFormControl key={"form-control-extra"} fullWidth={true}>
<label htmlFor={"route-extra"}>{L("routes.arguments")}</label>
<textarea id={"route-extra"}
ref={extraRef}
ref={extraRef} style={!isValid ? {borderColor: "red"} : {}}
value={extraArgs ?? route.extra}
onChange={e => setRoute({...route, extra: minifyJson(e.target.value)})}/>
<i>{
<Box mt={1} fontStyle={"italic"} display={"grid"} gridTemplateColumns={"30px auto"}>{
extraArgs === null ?
L("routes.json_err") :
(type !== "object" ? L("routes.json_not_obj") : L("routes.json_ok"))
}</i>
<><ErrorRounded color={"secondary"}/><span>{L("routes.json_err")}</span></> :
(type !== "object" ?
<><ErrorRounded color={"secondary"}/><span>{L("routes.json_not_object")}</span></> :
<><CheckCircle color={"primary"} /><span>{L("routes.json_ok")}</span></>)
}</Box>
</RouteFormControl>
);
} else if (route.type === "static") {

View File

@@ -0,0 +1,4 @@
import UserEditView from "./user-edit";
import UserListView from "./user-list";
export default { UserEditView, UserListView };