frontend fixes
This commit is contained in:
parent
755da257f8
commit
9fc0a19f59
16
Core/Localization/de_DE/logs.php
Normal file
16
Core/Localization/de_DE/logs.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
"title" => "Systemlog",
|
||||||
|
"severity" => "Schweregrad",
|
||||||
|
"timestamp" => "Zeitpunkt",
|
||||||
|
"module" => "Modul",
|
||||||
|
"message" => "Nachricht",
|
||||||
|
"search" => "Suche",
|
||||||
|
"search_query" => "Suchanfrage",
|
||||||
|
"no_entries_placeholder" => "Keine Log-Einträge zum Anzeigen",
|
||||||
|
"timestamp_placeholder" => "Datum und Zeitpunk Auswählen zum Filtern",
|
||||||
|
|
||||||
|
// dialog
|
||||||
|
"fetch_log_error" => "Fehler beim Holen der Log-Einträge",
|
||||||
|
];
|
@ -3,6 +3,7 @@
|
|||||||
return [
|
return [
|
||||||
"title" => "Berechtigungen",
|
"title" => "Berechtigungen",
|
||||||
"title_short" => "ACL",
|
"title_short" => "ACL",
|
||||||
|
"search" => "Suche",
|
||||||
"query" => "Suchanfrage",
|
"query" => "Suchanfrage",
|
||||||
"add_permission" => "Berechtigung hinzufügen",
|
"add_permission" => "Berechtigung hinzufügen",
|
||||||
"permission" => "Berechtigung",
|
"permission" => "Berechtigung",
|
||||||
|
16
Core/Localization/en_US/logs.php
Normal file
16
Core/Localization/en_US/logs.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
"title" => "System Log",
|
||||||
|
"severity" => "Severity",
|
||||||
|
"timestamp" => "Timestamp",
|
||||||
|
"module" => "Module",
|
||||||
|
"message" => "Message",
|
||||||
|
"search" => "Search",
|
||||||
|
"search_query" => "Search query",
|
||||||
|
"no_entries_placeholder" => "No log entries to display",
|
||||||
|
"timestamp_placeholder" => "Select date and time to filter",
|
||||||
|
|
||||||
|
// dialog
|
||||||
|
"fetch_log_error" => "Error fetching log entries",
|
||||||
|
];
|
@ -3,6 +3,7 @@
|
|||||||
return [
|
return [
|
||||||
"title" => "Permissions",
|
"title" => "Permissions",
|
||||||
"title_short" => "ACL",
|
"title_short" => "ACL",
|
||||||
|
"search" => "Search",
|
||||||
"query" => "Search query",
|
"query" => "Search query",
|
||||||
"add_permission" => "Add permission",
|
"add_permission" => "Add permission",
|
||||||
"permission" => "Permission",
|
"permission" => "Permission",
|
||||||
|
@ -9,5 +9,5 @@
|
|||||||
<noscript>{{ L("general.noscript") }}</noscript>
|
<noscript>{{ L("general.noscript") }}</noscript>
|
||||||
<div type="module" id="admin-panel"></div>
|
<div type="module" id="admin-panel"></div>
|
||||||
<script src="/react/dist/admin-panel/index.js" nonce="{{ site.csp.nonce }}"></script>
|
<script src="/react/dist/admin-panel/index.js" nonce="{{ site.csp.nonce }}"></script>
|
||||||
<link rel="stylesheet" href="/react/dist/admin-panel/index.css" nonce="{{ site.csp.nonce }}"></link>
|
<link rel="stylesheet" href="/react/dist/admin-panel/index.css" nonce="{{ site.csp.nonce }}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -37,8 +37,8 @@ export default function AdminDashboard(props) {
|
|||||||
|
|
||||||
const showDialog = useCallback((message, title, options=["Close"], onOption = null) => {
|
const showDialog = useCallback((message, title, options=["Close"], onOption = null) => {
|
||||||
setDialog({
|
setDialog({
|
||||||
show: true, message:
|
show: true,
|
||||||
message,
|
message: message,
|
||||||
title: title,
|
title: title,
|
||||||
options: options,
|
options: options,
|
||||||
onOption: onOption,
|
onOption: onOption,
|
||||||
@ -80,27 +80,12 @@ export default function AdminDashboard(props) {
|
|||||||
<Route path={"/admin/groups"} element={<GroupListView {...controlObj} />}/>
|
<Route path={"/admin/groups"} element={<GroupListView {...controlObj} />}/>
|
||||||
<Route path={"/admin/group/:groupId"} element={<EditGroupView {...controlObj} />}/>
|
<Route path={"/admin/group/:groupId"} element={<EditGroupView {...controlObj} />}/>
|
||||||
<Route path={"/admin/logs"} element={<LogView {...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"} element={<RouteListView {...controlObj} />}/>
|
||||||
<Route path={"/admin/routes/:routeId"} element={<RouteEditView {...controlObj} />}/>
|
<Route path={"/admin/routes/:routeId"} element={<RouteEditView {...controlObj} />}/>
|
||||||
<Route path={"*"} element={<View404 />} />
|
<Route path={"*"} element={<View404 />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
</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}/>
|
<Dialog {...dialog}/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,7 @@ export default function Sidebar(props) {
|
|||||||
"name": "admin.settings",
|
"name": "admin.settings",
|
||||||
"icon": "tools"
|
"icon": "tools"
|
||||||
},
|
},
|
||||||
"acl": {
|
"permissions": {
|
||||||
"name": "admin.acl",
|
"name": "admin.acl",
|
||||||
"icon": "door-open"
|
"icon": "door-open"
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
TableContainer,
|
TableContainer,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
IconButton, styled
|
IconButton, styled, FormGroup, FormLabel, FormControl, Box
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import {Add, Delete, Edit, Refresh} from "@material-ui/icons";
|
import {Add, Delete, Edit, Refresh} from "@material-ui/icons";
|
||||||
import {USER_GROUP_ADMIN} from "shared/constants";
|
import {USER_GROUP_ADMIN} from "shared/constants";
|
||||||
@ -224,39 +224,40 @@ export default function AccessControlList(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={"row"}>
|
<div className={"row"}>
|
||||||
<div className={"col-6"}>
|
<FormGroup className={"col-6"}>
|
||||||
<div className={"form-group"}>
|
<FormLabel>{L("permissions.search")}</FormLabel>
|
||||||
<label>{L("permissions.query")}</label>
|
|
||||||
<TextField
|
<TextField
|
||||||
className={"form-control"}
|
|
||||||
placeholder={L("permissions.query") + "…"}
|
placeholder={L("permissions.query") + "…"}
|
||||||
value={query}
|
value={query}
|
||||||
onChange={e => setQuery(e.target.value)}
|
onChange={e => setQuery(e.target.value)}
|
||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
size={"small"} />
|
size={"small"} />
|
||||||
</div>
|
</FormGroup>
|
||||||
</div>
|
|
||||||
<div className={"col-6 text-right"}>
|
<div className={"col-6 text-right"}>
|
||||||
<label>{L("general.controls")}</label>
|
<Box>
|
||||||
<div className={"form-group"}>
|
<FormLabel>{L("general.controls")}</FormLabel>
|
||||||
<Button variant={"outlined"} color={"primary"} className={"m-1"} startIcon={<Refresh />} onClick={() => onFetchACL(true)}>
|
</Box>
|
||||||
|
<Box mb={2}>
|
||||||
|
<Button variant={"outlined"} color={"primary"} className={"mr-1"}
|
||||||
|
startIcon={<Refresh />} onClick={() => onFetchACL(true)}>
|
||||||
{L("general.reload")}
|
{L("general.reload")}
|
||||||
</Button>
|
</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({
|
onClick={() => setDialogData({
|
||||||
open: true,
|
open: true,
|
||||||
title: L("permissions.add_permission"),
|
title: L("permissions.add_permission"),
|
||||||
inputs: [
|
inputs: [
|
||||||
{ type: "label", value: L("general.method") + ":" },
|
{ type: "label", value: L("permissions.method") + ":" },
|
||||||
{ type: "text", name: "method", value: "", placeholder: L("general.method") },
|
{ type: "text", name: "method", value: "", placeholder: L("permissions.method") },
|
||||||
{ type: "label", value: L("general.description") + ":" },
|
{ type: "label", value: L("permissions.description") + ":" },
|
||||||
{ type: "text", name: "description", maxLength: 128, placeholder: L("general.description") }
|
{ type: "text", name: "description", maxLength: 128, placeholder: L("permissions.description") }
|
||||||
],
|
],
|
||||||
onOption: (option, inputData) => option === 0 && onUpdatePermission(inputData, [])
|
onOption: (option, inputData) => option === 0 && onUpdatePermission(inputData, [])
|
||||||
})} >
|
})} >
|
||||||
{L("general.add")}
|
{L("general.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TableContainer component={Paper} style={{overflowX: "initial"}}>
|
<TableContainer component={Paper} style={{overflowX: "initial"}}>
|
||||||
|
@ -62,7 +62,6 @@ export default function GroupListView(props) {
|
|||||||
<div className={"container-fluid"}>
|
<div className={"container-fluid"}>
|
||||||
<div className={"row mb-2"}>
|
<div className={"row mb-2"}>
|
||||||
<div className={"col-sm-6"}>
|
<div className={"col-sm-6"}>
|
||||||
<h1 className={"m-0 text-dark"}>Users</h1>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={"col-sm-6"}>
|
<div className={"col-sm-6"}>
|
||||||
<ol className={"breadcrumb float-sm-right"}>
|
<ol className={"breadcrumb float-sm-right"}>
|
||||||
|
4
react/admin-panel/src/views/group/index.js
Normal file
4
react/admin-panel/src/views/group/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import EditGroupView from "./group-edit";
|
||||||
|
import GroupListView from "./group-list";
|
||||||
|
|
||||||
|
export default { EditGroupView, GroupListView };
|
@ -4,12 +4,12 @@ import {Link} from "react-router-dom";
|
|||||||
import usePagination from "shared/hooks/pagination";
|
import usePagination from "shared/hooks/pagination";
|
||||||
import {DataColumn, DataTable, DateTimeColumn, NumericColumn, StringColumn} from "shared/elements/data-table";
|
import {DataColumn, DataTable, DateTimeColumn, NumericColumn, StringColumn} from "shared/elements/data-table";
|
||||||
import {TextField} from "@mui/material";
|
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 { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
import {API_DATETIME_FORMAT} from "shared/constants";
|
import {API_DATETIME_FORMAT} from "shared/constants";
|
||||||
import {format, toDate} from "date-fns";
|
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) {
|
export default function LogView(props) {
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export default function LogView(props) {
|
|||||||
const [forceReload, setForceReload] = useState(0);
|
const [forceReload, setForceReload] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestModules(props.api, ["general"], currentLocale).then(data => {
|
requestModules(props.api, ["general", "logs"], currentLocale).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
props.showDialog("Error fetching translations: " + data.msg);
|
props.showDialog("Error fetching translations: " + data.msg);
|
||||||
}
|
}
|
||||||
@ -38,16 +38,22 @@ export default function LogView(props) {
|
|||||||
}, [currentLocale]);
|
}, [currentLocale]);
|
||||||
|
|
||||||
const onFetchLogs = useCallback((page, count, orderBy, sortOrder) => {
|
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,
|
api.fetchLogEntries(page, count, orderBy, sortOrder,
|
||||||
LOG_LEVELS[logLevel],
|
LOG_LEVELS[logLevel], apiTimeStamp, query).then((res) => {
|
||||||
timestamp ? format(timestamp, API_DATETIME_FORMAT) : null,
|
|
||||||
query
|
|
||||||
).then((res) => {
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setLogEntries(res.logs);
|
setLogEntries(res.logs);
|
||||||
pagination.update(res.pagination);
|
pagination.update(res.pagination);
|
||||||
} else {
|
} else {
|
||||||
showDialog(res.msg, "Error fetching log entries");
|
showDialog(res.msg, L("logs.fetch_logs_error"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -59,7 +65,7 @@ export default function LogView(props) {
|
|||||||
}, [query, timestamp, logLevel]);
|
}, [query, timestamp, logLevel]);
|
||||||
|
|
||||||
const messageColumn = (() => {
|
const messageColumn = (() => {
|
||||||
let column = new DataColumn(L("message"), "message");
|
let column = new DataColumn(L("logs.message"), "message");
|
||||||
column.sortable = false;
|
column.sortable = false;
|
||||||
column.renderData = (L, entry) => {
|
column.renderData = (L, entry) => {
|
||||||
return <pre>{entry.message}</pre>
|
return <pre>{entry.message}</pre>
|
||||||
@ -69,9 +75,9 @@ export default function LogView(props) {
|
|||||||
|
|
||||||
const columnDefinitions = [
|
const columnDefinitions = [
|
||||||
new NumericColumn(L("general.id"), "id"),
|
new NumericColumn(L("general.id"), "id"),
|
||||||
new StringColumn(L("module"), "module"),
|
new StringColumn(L("logs.module"), "module"),
|
||||||
new StringColumn(L("severity"), "severity"),
|
new StringColumn(L("logs.severity"), "severity"),
|
||||||
new DateTimeColumn(L("timestamp"), "timestamp", { precise: true }),
|
new DateTimeColumn(L("logs.timestamp"), "timestamp", { precise: true }),
|
||||||
messageColumn,
|
messageColumn,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -80,54 +86,55 @@ export default function LogView(props) {
|
|||||||
<div className={"container-fluid"}>
|
<div className={"container-fluid"}>
|
||||||
<div className={"row mb-2"}>
|
<div className={"row mb-2"}>
|
||||||
<div className={"col-sm-6"}>
|
<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>
|
||||||
<div className={"col-sm-6"}>
|
<div className={"col-sm-6"}>
|
||||||
<ol className={"breadcrumb float-sm-right"}>
|
<ol className={"breadcrumb float-sm-right"}>
|
||||||
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
|
<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>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={"content overflow-auto"}>
|
<div className={"content overflow-auto"}>
|
||||||
<div className={"row p-2"}>
|
<div className={"row p-2"}>
|
||||||
<div className={"col-2"}>
|
<FormGroup className={"col-2"}>
|
||||||
<div className={"form-group"}>
|
<FormLabel>{L("logs.severity")}</FormLabel>
|
||||||
<label>{L("log.severity")}</label>
|
<FormControl>
|
||||||
<Select native className={"form-control"} value={logLevel} onChange={e => setLogLevel(parseInt(e.target.value))}>
|
<TextField select variant={"outlined"} size={"small"} value={logLevel}
|
||||||
|
onChange={e => setLogLevel(parseInt(e.target.value))}
|
||||||
|
inputProps={{ size: "small" }}>
|
||||||
{LOG_LEVELS.map((value, index) =>
|
{LOG_LEVELS.map((value, index) =>
|
||||||
<option key={"option-" + value} value={index}>{value}</option>)
|
<MenuItem key={"option-" + value} value={index}>{value}</MenuItem>)
|
||||||
}
|
}
|
||||||
</Select>
|
</TextField>
|
||||||
</div>
|
</FormControl>
|
||||||
</div>
|
</FormGroup>
|
||||||
<div className={"col-4"}>
|
<FormGroup className={"col-4"}>
|
||||||
<div className={"form-group"}>
|
<FormLabel>{L("logs.timestamp")}</FormLabel>
|
||||||
<label>{L("log.timestamp")}</label>
|
<FormControl>
|
||||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
<DesktopDateTimePicker className={"form-control"}
|
<DateTimePicker label={L("logs.timestamp_placeholder") + "…"}
|
||||||
label={L("Select date time to filter...")}
|
|
||||||
value={timestamp ? toDate(new Date()) : null}
|
value={timestamp ? toDate(new Date()) : null}
|
||||||
format={L("general.datefns_datetime_format_precise")}
|
format={L("general.datefns_datetime_format_precise")}
|
||||||
onChange={(newValue) => setTimestamp(newValue)}
|
onChange={(newValue) => setTimestamp(newValue)}
|
||||||
slotProps={{ textField: { } }}
|
slotProps={{ textField: { size:'small' } }}
|
||||||
|
sx={{"& .MuiInputBase-input": { height: "23px", padding: 1 }}}
|
||||||
/>
|
/>
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
</div>
|
</FormControl>
|
||||||
</div>
|
</FormGroup>
|
||||||
<div className={"col-6"}>
|
<FormGroup className={"col-6"}>
|
||||||
<div className={"form-group"}>
|
<FormLabel>{L("logs.search")}</FormLabel>
|
||||||
<label>{L("log.query")}</label>
|
<FormControl>
|
||||||
<TextField
|
<TextField
|
||||||
className={"form-control"}
|
placeholder={L("logs.search_query") + "…"}
|
||||||
placeholder={L("log.search_query") + "…"}
|
|
||||||
value={query}
|
value={query}
|
||||||
onChange={e => setQuery(e.target.value)}
|
onChange={e => setQuery(e.target.value)}
|
||||||
variant={"outlined"}
|
variant={"outlined"}
|
||||||
size={"small"}/>
|
size={"small"}/>
|
||||||
</div>
|
</FormControl>
|
||||||
</div>
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className={"container-fluid"}>
|
<div className={"container-fluid"}>
|
||||||
<DataTable
|
<DataTable
|
||||||
@ -138,7 +145,7 @@ export default function LogView(props) {
|
|||||||
forceReload={forceReload}
|
forceReload={forceReload}
|
||||||
defaultSortColumn={3}
|
defaultSortColumn={3}
|
||||||
defaultSortOrder={"desc"}
|
defaultSortOrder={"desc"}
|
||||||
placeholder={"No log entries to display"}
|
placeholder={L("logs.no_entries_placeholder")}
|
||||||
columns={columnDefinitions} />
|
columns={columnDefinitions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
4
react/admin-panel/src/views/route/index.js
Normal file
4
react/admin-panel/src/views/route/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import RouteEditView from "./route-edit";
|
||||||
|
import RouteListView from "./route-list";
|
||||||
|
|
||||||
|
export default { RouteEditView, RouteListView };
|
@ -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 * as React from "react";
|
||||||
import {useCallback, useContext, useEffect, useRef} from "react";
|
import {useCallback, useContext, useEffect, useRef} from "react";
|
||||||
import {LocaleContext} from "shared/locale";
|
import {LocaleContext} from "shared/locale";
|
||||||
|
import {CheckCircle, ErrorRounded} from "@material-ui/icons";
|
||||||
|
|
||||||
const RouteFormControl = styled(FormControl)((props) => ({
|
const RouteFormControl = styled(FormControl)((props) => ({
|
||||||
"& > label": {
|
"& > label": {
|
||||||
@ -85,11 +86,12 @@ export default function RouteForm(props) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (route.type === "dynamic") {
|
if (route.type === "dynamic") {
|
||||||
let extraArgs, type;
|
let extraArgs, type, isValid = false;
|
||||||
try {
|
try {
|
||||||
extraArgs = JSON.parse(route.extra);
|
extraArgs = JSON.parse(route.extra);
|
||||||
type = typeof extraArgs;
|
type = typeof extraArgs;
|
||||||
extraArgs = JSON.stringify(extraArgs, null, 2);
|
extraArgs = JSON.stringify(extraArgs, null, 2);
|
||||||
|
isValid = type === "object";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
extraArgs = null
|
extraArgs = null
|
||||||
}
|
}
|
||||||
@ -97,14 +99,16 @@ export default function RouteForm(props) {
|
|||||||
<RouteFormControl key={"form-control-extra"} fullWidth={true}>
|
<RouteFormControl key={"form-control-extra"} fullWidth={true}>
|
||||||
<label htmlFor={"route-extra"}>{L("routes.arguments")}</label>
|
<label htmlFor={"route-extra"}>{L("routes.arguments")}</label>
|
||||||
<textarea id={"route-extra"}
|
<textarea id={"route-extra"}
|
||||||
ref={extraRef}
|
ref={extraRef} style={!isValid ? {borderColor: "red"} : {}}
|
||||||
value={extraArgs ?? route.extra}
|
value={extraArgs ?? route.extra}
|
||||||
onChange={e => setRoute({...route, extra: minifyJson(e.target.value)})}/>
|
onChange={e => setRoute({...route, extra: minifyJson(e.target.value)})}/>
|
||||||
<i>{
|
<Box mt={1} fontStyle={"italic"} display={"grid"} gridTemplateColumns={"30px auto"}>{
|
||||||
extraArgs === null ?
|
extraArgs === null ?
|
||||||
L("routes.json_err") :
|
<><ErrorRounded color={"secondary"}/><span>{L("routes.json_err")}</span></> :
|
||||||
(type !== "object" ? L("routes.json_not_obj") : L("routes.json_ok"))
|
(type !== "object" ?
|
||||||
}</i>
|
<><ErrorRounded color={"secondary"}/><span>{L("routes.json_not_object")}</span></> :
|
||||||
|
<><CheckCircle color={"primary"} /><span>{L("routes.json_ok")}</span></>)
|
||||||
|
}</Box>
|
||||||
</RouteFormControl>
|
</RouteFormControl>
|
||||||
);
|
);
|
||||||
} else if (route.type === "static") {
|
} else if (route.type === "static") {
|
||||||
|
4
react/admin-panel/src/views/user/index.js
Normal file
4
react/admin-panel/src/views/user/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import UserEditView from "./user-edit";
|
||||||
|
import UserListView from "./user-list";
|
||||||
|
|
||||||
|
export default { UserEditView, UserListView };
|
@ -1,5 +1,5 @@
|
|||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import {Box, MenuItem, Select, Pagination as MuiPagination} from "@mui/material";
|
import {Box, Select, Pagination as MuiPagination} from "@mui/material";
|
||||||
import {sprintf} from "sprintf-js";
|
import {sprintf} from "sprintf-js";
|
||||||
import {FormControl} from "@material-ui/core";
|
import {FormControl} from "@material-ui/core";
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user