SQL expression rewrite, Pagination, some frontend stuff

This commit is contained in:
2023-01-05 22:47:17 +01:00
parent 4bfd6754cf
commit 99bfd7e505
61 changed files with 1745 additions and 570 deletions

View File

@@ -0,0 +1,5 @@
export default function View404(props) {
return <b>Not found</b>
}

View File

@@ -0,0 +1,35 @@
import {useCallback, useEffect, useState} from "react";
import {useNavigate, useParams} from "react-router-dom";
export default function EditGroupView(props) {
// const [groupId, setGroupId] = useState(props?.match?.groupId !== "new" ? parseInt(props.match.groupId) : null);
const { groupId } = useParams();
const navigate = useNavigate();
const [fetchGroup, setFetchGroup] = useState(groupId !== "new");
const [group, setGroup] = useState(null);
const onFetchGroup = useCallback((force = false) => {
if (force || fetchGroup) {
setFetchGroup(false);
props.api.getGroup(groupId).then(res => {
if (!res.success) {
props.showDialog(res.msg, "Error fetching group");
navigate("/admin/groups");
} else {
setGroup(res.group);
}
});
}
}, []);
useEffect(() => {
onFetchGroup();
}, []);
return <></>
}

View File

@@ -0,0 +1,80 @@
import {Link, useNavigate} from "react-router-dom";
import {useCallback, useContext, useEffect, useState} from "react";
import {LocaleContext} from "shared/locale";
import {DataColumn, DataTable, NumericColumn, StringColumn} from "shared/elements/data-table";
import {Button, IconButton} from "@material-ui/core";
import EditIcon from '@mui/icons-material/Edit';
import AddIcon from '@mui/icons-material/Add';
export default function GroupListView(props) {
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
const navigate = useNavigate();
useEffect(() => {
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
if (!data.success) {
alert(data.msg);
}
});
}, [currentLocale]);
const onFetchGroups = useCallback(async (page, count, orderBy, sortOrder) => {
let res = await props.api.fetchGroups(page, count, orderBy, sortOrder);
if (res.success) {
return Promise.resolve([res.groups, res.pagination]);
} else {
props.showAlert("Error fetching groups", res.msg);
return null;
}
}, []);
const actionColumn = (() => {
let column = new DataColumn(L("general.actions"), null, false);
column.renderData = (entry) => <>
<IconButton size={"small"} title={L("general.edit")} onClick={() => navigate("/admin/group/" + entry.id)}>
<EditIcon />
</IconButton>
</>
return column;
})();
const columnDefinitions = [
new NumericColumn(L("general.id"), "id"),
new StringColumn(L("group.name"), "name"),
new NumericColumn(L("group.member_count"), "memberCount"),
actionColumn,
];
return <>
<div className={"content-header"}>
<div className={"container-fluid"}>
<div className={"row mb-2"}>
<div className={"col-sm-6"}>
<h1 className={"m-0 text-dark"}>Users</h1>
</div>
<div className={"col-sm-6"}>
<ol className={"breadcrumb float-sm-right"}>
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
<li className="breadcrumb-item active">Groups</li>
</ol>
</div>
</div>
</div>
<div className={"content"}>
<div className={"container-fluid"}>
<Link to="/admin/group/new">
<Button variant={"outlined"} startIcon={<AddIcon />} size={"small"}>
{L("general.create_new")}
</Button>
</Link>
<DataTable className={"table table-striped"}
fetchData={onFetchGroups}
placeholder={"No groups to display"}
columns={columnDefinitions} />
</div>
</div>
</div>
</>
}

View File

