Browse Source

Logout functionality

Roman Hergenreder 3 years ago
parent
commit
95b803a1e4

File diff suppressed because it is too large
+ 0 - 0
admin/dist/main.js


File diff suppressed because it is too large
+ 163 - 998
admin/package-lock.json


+ 0 - 1
admin/package.json

@@ -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"

+ 24 - 8
admin/src/api.js

@@ -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");
     }
 };

+ 22 - 21
admin/src/dialog.js

@@ -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]);
 
-    onClose() {
-        this.setState({ hidden: true });
-    }
+    return [value, setValue];
+}
 
-    render() {
+export default function Dialog(props) {
 
-        console.log("Rendering dialog with:", this.props);
+    const [value, setValue] = useStateFromProp(props);
 
-        let classes = "modal fade";
-        if (!this.state.hidden) {
-            classes *= " show";
-        }
+    function onClose() {
+        setValue({ });
+    }
 
-        return <div className={classes} id="modal-default" style={{paddingRight: "12px"}} aria-modal="true" onClick={() => this.onClose()}>
+    const show = typeof value.message !== "undefined";
+    const classes = "modal fade" + (show ? " show" : "");
+    const style = { paddingRight: "12px", display: (show ? "block" : "none") };
+
+    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>
-    }
+    );
 }

+ 2 - 1
admin/src/header.js

@@ -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>

+ 2 - 2
admin/src/icon.js

@@ -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");

File diff suppressed because it is too large
+ 1 - 1
admin/src/include/adminlte.min.css


File diff suppressed because it is too large
+ 0 - 5
admin/src/include/adminlte.min.js


+ 0 - 77
admin/src/include/index.css

@@ -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;
-}

+ 14 - 20
admin/src/index.js

@@ -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() {

+ 37 - 18
admin/src/sidebar.js

@@ -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%"}}>
+
+                            {/* 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 */}
+                            <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 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>
+                        </div>
+                    </div>
+                </div>
             </div>
         </aside>
     }

+ 7 - 1
core/Api/Request.class.php

@@ -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;
+        }
       }
     }
 

+ 0 - 4
core/Api/User/Logout.class.php

@@ -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;

+ 1 - 1
core/Documents/Admin.class.php

@@ -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);

+ 1 - 1
core/Views/Admin/AdminDashboardBody.class.php

@@ -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;
   }
 }

+ 0 - 1
core/Views/LoginBody.class.php

@@ -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


File diff suppressed because it is too large
+ 231 - 0
js/admin.min.js


Some files were not shown because too many files changed in this diff