User Create page
This commit is contained in:
parent
32df152ca7
commit
99672405c6
158
admin/dist/main.js
vendored
158
admin/dist/main.js
vendored
File diff suppressed because one or more lines are too long
16
admin/package-lock.json
generated
16
admin/package-lock.json
generated
@ -10962,6 +10962,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-tooltip": {
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-4.2.7.tgz",
|
||||
"integrity": "sha512-z5T3gNplT76rkT7ZImfx/uXfBtS+x7+WK2H8MVZ5skSwETDDx0hs0+P0jwL5gYDaBvsZ7LYiCBzAOd3tN85CMA==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2",
|
||||
"uuid": "^7.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
|
||||
|
@ -10,7 +10,8 @@
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.1"
|
||||
"react-scripts": "3.4.1",
|
||||
"react-tooltip": "^4.2.7"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode development && cp dist/main.js ../js/admin.min.js"
|
||||
|
@ -8,7 +8,7 @@ export default class View404 extends React.Component {
|
||||
<h2 className={"headline text-warning"}>404</h2>
|
||||
<div className={"error-content"}>
|
||||
<h3>
|
||||
<Icon icon={"exclamation-triangle"} classes={"text-warning"}/> Oops! Page not found.
|
||||
<Icon icon={"exclamation-triangle"} className={"text-warning"}/> Oops! Page not found.
|
||||
</h3>
|
||||
<p>
|
||||
We could not find the page you were looking for.
|
||||
|
@ -45,4 +45,12 @@ export default class API {
|
||||
async fetchGroups(pageNum = 1) {
|
||||
return this.apiCall("groups/fetch", { page: pageNum });
|
||||
}
|
||||
|
||||
async inviteUser(username, email) {
|
||||
return this.apiCall("user/invite", { username: username, email: email });
|
||||
}
|
||||
|
||||
async createUser(username, email, password, confirmPassword) {
|
||||
return this.apiCall("user/create", { username: username, email: email, password: password, confirmPassword: confirmPassword });
|
||||
}
|
||||
};
|
@ -11,7 +11,7 @@ export default function Alert(props) {
|
||||
return (
|
||||
<div className={"alert alert-danger alert-dismissible"}>
|
||||
<button type="button" className={"close"} data-dismiss={"alert"} aria-hidden={"true"} onClick={onClose}>×</button>
|
||||
<h5><Icon icon={"ban"} classes={"icon"} /> {title}</h5>
|
||||
<h5><Icon icon={"ban"} className={"icon"} /> {title}</h5>
|
||||
{message}
|
||||
</div>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
|
||||
export default function Icon(props) {
|
||||
|
||||
let classes = props.classes || [];
|
||||
let classes = props.className || [];
|
||||
classes = Array.isArray(classes) ? classes : classes.toString().split(" ");
|
||||
let type = props.type || "fas";
|
||||
let icon = props.icon;
|
||||
@ -15,7 +15,6 @@ export default function Icon(props) {
|
||||
}
|
||||
|
||||
let newProps = {...props, className: classes.join(" ") };
|
||||
delete newProps["classes"];
|
||||
delete newProps["type"];
|
||||
delete newProps["icon"];
|
||||
|
||||
|
@ -7,6 +7,7 @@ import Header from './header.js';
|
||||
import Sidebar from './sidebar.js';
|
||||
import UserOverview from './views/users.js';
|
||||
import Overview from './views/overview.js'
|
||||
import CreateUser from "./views/adduser";
|
||||
import Icon from "./elements/icon";
|
||||
import Dialog from "./elements/dialog";
|
||||
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
|
||||
@ -77,7 +78,8 @@ class AdminDashboard extends React.Component {
|
||||
<section className={"content"}>
|
||||
<Switch>
|
||||
<Route path={"/admin/dashboard"}><Overview {...this.controlObj} /></Route>
|
||||
<Route path={"/admin/users"}><UserOverview {...this.controlObj} /></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/logs"}><Logs {...this.controlObj} /></Route>
|
||||
<Route path={"*"}><View404 /></Route>
|
||||
</Switch>
|
||||
|
@ -56,7 +56,7 @@ export default function Sidebar(props) {
|
||||
li.push(
|
||||
<li key={id} className={"nav-item"}>
|
||||
<NavLink to={"/admin/" + id} className={"nav-link"} activeClassName={"active"}>
|
||||
<Icon icon={obj.icon} classes={"nav-icon"} /><p>{obj.name}{badge}</p>
|
||||
<Icon icon={obj.icon} className={"nav-icon"} /><p>{obj.name}{badge}</p>
|
||||
</NavLink>
|
||||
</li>
|
||||
);
|
||||
@ -64,7 +64,7 @@ export default function Sidebar(props) {
|
||||
|
||||
li.push(<li className={"nav-item"} key={"logout"}>
|
||||
<a href={"#"} onClick={() => onLogout()} className={"nav-link"}>
|
||||
<Icon icon={"arrow-left"} classes={"nav-icon"} />
|
||||
<Icon icon={"arrow-left"} className={"nav-icon"} />
|
||||
<p>Logout</p>
|
||||
</a>
|
||||
</li>);
|
||||
|
103
admin/src/views/adduser.js
Normal file
103
admin/src/views/adduser.js
Normal file
@ -0,0 +1,103 @@
|
||||
import * as React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import Alert from "../elements/alert";
|
||||
import Icon from "../elements/icon";
|
||||
import ReactTooltip from 'react-tooltip'
|
||||
|
||||
export default class CreateUser extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
errors: [],
|
||||
sendInvite: true
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let errors = [];
|
||||
for (let i = 0; i < this.state.errors.length; i++) {
|
||||
errors.push(<Alert key={"error-" + i} onClose={() => this.removeError(i)} {...this.state.errors[i]}/>)
|
||||
}
|
||||
|
||||
let passwordForm = null;
|
||||
if (!this.state.sendInvite) {
|
||||
passwordForm = <div className={"mt-2"}>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"password"}>Password</label>
|
||||
<input type={"password"} className={"form-control"} placeholder={"Password"}
|
||||
id={"password"} name={"password"}/>
|
||||
</div>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"confirmPassword"}>Confirm Password</label>
|
||||
<input type={"password"} className={"form-control"} placeholder={"Confirm Password"}
|
||||
id={"confirmPassword"} name={"confirmPassword"}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
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">Create a new 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"}>
|
||||
{errors}
|
||||
<div className={"row"}>
|
||||
<div className={"col-lg-6 p-3"}>
|
||||
<form role={"form"} onSubmit={(e) => this.submitForm(e)}>
|
||||
<div className={"form-group"}>
|
||||
<label htmlFor={"username"}>Username</label>
|
||||
<input type={"text"} className={"form-control"} placeholder={"Enter username"}
|
||||
name={"username"} id={"username"} maxLength={32}/>
|
||||
</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}/>
|
||||
</div>
|
||||
<div className={"form-check"}>
|
||||
<input type={"checkbox"} className={"form-check-input"}
|
||||
onChange={() => this.onCheckboxChange()}
|
||||
id={"sendInvite"} name={"sendInvite"} defaultChecked={this.state.sendInvite}/>
|
||||
<label className={"form-check-label"} htmlFor={"sendInvite"}>
|
||||
Send Invitation
|
||||
<Icon icon={"question-circle"} className={"ml-2"} style={{"color": "#0069d9"}}
|
||||
data-tip={"The user will receive an invitation token via email and can choose the password on his own."}
|
||||
data-type={"info"} data-place={"right"} data-effect={"solid"}/>
|
||||
</label>
|
||||
</div>
|
||||
{passwordForm}
|
||||
<button type={"submit"} className={"btn btn-primary mt-2"}>Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ReactTooltip/>
|
||||
</>;
|
||||
}
|
||||
|
||||
submitForm(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
onCheckboxChange() {
|
||||
this.setState({
|
||||
...this.state,
|
||||
sendInvite: !this.state.sendInvite,
|
||||
});
|
||||
}
|
||||
}
|
@ -193,6 +193,9 @@ 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"} >
|
||||
<Icon icon={"plus"}/>
|
||||
</Link>
|
||||
<a href={"#"} className={"btn btn-tool btn-sm"} onClick={() => this.fetchUsers()}>
|
||||
<Icon icon={"sync"}/>
|
||||
</a>
|
||||
@ -274,6 +277,9 @@ export default class UserOverview extends React.Component {
|
||||
<div className={"card-header border-0"}>
|
||||
<h3 className={"card-title"}>Groups</h3>
|
||||
<div className={"card-tools"}>
|
||||
<Link href={"#"} className={"btn btn-tool btn-sm"} to={"/admin/users/addgroup"} >
|
||||
<Icon icon={"plus"}/>
|
||||
</Link>
|
||||
<a href={"#"} className={"btn btn-tool btn-sm"} onClick={() => this.fetchGroups()}>
|
||||
<Icon icon={"sync"}/>
|
||||
</a>
|
||||
|
31
core/Api/User/Create.class.php
Normal file
31
core/Api/User/Create.class.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Api\User;
|
||||
|
||||
use Api\Parameter\StringType;
|
||||
use \Api\Request;
|
||||
|
||||
class Create extends Request {
|
||||
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'username' => new StringType('username', 32),
|
||||
'email' => new StringType('email', 64, true),
|
||||
'password' => new StringType('password'),
|
||||
'confirmPassword' => new StringType('confirmPassword'),
|
||||
));
|
||||
$this->csrfTokenRequired = true;
|
||||
$this->loginRequired = true;
|
||||
$this->requiredGroup = USER_GROUP_ADMIN;
|
||||
}
|
||||
|
||||
public function execute($values = array()) {
|
||||
if(!parent::execute($values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
}
|
29
core/Api/User/Invite.class.php
Normal file
29
core/Api/User/Invite.class.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Api\User;
|
||||
|
||||
use Api\Parameter\StringType;
|
||||
use \Api\Request;
|
||||
|
||||
class Invite extends Request {
|
||||
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'username' => new StringType('username', 32),
|
||||
'email' => new StringType('email', 64),
|
||||
));
|
||||
$this->csrfTokenRequired = true;
|
||||
$this->loginRequired = true;
|
||||
$this->requiredGroup = USER_GROUP_ADMIN;
|
||||
}
|
||||
|
||||
public function execute($values = array()) {
|
||||
if(!parent::execute($values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
}
|
158
js/admin.min.js
vendored
158
js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user