admin panel continuation

This commit is contained in:
Roman 2023-03-05 15:45:56 +01:00
parent c8965e209b
commit aece0cb92a
4 changed files with 186 additions and 2 deletions

@ -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>

@ -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>
}

@ -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>
}