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"} /> +
- -
- - -
+
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")}

    1. Home
    2. -
    3. System Log
    4. +
    5. {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) {