@@ -1,9 +1,33 @@
import * as React from "react";
import {Link} from "react-router-dom";
import {format, getDaysInMonth} from "date-fns";
import {Collapse} from "react-collapse";
import {Bar} from "react-chartjs-2";
import {CircularProgress, Icon} from "@material-ui/core";
import {useCallback, useEffect, useState} from "react";
export default function Overview(props) {
const [fetchStats, setFetchStats] = useState(true);
const [stats, setStats] = useState(null);
const onFetchStats = useCallback((force = false) => {
if (force || fetchStats) {
setFetchStats(false);
props.api.getStats().then((res) => {
if (res.success) {
setStats(res.data);
} else {
props.showDialog("Error fetching stats: " + res.msg, "Error fetching stats");
}
});
}
}, [fetchStats]);
useEffect(() => {
onFetchStats();
}, []);
const today = new Date();
const numDays = getDaysInMonth(today);
@@ -46,6 +70,8 @@ export default function Overview(props) {
}
*/
console.log(stats);
return <>
<div className={"content-header"}>
<div className={"container-fluid"}>
@@ -63,57 +89,28 @@ export default function Overview(props) {
</div>
</div>
<section className={"content"}>
<div className={"container-fluid"}>
<div className={"row"}>
<div className={"col-lg-3 col-6"}>
<div className="small-box bg-info">
<div className={"inner"}>
{stats ?
<>
<h3>{stats.userCount}</h3>
<p>Users registered</p>
</> : <CircularProgress variant={"determinate"} />
}
</div>
<div className="icon">
<Icon icon={"users"} />
</div>
<Link to={"/admin/users"} className="small-box-footer">
More info <Icon icon={"arrow-circle-right"}/>
</Link>
</div>
</div>
</div>
</div>
</section>
</>
}
/*
export default class Overview extends React.Component {
constructor(props) {
super(props);
this.state = {
chartVisible : true,
statusVisible : true,
userCount: 0,
notificationCount: 0,
visitorsTotal: 0,
visitors: { },
server: { load_avg: ["Unknown"] },
errors: []
}
}
removeError(i) {
if (i >= 0 && i < this.state.errors.length) {
let errors = this.state.errors.slice();
errors.splice(i, 1);
this.setState({...this.state, errors: errors});
}
}
componentDidMount() {
this.parent.api.getStats().then((res) => {
if(!res.success) {
let errors = this.state.errors.slice();
errors.push({ message: res.msg, title: "Error fetching Stats" });
this.setState({ ...this.state, errors: errors });
} else {
this.setState({
...this.state,
userCount: res.userCount,
pageCount: res.pageCount,
visitors: res.visitors,
visitorsTotal: res.visitorsTotal,
server: res.server
});
}
});
}
render() {
}
}*/

View File

@@ -0,0 +1,90 @@
import {Link, Navigate, useNavigate} from "react-router-dom";
import {useCallback, useContext, useEffect} from "react";
import {LocaleContext} from "shared/locale";
import {DataColumn, DataTable, NumericColumn, StringColumn} from "shared/elements/data-table";
import {Button, IconButton} from "@material-ui/core";
import EditIcon from '@mui/icons-material/Edit';
import {Chip} from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
export default function UserListView(props) {
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
const navigate = useNavigate();
useEffect(() => {
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
if (!data.success) {
props.showDialog("Error fetching translations: " + data.msg);
}
});
}, [currentLocale]);
const onFetchUsers = useCallback(async (page, count, orderBy, sortOrder) => {
let res = await props.api.fetchUsers(page, count, orderBy, sortOrder);
if (res.success) {
return Promise.resolve([res.users, res.pagination]);
} else {
props.showDialog(res.msg, "Error fetching users");
return null;
}
}, []);
const groupColumn = (() => {
let column = new DataColumn(L("account.groups"), "groups");
column.renderData = (entry) => {
return Object.values(entry.groups).map(group => <Chip key={"group-" + group.id} label={group.name}/>)
}
return column;
})();
const actionColumn = (() => {
let column = new DataColumn(L("general.actions"), null, false);
column.renderData = (entry) => <>
<IconButton size={"small"} title={L("general.edit")} onClick={() => navigate("/admin/user/" + entry.id)}>
<EditIcon />
</IconButton>
</>
return column;
})();
const columnDefinitions = [
new NumericColumn(L("general.id"), "id"),
new StringColumn(L("account.username"), "name"),
new StringColumn(L("account.email"), "email"),
groupColumn,
new StringColumn(L("account.full_name"), "full_name"),
actionColumn,
];
return <>
<div className={"content-header"}>
<div className={"container-fluid"}>
<div className={"row mb-2"}>
<div className={"col-sm-6"}>
<h1 className={"m-0 text-dark"}>Users</h1>
</div>
<div className={"col-sm-6"}>
<ol className={"breadcrumb float-sm-right"}>
<li className={"breadcrumb-item"}><Link to={"/admin/dashboard"}>Home</Link></li>
<li className="breadcrumb-item active">Users</li>
</ol>
</div>
</div>
</div>
<div className={"content"}>
<div className={"container-fluid"}>
<Link to="/admin/user/new">
<Button variant={"outlined"} startIcon={<AddIcon />} size={"small"}>
{L("general.create_new")}
</Button>
</Link>
<DataTable className={"table table-striped"}
fetchData={onFetchUsers}
placeholder={"No users to display"} columns={columnDefinitions} />
</div>
</div>
</div>
</>
}