added api connectin to create and update user interface

This commit is contained in:
Jean Jacques Avril 2022-03-11 19:22:34 +01:00
parent c97d12e669
commit a0adef9ec5
18 changed files with 367 additions and 186 deletions

View File

@ -1 +1 @@
[{"timestamp":1646924506348,"files":[{"filename":"index.html","previous":487,"size":487,"diff":0},{"filename":"bundle.45d14.css","previous":1691,"size":1691,"diff":0},{"filename":"bundle.*****.js","previous":12153,"size":14680,"diff":2527},{"filename":"polyfills.*****.js","previous":2288,"size":2288,"diff":0}]},{"timestamp":1646755354217,"files":[{"filename":"bundle.*****.esm.js","previous":9839,"size":0,"diff":-9839},{"filename":"polyfills.*****.esm.js","previous":2187,"size":0,"diff":-2187},{"filename":"sw.js","previous":10597,"size":0,"diff":-10597},{"filename":"sw-esm.js","previous":10603,"size":0,"diff":-10603},{"filename":"polyfills.058fb.js","previous":2288,"size":0,"diff":-2288},{"filename":"index.html","previous":521,"size":487,"diff":-34},{"filename":"200.html","previous":521,"size":0,"diff":-521},{"filename":"bundle.45d14.css","previous":1691,"size":1691,"diff":0},{"filename":"bundle.caa2d.js","previous":12219,"size":0,"diff":-12219},{"filename":"bundle.*****.js","previous":0,"size":12153,"diff":12153},{"filename":"polyfills.*****.js","previous":0,"size":2288,"diff":2288}]},{"timestamp":1646755194517,"files":[{"filename":"bundle.2a54a.css","previous":1709,"size":0,"diff":-1709},{"filename":"bundle.*****.esm.js","previous":9842,"size":9839,"diff":-3},{"filename":"polyfills.*****.esm.js","previous":2187,"size":2187,"diff":0},{"filename":"sw.js","previous":10595,"size":10597,"diff":2},{"filename":"sw-esm.js","previous":10600,"size":10603,"diff":3},{"filename":"polyfills.058fb.js","previous":2288,"size":2288,"diff":0},{"filename":"index.html","previous":536,"size":521,"diff":-15},{"filename":"200.html","previous":536,"size":521,"diff":-15},{"filename":"bundle.3030b.js","previous":12224,"size":0,"diff":-12224},{"filename":"bundle.45d14.css","previous":0,"size":1691,"diff":1691},{"filename":"bundle.caa2d.js","previous":0,"size":12219,"diff":12219}]},{"timestamp":1646755054393,"files":[{"filename":"bundle.2a54a.css","previous":1709,"size":1709,"diff":0},{"filename":"bundle.*****.esm.js","previous":9856,"size":9842,"diff":-14},{"filename":"polyfills.*****.esm.js","previous":2187,"size":2187,"diff":0},{"filename":"sw.js","previous":10599,"size":10595,"diff":-4},{"filename":"sw-esm.js","previous":10603,"size":10600,"diff":-3},{"filename":"bundle.1a7a6.js","previous":11947,"size":0,"diff":-11947},{"filename":"polyfills.058fb.js","previous":2288,"size":2288,"diff":0},{"filename":"index.html","previous":536,"size":536,"diff":0},{"filename":"200.html","previous":536,"size":536,"diff":0},{"filename":"bundle.3030b.js","previous":0,"size":12224,"diff":12224}]},{"timestamp":1646751614979,"files":[{"filename":"bundle.2a54a.css","previous":0,"size":1709,"diff":1709},{"filename":"bundle.*****.esm.js","previous":0,"size":9856,"diff":9856},{"filename":"polyfills.*****.esm.js","previous":0,"size":2187,"diff":2187},{"filename":"sw.js","previous":0,"size":10599,"diff":10599},{"filename":"sw-esm.js","previous":0,"size":10603,"diff":10603},{"filename":"bundle.1a7a6.js","previous":0,"size":11947,"diff":11947},{"filename":"polyfills.058fb.js","previous":0,"size":2288,"diff":2288},{"filename":"index.html","previous":0,"size":536,"diff":536},{"filename":"200.html","previous":0,"size":536,"diff":536}]}] [{"timestamp":1646927116411,"files":[{"filename":"index.html","previous":487,"size":487,"diff":0},{"filename":"bundle.45d14.css","previous":1691,"size":1691,"diff":0},{"filename":"bundle.*****.js","previous":14680,"size":14692,"diff":12},{"filename":"polyfills.*****.js","previous":2288,"size":2288,"diff":0}]},{"timestamp":1646924506348,"files":[{"filename":"index.html","previous":487,"size":487,"diff":0},{"filename":"bundle.45d14.css","previous":1691,"size":1691,"diff":0},{"filename":"bundle.*****.js","previous":12153,"size":14680,"diff":2527},{"filename":"polyfills.*****.js","previous":2288,"size":2288,"diff":0}]},{"timestamp":1646755354217,"files":[{"filename":"bundle.*****.esm.js","previous":9839,"size":0,"diff":-9839},{"filename":"polyfills.*****.esm.js","previous":2187,"size":0,"diff":-2187},{"filename":"sw.js","previous":10597,"size":0,"diff":-10597},{"filename":"sw-esm.js","previous":10603,"size":0,"diff":-10603},{"filename":"polyfills.058fb.js","previous":2288,"size":0,"diff":-2288},{"filename":"index.html","previous":521,"size":487,"diff":-34},{"filename":"200.html","previous":521,"size":0,"diff":-521},{"filename":"bundle.45d14.css","previous":1691,"size":1691,"diff":0},{"filename":"bundle.caa2d.js","previous":12219,"size":0,"diff":-12219},{"filename":"bundle.*****.js","previous":0,"size":12153,"diff":12153},{"filename":"polyfills.*****.js","previous":0,"size":2288,"diff":2288}]},{"timestamp":1646755194517,"files":[{"filename":"bundle.2a54a.css","previous":1709,"size":0,"diff":-1709},{"filename":"bundle.*****.esm.js","previous":9842,"size":9839,"diff":-3},{"filename":"polyfills.*****.esm.js","previous":2187,"size":2187,"diff":0},{"filename":"sw.js","previous":10595,"size":10597,"diff":2},{"filename":"sw-esm.js","previous":10600,"size":10603,"diff":3},{"filename":"polyfills.058fb.js","previous":2288,"size":2288,"diff":0},{"filename":"index.html","previous":536,"size":521,"diff":-15},{"filename":"200.html","previous":536,"size":521,"diff":-15},{"filename":"bundle.3030b.js","previous":12224,"size":0,"diff":-12224},{"filename":"bundle.45d14.css","previous":0,"size":1691,"diff":1691},{"filename":"bundle.caa2d.js","previous":0,"size":12219,"diff":12219}]},{"timestamp":1646755054393,"files":[{"filename":"bundle.2a54a.css","previous":1709,"size":1709,"diff":0},{"filename":"bundle.*****.esm.js","previous":9856,"size":9842,"diff":-14},{"filename":"polyfills.*****.esm.js","previous":2187,"size":2187,"diff":0},{"filename":"sw.js","previous":10599,"size":10595,"diff":-4},{"filename":"sw-esm.js","previous":10603,"size":10600,"diff":-3},{"filename":"bundle.1a7a6.js","previous":11947,"size":0,"diff":-11947},{"filename":"polyfills.058fb.js","previous":2288,"size":2288,"diff":0},{"filename":"index.html","previous":536,"size":536,"diff":0},{"filename":"200.html","previous":536,"size":536,"diff":0},{"filename":"bundle.3030b.js","previous":0,"size":12224,"diff":12224}]},{"timestamp":1646751614979,"files":[{"filename":"bundle.2a54a.css","previous":0,"size":1709,"diff":1709},{"filename":"bundle.*****.esm.js","previous":0,"size":9856,"diff":9856},{"filename":"polyfills.*****.esm.js","previous":0,"size":2187,"diff":2187},{"filename":"sw.js","previous":0,"size":10599,"diff":10599},{"filename":"sw-esm.js","previous":0,"size":10603,"diff":10603},{"filename":"bundle.1a7a6.js","previous":0,"size":11947,"diff":11947},{"filename":"polyfills.058fb.js","previous":0,"size":2288,"diff":2288},{"filename":"index.html","previous":0,"size":536,"diff":536},{"filename":"200.html","previous":0,"size":536,"diff":536}]}]

