diff --git a/Core/Localization/de_DE/logs.php b/Core/Localization/de_DE/logs.php
new file mode 100644
index 0000000..05666aa
--- /dev/null
+++ b/Core/Localization/de_DE/logs.php
@@ -0,0 +1,16 @@
+ "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",
+];
\ No newline at end of file
diff --git a/Core/Localization/de_DE/permissions.php b/Core/Localization/de_DE/permissions.php
index 5d513ad..4976bb1 100644
--- a/Core/Localization/de_DE/permissions.php
+++ b/Core/Localization/de_DE/permissions.php
@@ -3,6 +3,7 @@
return [
"title" => "Berechtigungen",
"title_short" => "ACL",
+ "search" => "Suche",
"query" => "Suchanfrage",
"add_permission" => "Berechtigung hinzufügen",
"permission" => "Berechtigung",
diff --git a/Core/Localization/en_US/logs.php b/Core/Localization/en_US/logs.php
new file mode 100644
index 0000000..b9c23f8
--- /dev/null
+++ b/Core/Localization/en_US/logs.php
@@ -0,0 +1,16 @@
+ "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",
+];
\ No newline at end of file
diff --git a/Core/Localization/en_US/permissions.php b/Core/Localization/en_US/permissions.php
index fc1ab82..01c1244 100644
--- a/Core/Localization/en_US/permissions.php
+++ b/Core/Localization/en_US/permissions.php
@@ -3,6 +3,7 @@
return [
"title" => "Permissions",
"title_short" => "ACL",
+ "search" => "Search",
"query" => "Search query",
"add_permission" => "Add permission",
"permission" => "Permission",
diff --git a/Core/Templates/admin.twig b/Core/Templates/admin.twig
index 7d669c1..f803600 100644
--- a/Core/Templates/admin.twig
+++ b/Core/Templates/admin.twig
@@ -9,5 +9,5 @@
-
+
{% endblock %}
diff --git a/react/admin-panel/src/AdminDashboard.jsx b/react/admin-panel/src/AdminDashboard.jsx
index b964fe3..4fab9b3 100644
--- a/react/admin-panel/src/AdminDashboard.jsx
+++ b/react/admin-panel/src/AdminDashboard.jsx
@@ -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) {
}/>
}/>
}/>
- }/>
+ }/>
}/>
}/>
} />
- {/*
-
- {
- let newProps = {...props, ...this.controlObj};
- return
- }}/>
-
-
-
-
-
-
-
-
- */}
diff --git a/react/admin-panel/src/elements/sidebar.js b/react/admin-panel/src/elements/sidebar.js
index da8b2aa..6830439 100644
--- a/react/admin-panel/src/elements/sidebar.js
+++ b/react/admin-panel/src/elements/sidebar.js
@@ -44,7 +44,7 @@ export default function Sidebar(props) {
"name": "admin.settings",
"icon": "tools"
},
- "acl": {
+ "permissions": {
"name": "admin.acl",
"icon": "door-open"
},
diff --git a/react/admin-panel/src/views/access-control-list.js b/react/admin-panel/src/views/access-control-list.js
index 6733709..9135b90 100644
--- a/react/admin-panel/src/views/access-control-list.js
+++ b/react/admin-panel/src/views/access-control-list.js
@@ -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) {
-
-
-
- setQuery(e.target.value)}
- variant={"outlined"}
- size={"small"} />
-
-
+
+ {L("permissions.search")}
+ setQuery(e.target.value)}
+ variant={"outlined"}
+ size={"small"} />
+
-
-
- } onClick={() => onFetchACL(true)}>
+
+ {L("general.controls")}
+
+
+ } onClick={() => onFetchACL(true)}>
{L("general.reload")}
- } disabled={!props.api.hasGroup(USER_GROUP_ADMIN)}
+ }
+ 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")}
-
+
diff --git a/react/admin-panel/src/views/group/group-list.js b/react/admin-panel/src/views/group/group-list.js
index 9c3b3d2..8c10825 100644
--- a/react/admin-panel/src/views/group/group-list.js
+++ b/react/admin-panel/src/views/group/group-list.js
@@ -62,7 +62,6 @@ export default function GroupListView(props) {
-
Users
diff --git a/react/admin-panel/src/views/group/index.js b/react/admin-panel/src/views/group/index.js
new file mode 100644
index 0000000..f9ac230
--- /dev/null
+++ b/react/admin-panel/src/views/group/index.js
@@ -0,0 +1,4 @@
+import EditGroupView from "./group-edit";
+import GroupListView from "./group-list";
+
+export default { EditGroupView, GroupListView };
\ No newline at end of file
diff --git a/react/admin-panel/src/views/log-view.js b/react/admin-panel/src/views/log-view.js
index 97dd80d..e04ae8c 100644
--- a/react/admin-panel/src/views/log-view.js
+++ b/react/admin-panel/src/views/log-view.js
@@ -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 {entry.message}
@@ -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) {
-
System Log
+ {L("logs.title")}
- Home
- - System Log
+ - {L("logs.title")}
-
-
-
-
-
-
-
-
-
+
+
+
+
+ {L("logs.timestamp")}
+
- setTimestamp(newValue)}
- slotProps={{ textField: { } }}
+ slotProps={{ textField: { size:'small' } }}
+ sx={{"& .MuiInputBase-input": { height: "23px", padding: 1 }}}
/>
-
-
-
-
-
+
+
+
+ {L("logs.search")}
+
setQuery(e.target.value)}
variant={"outlined"}
size={"small"}/>
-
-
+
+
diff --git a/react/admin-panel/src/views/route/index.js b/react/admin-panel/src/views/route/index.js
new file mode 100644
index 0000000..f63697e
--- /dev/null
+++ b/react/admin-panel/src/views/route/index.js
@@ -0,0 +1,4 @@
+import RouteEditView from "./route-edit";
+import RouteListView from "./route-list";
+
+export default { RouteEditView, RouteListView };
\ No newline at end of file
diff --git a/react/admin-panel/src/views/route/route-form.js b/react/admin-panel/src/views/route/route-form.js
index fd32030..12e6835 100644
--- a/react/admin-panel/src/views/route/route-form.js
+++ b/react/admin-panel/src/views/route/route-form.js
@@ -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) {
);
} else if (route.type === "static") {
diff --git a/react/admin-panel/src/views/user/index.js b/react/admin-panel/src/views/user/index.js
new file mode 100644
index 0000000..15aac30
--- /dev/null
+++ b/react/admin-panel/src/views/user/index.js
@@ -0,0 +1,4 @@
+import UserEditView from "./user-edit";
+import UserListView from "./user-list";
+
+export default { UserEditView, UserListView };
\ No newline at end of file
diff --git a/react/shared/hooks/pagination.js b/react/shared/hooks/pagination.js
index bc675e2..d9b04e6 100644
--- a/react/shared/hooks/pagination.js
+++ b/react/shared/hooks/pagination.js
@@ -1,5 +1,5 @@
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 {FormControl} from "@material-ui/core";