diff --git a/react/admin-panel/src/AdminDashboard.jsx b/react/admin-panel/src/AdminDashboard.jsx
index 436d9b7..d280da5 100644
--- a/react/admin-panel/src/AdminDashboard.jsx
+++ b/react/admin-panel/src/AdminDashboard.jsx
@@ -13,7 +13,8 @@ import './res/adminlte.min.css';
// views
import View404 from "./views/404";
const Overview = lazy(() => import('./views/overview'));
-const UserListView = lazy(() => import('./views/user-list'));
+const UserListView = lazy(() => import('./views/user/user-list'));
+const UserEditView = lazy(() => import('./views/user/user-edit'));
const GroupListView = lazy(() => import('./views/group-list'));
const EditGroupView = lazy(() => import('./views/group-edit'));
@@ -66,6 +67,7 @@ export default function AdminDashboard(props) {
}/>
}/>
}/>
+ }/>
}/>
}/>
} />
diff --git a/react/admin-panel/src/views/login.jsx b/react/admin-panel/src/views/login.jsx
index 279e45e..1fbd39d 100644
--- a/react/admin-panel/src/views/login.jsx
+++ b/react/admin-panel/src/views/login.jsx
@@ -250,7 +250,7 @@ export default function LoginForm(props) {
?
{error}
{emailConfirmed === false
- ? <> Click here to resend the confirmation email.>
+ ? <> Click here to resend the confirmation email.>
: <>>
}
diff --git a/react/admin-panel/src/views/user/user-edit.js b/react/admin-panel/src/views/user/user-edit.js
new file mode 100644
index 0000000..ebc8883
--- /dev/null
+++ b/react/admin-panel/src/views/user/user-edit.js
@@ -0,0 +1,89 @@
+import {Link, useNavigate, useParams} from "react-router-dom";
+import {useCallback, useContext, useEffect, useState} from "react";
+import {CircularProgress} from "@material-ui/core";
+import {LocaleContext} from "shared/locale";
+import * as React from "react";
+
+
+export default function UserEditView(props) {
+
+ const { api, showDialog } = props;
+ const { userId } = useParams();
+ const navigate = useNavigate();
+ const isNewUser = userId === "new";
+ const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
+ const [fetchUser, setFetchUser] = useState(!isNewUser);
+ const [user, setUser] = useState(isNewUser ? {
+ name: "",
+ fullName: "",
+ email: "",
+ password: "",
+ groups: [],
+ confirmed: false,
+ } : null);
+
+ useEffect(() => {
+ requestModules(props.api, ["general", "account"], currentLocale).then(data => {
+ if (!data.success) {
+ props.showDialog("Error fetching translations: " + data.msg);
+ }
+ });
+ }, [currentLocale]);
+
+ const onFetchUser = useCallback((force = false) => {
+ if (!isNewUser && (force || fetchUser)) {
+ setFetchUser(false);
+ api.getUser(userId).then((res) => {
+ if (!res.success) {
+ showDialog(res.msg, L("account.error_user_get"));
+ if (user === null) {
+ navigate("/admin/users");
+ }
+ } else {
+ setUser(res.user);
+ }
+ });
+ }
+ }, [api, showDialog, fetchUser, isNewUser, userId, user]);
+
+ useEffect(() => {
+ if (!isNewUser) {
+ onFetchUser(true);
+ }
+ }, []);
+
+ if (user === null) {
+ return
+ }
+
+ return
+
+
+ - Home
+ - User
+ - { isNewUser ? "New" : "Edit" }
+
+
+
+
+
{L(isNewUser ? "Create new User" : "Edit User")}
+
+
+
+
+}
\ No newline at end of file
diff --git a/react/admin-panel/src/views/user/user-list.js b/react/admin-panel/src/views/user/user-list.js
new file mode 100644
index 0000000..a3402be
--- /dev/null
+++ b/react/admin-panel/src/views/user/user-list.js
@@ -0,0 +1,93 @@
+import {Link, useNavigate} from "react-router-dom";
+import {useCallback, useContext, useEffect, useState} from "react";
+import {LocaleContext} from "shared/locale";
+import {
+ BoolColumn,
+ ControlsColumn,
+ DataColumn,
+ DataTable,
+ NumericColumn,
+ StringColumn
+} from "shared/elements/data-table";
+import {Button} from "@material-ui/core";
+import EditIcon from '@mui/icons-material/Edit';
+import {Chip} from "@mui/material";
+import AddIcon from "@mui/icons-material/Add";
+import usePagination from "shared/hooks/pagination";
+
+
+export default function UserListView(props) {
+
+ const api = props.api;
+ const showDialog = props.showDialog;
+ const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
+ const navigate = useNavigate();
+ const pagination = usePagination();
+ const [users, setUsers] = useState([]);
+
+ useEffect(() => {
+ requestModules(props.api, ["general", "account"], currentLocale).then(data => {
+ if (!data.success) {
+ props.showDialog("Error fetching translations: " + data.msg);
+ }
+ });
+ }, [currentLocale]);
+
+ const onFetchUsers = useCallback((page, count, orderBy, sortOrder) => {
+ api.fetchUsers(page, count, orderBy, sortOrder).then((res) => {
+ if (res.success) {
+ setUsers(res.users);
+ pagination.update(res.pagination);
+ } else {
+ showDialog(res.msg, "Error fetching users");
+ return null;
+ }
+ });
+ }, [api, showDialog]);
+
+ const groupColumn = (() => {
+ let column = new DataColumn(L("account.groups"), "groups");
+ column.renderData = (L, entry) => {
+ return Object.values(entry.groups).map(group => )
+ }
+ return column;
+ })();
+
+ const columnDefinitions = [
+ new NumericColumn(L("general.id"), "id"),
+ new StringColumn(L("account.username"), "name"),
+ new StringColumn(L("account.full_name"), "fullName"),
+ new StringColumn(L("account.email"), "email"),
+ groupColumn,
+ new BoolColumn(L("account.confirmed"), "confirmed", { align: "center" }),
+ new ControlsColumn(L("general.controls"), [
+ { label: L("general.edit"), element: EditIcon, onClick: (entry) => navigate(`/admin/user/${entry.id}`) }
+ ]),
+ ];
+
+ return
+
+
+
+
+
+ } size={"small"}>
+ {L("general.create_new")}
+
+
+
+
+
+}
\ No newline at end of file