View File

@ -1,40 +1,70 @@
const server = 'http://192.168.4.22' const server = 'http://192.168.4.22'
async function login(username,password){ async function login(username, password) {
let formData = new FormData(); let formData = new FormData();
formData.append('username',username) formData.append('username', username)
formData.append('password',password); formData.append('password', password);
formData.append('action','login'); formData.append('action', 'login');
const resp = await fetch(`${server}/api/auth`,{method: 'POST',mode: 'cors',body:formData }).then(resp=>resp.text()) const resp = fetch(`${server}/api/auth`, { method: 'POST', mode: 'cors', body: formData }).then(resp => resp.text());
return resp; return resp;
} }
function logout(){ async function logout(token) {
return 'DUMMYTOKEN'; let formData = new FormData()
formData.append('token', token);
formData.append('action', 'logout');
const resp = fetch(`${server}/api/auth`, { method: 'POST', mode: 'cors', body: formData }).then(resp => resp.text());
return resp;
} }
function checkAuth(token){ function checkAuth(token) {
return token==='DUMMYTOKEN'; return token === 'DUMMYTOKEN';
} }
async function fetchdb(token='azif7eqCl5'){ async function fetchdb(token = 'azif7eqCl5') {
//fetch(`${server}/api/userdb`).then() //fetch(`${server}/api/userdb`).then()
//let xmlHttp = new XMLHttpRequest(); //let xmlHttp = new XMLHttpRequest();
//xmlHttp.open( "GET", `${server}/api/userdb`, false ); // false for synchronous request //xmlHttp.open( "GET", `${server}/api/userdb`, false ); // false for synchronous request
//xmlHttp.send( null ); //xmlHttp.send( null );
const resp = await fetch(`${server}/api/userdb`,{method: 'GET',mode: 'cors',headers:{Authentification:token}}) const resp = await fetch(`${server}/api/userdb`, { method: 'GET', mode: 'cors', headers: { Authentification: token } })
.then(resp=>resp.text()).then(text=>parsedb(text)) .then(resp => resp.text()).then(text => parsedb(text))
return resp; return resp;
} }
function parsedb(raw){ function createCsvTable(userdb) {
let res = '';
let first = true;
for (let u of userdb) {
if (first)
first = false;
res += `${!first && '\n'}${u.uid},${u.first_name},${u.last_name},${u.rfid_uid},${u.user_pin},${u.enabled ? '1' : '0'}`;
}
return res;
}
async function updateUser(token, user) {
const resp = await fetch(`${server}/api/user/`, { method: 'POST', body: JSON.stringify(user), mode: 'cors', headers: { Authentification: token } })
.then(resp => resp.json());
return resp;
}
async function deleteUser(token, user) {
const resp = await fetch(`${server}/api/user/${user.uid}`, { method: 'DELETE', body: JSON.stringify(user), mode: 'cors', headers: { Authentification: token } })
.then(resp => resp.json());
return resp;
}
async function createUser(token, user) {
const resp = await fetch(`${server}/api/user/`, { method: 'PUT', body: JSON.stringify(user), mode: 'cors', headers: { Authentification: token } })
.then(resp => resp.json());
return resp;
}
function parsedb(raw) {
let lines = raw.split('\n'); let lines = raw.split('\n');
let users = []; let users = [];
lines.map((l,line)=>{ lines.map((l, line) => {
let [uid, first_name, last_name, rfid_uid, user_pin, active] = l.split([';']); let [uid, first_name, last_name, rfid_uid, user_pin, enabled] = l.split([';']);
users.push({ line, uid, first_name, last_name, rfid_uid, user_pin, enabled: active[0]==='1' }); users.push({ line, uid, first_name, last_name, rfid_uid, user_pin, enabled: enabled[0] === '1' });
}); });
return users; return users;
} }
const publicfunctions = {login, logout, checkAuth, parsedb, fetchdb}; const publicfunctions = { login, logout, checkAuth, parsedb,fetchdb, createCsvTable, updateUser, deleteUser, createUser };
export default {...publicfunctions} export default { ...publicfunctions }
export {login, logout, checkAuth, parsedb} export { login, logout, checkAuth, parsedb,fetchdb, createCsvTable, updateUser, deleteUser, createUser }

