User Stuff

This commit is contained in:
2020-06-23 16:26:04 +02:00
parent a4504d8336
commit 7ac6b4d245
7 changed files with 223 additions and 12 deletions

View File

@@ -43,6 +43,10 @@ export default class API {
return this.apiCall("notifications/fetch");
}
async getUser(id) {
return this.apiCall("user/get", { id: id });
}
async fetchUsers(pageNum = 1, count = 20) {
return this.apiCall("user/fetch", { page: pageNum, count: count });
}

View File

@@ -4,7 +4,7 @@ import React from "react";
export default function Alert(props) {
const onClose = props.onClose || function() { };
const onClose = props.onClose || null;
const title = props.title || "Untitled Alert";
const message = props.message || "Alert message";
const type = props.type || "danger";
@@ -18,7 +18,7 @@ export default function Alert(props) {
return (
<div className={"alert alert-" + type + " alert-dismissible"}>
<button type="button" className={"close"} data-dismiss={"alert"} aria-hidden={"true"} onClick={onClose}>×</button>
{onClose ? <button type="button" className={"close"} data-dismiss={"alert"} aria-hidden={"true"} onClick={onClose}>×</button> : null}
<h5><Icon icon={icon} className={"icon"} /> {title}</h5>
{message}
</div>

View File

@@ -16,6 +16,7 @@ import Logs from "./views/logs";
import PageOverview from "./views/pages";
import HelpPage from "./views/help";
import Footer from "./footer";
import EditUser from "./views/edituser";
class AdminDashboard extends React.Component {
@@ -83,7 +84,11 @@ class AdminDashboard extends React.Component {
<Switch>
<Route path={"/admin/dashboard"}><Overview {...this.controlObj} notifications={this.state.notifications} /></Route>
<Route exact={true} path={"/admin/users"}><UserOverview {...this.controlObj} /></Route>
<Route exact={true} path={"/admin/users/adduser"}><CreateUser {...this.controlObj} /></Route>
<Route path={"/admin/user/add"}><CreateUser {...this.controlObj} /></Route>
<Route path={"/admin/user/edit/:userId"} render={(props) => {
let newProps = {...props, ...this.controlObj};
return <EditUser {...newProps} />
}}/>
<Route path={"/admin/logs"}><Logs {...this.controlObj} /></Route>
<Route path={"/admin/pages"}><PageOverview {...this.controlObj} /></Route>
<Route path={"/admin/help"}><HelpPage {...this.controlObj} /></Route>

115
src/src/views/edituser.js Normal file
View File

@@ -0,0 +1,115 @@
import * as React from "react";
import Icon from "../elements/icon";
import Alert from "../elements/alert";
import {Link} from "react-router-dom";
export default class EditUser extends React.Component {
constructor(props) {
super(props);
this.parent = {
api: props.api
};
this.state = {
user: {},
errors: [],
fetchError: null,
loaded: false,
isSaving: false
}
}
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.getUser(this.props.match.params["userId"]).then((res) => {
if (res.success) {
this.setState({ ...this.state, user: res.user, loaded: true });
} else {
this.setState({ ...this.state, fetchError: res.msg, loaded: true });
}
});
}
render() {
if (!this.state.loaded) {
return <h2 className={"text-center"}>
Loading<br/>
<Icon icon={"spinner"} className={"mt-3 text-muted fa-2x"}/>
</h2>
}
let errors = [];
let form = null;
if(this.state.fetchError) {
errors.push(
<Alert key={"error-fetch"} title={"Error fetching user details"} type={"danger"} message={
<div>{this.state.fetchError}<br/>You can meanwhile return to the&nbsp;
<Link to={"/admin/users"}>user overview</Link>
</div>
}/>
)
} else {
for (let i = 0; i < this.state.errors.length; i++) {
errors.push(<Alert key={"error-" + i} onClose={() => this.removeError(i)} {...this.state.errors[i]}/>)
}
form = <form role={"form"} onSubmit={(e) => e.preventDefault()}>
<div className={"form-group"}>
<label htmlFor={"username"}>Username</label>
<input type={"text"} className={"form-control"} placeholder={"Enter username"}
name={"username"} id={"username"} maxLength={32} value={this.state.user.name}/>
</div>
<div className={"form-group"}>
<label htmlFor={"email"}>E-Mail</label>
<input type={"email"} className={"form-control"} placeholder={"E-Mail address"}
id={"email"} name={"email"} maxLength={64} value={this.state.user.email} />
</div>
<Link to={"/admin/users"} className={"btn btn-info mt-2 mr-2"}>
<Icon icon={"arrow-left"}/>
&nbsp;Back
</Link>
{ this.state.isSaving
? <button type={"submit"} className={"btn btn-primary mt-2"} disabled>Saving&nbsp;<Icon icon={"circle-notch"} /></button>
: <button type={"submit"} className={"btn btn-primary mt-2"}>Save</button>
}
</form>
}
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">Edit User</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"><Link to={"/admin/users"}>Users</Link></li>
<li className="breadcrumb-item active">Add User</li>
</ol>
</div>
</div>
</div>
</div>
<div className={"content"}>
<div className={"row"}>
<div className={"col-lg-6 pl-5 pr-5"}>
{errors}
{form}
</div>
</div>
</div>
</>
}
}

View File

@@ -3,6 +3,7 @@ import Icon from "../elements/icon";
import {Link} from "react-router-dom";
import {getPeriodString} from "../global";
import Alert from "../elements/alert";
import ReactTooltip from "react-tooltip";
const TABLE_SIZE = 10;
@@ -77,7 +78,7 @@ export default class UserOverview extends React.Component {
totalCount: res.totalCount,
}
});
this.rowCount = Math.max(this.rowCount, Object.keys(res.groups).length);
this.rowCount = Math.max(this.rowCount, Object.keys(res.users).length);
} else {
let errors = this.state.errors.slice();
errors.push({title: "Error fetching users", message: res.msg});
@@ -145,6 +146,7 @@ export default class UserOverview extends React.Component {
</div>
</div>
</div>
<ReactTooltip />
</>;
}
@@ -176,7 +178,20 @@ export default class UserOverview extends React.Component {
<td>{user.name}</td>
<td>{user.email}</td>
<td>{groups}</td>
<td>{getPeriodString(user.registered_at)}</td>
<td>
<span data-effect={"solid"}
data-tip={user.registered_at}
data-place={"bottom"}>
{getPeriodString(user.registered_at)}
</span>
</td>
<td>
<Link to={"/admin/user/edit/" + uid} className={"text-reset"}>
<Icon icon={"pencil-alt"} data-effect={"solid"}
data-tip={"Modify user details & group membership"}
data-type={"info"} data-place={"right"}/>
</Link>
</td>
</tr>
);
}
@@ -188,6 +203,7 @@ export default class UserOverview extends React.Component {
<td/>
<td/>
<td/>
<td/>
</tr>
);
}
@@ -211,7 +227,7 @@ export default class UserOverview extends React.Component {
<div className={"card-header border-0"}>
<h3 className={"card-title"}>Users</h3>
<div className={"card-tools"}>
<Link href={"#"} className={"btn btn-tool btn-sm"} to={"/admin/users/adduser"} >
<Link href={"#"} className={"btn btn-tool btn-sm"} to={"/admin/user/add"} >
<Icon icon={"plus"}/>
</Link>
<a href={"#"} className={"btn btn-tool btn-sm"} onClick={() => this.fetchUsers()}>
@@ -227,6 +243,7 @@ export default class UserOverview extends React.Component {
<th>Email</th>
<th>Groups</th>
<th>Registered</th>
<th/>
</tr>
</thead>
<tbody>
@@ -273,7 +290,7 @@ export default class UserOverview extends React.Component {
<td>{group.name}</td>
<td className={"text-center"}>{group.memberCount}</td>
<td>
<span className={"badge text-white mr-1"} style={{backgroundColor: group.color}}>
<span className={"badge text-white mr-1"} style={{backgroundColor: group.color, fontFamily: "monospace"}}>
{group.color}
</span>
</td>