Added: Errormessage for users incl. hints, reading of of current rfid into user-editor

This commit is contained in:
Jean Jacques Avril 2022-03-15 21:13:40 +01:00
parent 32e6018415
commit 37f134fd13
10 changed files with 156 additions and 39 deletions

View File

@ -1,16 +1,15 @@
const server = 'http://192.168.4.22' const server = 'http://192.168.4.22'
async function restoreSession(reducer = null) {
async function restoreSession(reducer=null) {
let token = localStorage.getItem('token'); let token = localStorage.getItem('token');
let username = localStorage.getItem('username'); let username = localStorage.getItem('username');
console.log({type:'start', token, username}); console.log({ type: 'start', token, username });
let active = token!==null && username!==null && await checkAuth(token); let active = token !== null && username !== null && await checkAuth(token);
if(active&&reducer!==null){ if (active && reducer !== null) {
reducer({type:'start', token, username}); reducer({ type: 'start', token, username });
console.log({type:'start', token, username}); console.log({ type: 'start', token, username });
} }
return (active ? { active: true, token, username } : {active:false}); return (active ? { active: true, token, username } : { active: false });
} }
async function storeSession(username, token) { async function storeSession(username, token) {
token = await token; token = await token;
@ -24,7 +23,19 @@ async function login(username, password) {
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 = fetch(`${server}/api/auth`, { method: 'POST', mode: 'cors', body: formData }).then(resp => storeSession(username, resp.text())); const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 1500);
const resp = fetch(`${server}/api/auth`, { signal: controller.signal, method: 'POST', mode: 'cors', body: formData })
.then(resp => {
if (resp.ok) return storeSession(username, resp.text())
else if(resp.status===401) return 'login_failed';
else throw new Error(resp.error);
})
.catch((error) => {
console.log('Error is:', error);
return 'network_error';
})
;
return resp; return resp;
} }
async function logout(token) { async function logout(token) {
@ -87,8 +98,13 @@ function parsedb(raw) {
users.push({ line, uid, first_name, last_name, rfid_uid, user_pin, enabled: enabled[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, createCsvTable, updateUser, deleteUser, createUser,restoreSession };
async function catchRFID(token){
const resp = await fetch(`${server}/api/rfid`, { method: 'GET', mode: 'cors', headers: { Authentification: token } })
.then(resp => resp.json());
return resp;
}
const publicfunctions = { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID };
export default { ...publicfunctions } export default { ...publicfunctions }
export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession } export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID }

View File

@ -1,9 +1,9 @@
import { h } from 'preact' import { h } from 'preact'
function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength }) { function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength, overridevalue }) {
let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.value }); let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.value });
return (<div className={'textbox'} > return (<div className={'textbox'} >
<input placeholder=" " disabled={disabled} type={type} value={formdata[id]?formdata[id]:''} id={id} onInput={onChange} maxlength={maxlength} name={`form-${id}`} /> <input placeholder=" " disabled={disabled} type={type} value={overridevalue?overridevalue:formdata[id]?formdata[id]:''} id={id} onInput={onChange} maxlength={maxlength} name={`form-${id}`} />
<label for={id}>{label}</label> <label for={id}>{label}</label>
</div>) </div>)
} }

View File

@ -4,6 +4,6 @@ import Pageselector from "./Pageselector";
import Header from "./header"; import Header from "./header";
import UserList from "./userlist"; import UserList from "./userlist";
import Menu from "./menu"; import Menu from "./menu";
import Warnbox from "./warnbox";
export {App, Breadcrumbs, Pageselector, Header, UserList, Menu} export {App, Breadcrumbs, Pageselector, Header, UserList, Menu, Warnbox}
export default App export default App

View File

@ -0,0 +1,15 @@
function Warnbox({ title = "Fehler", children }) {
return (<div className={'warnbox'} >
<span className='warnbox__icon'></span>
<div className="column">
<h3>{title}</h3>
{children && <span> {children}</span>}
</div>
</div>)
}
export default Warnbox

View File