View File

@ -8,12 +8,13 @@ import { AppStateProvider, UserTableProvider, menuReducer, sessionReducer, userT
function App() { function App() {
// useReducer // useReducer
const menu = useReducer(menuReducer, false); const menu = useReducer(menuReducer, false);
const session = useReducer(sessionReducer, { active: false }); const session = useReducer(sessionReducer, { active:true ,token: "0t6BF94Y92" });
const [usertable, userreducer] = useReducer(userTableReducer, []); const [usertable, userreducer] = useReducer(userTableReducer, []);
this.menu_items = [ this.menu_items = [
{ text: "Übersicht", path: "/" }, { text: "Übersicht", path: "/" },
{ text: "Benutzer", path: "/users" }, { text: "Benutzer anlegen", path: "/newuser" },
{ text: "Benutzer verwalten", path: "/users" },
{ text: "System", path: "/system" }, { text: "System", path: "/system" },
{ text: "Abmelden", path: "/logout" } { text: "Abmelden", path: "/logout" }
] ]
@ -28,7 +29,7 @@ function App() {
<AppStateProvider value={{ menu, session }} > <AppStateProvider value={{ menu, session }} >
<div id="wrapper"> <div id="wrapper">
<Header /> <Header title={"Doorlock"} />
<div class="page"> <div class="page">
<Menu items={this.menu_items} /> <Menu items={this.menu_items} />
{!menu[0] && {!menu[0] &&
@ -39,7 +40,8 @@ function App() {
<System path="/system" /> <System path="/system" />
<Logout path="/logout" /> <Logout path="/logout" />
<Users path="/users/:pageid?" /> <Users path="/users/:pageid?" />
<EditUser path="/edituser/:userid" /> <EditUser path="/edituser/:userid?" />
<EditUser path="/newuser" />
<div class="container" default>Error 404</div> <div class="container" default>Error 404</div>
</Router> </Router>
</UserTableProvider> </UserTableProvider>

View File

@ -1,13 +1,13 @@
import { useContext } from 'preact/hooks'; import { useContext } from 'preact/hooks';
import AppState from '../../store/AppState'; import AppState from '../../store/AppState';
const Header = () => { const Header = ({title}) => {
let { menu, session } = useContext(AppState); let { menu, session } = useContext(AppState);
let [menu_shown, toggle_menu] = menu; let [menu_shown, toggle_menu] = menu;
let [sessiondata] = session; let [sessiondata] = session;
return ( return (
<header className='header'> <header className='header'>
<div className="container"> <div className="container">
<h1>Login</h1> <h1>{title}</h1>
{ sessiondata.active &&(<div id="hamburger-button" className={`hamburger ${menu_shown && 'hamburger-active'}`} { sessiondata.active &&(<div id="hamburger-button" className={`hamburger ${menu_shown && 'hamburger-active'}`}
onClick={() => toggle_menu('toggle')}> onClick={() => toggle_menu('toggle')}>
<hr /> <hr />

View File

@ -1,35 +1,54 @@
import {h} from 'preact'; import { h } from 'preact';
import { useContext, useState } from 'preact/hooks'; import { useContext, useEffect, useState } from 'preact/hooks';
import { route } from 'preact-router'; import { route } from 'preact-router';
import { UserTable } from "../../store"; import AppState, { UserTable } from "../../store";
function EditUser({userid}){ import api from '../../api'
function EditUser({ userid }) {
let [sessiondata,] = useContext(AppState).session;
const { usertable, userreducer } = useContext(UserTable); const { usertable, userreducer } = useContext(UserTable);
let user = usertable.find(u=>u.uid===userid)
const [formdata, formchange] = useState(user?user:{}); const [formdata, formchange] = useState({});
const onChange=(e)=>{ const onChange = (e) => {
console.log(e); console.log(e);
if(e.target.type==='checkbox') if (e.target.type === 'checkbox')
formchange({...formdata, [e.target.id]:e.target.checked}) formchange({ ...formdata, [e.target.id]: e.target.checked })
else else
formchange({...formdata, [e.target.id]:e.target.value}) formchange({ ...formdata, [e.target.id]: e.target.value })
} }
const onSubmit=(e)=>{ useEffect(()=>{
let user = userid !== undefined?usertable.find(u => u.uid === userid):undefined;
if(user!==undefined){
formchange(user);
}
},[userid]);
const onSubmit = (e) => {
e.preventDefault(); e.preventDefault();
userreducer({type:'update', user: formdata}) console.log(api);
if(formdata.line!==undefined){
api.updateUser(sessiondata.token, formdata).then(r => {
userreducer({ type: 'update', user: r })
});
}else{
api.createUser(sessiondata.token, formdata).then(r => {
userreducer({ type: 'create', user: r })
});
}
route('/users'); route('/users');
} }
const Input = ({id, type='text', label, disabled})=>(<div><label for={id}>{label}</label><input disabled={disabled} type={type} checked={type=='checkbox'&&formdata[id]?'on':''} value={formdata[id]} id={id} onChange={onChange} name={`edit-${id}`} /></div>) const Input = ({ id, type = 'text', label, disabled }) => (<div className={'textbox'} ><input placeholder=" " disabled={disabled} type={type} checked={type == 'checkbox' && formdata[id] ? 'on' : ''} value={formdata[id]} id={id} onChange={onChange} name={`edit-${id}`} /><label for={id}>{label}</label></div>)
return( return (
<div className='container'> <div className='container'>
<h1>Benutzer Bearbeiten</h1> <h1>{formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"}</h1>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<Input id="uid" label="UserID" /> <Input id="uid" label="UserID" />
<Input id="first_name" label="Vorname" /> <Input id="first_name" label="Vorname" />
<Input id="last_name" label="NachName" /> <Input id="last_name" label="NachName" />
<Input id="rfid_uid" label="RFID" /> <Input id="rfid_uid" label="RFID" />
<Input id="user_pin" label="Pin" /> <Input id="user_pin" label="Pin" />
<Input id="enabled" type={'checkbox'} label="Vorname" /> <Input id="enabled" type={'checkbox'} label="" />
<input type={'submit'}>Speichern</input> <div className={'button'} ><input type={'submit'}>Speichern</input> </div>
</form> </form>
</div> </div>
) )

View File

@ -1,10 +1,29 @@
import { h } from 'preact'; import { h } from 'preact';
import { Link } from 'preact-router';
import { useContext, useState } from 'preact/hooks';
import AppState from '../../store/AppState';
function Home() {return( function Home() {
let [sessiondata, ] = useContext(AppState).session;
return (
<div class="container"> <div class="container">
<h1>Home</h1> <div className={'contentbox'} >
<p>This is the Home component.</p> <h2>Startseite</h2>
<p>Willkommen zurück {sessiondata.username} </p>
<div>
<h3>Status</h3>
<ul>
<li>Aktiv seit: 12.01.2022</li>
</ul>
</div> </div>
); <div>
<p>Hier können Sie den Türöffner manuell aktivieren.</p>
<p>Um Benutzer anzulegen, öffnen sie das Menü und tippen auf <Link href="/newuser">Benutzer anlegen</Link> </p>
</div>
</div>
</div>
);
} }
export default Home; export default Home;

View File

@ -1,19 +1,19 @@
import { h } from 'preact'; import { h } from 'preact';
import { route } from 'preact-router'; import { route } from 'preact-router';
import { useContext,useState } from 'preact/hooks'; import { useContext, useState } from 'preact/hooks';
import AppState from '../../store/AppState'; import AppState from '../../store/AppState';
import Breadcrumbs from '../../components/breadcrumbs'; import Breadcrumbs from '../../components/breadcrumbs';
import api from '../../api' import api from '../../api'
function Login() { function Login() {
let [sessiondata, setsession]= useContext(AppState).session; let [sessiondata, setsession] = useContext(AppState).session;
const [val, set] = useState({ username: '', password: '', error: null }); const [val, set] = useState({ username: '', password: '', error: null });
const navigation = ["Login"]; const navigation = ["Login"];
if(sessiondata.active) if (sessiondata.active)
route('/', true); route('/', true);
function onSubmit(e) { function onSubmit(e) {
e.preventDefault(); e.preventDefault();
api.login(val.username, val.password).then(token=>{ api.login(val.username, val.password).then(token => {
if (token!='failed!') { if (token != 'failed!') {
let newsession = { let newsession = {
type: 'start', type: 'start',
username: val.username, username: val.username,
@ -31,13 +31,13 @@ function Login() {
return ( return (
<div class="container"> <div class="container">
<Breadcrumbs items={navigation} /> <Breadcrumbs items={navigation} />
<div className={'contentbox'} >
<form id="login_form" onSubmit={onSubmit} > <h2>Anmeldung</h2>
<p > <p >Bitte melden Sie sich mit ihren Nutzerdaten an.</p>
Bitte melden Sie sich mit ihren Nutzerdaten an.
</p>
{val.error !== null && <span style={'color: red'}>Fehler: Ungültige Anmeldedaten.</span>} {val.error !== null && <span style={'color: red'}>Fehler: Ungültige Anmeldedaten.</span>}
<form id="login_form" onSubmit={onSubmit} >
<div class="input-box"> <div class="input-box">
<input id="name" type="text" placeholder="Username" onInput={e => set({ ...val, username: e.target.value })} value={val.username} /> <input id="name" type="text" placeholder="Username" onInput={e => set({ ...val, username: e.target.value })} value={val.username} />
<label for="name">Benutzername</label> <label for="name">Benutzername</label>
@ -46,10 +46,11 @@ function Login() {
<input id="pass" type="password" placeholder="Passwort" onInput={e => set({ ...val, password: e.target.value })} value={val.password} /> <input id="pass" type="password" placeholder="Passwort" onInput={e => set({ ...val, password: e.target.value })} value={val.password} />
<label for="pass">Password</label> <label for="pass">Password</label>
</div> </div>
<input type="submit" value="Submit" /> <div class={'button'} onClick={onSubmit} ><input type="submit" value="Anmelden" /></div>
</form> </form>
</div> </div>
</div>
); );
} }

View File

@ -1,29 +1,38 @@
import { h } from 'preact'; import { h } from 'preact';
import { Link } from 'preact-router'; import { Link } from 'preact-router';
import { useContext } from 'preact/hooks'; import { useContext, useState } from 'preact/hooks';
import AppState from '../../store/AppState'; import AppState from '../../store/AppState';
import { Breadcrumbs } from '../../components'; import { Breadcrumbs } from '../../components';
import api from '../../api'
function Logout() { function Logout() {
const navigation = ["Logout"]; const navigation = ["Logout"];
let [text, settext] = useState('');
let [sessiondata, setsession] = useContext(AppState).session; let [sessiondata, setsession] = useContext(AppState).session;
this.shouldComponentUpdate = function () { this.shouldComponentUpdate = function () {
console.log('functional component vs closures'); console.log('functional component vs closures');
} }
if (sessiondata.active) { if (sessiondata.active) {
api.logout(sessiondata.token).then((r) => r === 'success' ? settext('Sitzung beendet.') : settext('Sitzung war bereits nicht mehr vorhanden.'));
setsession({ type: 'end' }) setsession({ type: 'end' })
} }
return ( return (
<div class="container"> <div class="container">
<Breadcrumbs items={navigation} /> <Breadcrumbs items={navigation} />
<div className={'contentbox'} >
<h2>
Erfolgreich abgemeldet:
</h2>
<p> <p>
Erfolgreich abgemeldet. {text}<br />
<Link href="/login">Erneut Anmelden</Link> <Link className={'button'} href="/login">Erneut Anmelden</Link>
</p> </p>
</div>
</div> </div>
); );

View File

@ -5,32 +5,33 @@ function System() {
return ( return (
<div className='container'> <div className='container'>
<h1>System</h1> <div className={'contentbox'} >
<h2>WiFi Setup</h2> <h2>System</h2>
<h3>WiFi Setup</h3>
<hr /> <hr />
<form> <form>
<div> <div className={'textbox'} >
<label>SSID</label>
<input type="text" placeholder='Wifi network name' /> <input type="text" placeholder='Wifi network name' />
<label>SSID</label>
</div> </div>
<div> <div className={'textbox'} >
<label>SSID</label>
<input type="text" placeholder='Wifi network name' /> <input type="text" placeholder='Wifi network name' />
<label>Password</label>
</div> </div>
</form> </form>
<h2>Admin User</h2> <h3>Admin User</h3>
<hr /> <hr />
<form> <form>
<div> <div className={'textbox'} >
<label>SSID</label>
<input type="text" placeholder='Wifi network name' /> <input type="text" placeholder='Wifi network name' />
<label>Username</label>
</div> </div>
<div> <div className={'textbox'} >
<label>SSID</label>
<input type="text" placeholder='Wifi network name' /> <input type="text" placeholder='Wifi network name' />
<label>Password</label>
</div> </div>
</form> </form>
<h2>Datenbank Backup</h2><hr /> <h3>Datenbank Backup</h3><hr />
<div> <div>
<form> <form>
<h3>Backup einspielen</h3> <h3>Backup einspielen</h3>
@ -41,7 +42,7 @@ function System() {
<button>Download</button> <button>Download</button>
</div> </div>
</div>
</div>) </div>)
} }
export default System export default System

View File

@ -7,7 +7,7 @@ import AppState, { UserTable } from "../../store";
function Users({ pageid }) { function Users({ pageid }) {
const [viewstate, setview] = useState({ limit: 100, page: 1, pages: null }) const [viewstate, setview] = useState({ limit: 100, page: 1, pages: null })
const { usertable, userreducer } = useContext(UserTable); const { usertable, userreducer } = useContext(UserTable);
let [sessiondata, setsession] = useContext(AppState).session; let [sessiondata,] = useContext(AppState).session;
const setPage = (e) => { const setPage = (e) => {
e.preventDefault(); e.preventDefault();
console.log(e) console.log(e)
@ -25,7 +25,7 @@ function Users({ pageid }) {
let action = { type: 'import', imported }; let action = { type: 'import', imported };
userreducer(action); userreducer(action);
}); });
},[sessiondata]); }, [sessiondata]);
useEffect(() => { useEffect(() => {
setview({ ...viewstate, pages: Math.ceil(usertable.length / viewstate.limit) }); setview({ ...viewstate, pages: Math.ceil(usertable.length / viewstate.limit) });
@ -36,11 +36,14 @@ function Users({ pageid }) {
setview({ ...viewstate, page: pageid }) setview({ ...viewstate, page: pageid })
}, [pageid]) }, [pageid])
const deleteUser = (user) => { const deleteUser = (user) => {
api.deleteUser(sessiondata.token,user).then(r=>{
let action = { let action = {
type: 'delete', type: 'delete',
user user, r
} }
userreducer(action) userreducer(action)
})
} }
const calculateView = () => { const calculateView = () => {
let start = viewstate.limit * (viewstate.page - 1); let start = viewstate.limit * (viewstate.page - 1);
@ -55,10 +58,13 @@ function Users({ pageid }) {
return ( return (
<div class="container"> <div class="container">
<Breadcrumbs items={navigation} /> <Breadcrumbs items={navigation} />
<div className={'contentbox'} >
<div>Suche: <input type={'text'} /><button>Hinzufügen</button> Limit: <select><optgroup label={'Anzahl'}><option>10</option><option>25</option><option>50</option><option>100</option></optgroup><option>Alle</option></select></div> <div>Suche: <input type={'text'} /><button>Hinzufügen</button> Limit: <select><optgroup label={'Anzahl'}><option>10</option><option>25</option><option>50</option><option>100</option></optgroup><option>Alle</option></select></div>
</div>
<UserList {...calculateView()} userlist={usertable} deleteUser={deleteUser} editUser={editUser} /> <UserList {...calculateView()} userlist={usertable} deleteUser={deleteUser} editUser={editUser} />
<Pageselector start={1} end={viewstate.pages} current={viewstate.page} setPage={setPage} /> <Pageselector start={1} end={viewstate.pages} current={viewstate.page} setPage={setPage} />
</div> </div>
); );

View File

@ -23,7 +23,7 @@ export const userTableReducer = (state, action) => {
let newstate = []; let newstate = [];
let newindex = 0; let newindex = 0;
state.forEach((u, i) => { state.forEach((u, i) => {
if (user.uid && u.uid !== user.uid|| user.line && i !== user.line ) if (user.uid && u.uid != user.uid|| user.line && i != user.line )
newstate.push({ ...u, line: newindex++ }) newstate.push({ ...u, line: newindex++ })
return newstate; return newstate;
}, []); }, []);
@ -32,7 +32,7 @@ export const userTableReducer = (state, action) => {
case 'update': { case 'update': {
let newstate = []; let newstate = [];
state.forEach((u, i) => { state.forEach((u, i) => {
if (user.uid && u.uid === user.uid || user.line && i === user.line) if (user.uid && u.uid == user.uid || user.line && i == user.line)
newstate.push(user); newstate.push(user);
else else
newstate.push(u); newstate.push(u);

View File

@ -5,7 +5,7 @@
background: #eee background: #eee
box-shadow: inset 0 0 .3em #ccc, 0 0 .5em #ddd box-shadow: inset 0 0 .3em #ccc, 0 0 .5em #ddd
padding: .3em padding: .3em
margin: 0 margin: 0 .5em
border: 0.05em solid #fff border: 0.05em solid #fff
border-radius: .3em border-radius: .3em
list-style-type: none list-style-type: none

46
src/style/_button.sass Normal file
View File

@ -0,0 +1,46 @@
@mixin default
z-index: 1
margin: 1em
padding: 0.5em
text-align: center
display: inline-block
position: relative
color: #333
font-weight: bold
text-decoration: none
border: solid 1px #999
border-radius: .2rem
input[type=submit]
display: block
background: none
width: 100%
border: none
border-radius: none
font-size: 2em
transition: all ease-in-out 100ms
&::before
z-index: -1
border-radius: .2rem
position: absolute
top: 0
right: 0
content: ''
background: transparent
width: 0%
height: 100%
transition: all ease-in-out 250ms
&:hover
color: #fff
input[type=submit]
background: #333
color: #fff
border-radius: .3rem
&::before
top: 0
left: 0
right: auto
content: ''
background: #333
width: 100%
height: 100%

37
src/style/_input.sass Normal file
View File

@ -0,0 +1,37 @@
@mixin textfield
position: relative
display: inline-block
margin: 0 .25em 1em .25em
background: #fafafa
border-radius: .3em
border-bottom: 1px solid #ccc
&:hover
background: #fff
input
display: block
background: none
width: 100%
border-radius: none
outline: none
border: none
font-size: 1em
padding: 0.7em
+ label
user-select: none
position: absolute
top: 0.15em
left: 0.25em
font-size: .7em
cursor: text
transition: 250ms all
&::placeholder
color: transparent
&:placeholder-shown + label
color: grey
position: absolute
padding: .7em
top: 0em
left: 0em
font-size: 1em

View File

@ -1,40 +1,13 @@
@mixin text-input @use 'input'
position: relative
input
display: block
width: 100%
border-radius: none
outline: none
border: none
font-size: 1.5em
padding: .7em
+ label
position: absolute
top: 0
left: 0
cursor: text
transition: 250ms all
&::placeholder
color: transparent
&:placeholder-shown + label
color: grey
font-size: 1.5em
position: absolute
padding: .7em
@mixin login-form @mixin login-form
padding: 1em padding: 1em
width: 100%
margin: 0 auto margin: 0 auto
display: flex display: flex
flex-direction: column flex-direction: column
.input-box .input-box
@include text-input @include input.textfield
font-size: 1.5em
p p
font-size: 2em font-size: 2em
input[type=submit]
border: none
border-radius: none
font-size: 2em

View File

@ -15,10 +15,13 @@
color: #666 color: #666
//text-shadow: 0 0 .2em #000 //text-shadow: 0 0 .2em #000
width: 100% width: 100%
transition: all ease-in-out 150ms
//background: #ccc //background: #ccc
&:hover &:hover
background: #bbb padding-left: .5em
color: #333 background: #888
color: #fff
text-shadow: 0 0 1em #fff
&::after &::after
top: 100% top: 100%
left: 0 left: 0
@ -27,5 +30,5 @@
position: absolute position: absolute
width: 100% width: 100%
height: .1em height: .1em
background: #8a8080 background: #ccc
//border-bottom-left-radius: 2em 1em //border-bottom-left-radius: 2em 1em

View File

@ -1,16 +1,22 @@
@mixin pageselector @mixin pageselector
text-align: center text-align: center
//background-color: #ccc //background-color: #ccc
margin: 1em margin: 2em
ul ul
list-style: none list-style: none
justify-content: center justify-content: center
margin: 0 margin: 0
padding: 0 padding: 0
display: flex display: flex
flex-wrap: wrap
li a li a
display: inline-flex
text-decoration: none text-decoration: none
font-weight: bold font-weight: bold
min-width: 2rem
min-height: 2rem
justify-content: center
align-items: center
padding: .2em .5em padding: .2em .5em
margin: .3em margin: .3em
border-radius: .3em border-radius: .3em
@ -23,4 +29,4 @@
&:hover &:hover
color: #333 color: #333
background: #fff background: #fff
border: .1em solid #ccc border: .1em solid #444

View File

@ -4,9 +4,12 @@
@use 'breadcrumbs' @use 'breadcrumbs'
@use 'pageselector' @use 'pageselector'
@use 'footer' @use 'footer'
@use 'button'
@use 'input'
* *
//border: 1px dotted red
box-sizing: border-box box-sizing: border-box
html html
font-family: Helvetica, sans-serif font-family: Helvetica, sans-serif
font-size: 16px font-size: 16px
@ -30,11 +33,12 @@ body
position: fixed position: fixed
top: 0em top: 0em
width: 100% width: 100%
max-width: 100vw
height: 4rem height: 4rem
display: flex display: flex
flex-direction: column flex-direction: column
justify-content: flex-end justify-content: center
padding: .5em 0 padding: 0.5em .5em
margin: 0 0 0 0 margin: 0 0 0 0
background: linear-gradient(#9e9e9e 0% , #d1d1d1 100%) background: linear-gradient(#9e9e9e 0% , #d1d1d1 100%)
box-shadow: 0 0 .2em #444 box-shadow: 0 0 .2em #444
@ -49,6 +53,7 @@ body
.container .container
width: clamp(5ch, 100%,75ch) width: clamp(5ch, 100%,75ch)
margin: 0 auto margin: 0 auto
.breadcrumb .breadcrumb
@include breadcrumbs.breadcrumbs @include breadcrumbs.breadcrumbs
@ -73,6 +78,7 @@ body
display: flex display: flex
flex-direction: row flex-direction: row
background: #eee background: #eee
margin: 0 0.5em
padding: .5em 0 padding: .5em 0
border-radius: .3em border-radius: .3em
&:not(:last-child) &:not(:last-child)
@ -132,3 +138,26 @@ body
footer footer
@include footer.footer @include footer.footer
.contentbox
display: flex
flex-wrap: wrap
flex-direction: column
align-content: stretch
padding: 1em
margin: 1em 0.5em
border: 1px solid #fff
background: #f4f4f4
box-shadow: 0 0 .5em #ddd, inset 0 0 1em #fff
border-radius: .5em
h2
margin: 0
display: flex
flex-break: after
flex-basis: 100%
.button
@include button.default
.textbox
@include input.textfield
font-size: 1.5em