admin panel continuation
This commit is contained in:
parent
c8965e209b
commit
aece0cb92a
@ -13,7 +13,8 @@ import './res/adminlte.min.css';
|
|||||||
// views
|
// views
|
||||||
import View404 from "./views/404";
|
import View404 from "./views/404";
|
||||||
const Overview = lazy(() => import('./views/overview'));
|
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 GroupListView = lazy(() => import('./views/group-list'));
|
||||||
const EditGroupView = lazy(() => import('./views/group-edit'));
|
const EditGroupView = lazy(() => import('./views/group-edit'));
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ export default function AdminDashboard(props) {
|
|||||||
<Route path={"/admin"} element={<Overview {...controlObj} />}/>
|
<Route path={"/admin"} element={<Overview {...controlObj} />}/>
|
||||||
<Route path={"/admin/dashboard"} element={<Overview {...controlObj} />}/>
|
<Route path={"/admin/dashboard"} element={<Overview {...controlObj} />}/>
|
||||||
<Route path={"/admin/users"} element={<UserListView {...controlObj} />}/>
|
<Route path={"/admin/users"} element={<UserListView {...controlObj} />}/>
|
||||||
|
<Route path={"/admin/user/:userId"} element={<UserEditView {...controlObj} />}/>
|
||||||
<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={"*"} element={<View404 />} />
|
<Route path={"*"} element={<View404 />} />
|
||||||
|
@ -250,7 +250,7 @@ export default function LoginForm(props) {
|
|||||||
? <Alert severity="error">
|
? <Alert severity="error">
|
||||||
{error}
|
{error}
|
||||||
{emailConfirmed === false
|
{emailConfirmed === false
|
||||||
? <> <Link href={"/resendConfirmation"}>Click here</Link> to resend the confirmation email.</>
|
? <> <Link href={"/resendConfirmEmail"}>Click here</Link> to resend the confirmation email.</>
|
||||||
: <></>
|
: <></>
|
||||||
}
|
}
|
||||||
</Alert>
|
</Alert>
|
||||||
|
89
react/admin-panel/src/views/user/user-edit.js
Normal file
89
react/admin-panel/src/views/user/user-edit.js
Normal file
@ -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 <CircularProgress />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className={"content-header"}>
|
||||||
|
<div className={"container-fluid"}>
|
||||||
|
<ol className={"breadcrumb"}>
|
||||||
|
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
|
||||||
|
<li className="breadcrumb-item active"><Link to={"/admin/users"}>User</Link></li>
|
||||||
|
<li className="breadcrumb-item active">{ isNewUser ? "New" : "Edit" }</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div className={"content"}>
|
||||||
|
<div className={"container-fluid"}>
|
||||||
|
<h3>{L(isNewUser ? "Create new User" : "Edit User")}</h3>
|
||||||
|
<div className={"col-sm-12 col-lg-6"}>
|
||||||
|
<div className={"row"}>
|
||||||
|
<div className={"col-sm-6 form-group"}>
|
||||||
|
<label htmlFor={"username"}>{L("account.username")}</label>
|
||||||
|
<input type={"text"} className={"form-control"} placeholder={L("account.username")}
|
||||||
|
name={"username"} id={"username"} maxLength={32} value={user.name}
|
||||||
|
onChange={(e) => setUser({...user, name: e.target.value})}/>
|
||||||
|
</div>
|
||||||
|
<div className={"col-sm-6 form-group"}>
|
||||||
|
<label htmlFor={"fullName"}>{L("account.full_name")}</label>
|
||||||
|
<input type={"text"} className={"form-control"} placeholder={L("account.full_name")}
|
||||||
|
name={"fullName"} id={"fullName"} maxLength={32} value={user.fullName}
|
||||||
|
onChange={(e) => setUser({...user, fullName: e.target.value})}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
93
react/admin-panel/src/views/user/user-list.js
Normal file
93
react/admin-panel/src/views/user/user-list.js
Normal file
@ -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 => <Chip key={"group-" + group.id} label={group.name}/>)
|
||||||
|
}
|
||||||
|
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 <div className={"content-header"}>
|
||||||
|
<div className={"container-fluid"}>
|
||||||
|
<ol className={"breadcrumb"}>
|
||||||
|
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
|
||||||
|
<li className="breadcrumb-item active">Users</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div className={"content"}>
|
||||||
|
<div className={"container-fluid"}>
|
||||||
|
<DataTable
|
||||||
|
data={users}
|
||||||
|
pagination={pagination}
|
||||||
|
className={"table table-striped"}
|
||||||
|
fetchData={onFetchUsers}
|
||||||
|
placeholder={"No users to display"}
|
||||||
|
title={L("account.users")}
|
||||||
|
columns={columnDefinitions} />
|
||||||
|
<Link to="/admin/user/new">
|
||||||
|
<Button variant={"outlined"} startIcon={<AddIcon />} size={"small"}>
|
||||||
|
{L("general.create_new")}
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user