Logout functionality
This commit is contained in:
parent
11323f2781
commit
95b803a1e4
16
admin/dist/main.js
vendored
16
admin/dist/main.js
vendored
File diff suppressed because one or more lines are too long
2918
admin/package-lock.json
generated
2918
admin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"adminlte-reactjs": "^1.0.6",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "3.4.1"
|
||||
|
@ -2,19 +2,35 @@ import 'babel-polyfill';
|
||||
|
||||
export default class API {
|
||||
constructor() {
|
||||
this.user = {};
|
||||
this.baseUrl = "http://localhost"
|
||||
this.loggedIn = false;
|
||||
this.user = { };
|
||||
}
|
||||
|
||||
csrfToken() {
|
||||
return this.loggedIn ? this.user.session.csrf_token : null;
|
||||
}
|
||||
|
||||
async apiCall(method, params) {
|
||||
params = params || { };
|
||||
params.csrf_token = this.csrfToken();
|
||||
let response = await fetch("/api/" + method, {
|
||||
method: 'post',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(params)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async fetchUser() {
|
||||
let response = await fetch(this.baseUrl + "/api/user/fetch");
|
||||
let data = await response.json()
|
||||
this.user = data["users"][0];
|
||||
return data && data.success && data.hasOwnProperty("logoutIn");
|
||||
let response = await fetch("/api/user/info");
|
||||
let data = await response.json();
|
||||
this.user = data["user"];
|
||||
this.loggedIn = data["loggedIn"];
|
||||
return data && data.success && data.loggedIn;
|
||||
}
|
||||
|
||||
async logout() {
|
||||
let response = await fetch(this.baseUrl + "/api/user/logout");
|
||||
return await response.json();
|
||||
return this.apiCall("user/logout");
|
||||
}
|
||||
};
|
@ -1,42 +1,43 @@
|
||||
import React from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
|
||||
export default class Dialog extends React.Component {
|
||||
function useStateFromProp(initialValue) {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hidden: !!props.hidden };
|
||||
useEffect(() => setValue(initialValue), [initialValue]);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
|
||||
export default function Dialog(props) {
|
||||
|
||||
const [value, setValue] = useStateFromProp(props);
|
||||
|
||||
function onClose() {
|
||||
setValue({ });
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.setState({ hidden: true });
|
||||
}
|
||||
const show = typeof value.message !== "undefined";
|
||||
const classes = "modal fade" + (show ? " show" : "");
|
||||
const style = { paddingRight: "12px", display: (show ? "block" : "none") };
|
||||
|
||||
render() {
|
||||
|
||||
console.log("Rendering dialog with:", this.props);
|
||||
|
||||
let classes = "modal fade";
|
||||
if (!this.state.hidden) {
|
||||
classes *= " show";
|
||||
}
|
||||
|
||||
return <div className={classes} id="modal-default" style={{paddingRight: "12px"}} aria-modal="true" onClick={() => this.onClose()}>
|
||||
return (
|
||||
<div className={classes} id="modal-default" style={style} aria-modal="true" onClick={() => onClose()}>
|
||||
<div className="modal-dialog" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h4 className="modal-title">{this.props.title}</h4>
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close" onClick={() => this.onClose()}>
|
||||
<h4 className="modal-title">{props.title}</h4>
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close" onClick={() => onClose()}>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>{this.props.message}</p>
|
||||
<p>{props.message}</p>
|
||||
</div>
|
||||
<div className="modal-footer justify-content-between">
|
||||
<button type="button" className="btn btn-default" data-dismiss="modal" onClick={() => this.onClose()}>Close</button>
|
||||
<button type="button" className="btn btn-default" data-dismiss="modal" onClick={() => onClose()}>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
);
|
||||
}
|
@ -37,10 +37,11 @@ export default class Header extends React.Component {
|
||||
|
||||
{/* Right navbar links */}
|
||||
<ul className={"navbar-nav ml-auto"}>
|
||||
|
||||
{/* Notifications Dropdown Menu */}
|
||||
<li className={"nav-item dropdown"}>
|
||||
<a href={"#"} className={"nav-link"} data-toggle={"dropdown"}>
|
||||
<Icon class={"bell"} type={"far"} />
|
||||
<Icon icon={"bell"} type={"far"} />
|
||||
<span className={"badge badge-warning navbar-badge"}>
|
||||
{notificationCount}
|
||||
</span>
|
||||
|
@ -7,8 +7,8 @@ export default function Icon(props) {
|
||||
let type = props.type || "fas";
|
||||
let icon = props.icon;
|
||||
|
||||
classes.push("fa");
|
||||
classes.push(type + "-" + icon);
|
||||
classes.push(type);
|
||||
classes.push("fa-" + icon);
|
||||
|
||||
if (icon === "spinner") {
|
||||
classes.push("fa-spin");
|
||||
|
4
admin/src/include/adminlte.min.css
vendored
Executable file → Normal file
4
admin/src/include/adminlte.min.css
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
7
admin/src/include/adminlte.min.js
vendored
7
admin/src/include/adminlte.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,77 +0,0 @@
|
||||
.main-header {
|
||||
transition: all 0.3s;
|
||||
margin-left: 75px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding: 0.7rem;
|
||||
}
|
||||
|
||||
.navbar-badge {
|
||||
font-size: .6rem;
|
||||
font-weight: 300;
|
||||
padding: 2px 4px;
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.navbar-white {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.main-wrapper:not(.sidebar-collapsed) .main-header {
|
||||
transition: all 0.3s;
|
||||
margin-left: 250px;
|
||||
}
|
||||
|
||||
.main-sidebar {
|
||||
background-color: #343a40;
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
z-index: 999;
|
||||
color: #fff;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.main-sidebar.collapsed {
|
||||
margin-left: 0;
|
||||
width: 4.6rem;
|
||||
}
|
||||
|
||||
.hide-collapsed {
|
||||
transition: all 0.2s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.main-sidebar.collapsed .hide-collapsed {
|
||||
opacity: 0;
|
||||
font-size: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown-menu-lg {
|
||||
max-width: 300px;
|
||||
min-width: 280px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.brand-link {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
line-height: 2;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.show {
|
||||
display: block !important;
|
||||
}
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './include/index.css';
|
||||
import './include/adminlte.min.css';
|
||||
// import './include/adminlte.min.js';
|
||||
import API from './api.js';
|
||||
import Header from './header.js';
|
||||
import Sidebar from './sidebar.js';
|
||||
@ -19,18 +18,16 @@ class AdminDashboard extends React.Component {
|
||||
this.state = {
|
||||
currentView: "dashboard",
|
||||
loaded: false,
|
||||
dialog: { hidden: true }
|
||||
dialog: { }
|
||||
};
|
||||
}
|
||||
|
||||
onChangeView(view) {
|
||||
console.log("changing view to: " + view);
|
||||
this.setState({ ...this.state, currentView: view || "dashboard" });
|
||||
this.setState({ ...this.state, currentView: view || "dashboard", dialog: { } });
|
||||
}
|
||||
|
||||
showDialog(props) {
|
||||
props = props || { hidden: true };
|
||||
if (!props.hasOwnProperty("hidden")) props.hidden = false;
|
||||
props = props || { };
|
||||
this.setState({ ...this.state, dialog: props });
|
||||
}
|
||||
|
||||
@ -47,20 +44,17 @@ class AdminDashboard extends React.Component {
|
||||
return <b>Loading… <Icon icon={"spinner"} /></b>
|
||||
}
|
||||
|
||||
console.log("Rendering mainview with:", this.state.dialog);
|
||||
const dialog = <Dialog {...this.state.dialog}/>
|
||||
const content = this.createContent();
|
||||
|
||||
return <div className={"wrapper"}>
|
||||
<Header />
|
||||
<Sidebar currentView={this.state.currentView} onChangeView={this.onChangeView.bind(this)} showDialog={this.showDialog.bind(this)} api={this.api} />
|
||||
<div className={"content-wrapper p-2"}>
|
||||
<section className={"content"}>
|
||||
{content}
|
||||
{dialog}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
console.log("index.render, state=", this.state);
|
||||
return <>
|
||||
<Header />
|
||||
<Sidebar currentView={this.state.currentView} onChangeView={this.onChangeView.bind(this)} showDialog={this.showDialog.bind(this)} api={this.api} />
|
||||
<div className={"content-wrapper p-2"}>
|
||||
<section className={"content"}>
|
||||
{this.createContent()}
|
||||
<Dialog {...this.state.dialog}/>
|
||||
</section>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
createContent() {
|
||||
|
@ -9,7 +9,7 @@ export default class Sidebar extends React.Component {
|
||||
onChangeView: props.onChangeView || function() { },
|
||||
showDialog: props.showDialog || function() {},
|
||||
api: props.api
|
||||
}
|
||||
};
|
||||
this.state = { currentView: props.currentView, }
|
||||
}
|
||||
|
||||
@ -50,17 +50,21 @@ export default class Sidebar extends React.Component {
|
||||
};
|
||||
|
||||
let li = [];
|
||||
// li.push(<li className={"nav-item"} key={"logged-in-as"}><span className={""}>Logged in as: {this.parent.api.user.name}</span><hr/></li>);
|
||||
// li.push(<li key={"hr"}><hr/></li>);
|
||||
// li.push(<li key={"header"} className={"header"}>MAIN NAVIGATION</li>);
|
||||
|
||||
for (let id in menuItems) {
|
||||
let obj = menuItems[id];
|
||||
let active = this.state.currentView === id ? " active" : "";
|
||||
li.push(<li key={id} className={"nav-item"}>
|
||||
<a href={"#"} onClick={() => this.onChangeView(id)} className={"nav-link" + active}>
|
||||
<Icon icon={obj.icon} /><p>{obj.name}</p>
|
||||
<Icon icon={obj.icon} classes={"nav-icon"} /><p>{obj.name}</p>
|
||||
</a>
|
||||
</li>);
|
||||
}
|
||||
|
||||
li.push(<li key={"logout"} className={"nav-item"}>
|
||||
li.push(<li className={"nav-item"} key={"logout"}>
|
||||
<a href={"#"} onClick={() => this.onLogout()} className={"nav-link"}>
|
||||
<Icon icon={"arrow-left"} classes={"nav-icon"} />
|
||||
<p>Logout</p>
|
||||
@ -68,26 +72,41 @@ export default class Sidebar extends React.Component {
|
||||
</li>);
|
||||
|
||||
return <aside className={"main-sidebar sidebar-dark-primary elevation-4"}>
|
||||
<a href={"#"} onClick={() => this.onChangeView("dashboard") } className={"brand-link"}>
|
||||
<img src={"/img/web_base_logo.png"} alt={"WebBase Logo"} className={"brand-image img-circle elevation-3"} style={{"opacity": ".8"}} />
|
||||
<span className={"brand-text font-weight-light"}>WebBase</span>
|
||||
<a href={"#"} className={"brand-link"} onClick={() => this.onChangeView("dashboard")}>
|
||||
<img src={"/img/icons/logo.png"} alt={"Logo"} className={"brand-image img-circle elevation-3"} style={{opacity: ".8"}} />
|
||||
<span className={"brand-text font-weight-light ml-2"}>WebBase</span>
|
||||
</a>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className={"sidebar"}>
|
||||
|
||||
<div className={"mt-2"}>
|
||||
Logged in as: {this.parent.api.user.name}
|
||||
<div className={"sidebar os-host os-theme-light os-host-overflow os-host-overflow-y os-host-resize-disabled os-host-scrollbar-horizontal-hidden os-host-transition"}>
|
||||
{/* IDK what this is */}
|
||||
<div className={"os-resize-observer-host"}>
|
||||
<div className={"os-resize-observer observed"} style={{left: "0px", right: "auto"}}/>
|
||||
</div>
|
||||
<div className={"os-size-auto-observer"} style={{height: "calc(100% + 1px)", float: "left"}}>
|
||||
<div className={"os-resize-observer observed"}/>
|
||||
</div>
|
||||
<div className={"os-content-glue"} style={{margin: "0px -8px"}}/>
|
||||
<div className={"os-padding"}>
|
||||
<div className={"os-viewport os-viewport-native-scrollbars-invisible"} style={{right: "0px", bottom: "0px"}}>
|
||||
<div className={"os-content"} style={{padding: "0px 0px", height: "100%", width: "100%"}}>
|
||||
|
||||
<hr />
|
||||
{/* LOGGED IN AS */}
|
||||
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
|
||||
<div className="info">
|
||||
<a href="#" className="d-block">Logged in as: {this.parent.api.user.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Menu */}
|
||||
<nav className={"mt-2"}>
|
||||
<ul className={"nav nav-pills nav-sidebar flex-column"} data-widget={"treeview"} role={"menu"} data-accordion={"false"}>
|
||||
{li}
|
||||
</ul>
|
||||
</nav>
|
||||
{/* SIDEBAR */}
|
||||
<nav className={"mt-2"}>
|
||||
<ul className={"nav nav-pills nav-sidebar flex-column"} data-widget={"treeview"} role={"menu"} data-accordion={"false"}>
|
||||
{li}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
}
|
||||
|
@ -91,7 +91,13 @@ class Request {
|
||||
$values = $_REQUEST;
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER["CONTENT_TYPE"]) && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"]))) {
|
||||
$jsonData = json_decode(file_get_contents('php://input'), true);
|
||||
$values = array_merge($values, $jsonData);
|
||||
if ($jsonData) {
|
||||
$values = array_merge($values, $jsonData);
|
||||
} else {
|
||||
$this->lastError = 'Invalid request body.';
|
||||
header('HTTP 1.1 400 Bad Request');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,6 @@ class Logout extends Request {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->lastError = "CUSTOM ERROR MESSAGE";
|
||||
$this->success = false;
|
||||
return false;
|
||||
|
||||
$this->success = $this->user->logout();
|
||||
$this->lastError = $this->user->getSQL()->getLastError();
|
||||
return $this->success;
|
||||
|
@ -30,7 +30,7 @@ namespace Documents\Admin {
|
||||
|
||||
protected function initSources() {
|
||||
// $this->loadJQuery();
|
||||
// $this->loadFontawesome();
|
||||
$this->loadFontawesome();
|
||||
// $this->addJS(Script::CORE);
|
||||
// $this->addCSS(Link::CORE);
|
||||
// $this->addJS(Script::ADMIN);
|
||||
|
@ -273,7 +273,7 @@ class AdminDashboardBody extends Body {
|
||||
*/
|
||||
|
||||
$script = new Script(Script::MIME_TEXT_JAVASCRIPT, "/js/admin.min.js");
|
||||
$html .= "<body id=\"root\">$script</body>";
|
||||
$html .= "<body><div class=\"wrapper\" id=\"root\">$script</div></body>";
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ class LoginBody extends Body {
|
||||
$head = $this->getDocument()->getHead();
|
||||
$head->loadBootstrap();
|
||||
$head->loadJQuery();
|
||||
$head->loadFontawesome();
|
||||
$head->addJS(Script::CORE);
|
||||
$head->addCSS(Link::CORE);
|
||||
$head->addJS(Script::ADMIN);
|
||||
|
BIN
img/icons/logo.png
Normal file
BIN
img/icons/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
4147
js/admin.min.js
vendored
Normal file
4147
js/admin.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user