@ -1,5 +1,5 @@
import { h } from 'preact'; import { h } from 'preact';
import { useContext, useEffect, useState } from 'preact/hooks'; import { useContext, useEffect, useState, useCallback } from 'preact/hooks';
import { route } from 'preact-router'; import { route } from 'preact-router';
import AppState, { UserTable } from "../../store"; import AppState, { UserTable } from "../../store";
import api from '../../api' import api from '../../api'
@ -8,12 +8,28 @@ import { CheckBox, Button, TextBox } from '../../components/controls';
function EditUser({ userid }) { function EditUser({ userid }) {
let [sessiondata,] = useContext(AppState).session; let [sessiondata,] = useContext(AppState).session;
const { usertable, userreducer } = useContext(UserTable); const { usertable, userreducer } = useContext(UserTable);
const [formdata, formchange] = useState({}); const [formdata, formchange] = useState({});
const [rfidscan, setscan] = useState({active:false, rfidscaninterval:null});
let rfidscaninterval = null;
let maxlength_uid = 10; let maxlength_uid = 10;
let maxlength_name = 25; let maxlength_name = 25;
let maxlength_pin = 10; let maxlength_pin = 10;
let maxlength_rfid = 8; let maxlength_rfid = 8;
useEffect(() => {
if (rfidscan.rfidscaninterval === null && rfidscan.active) {
rfidscan.rfidscaninterval = setInterval(() => {
api.catchRFID(sessiondata.token).then(result => {
if (result.rfid_uid) {
formchange(old => ({ ...old, rfid_uid: result.rfid_uid }))
setscan(p => ({...p,active:false}))
}
}).catch(() => { setscan(p => ({...p,active:false})) });
}, 1000);
} else if (rfidscan.rfidscaninterval !== null && !rfidscan.active) {
clearInterval(rfidscan.rfidscaninterval);
setscan(p => ({...p,rfidscaninterval:null}));
}
}, [rfidscan]);
useEffect(() => { useEffect(() => {
let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined; let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined;
if (user !== undefined) { if (user !== undefined) {
@ -28,6 +44,16 @@ function EditUser({ userid }) {
let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined; let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined;
formchange(user !== undefined ? user : {}); formchange(user !== undefined ? user : {});
} }
const runScan = (e) => {
e.preventDefault();
setscan(p => ({...p,active:!p.active}));
}
const onSubmit = (e) => { const onSubmit = (e) => {
e.preventDefault(); e.preventDefault();
let idcount = uidCount(usertable, formdata); let idcount = uidCount(usertable, formdata);
@ -49,7 +75,7 @@ function EditUser({ userid }) {
} }
return ( return (
<div className='container'> <div className='container'>
<Breadcrumbs items={['Benutzerverwaltung',formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"]} /> <Breadcrumbs items={['Benutzerverwaltung', formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"]} />
<div className={'contentbox'} > <div className={'contentbox'} >
<h2>{formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"}</h2> <h2>{formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"}</h2>
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
@ -57,7 +83,7 @@ function EditUser({ userid }) {
<div className='row'> <div className='row'>
<div className='column'> <div className='column'>
<TextBox info={true} formdata={formdata} formchange={formchange} id="uid" label="Benutzer-ID" maxlength={maxlength_uid} /> <TextBox info={true} formdata={formdata} formchange={formchange} id="uid" label="Benutzer-ID" maxlength={maxlength_uid} />
{<span class="textbox__info"> {formdata["uid"]?formdata["uid"].length:0} von {maxlength_uid} Zeichen verwendet.</span>} {<span class="textbox__info"> {formdata["uid"] ? formdata["uid"].length : 0} von {maxlength_uid} Zeichen verwendet.</span>}
</div> </div>
<div className='column'> <div className='column'>
<b>Info</b> <b>Info</b>
@ -69,22 +95,32 @@ function EditUser({ userid }) {
<div className='row'> <div className='row'>
<div className='column'> <div className='column'>
<TextBox formdata={formdata} formchange={formchange} id="first_name" label="Vorname" maxlength={maxlength_name} /> <TextBox formdata={formdata} formchange={formchange} id="first_name" label="Vorname" maxlength={maxlength_name} />
{<span class="textbox__info"> {formdata["first_name"] ?formdata["first_name"].length:0} von {maxlength_name} Zeichen verwendet.</span>} {<span class="textbox__info"> {formdata["first_name"] ? formdata["first_name"].length : 0} von {maxlength_name} Zeichen verwendet.</span>}
</div> </div>
<div className='column'> <div className='column'>
<TextBox formdata={formdata} formchange={formchange} id="last_name" label="NachName" maxlength={maxlength_name} /> <TextBox formdata={formdata} formchange={formchange} id="last_name" label="NachName" maxlength={maxlength_name} />
{<span class="textbox__info"> {formdata["last_name"]?formdata["last_name"].length:0} von {maxlength_name} Zeichen verwendet.</span>} {<span class="textbox__info"> {formdata["last_name"] ? formdata["last_name"].length : 0} von {maxlength_name} Zeichen verwendet.</span>}
</div> </div>
</div> </div>
<h3>Authentifizierung</h3> <h3>Authentifizierung</h3>
<div className='row'> <div className='row'>
<div className='column'> <div className='column'>
<TextBox formdata={formdata} formchange={formchange} id="rfid_uid" label="RFID" maxlength={maxlength_rfid} /> <TextBox formdata={formdata} formchange={formchange} id="rfid_uid" label="RFID" maxlength={maxlength_rfid} disabled={rfidscan.active} overridevalue={rfidscan.active&&"Scannen..."}/>
{<span class="textbox__info"> {formdata["rfid_uid"]?formdata["rfid_uid"].length:0} von {maxlength_rfid} Zeichen verwendet.</span>} {<span class="textbox__info"> {formdata["rfid_uid"] ? formdata["rfid_uid"].length : 0} von {maxlength_rfid} Zeichen verwendet.</span>}
</div> </div>
<div className='column'>
<Button onClick={runScan}>{rfidscan.active ? "Abbrechen" : "Scannen"}</Button>
</div>
</div>
<div className='row'>
<div className='column'> <div className='column'>
<TextBox formdata={formdata} formchange={formchange} id="user_pin" label="Pin" maxlength={maxlength_pin} /> <TextBox formdata={formdata} formchange={formchange} id="user_pin" label="Pin" maxlength={maxlength_pin} />
{<span class="textbox__info"> {formdata["user_pin"] ?formdata["user_pin"].length:0} von {maxlength_pin} Zeichen verwendet.</span>} {<span class="textbox__info"> {formdata["user_pin"] ? formdata["user_pin"].length : 0} von {maxlength_pin} Zeichen verwendet.</span>}
</div>
<div className='column'>
<b>Info</b>
<span>Achten Sie darauf, eine PIN <br />nicht mehrfach zu benutzen.</span>
</div> </div>
</div> </div>
<h3>Status</h3> <h3>Status</h3>

View File

@ -2,7 +2,7 @@ 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, Warnbox} from '../../components';
import { CheckBox, Button, TextBox } from '../../components/controls'; import { CheckBox, Button, TextBox } from '../../components/controls';
import api from '../../api' import api from '../../api'
function Login() { function Login() {
@ -13,19 +13,23 @@ function Login() {
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(result => {
if (token != 'failed!') { if(result == 'login_failed'){
set(prev=>({ ...prev, error: "login_failed",password: '' }));
}
else if(result=='network_error')
set(prev=>({ ...prev, error: "network_connection" }));
else {
console.log(typeof(result))
let newsession = { let newsession = {
type: 'start', type: 'start',
username: val.username, username: val.username,
token token: result
} }
setsession(newsession); setsession(newsession);
}
else {
set(prev=>({ ...prev, error: "user" }));
}
set({ username: '', password: '' }); set({ username: '', password: '' });
}
}) })
} }
@ -35,7 +39,9 @@ function Login() {
<div className={'contentbox'} > <div className={'contentbox'} >
<h2>Anmeldung</h2> <h2>Anmeldung</h2>
<p >Bitte melden Sie sich mit ihren Nutzerdaten an.</p> <p >Bitte melden Sie sich mit ihren Nutzerdaten an.</p>
{val.error !== null && <span style={'color: red'}>Fehler: Ungültige Anmeldedaten.</span>}
{val.error ==='login_failed' && <Warnbox title='Anmeldefehler'>Ungültige Anmeldedaten.<br />Bitte überprüfen Sie den eingebenen Benutzernamen und das Passwort.</Warnbox>}
{val.error ==='network_connection' && <Warnbox title='Netwerkfehler'>Die Kommunikation mit dem Gerät ist zurzeit nicht möglich.<br />Bitte überprüfen Sie die Netzwerkverbindung.</Warnbox>}
<form id="login_form" onSubmit={onSubmit} > <form id="login_form" onSubmit={onSubmit} >
<div className='row'> <div className='row'>
<TextBox maxlength={25} formdata={val} formchange={set} id="username" label="Benutzername" /> <TextBox maxlength={25} formdata={val} formchange={set} id="username" label="Benutzername" />

View File

@ -1,19 +1,25 @@
@mixin default @mixin default
z-index: 1 z-index: 1
margin: 1em margin: 0
padding: 0.5em padding: 0.5em
text-align: center text-align: center
display: inline-block display: block
width: auto
position: relative position: relative
color: #333 color: #333
font-weight: bold font-weight: bold
text-decoration: none text-decoration: none
border: solid 1px #999 border: solid 1px #999
border-radius: .2rem border-radius: .2rem
min-width: 100%
overflow: hidden
&--disabled
color: #999
button button
display: block display: block
background: none background: none
width: 100% min-width: 100%
border: none border: none
border-radius: none border-radius: none
font-size: 2em font-size: 2em

View File

@ -1,9 +1,10 @@
@mixin textfield @mixin textfield
margin: 0 1em margin: 0 1em
width: 100% min-width: 100%
position: relative position: relative
display: block display: block
margin: 0 margin: 0
overflow: hidden
background: #fafafa background: #fafafa
border-radius: .3em border-radius: .3em
border-bottom: 1px solid #ccc border-bottom: 1px solid #ccc
@ -12,7 +13,7 @@
input input
display: block display: block
background: none background: none
width: 100% min-width: 100%
border-radius: none border-radius: none
outline: none outline: none
border: none border: none
@ -23,19 +24,24 @@
position: absolute position: absolute
top: 0.15em top: 0.15em
left: 0.25em left: 0.25em
right: 0
font-size: .7em font-size: .7em
cursor: text cursor: text
transition: 250ms all transition: 250ms all
text-overflow: ellipsis
&::placeholder &::placeholder
color: transparent color: transparent
&:placeholder-shown + label &:placeholder-shown + label
text-overflow: ellipsis
color: grey color: grey
position: absolute position: absolute
padding: .7em padding: .7em
top: 0em top: 0em
left: 0em left: 0em
right: 0
bottom: 0
font-size: 1em font-size: 1em
&__info &__info
color: #ccc color: #ccc

31
src/style/_warnbox.sass Normal file
View File

@ -0,0 +1,31 @@
.warnbox
display: flex
flex-direction: row
//flex-wrap: wrap
background: #ffc964
box-shadow: 0 0 1em #ffc964
border-radius: .3em
border: 1px solid #fff
align-items: stretch
justify-content: stretch
padding: .3em
&__icon
font-size: 2em
line-height: 0.5em
//width: 2em
border-right: 1px solid #fff
color: #fff
font-weight: bold
text-shadow: 0 0 .2em #fff
padding: .25em
margin: 0
display: flex
justify-content: center
align-items: center
//align-self: flex-start
h3
margin: .3em
color: #fff
div span
margin-left: 1em

View File

@ -6,6 +6,7 @@
@use 'footer' @use 'footer'
@use 'button' @use 'button'
@use 'input' @use 'input'
@import 'warnbox'
* *
//border: 1px dotted red //border: 1px dotted red
box-sizing: border-box box-sizing: border-box