frontend, added user active flag, localization
This commit is contained in:
@@ -56,7 +56,7 @@ export default function EditGroupView(props) {
|
||||
<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"><Link to={"/admin/groups"}>Group</Link></li>
|
||||
<li className="breadcrumb-item active"><Link to={"/admin/groups"}>{L("account.group")}</Link></li>
|
||||
<li className="breadcrumb-item active">{ isNewGroup ? L("general.new") : groupId }</li>
|
||||
</ol>
|
||||
</div>
|
||||
@@ -68,13 +68,15 @@ export default function EditGroupView(props) {
|
||||
<div className={"col-6 pl-5 pr-5"}>
|
||||
<form role={"form"} onSubmit={(e) => this.submitForm(e)}>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"name"}>Group Name</label>
|
||||
<label htmlFor={"name"}>{L("account.group_name")}</label>
|
||||
<input type={"text"} className={"form-control"} placeholder={"Name"}
|
||||
name={"name"} id={"name"} maxLength={32} value={group.name}/>
|
||||
</div>
|
||||
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"color"}>Color</label>
|
||||
<label htmlFor={"color"}>
|
||||
{L("account.color")}
|
||||
</label>
|
||||
<div>
|
||||
<ColorPicker
|
||||
value={group.color}
|
||||
@@ -88,13 +90,15 @@ export default function EditGroupView(props) {
|
||||
</div>
|
||||
|
||||
<Link to={"/admin/groups"} className={"btn btn-info mt-2 mr-2"}>
|
||||
Back
|
||||
{L("general.go_back")}
|
||||
</Link>
|
||||
<button type={"submit"} className={"btn btn-primary mt-2"}>Submit</button>
|
||||
<button type={"submit"} className={"btn btn-primary mt-2"}>
|
||||
{L("general.submit")}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div className={"col-6"}>
|
||||
<h3>Members</h3>
|
||||
<h3>{L("account.members")}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -52,8 +52,8 @@ export default function GroupListView(props) {
|
||||
|
||||
const columnDefinitions = [
|
||||
new NumericColumn(L("general.id"), "id"),
|
||||
new StringColumn(L("group.name"), "name"),
|
||||
new NumericColumn(L("group.member_count"), "memberCount"),
|
||||
new StringColumn(L("account.name"), "name"),
|
||||
new NumericColumn(L("account.member_count"), "memberCount"),
|
||||
actionColumn,
|
||||
];
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function GroupListView(props) {
|
||||
<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>
|
||||
<li className="breadcrumb-item active">{L("account.groups")}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,8 @@ 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 {FormControl, FormGroup, FormLabel, MenuItem, Select} from "@material-ui/core";
|
||||
import {Box, FormControl, FormGroup, FormLabel, IconButton, MenuItem, Select} from "@material-ui/core";
|
||||
import {ExpandLess, ExpandMore} from "@material-ui/icons";
|
||||
|
||||
export default function LogView(props) {
|
||||
|
||||
@@ -59,6 +60,16 @@ export default function LogView(props) {
|
||||
});
|
||||
}, [api, showDialog, logLevel, timestamp, query]);
|
||||
|
||||
const onToggleDetails = useCallback(entry => {
|
||||
let newLogEntries = [...logEntries];
|
||||
for (const logEntry of newLogEntries) {
|
||||
if (logEntry.id === entry.id) {
|
||||
logEntry.showDetails = !logEntry.showDetails;
|
||||
}
|
||||
}
|
||||
setLogEntries(newLogEntries);
|
||||
}, [logEntries]);
|
||||
|
||||
useEffect(() => {
|
||||
// TODO: wait for user to finish typing before force reloading
|
||||
setForceReload(forceReload + 1);
|
||||
@@ -68,7 +79,19 @@ export default function LogView(props) {
|
||||
let column = new DataColumn(L("logs.message"), "message");
|
||||
column.sortable = false;
|
||||
column.renderData = (L, entry) => {
|
||||
return <pre>{entry.message}</pre>
|
||||
return <Box display={"grid"} gridTemplateColumns={"40px auto"}>
|
||||
<Box alignSelf={"top"} textAlign={"center"}>
|
||||
<IconButton size={"small"} onClick={() => onToggleDetails(entry)}
|
||||
title={L(entry.showDetails ? "logs.hide_details" : "logs.show_details")}>
|
||||
{entry.showDetails ? <ExpandLess /> : <ExpandMore />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box alignSelf={"center"}>
|
||||
<pre>
|
||||
{entry.showDetails ? entry.message : entry.message.split("\n")[0]}
|
||||
</pre>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
return column;
|
||||
})();
|
||||
|
||||
@@ -1,13 +1,45 @@
|
||||
import * as React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {format, getDaysInMonth} from "date-fns";
|
||||
import {CircularProgress, Icon} from "@material-ui/core";
|
||||
import {useCallback, useEffect, useState} from "react";
|
||||
import {CircularProgress} from "@material-ui/core";
|
||||
import {useCallback, useContext, useEffect, useState} from "react";
|
||||
import {LocaleContext} from "shared/locale";
|
||||
import {LibraryBooks, People} from "@material-ui/icons";
|
||||
import {ArrowCircleRight, Groups} from "@mui/icons-material";
|
||||
|
||||
const StatBox = (props) => <div className={"col-lg-3 col-6"}>
|
||||
<div className={"small-box bg-" + props.color}>
|
||||
<div className={"inner"}>
|
||||
{props.count ?
|
||||
<>
|
||||
<h3>{props.count}</h3>
|
||||
<p>{props.text}</p>
|
||||
</> : <CircularProgress variant={"determinate"} />
|
||||
}
|
||||
</div>
|
||||
<div className={"icon"}>
|
||||
{props.icon}
|
||||
</div>
|
||||
<Link to={props.link} className={"small-box-footer text-right p-1"}>
|
||||
More info <ArrowCircleRight />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
export default function Overview(props) {
|
||||
|
||||
const [fetchStats, setFetchStats] = useState(true);
|
||||
const [stats, setStats] = useState(null);
|
||||
const {translate: L, currentLocale, requestModules} = useContext(LocaleContext);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
requestModules(props.api, ["general", "admin"], currentLocale).then(data => {
|
||||
if (!data.success) {
|
||||
props.showDialog("Error fetching translations: " + data.msg);
|
||||
}
|
||||
});
|
||||
}, [currentLocale]);
|
||||
|
||||
const onFetchStats = useCallback((force = false) => {
|
||||
if (force || fetchStats) {
|
||||
@@ -16,7 +48,7 @@ export default function Overview(props) {
|
||||
if (res.success) {
|
||||
setStats(res.data);
|
||||
} else {
|
||||
props.showDialog("Error fetching stats: " + res.msg, "Error fetching stats");
|
||||
props.showDialog(res.msg, L("admin.fetch_stats_error"));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -73,12 +105,12 @@ export default function Overview(props) {
|
||||
<div className={"container-fluid"}>
|
||||
<div className={"row mb-2"}>
|
||||
<div className={"col-sm-6"}>
|
||||
<h1 className={"m-0 text-dark"}>Dashboard</h1>
|
||||
<h1 className={"m-0 text-dark"}>{L("admin.dashboard")}</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">Dashboard</li>
|
||||
<li className="breadcrumb-item active">{L("admin.dashboard")}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,24 +119,18 @@ export default function Overview(props) {
|
||||
<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>
|
||||
<StatBox color={"info"} count={stats?.userCount}
|
||||
text={L("admin.users_registered")}
|
||||
icon={<People />}
|
||||
link={"/admin/users"} />
|
||||
<StatBox color={"success"} count={stats?.groupCount}
|
||||
text={L("admin.available_groups")}
|
||||
icon={<Groups />}
|
||||
link={"/admin/users"} />
|
||||
<StatBox color={"warning"} count={stats?.pageCount}
|
||||
text={L("admin.routes_defined")}
|
||||
icon={<LibraryBooks />}
|
||||
link={"/admin/routes"} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
BoolColumn,
|
||||
ControlsColumn,
|
||||
DataColumn,
|
||||
DataTable,
|
||||
DataTable, DateTimeColumn,
|
||||
NumericColumn,
|
||||
StringColumn
|
||||
} from "shared/elements/data-table";
|
||||
@@ -48,7 +48,11 @@ export default function UserListView(props) {
|
||||
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 Object.values(entry.groups).map(group => <Chip
|
||||
key={"group-" + group.id}
|
||||
style={{ backgroundColor: group.color }}
|
||||
label={group.name} />
|
||||
)
|
||||
}
|
||||
return column;
|
||||
})();
|
||||
@@ -59,6 +63,8 @@ export default function UserListView(props) {
|
||||
new StringColumn(L("account.full_name"), "fullName"),
|
||||
new StringColumn(L("account.email"), "email"),
|
||||
groupColumn,
|
||||
new DateTimeColumn(L("account.registered_at"), "registeredAt"),
|
||||
new BoolColumn(L("account.active"), "active", { align: "center" }),
|
||||
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}`) }
|
||||
|
||||
Reference in New Issue
Block a user