began with settings page and with the input sanity check + hint
This commit is contained in:
parent
37f134fd13
commit
3c5354afb9
@ -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) {
|
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 });
|
|
||||||
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 });
|
|
||||||
}
|
}
|
||||||
return (active ? { active: true, token, username } : { active: false });
|
return (active ? { active: true, token, username } : { active: false });
|
||||||
}
|
}
|
||||||
async function storeSession(username, token) {
|
async function storeSession(result, token) {
|
||||||
token = await token;
|
result.token = await token;
|
||||||
localStorage.setItem('token', token);
|
result.type = 'start';
|
||||||
localStorage.setItem('username', username);
|
console.log(result)
|
||||||
return token
|
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();
|
let formData = new FormData();
|
||||||
formData.append('username', username)
|
formData.append('username', formdata.username)
|
||||||
formData.append('password', password);
|
formData.append('password', formdata.password);
|
||||||
formData.append('action', 'login');
|
formData.append('action', 'login');
|
||||||
|
let result = { username: formdata.username, permanent: formdata.permanent }
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const id = setTimeout(() => controller.abort(), 1500);
|
const id = setTimeout(() => controller.abort(), timeout);
|
||||||
const resp = fetch(`${server}/api/auth`, { signal: controller.signal, method: 'POST', mode: 'cors', body: formData })
|
const resp = await fetch(`${server}/api/auth`, { signal: controller.signal, method: 'POST', mode: 'cors', body: formData })
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
if (resp.ok) return storeSession(username, resp.text())
|
if (resp.ok) return storeSession(result, resp.text());
|
||||||
else if(resp.status===401) return 'login_failed';
|
else if (resp.status === 401)
|
||||||
|
result.error = 'login_failed';
|
||||||
else throw new Error(resp.error);
|
else throw new Error(resp.error);
|
||||||
|
return result;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log('Error is:', error);
|
result.error = 'network_connection';
|
||||||
return 'network_error';
|
|
||||||
})
|
})
|
||||||
;
|
return result;
|
||||||
return resp;
|
|
||||||
}
|
}
|
||||||
async function logout(token) {
|
async function logout(token) {
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
@ -54,10 +58,6 @@ async function checkAuth(token) {
|
|||||||
return await resp;
|
return await resp;
|
||||||
}
|
}
|
||||||
async function fetchdb(token = 'azif7eqCl5') {
|
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 } })
|
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;
|
||||||
@ -100,11 +100,22 @@ function parsedb(raw) {
|
|||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function catchRFID(token){
|
async function catchRFID(token) {
|
||||||
const resp = await fetch(`${server}/api/rfid`, { method: 'GET', mode: 'cors', headers: { Authentification: 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;
|
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 default { ...publicfunctions }
|
||||||
export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID }
|
export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession, catchRFID, config }
|
@ -1,53 +1,54 @@
|
|||||||
import { h } from "preact";
|
import { h } from "preact";
|
||||||
import { Router, route } from "preact-router";
|
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 { Header, Menu } from "./index.js";
|
||||||
import { Home, Users, EditUser, Login, Logout, System } from "../route";
|
import { Home, Users, EditUser, Login, Logout, System } from "../route";
|
||||||
import { AppStateProvider, UserTableProvider, menuReducer, sessionReducer, userTableReducer } from "../store";
|
import { AppStateProvider, UserTableProvider, menuReducer, sessionReducer, userTableReducer } from "../store";
|
||||||
import api from "../api/index.js";
|
import api from "../api/index.js";
|
||||||
function App() {
|
|
||||||
// useReducer
|
|
||||||
const menu = useReducer(menuReducer, false);
|
|
||||||
const session = useReducer(sessionReducer, {});
|
|
||||||
const [usertable, userreducer] = useReducer(userTableReducer, []);
|
|
||||||
console.log(session[0]);
|
|
||||||
if(!session[0]||(session[0]&&session[0].active===undefined))
|
|
||||||
api.restoreSession(session[1]);
|
|
||||||
|
|
||||||
useEffect(()=>{
|
const menu_items = [
|
||||||
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: "Übersicht", path: "/" },
|
||||||
{ text: "Benutzer anlegen", path: "/newuser" },
|
{ text: "Benutzer anlegen", path: "/newuser" },
|
||||||
{ text: "Benutzer verwalten", path: "/users" },
|
{ text: "Benutzer verwalten", path: "/users" },
|
||||||
{ text: "System", path: "/system" },
|
{ text: "System", path: "/system" },
|
||||||
{ text: "Abmelden", path: "/logout" }
|
{ text: "Abmelden", path: "/logout" }
|
||||||
]
|
]
|
||||||
this.handleRoute = async e => {
|
|
||||||
|
function App() {
|
||||||
|
// useReducer
|
||||||
|
const menu = useReducer(menuReducer, false);
|
||||||
|
const session = useReducer(sessionReducer, {});
|
||||||
|
const [usertable, userreducer] = useReducer(userTableReducer, []);
|
||||||
|
const [lasturl, setlasturl] = useState("/");
|
||||||
|
if(!session[0]||(session[0]&&session[0].active===undefined))
|
||||||
|
api.restoreSession(session[1]);
|
||||||
|
useEffect(()=>{
|
||||||
|
if(session[0]&&session[0].active){
|
||||||
|
api.fetchdb(session[0].token).then(imported => {
|
||||||
|
let action = { type: 'import', imported };
|
||||||
|
userreducer(action);
|
||||||
|
});
|
||||||
|
}},[session]);
|
||||||
|
const handleRoute = async e => {
|
||||||
|
let active = session[0].active;
|
||||||
|
|
||||||
switch (e.url) {
|
switch (e.url) {
|
||||||
default:
|
default:
|
||||||
if (!session[0].active) route('/login', true);
|
if (!active) route('/login', true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<AppStateProvider value={{ menu, session }} >
|
<AppStateProvider value={{ menu, session }} >
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<Header title={"Doorlock"} />
|
<Header title={"Doorlock"} />
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<Menu items={this.menu_items} />
|
<Menu items={menu_items} />
|
||||||
{!menu[0] &&
|
{!menu[0] &&
|
||||||
<UserTableProvider value={{ usertable, userreducer }} >
|
<UserTableProvider value={{ usertable, userreducer }} >
|
||||||
<Router onChange={this.handleRoute}>
|
<Router onChange={handleRoute}>
|
||||||
<Home path="/" user="me" />
|
<Home path="/" user="me" />
|
||||||
<Login path="login" />
|
<Login path="login" />
|
||||||
<System path="/system" />
|
<System path="/system" />
|
||||||
|
@ -1,11 +1,26 @@
|
|||||||
import { h } from 'preact'
|
import { h, render } from 'preact'
|
||||||
|
|
||||||
function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength, overridevalue }) {
|
function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength, overridevalue, onChange, onInput, checkInput, allowdChars, showavailable,hinttext }) {
|
||||||
let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.value });
|
|
||||||
return (<div className={'textbox'} >
|
if (typeof (onInput) === 'undefined' && typeof (formchange) === 'function') {
|
||||||
<input placeholder=" " disabled={disabled} type={type} value={overridevalue?overridevalue:formdata[id]?formdata[id]:''} id={id} onInput={onChange} maxlength={maxlength} name={`form-${id}`} />
|
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 (<>
|
||||||
|
<div className={'textbox'} >
|
||||||
|
<input placeholder=" " disabled={disabled} type={type} value={overridevalue ? overridevalue : formdata[id] ? formdata[id] : ''} id={id} onBlur={onBlur} onInput={onInput} onChange={onChange} maxlength={maxlength} name={`form-${id}`} />
|
||||||
<label for={id}>{label}</label>
|
<label for={id}>{label}</label>
|
||||||
</div>)
|
{formdata&&formdata.error&&formdata.error[id]&&hinttext&&<div className='textbox__hint'>{hinttext}</div>}
|
||||||
|
</div>
|
||||||
|
{showavailable && <span class="textbox__info"> {formdata[id] ? formdata[id].length : 0} von {maxlength} Zeichen verwendet.</span>}
|
||||||
|
</>)
|
||||||
}
|
}
|
||||||
function CheckBox({ id, disabled, label, formdata, formchange }) {
|
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 });
|
||||||
|
@ -82,8 +82,7 @@ function EditUser({ userid }) {
|
|||||||
<h3>Eindeutige Identifikationsnummer</h3>
|
<h3>Eindeutige Identifikationsnummer</h3>
|
||||||
<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} showavailable />
|
||||||
{<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>
|
||||||
@ -94,19 +93,16 @@ function EditUser({ userid }) {
|
|||||||
<h3>Persönliche Daten</h3>
|
<h3>Persönliche Daten</h3>
|
||||||
<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} showavailable />
|
||||||
{<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} showavailable />
|
||||||
{<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} disabled={rfidscan.active} overridevalue={rfidscan.active&&"Scannen..."}/>
|
<TextBox formdata={formdata} formchange={formchange} id="rfid_uid" label="RFID" maxlength={maxlength_rfid} disabled={rfidscan.active} overridevalue={rfidscan.active&&"Scannen..."} showavailable />
|
||||||
{<span class="textbox__info"> {formdata["rfid_uid"] ? formdata["rfid_uid"].length : 0} von {maxlength_rfid} Zeichen verwendet.</span>}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='column'>
|
<div className='column'>
|
||||||
<Button onClick={runScan}>{rfidscan.active ? "Abbrechen" : "Scannen"}</Button>
|
<Button onClick={runScan}>{rfidscan.active ? "Abbrechen" : "Scannen"}</Button>
|
||||||
@ -115,8 +111,7 @@ function EditUser({ userid }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className='row'>
|
<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} showavailable />
|
||||||
{<span class="textbox__info"> {formdata["user_pin"] ? formdata["user_pin"].length : 0} von {maxlength_pin} Zeichen verwendet.</span>}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='column'>
|
<div className='column'>
|
||||||
<b>Info</b>
|
<b>Info</b>
|
||||||
|
@ -2,34 +2,23 @@ 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, Warnbox} from '../../components';
|
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() {
|
||||||
let [sessiondata, setsession] = useContext(AppState).session;
|
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"];
|
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(result => {
|
api.login(fromdata).then(result => {
|
||||||
if(result == 'login_failed'){
|
if (result.error !== undefined)
|
||||||
set(prev=>({ ...prev, error: "login_failed",password: '' }));
|
setform(result);
|
||||||
}
|
|
||||||
else if(result=='network_error')
|
|
||||||
set(prev=>({ ...prev, error: "network_connection" }));
|
|
||||||
else {
|
else {
|
||||||
console.log(typeof(result))
|
setsession(result);
|
||||||
let newsession = {
|
|
||||||
type: 'start',
|
|
||||||
username: val.username,
|
|
||||||
token: result
|
|
||||||
}
|
}
|
||||||
setsession(newsession);
|
|
||||||
set({ username: '', password: '' });
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -40,18 +29,18 @@ function Login() {
|
|||||||
<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 ==='login_failed' && <Warnbox title='Anmeldefehler'>Ungültige Anmeldedaten.<br />Bitte überprüfen Sie den eingebenen Benutzernamen und das Passwort.</Warnbox>}
|
{fromdata.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>}
|
{fromdata.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={fromdata} formchange={setform} id="username" label="Benutzername" />
|
||||||
</div>
|
</div>
|
||||||
<div className='row'>
|
<div className='row'>
|
||||||
<TextBox maxlength={25} formdata={val} formchange={set} id="password" label="Passwort" type='password' />
|
<TextBox maxlength={25} formdata={fromdata} formchange={setform} id="password" label="Passwort" type='password' />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className='row'>
|
<div className='row'>
|
||||||
<CheckBox id="permanent" formdata={val} formchange={set} label="Angemeldet bleiben?" />
|
<CheckBox id="permanent" formdata={fromdata} formchange={setform} label="Angemeldet bleiben?" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onSubmit}>Anmelden</Button>
|
<Button onClick={onSubmit}>Anmelden</Button>
|
||||||
|
@ -1,48 +1,112 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { Breadcrumbs } from "../../components";
|
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() {
|
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 (
|
return (
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
<Breadcrumbs items={['Systemeinstellungen']} />
|
<Breadcrumbs items={['Systemeinstellungen']} />
|
||||||
<div className={'contentbox'} >
|
<div className={'contentbox'} >
|
||||||
<h2>System</h2>
|
<h2>System</h2>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<h3>Geräteeinstellungen</h3>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="mode" label="Modus" checkInput={(v)=>!isNaN(v)&&v>=0&&v<=1} maxlength={1} showavailable hinttext="Nur Zahlen 0 bis 1" />
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="fail_timeout" label="Login Timeout" checkInput={(v)=>!isNaN(v)&&v>=0&&v<=255} maxlength={3} showavailable hinttext="Nur Zahlen 0 bis 255" />
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="hold_time" label="Haltezeit" checkInput={(v)=>!isNaN(v)&&v>=0&&v<=255} maxlength={3} showavailable hinttext="Nur Zahlen 0 bis 255"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h3>WiFi Setup</h3>
|
<h3>WiFi Setup</h3>
|
||||||
<hr />
|
<div className='row'>
|
||||||
<form>
|
<div className='column'>
|
||||||
<div className={'textbox'} >
|
<TextBox info={true} formdata={formdata} formchange={setform} id="SSID" label="WiFi Name" maxlength={31} showavailable />
|
||||||
<input type="text" placeholder='Wifi network name' />
|
</div>
|
||||||
<label>SSID</label>
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="PASS" label="WiFi Passwort" maxlength={31} showavailable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="ip" label="IP Adresse" maxlength={15} showavailable checkInput={checkIP} hinttext="Beispiel: 192.168.20.1" />
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="subnet" label="Netzmaske" maxlength={15} showavailable checkInput={checkIP} hinttext="Beispiel: 255.255.255.0"/>
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="gw" label="Gateway" maxlength={15} showavailable checkInput={checkIP} hinttext="Beispiel: 0.0.0.0"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={() => null} >Speichern</Button>
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={() => null} >Neustart</Button>
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={() => null} >Zurücksetzen</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={'textbox'} >
|
|
||||||
<input type="text" placeholder='Wifi network name' />
|
|
||||||
<label>Password</label>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<form>
|
||||||
<h3>Admin User</h3>
|
<h3>Admin User</h3>
|
||||||
<hr />
|
<div className='row'>
|
||||||
<form>
|
<div className='column'>
|
||||||
<div className={'textbox'} >
|
<TextBox info={true} formdata={formdata} formchange={setform} id="ip" label="Name" maxlength={15} showavailable />
|
||||||
<input type="text" placeholder='Wifi network name' />
|
</div>
|
||||||
<label>Username</label>
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="subnet" label="Passwort" maxlength={15} showavailable />
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox info={true} formdata={formdata} formchange={setform} id="subnet" label="Passwort wiederholen" maxlength={15} showavailable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={() => null} >Speichern</Button>
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={() => null} >Zurücksetzen</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={'textbox'} >
|
|
||||||
<input type="text" placeholder='Wifi network name' />
|
|
||||||
<label>Password</label>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<h3>Datenbank Backup</h3><hr />
|
<h3>Datenbank Backup</h3>
|
||||||
<div>
|
<div className='row'>
|
||||||
|
<div className="column">
|
||||||
|
<h4>Backup einspielen</h4>
|
||||||
<form>
|
<form>
|
||||||
<h3>Backup einspielen</h3>
|
|
||||||
<input type={'file'} />
|
<input type={'file'} />
|
||||||
<input type={'submit'} value={'Hochladen'} />
|
<input type={'submit'} value={'Hochladen'} />
|
||||||
</form>
|
</form>
|
||||||
<h3>Backup herunterladen</h3>
|
</div>
|
||||||
|
<div className="column">
|
||||||
|
<h4>Backup herunterladen</h4>
|
||||||
<button>Download</button>
|
<button>Download</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,13 @@
|
|||||||
button
|
button
|
||||||
display: block
|
display: block
|
||||||
background: none
|
background: none
|
||||||
min-width: 100%
|
width: 100%
|
||||||
border: none
|
border: none
|
||||||
border-radius: none
|
border-radius: none
|
||||||
font-size: 2em
|
font-size: 2em
|
||||||
transition: all ease-in-out 100ms
|
transition: all ease-in-out 100ms
|
||||||
|
overflow: hidden
|
||||||
|
text-overflow: ellipsis
|
||||||
&::before
|
&::before
|
||||||
z-index: -1
|
z-index: -1
|
||||||
border-radius: .2rem
|
border-radius: .2rem
|
||||||
|
@ -4,16 +4,17 @@
|
|||||||
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
|
||||||
&:hover
|
&:hover
|
||||||
background: #fff
|
background: #fff
|
||||||
input
|
input
|
||||||
|
//overflow: hidden
|
||||||
display: block
|
display: block
|
||||||
background: none
|
background: none
|
||||||
min-width: 100%
|
width: 100%
|
||||||
border-radius: none
|
border-radius: none
|
||||||
outline: none
|
outline: none
|
||||||
border: none
|
border: none
|
||||||
@ -28,13 +29,12 @@
|
|||||||
font-size: .7em
|
font-size: .7em
|
||||||
cursor: text
|
cursor: text
|
||||||
transition: 250ms all
|
transition: 250ms all
|
||||||
|
overflow: hidden
|
||||||
text-overflow: ellipsis
|
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
|
||||||
@ -46,6 +46,17 @@
|
|||||||
&__info
|
&__info
|
||||||
color: #ccc
|
color: #ccc
|
||||||
font-size: 0.7em
|
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
|
@mixin checkbox
|
||||||
position: relative
|
position: relative
|
||||||
|
Loading…
x
Reference in New Issue
Block a user