Logout functionality

This commit is contained in:
Roman Hergenreder 2020-06-14 22:35:01 +02:00
parent 11323f2781
commit 95b803a1e4
19 changed files with 4620 additions and 2742 deletions

16
admin/dist/main.js vendored

File diff suppressed because one or more lines are too long

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.loggedIn = false;
this.user = { };
this.baseUrl = "http://localhost"
}
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];
}
onClose() {
this.setState({ hidden: true });
export default function Dialog(props) {
const [value, setValue] = useStateFromProp(props);
function onClose() {
setValue({ });
}
render() {
const show = typeof value.message !== "undefined";
const classes = "modal fade" + (show ? " show" : "");
const style = { paddingRight: "12px", display: (show ? "block" : "none") };
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

File diff suppressed because one or more lines are too long

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"}>
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"}>
{content}
{dialog}
{this.createContent()}
<Dialog {...this.state.dialog}/>
</section>
</div>
</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={"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%"}}>
<div className={"mt-2"}>
Logged in as: {this.parent.api.user.name}
{/* 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>
<hr />
{/* Sidebar Menu */}
{/* 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);
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

4147
js/admin.min.js vendored Normal file

File diff suppressed because one or more lines are too long