diff --git a/src/api/index.js b/src/api/index.js index 896f758..cb49bd8 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,42 +1,46 @@ -const server = 'http://192.168.4.22' +const server = 'http://192.168.20.1' +const timeout = 1500; async function restoreSession(reducer = null) { let token = localStorage.getItem('token'); let username = localStorage.getItem('username'); - console.log({ type: 'start', token, username }); let active = token !== null && username !== null && await checkAuth(token); if (active && reducer !== null) { reducer({ type: 'start', token, username }); - console.log({ type: 'start', token, username }); } return (active ? { active: true, token, username } : { active: false }); } -async function storeSession(username, token) { - token = await token; - localStorage.setItem('token', token); - localStorage.setItem('username', username); - return token +async function storeSession(result, token) { + result.token = await token; + result.type = 'start'; + console.log(result) + if (result.permanent) { + localStorage.setItem('token', result.token); + localStorage.setItem('username', result.username); + } + return result; } -async function login(username, password) { +async function login(formdata) { let formData = new FormData(); - formData.append('username', username) - formData.append('password', password); + formData.append('username', formdata.username) + formData.append('password', formdata.password); formData.append('action', 'login'); + let result = { username: formdata.username, permanent: formdata.permanent } 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 }) + const id = setTimeout(() => controller.abort(), timeout); + const resp = await 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'; + if (resp.ok) return storeSession(result, resp.text()); + else if (resp.status === 401) + result.error = 'login_failed'; else throw new Error(resp.error); + return result; }) .catch((error) => { - console.log('Error is:', error); - return 'network_error'; + result.error = 'network_connection'; }) - ; - return resp; + return result; } async function logout(token) { let formData = new FormData() @@ -54,10 +58,6 @@ async function checkAuth(token) { return await resp; } async function fetchdb(token = 'azif7eqCl5') { - //fetch(`${server}/api/userdb`).then() - //let xmlHttp = new XMLHttpRequest(); - //xmlHttp.open( "GET", `${server}/api/userdb`, false ); // false for synchronous request - //xmlHttp.send( null ); const resp = await fetch(`${server}/api/userdb`, { method: 'GET', mode: 'cors', headers: { Authentification: token } }) .then(resp => resp.text()).then(text => parsedb(text)) return resp; @@ -100,11 +100,22 @@ function parsedb(raw) { return users; } -async function catchRFID(token){ +async function catchRFID(token) { const resp = await fetch(`${server}/api/rfid`, { method: 'GET', mode: 'cors', headers: { Authentification: token } }) - .then(resp => resp.json()); + .then(resp => resp.json()); return resp; } -const publicfunctions = { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID }; + +async function config(token, payload) { + let reqdata = new FormData(); + for (let [k, v] of Object.entries(payload)) + if (['number', 'string'].includes(typeof (v))) + reqdata.append(k, v); + const abort = new AbortController(); + const aborttimer = setTimeout(() => abort.abort(), timeout); + return fetch(`${server}/api/config`, { signal: abort.signal, method: 'POST', mode: 'cors', body: reqdata, headers:{Authentification: token} }). + then(resp => resp.ok ? resp.json() : { error: resp.status }); +} +const publicfunctions = { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID, config }; export default { ...publicfunctions } -export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID } \ No newline at end of file +export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID, config } \ No newline at end of file diff --git a/src/components/app.js b/src/components/app.js index bc9329c..1ec54dc 100644 --- a/src/components/app.js +++ b/src/components/app.js @@ -1,53 +1,54 @@ import { h } from "preact"; import { Router, route } from "preact-router"; -import { useEffect, useReducer } from "preact/hooks"; +import { useEffect, useReducer, useState } from "preact/hooks"; import { Header, Menu } from "./index.js"; import { Home, Users, EditUser, Login, Logout, System } from "../route"; import { AppStateProvider, UserTableProvider, menuReducer, sessionReducer, userTableReducer } from "../store"; import api from "../api/index.js"; + +const menu_items = [ + { text: "Übersicht", path: "/" }, + { text: "Benutzer anlegen", path: "/newuser" }, + { text: "Benutzer verwalten", path: "/users" }, + { text: "System", path: "/system" }, + { text: "Abmelden", path: "/logout" } +] + function App() { // useReducer const menu = useReducer(menuReducer, false); const session = useReducer(sessionReducer, {}); const [usertable, userreducer] = useReducer(userTableReducer, []); - console.log(session[0]); + const [lasturl, setlasturl] = useState("/"); if(!session[0]||(session[0]&&session[0].active===undefined)) api.restoreSession(session[1]); - useEffect(()=>{ if(session[0]&&session[0].active){ - console.log("triggerimport"); api.fetchdb(session[0].token).then(imported => { let action = { type: 'import', imported }; userreducer(action); }); }},[session]); - - console.log(session); - this.menu_items = [ - { text: "Übersicht", path: "/" }, - { text: "Benutzer anlegen", path: "/newuser" }, - { text: "Benutzer verwalten", path: "/users" }, - { text: "System", path: "/system" }, - { text: "Abmelden", path: "/logout" } - ] - this.handleRoute = async e => { + const handleRoute = async e => { + let active = session[0].active; + switch (e.url) { default: - if (!session[0].active) route('/login', true); + if (!active) route('/login', true); break; } }; + return (
- + {!menu[0] && - + diff --git a/src/components/controls/input.jsx b/src/components/controls/input.jsx index 4172d60..5afedff 100644 --- a/src/components/controls/input.jsx +++ b/src/components/controls/input.jsx @@ -1,14 +1,29 @@ -import { h } from 'preact' +import { h, render } from 'preact' -function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength, overridevalue }) { - let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.value }); - return (
- - -
) +function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength, overridevalue, onChange, onInput, checkInput, allowdChars, showavailable,hinttext }) { + + if (typeof (onInput) === 'undefined' && typeof (formchange) === 'function') { + onInput = (e) => formchange(p => { + if (typeof (checkInput) === 'function' && !checkInput(e.target.value)) { + return ({ ...p, error: { [e.target.id]: 'input_error' } }) + } + return ({ ...p, [e.target.id]: e.target.value, error: { [e.target.id]: undefined } }) + }); + } + const onBlur = (e)=>{ + formchange(p =>({ ...p, error: { [e.target.id]: undefined } })); + } + return (<> +
+ + + {formdata&&formdata.error&&formdata.error[id]&&hinttext&&
{hinttext}
} +
+ {showavailable && {formdata[id] ? formdata[id].length : 0} von {maxlength} Zeichen verwendet.} + ) } function CheckBox({ id, disabled, label, formdata, formchange }) { - let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.checked }); + let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.checked }); return (
diff --git a/src/route/edituser/index.js b/src/route/edituser/index.js index 6aa77ba..479d21c 100644 --- a/src/route/edituser/index.js +++ b/src/route/edituser/index.js @@ -82,8 +82,7 @@ function EditUser({ userid }) {

Eindeutige Identifikationsnummer

- - { {formdata["uid"] ? formdata["uid"].length : 0} von {maxlength_uid} Zeichen verwendet.} +
Info @@ -94,19 +93,16 @@ function EditUser({ userid }) {

Persönliche Daten

- - { {formdata["first_name"] ? formdata["first_name"].length : 0} von {maxlength_name} Zeichen verwendet.} +
- - { {formdata["last_name"] ? formdata["last_name"].length : 0} von {maxlength_name} Zeichen verwendet.} +

Authentifizierung

- - { {formdata["rfid_uid"] ? formdata["rfid_uid"].length : 0} von {maxlength_rfid} Zeichen verwendet.} +
@@ -115,8 +111,7 @@ function EditUser({ userid }) {
- - { {formdata["user_pin"] ? formdata["user_pin"].length : 0} von {maxlength_pin} Zeichen verwendet.} +
Info diff --git a/src/route/login/index.jsx b/src/route/login/index.jsx index 793dcaa..9ed7132 100644 --- a/src/route/login/index.jsx +++ b/src/route/login/index.jsx @@ -2,34 +2,23 @@ import { h } from 'preact'; import { route } from 'preact-router'; import { useContext, useState } from 'preact/hooks'; import AppState from '../../store/AppState'; -import {Breadcrumbs, Warnbox} from '../../components'; +import { Breadcrumbs, Warnbox } from '../../components'; import { CheckBox, Button, TextBox } from '../../components/controls'; import api from '../../api' function Login() { let [sessiondata, setsession] = useContext(AppState).session; - const [val, set] = useState({ username: '', password: '', error: null }); + const [fromdata, setform] = useState({ username: '', password: '', error: null }); const navigation = ["Login"]; if (sessiondata.active) route('/', true); function onSubmit(e) { e.preventDefault(); - api.login(val.username, val.password).then(result => { - if(result == 'login_failed'){ - set(prev=>({ ...prev, error: "login_failed",password: '' })); - } - else if(result=='network_error') - set(prev=>({ ...prev, error: "network_connection" })); + api.login(fromdata).then(result => { + if (result.error !== undefined) + setform(result); else { - console.log(typeof(result)) - let newsession = { - type: 'start', - username: val.username, - token: result - } - setsession(newsession); - set({ username: '', password: '' }); + setsession(result); } - }) } @@ -39,19 +28,19 @@ function Login() {

Anmeldung

Bitte melden Sie sich mit ihren Nutzerdaten an.

- - {val.error ==='login_failed' && Ungültige Anmeldedaten.
Bitte überprüfen Sie den eingebenen Benutzernamen und das Passwort.
} - {val.error ==='network_connection' && Die Kommunikation mit dem Gerät ist zurzeit nicht möglich.
Bitte überprüfen Sie die Netzwerkverbindung.
} + + {fromdata.error === 'login_failed' && Ungültige Anmeldedaten.
Bitte überprüfen Sie den eingebenen Benutzernamen und das Passwort.
} + {fromdata.error === 'network_connection' && Die Kommunikation mit dem Gerät ist zurzeit nicht möglich.
Bitte überprüfen Sie die Netzwerkverbindung.
}
- +
- +
- +
diff --git a/src/route/system/index.jsx b/src/route/system/index.jsx index b4e2fee..ebd74d8 100644 --- a/src/route/system/index.jsx +++ b/src/route/system/index.jsx @@ -1,48 +1,112 @@ import { h } from 'preact'; import { Breadcrumbs } from "../../components"; - +import { useContext, useEffect, useState } from 'preact/hooks'; +import { CheckBox, Button, TextBox } from '../../components/controls'; +import AppState from '../../store/AppState'; +import api from '../../api' +const checkIP = (v)=>{ + let parts = v.split('.'); + if(parts.length>4) return false; + for(let part of parts) + if(isNaN(part)||part>255||part<0||part.length>3) + return false; + return true; +} function System() { - + let [session,] = useContext(AppState).session; + let [formdata, setform] = useState({}); + useEffect(() => { + api.config(session.token, { action: "get" }).then(r => setform(p => ({ ...p, ...r }))); + }, [session]) return (

System

-

WiFi Setup

-
- -
- - -
-
- - -
- -

Admin User

-
-
-
- - -
-
- - -
-
-

Datenbank Backup


-
-
-

Backup einspielen

- - -
-

Backup herunterladen

- -
+
+

Geräteeinstellungen

+
+
+ !isNaN(v)&&v>=0&&v<=1} maxlength={1} showavailable hinttext="Nur Zahlen 0 bis 1" /> +
+
+ !isNaN(v)&&v>=0&&v<=255} maxlength={3} showavailable hinttext="Nur Zahlen 0 bis 255" /> +
+
+ !isNaN(v)&&v>=0&&v<=255} maxlength={3} showavailable hinttext="Nur Zahlen 0 bis 255"/> +
+
+

WiFi Setup

+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+

Admin User

+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+

Datenbank Backup

+
+
+

Backup einspielen

+
+ + +
+
+
+

Backup herunterladen

+ +
+
) } diff --git a/src/style/_button.sass b/src/style/_button.sass index 0a3633a..65e8113 100644 --- a/src/style/_button.sass +++ b/src/style/_button.sass @@ -15,16 +15,17 @@ overflow: hidden &--disabled color: #999 - + button display: block background: none - min-width: 100% + width: 100% border: none border-radius: none font-size: 2em transition: all ease-in-out 100ms - + overflow: hidden + text-overflow: ellipsis &::before z-index: -1 border-radius: .2rem diff --git a/src/style/_input.sass b/src/style/_input.sass index 5b0c589..961a82b 100644 --- a/src/style/_input.sass +++ b/src/style/_input.sass @@ -4,16 +4,17 @@ position: relative display: block margin: 0 - overflow: hidden + background: #fafafa border-radius: .3em border-bottom: 1px solid #ccc &:hover background: #fff input + //overflow: hidden display: block background: none - min-width: 100% + width: 100% border-radius: none outline: none border: none @@ -28,13 +29,12 @@ font-size: .7em cursor: text transition: 250ms all + overflow: hidden text-overflow: ellipsis - &::placeholder color: transparent &:placeholder-shown + label - text-overflow: ellipsis color: grey position: absolute padding: .7em @@ -46,6 +46,17 @@ &__info color: #ccc font-size: 0.7em + &__hint + position: absolute + z-index: 1 + left: 0 + right: 0 + top: calc(100% - .6em) + border-bottom-left-radius: .3em + border-bottom-right-radius: .3em + color: #fff + font-size: .7em + background: red @mixin checkbox position: relative