added search, unified controls and input elements, rendering improved
This commit is contained in:
parent
a0adef9ec5
commit
32e6018415
30231
package-lock.json
generated
30231
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -18,20 +18,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"enzyme": "^3.10.0",
|
|
||||||
"enzyme-adapter-preact-pure": "^2.0.0",
|
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.10.0",
|
||||||
"eslint-config-preact": "^1.1.0",
|
"eslint-config-preact": "^1.1.0",
|
||||||
"jest": "^27.5.1",
|
|
||||||
"jest-preset-preact": "^1.0.0",
|
|
||||||
"node-sass": "^6.0.1",
|
|
||||||
"preact-cli": "^3.3.5",
|
"preact-cli": "^3.3.5",
|
||||||
"sass-loader": "^10.2.1",
|
"preact-render-to-string": "^5.1.4",
|
||||||
"sirv-cli": "1.0.3"
|
"sass-loader": "^10.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preact": "^10.3.2",
|
"preact": "^10.3.2",
|
||||||
"preact-render-to-string": "^5.1.4",
|
|
||||||
"preact-router": "^3.2.1",
|
"preact-router": "^3.2.1",
|
||||||
"sass": "^1.49.9"
|
"sass": "^1.49.9"
|
||||||
},
|
},
|
||||||
|
@ -1,11 +1,30 @@
|
|||||||
const server = 'http://192.168.4.22'
|
const server = 'http://192.168.4.22'
|
||||||
|
|
||||||
|
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 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 = 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 => storeSession(username, resp.text()));
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
async function logout(token) {
|
async function logout(token) {
|
||||||
@ -13,10 +32,15 @@ async function logout(token) {
|
|||||||
formData.append('token', token);
|
formData.append('token', token);
|
||||||
formData.append('action', 'logout');
|
formData.append('action', 'logout');
|
||||||
const resp = 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());
|
||||||
|
localStorage.clear();
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
function checkAuth(token) {
|
async function checkAuth(token) {
|
||||||
return token === 'DUMMYTOKEN';
|
let formData = new FormData()
|
||||||
|
formData.append('token', token);
|
||||||
|
formData.append('action', 'check');
|
||||||
|
const resp = await fetch(`${server}/api/auth`, { method: 'POST', mode: 'cors', body: formData }).then(resp => resp.text()).then(text => text === 'valid');
|
||||||
|
return await resp;
|
||||||
}
|
}
|
||||||
async function fetchdb(token = 'azif7eqCl5') {
|
async function fetchdb(token = 'azif7eqCl5') {
|
||||||
//fetch(`${server}/api/userdb`).then()
|
//fetch(`${server}/api/userdb`).then()
|
||||||
@ -65,6 +89,6 @@ function parsedb(raw) {
|
|||||||
return users;
|
return users;
|
||||||
|
|
||||||
}
|
}
|
||||||
const publicfunctions = { login, logout, checkAuth, parsedb,fetchdb, createCsvTable, updateUser, deleteUser, createUser };
|
const publicfunctions = { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser,restoreSession };
|
||||||
export default { ...publicfunctions }
|
export default { ...publicfunctions }
|
||||||
export { login, logout, checkAuth, parsedb,fetchdb, createCsvTable, updateUser, deleteUser, createUser }
|
export { login, logout, checkAuth, parsedb, fetchdb, createCsvTable, updateUser, deleteUser, createUser, restoreSession }
|
@ -1,16 +1,29 @@
|
|||||||
import { h } from "preact";
|
import { h } from "preact";
|
||||||
import { Router, route } from "preact-router";
|
import { Router, route } from "preact-router";
|
||||||
import { useReducer } from "preact/hooks";
|
import { useEffect, useReducer } 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";
|
||||||
function App() {
|
function App() {
|
||||||
// useReducer
|
// useReducer
|
||||||
const menu = useReducer(menuReducer, false);
|
const menu = useReducer(menuReducer, false);
|
||||||
const session = useReducer(sessionReducer, { active:true ,token: "0t6BF94Y92" });
|
const session = useReducer(sessionReducer, {});
|
||||||
const [usertable, userreducer] = useReducer(userTableReducer, []);
|
const [usertable, userreducer] = useReducer(userTableReducer, []);
|
||||||
|
console.log(session[0]);
|
||||||
|
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 = [
|
this.menu_items = [
|
||||||
{ text: "Übersicht", path: "/" },
|
{ text: "Übersicht", path: "/" },
|
||||||
{ text: "Benutzer anlegen", path: "/newuser" },
|
{ text: "Benutzer anlegen", path: "/newuser" },
|
||||||
|
2
src/components/controls/index.js
Normal file
2
src/components/controls/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import {TextBox,CheckBox,Button} from './input'
|
||||||
|
export {TextBox,CheckBox,Button}
|
22
src/components/controls/input.jsx
Normal file
22
src/components/controls/input.jsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { h } from 'preact'
|
||||||
|
|
||||||
|
function TextBox({ id, type = 'text', label, disabled, formdata, formchange, maxlength }) {
|
||||||
|
let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.value });
|
||||||
|
return (<div className={'textbox'} >
|
||||||
|
<input placeholder=" " disabled={disabled} type={type} value={formdata[id]?formdata[id]:''} id={id} onInput={onChange} maxlength={maxlength} name={`form-${id}`} />
|
||||||
|
<label for={id}>{label}</label>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
function CheckBox({ id, disabled, label, formdata, formchange }) {
|
||||||
|
let onChange = (e) => formchange({ ...formdata, [e.target.id]: e.target.checked });
|
||||||
|
return (<div className={'checkbox'} >
|
||||||
|
<input disabled={disabled} type={'checkbox'} checked={formdata[id] ? 'on' : ''} id={id} onChange={onChange} name={`from-${id}`} />
|
||||||
|
<label for={id}>{label}</label>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
function Button({ onClick, children, type }) {
|
||||||
|
return (<div className={'button'} onClick={onClick} >
|
||||||
|
<button type={type} >{children}</button>
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
export { TextBox, CheckBox, Button }
|
@ -24,9 +24,9 @@ function UserList({userlist,editUser, deleteUser, start, end}) {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="row">
|
||||||
{userlist&&(start!==undefined&&end?userlist.slice(start,end).map((user)=>displayUser(user)):userlist.map((user)=>displayUser(user)))}
|
{userlist&&(start!==undefined&&end?userlist.slice(start,end).map((user)=>displayUser(user)):userlist.map((user)=>displayUser(user)))}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,54 +3,108 @@ import { useContext, useEffect, useState } 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'
|
||||||
|
import { Breadcrumbs } from "../../components";
|
||||||
|
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 onChange = (e) => {
|
let maxlength_uid = 10;
|
||||||
console.log(e);
|
let maxlength_name = 25;
|
||||||
if (e.target.type === 'checkbox')
|
let maxlength_pin = 10;
|
||||||
formchange({ ...formdata, [e.target.id]: e.target.checked })
|
let maxlength_rfid = 8;
|
||||||
else
|
useEffect(() => {
|
||||||
formchange({ ...formdata, [e.target.id]: e.target.value })
|
let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined;
|
||||||
}
|
if (user !== undefined) {
|
||||||
useEffect(()=>{
|
|
||||||
let user = userid !== undefined?usertable.find(u => u.uid === userid):undefined;
|
|
||||||
if(user!==undefined){
|
|
||||||
formchange(user);
|
formchange(user);
|
||||||
}
|
}
|
||||||
},[userid]);
|
}, [userid, usertable]);
|
||||||
|
function uidCount(state, user) {
|
||||||
|
return state.reduce((pre, curr) => pre + curr.uid == user.uid ? 1 : 0, 0);
|
||||||
|
}
|
||||||
|
const onReset = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let user = userid !== undefined ? usertable.find(u => u.uid === userid) : undefined;
|
||||||
|
formchange(user !== undefined ? user : {});
|
||||||
|
}
|
||||||
const onSubmit = (e) => {
|
const onSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(api);
|
let idcount = uidCount(usertable, formdata);
|
||||||
if(formdata.line!==undefined){
|
console.log(idcount);
|
||||||
|
if (formdata.line !== undefined) {
|
||||||
api.updateUser(sessiondata.token, formdata).then(r => {
|
api.updateUser(sessiondata.token, formdata).then(r => {
|
||||||
userreducer({ type: 'update', user: r })
|
userreducer({ type: 'update', user: r })
|
||||||
});
|
});
|
||||||
}else{
|
route('/users');
|
||||||
|
} else if (idcount === 0) {
|
||||||
api.createUser(sessiondata.token, formdata).then(r => {
|
api.createUser(sessiondata.token, formdata).then(r => {
|
||||||
userreducer({ type: 'create', user: r })
|
userreducer({ type: 'create', user: r })
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
route('/users');
|
route('/users');
|
||||||
}
|
}
|
||||||
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>)
|
else {
|
||||||
|
alert(`UID ${formdata.uid} wurde bereits ${idcount} mal benutzt`);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
<h1>{formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"}</h1>
|
<Breadcrumbs items={['Benutzerverwaltung',formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"]} />
|
||||||
|
<div className={'contentbox'} >
|
||||||
|
<h2>{formdata.line != undefined ? "Benutzer Bearbeiten" : "Neuer Benutzer"}</h2>
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<Input id="uid" label="UserID" />
|
<h3>Eindeutige Identifikationsnummer</h3>
|
||||||
<Input id="first_name" label="Vorname" />
|
<div className='row'>
|
||||||
<Input id="last_name" label="NachName" />
|
<div className='column'>
|
||||||
<Input id="rfid_uid" label="RFID" />
|
<TextBox info={true} formdata={formdata} formchange={formchange} id="uid" label="Benutzer-ID" maxlength={maxlength_uid} />
|
||||||
<Input id="user_pin" label="Pin" />
|
{<span class="textbox__info"> {formdata["uid"]?formdata["uid"].length:0} von {maxlength_uid} Zeichen verwendet.</span>}
|
||||||
<Input id="enabled" type={'checkbox'} label="" />
|
</div>
|
||||||
<div className={'button'} ><input type={'submit'}>Speichern</input> </div>
|
<div className='column'>
|
||||||
|
<b>Info</b>
|
||||||
|
<span> Nummer muss einmalig sein</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Persönliche Daten</h3>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<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>}
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<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>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Authentifizierung</h3>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<TextBox formdata={formdata} formchange={formchange} id="rfid_uid" label="RFID" maxlength={maxlength_rfid} />
|
||||||
|
{<span class="textbox__info"> {formdata["rfid_uid"]?formdata["rfid_uid"].length:0} von {maxlength_rfid} Zeichen verwendet.</span>}
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<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>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Status</h3>
|
||||||
|
<div className='row'>
|
||||||
|
<div className='column'>
|
||||||
|
<CheckBox formdata={formdata} formchange={formchange} id="enabled" type={'checkbox'} label="Benutzer darf sich anmelden" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className='row' style={'justify-content: space-between'}>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={onSubmit} >Speichern</Button>
|
||||||
|
</div>
|
||||||
|
<div className='column'>
|
||||||
|
<Button onClick={onReset} >Zurücksetzen</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default EditUser;
|
export default EditUser;
|
@ -1,12 +1,13 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { Link } from 'preact-router';
|
import { Link } from 'preact-router';
|
||||||
import { useContext, useState } from 'preact/hooks';
|
import { useContext } from 'preact/hooks';
|
||||||
import AppState from '../../store/AppState';
|
import AppState from '../../store/AppState';
|
||||||
|
import { Breadcrumbs } from "../../components";
|
||||||
function Home() {
|
function Home() {
|
||||||
let [sessiondata, ] = useContext(AppState).session;
|
let [sessiondata, ] = useContext(AppState).session;
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<Breadcrumbs items={['Startseite']} />
|
||||||
<div className={'contentbox'} >
|
<div className={'contentbox'} >
|
||||||
<h2>Startseite</h2>
|
<h2>Startseite</h2>
|
||||||
<p>Willkommen zurück {sessiondata.username} </p>
|
<p>Willkommen zurück {sessiondata.username} </p>
|
||||||
@ -18,7 +19,6 @@ function Home() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>Hier können Sie den Türöffner manuell aktivieren.</p>
|
<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>
|
<p>Um Benutzer anzulegen, öffnen sie das Menü und tippen auf <Link href="/newuser">Benutzer anlegen</Link> </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,7 @@ 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 { 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;
|
||||||
@ -22,7 +23,7 @@ function Login() {
|
|||||||
setsession(newsession);
|
setsession(newsession);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
set({ ...val, error: "user" });
|
set(prev=>({ ...prev, error: "user" }));
|
||||||
}
|
}
|
||||||
set({ username: '', password: '' });
|
set({ username: '', password: '' });
|
||||||
})
|
})
|
||||||
@ -36,17 +37,18 @@ function Login() {
|
|||||||
<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 !== null && <span style={'color: red'}>Fehler: Ungültige Anmeldedaten.</span>}
|
||||||
<form id="login_form" onSubmit={onSubmit} >
|
<form id="login_form" onSubmit={onSubmit} >
|
||||||
|
<div className='row'>
|
||||||
|
<TextBox maxlength={25} formdata={val} formchange={set} id="username" label="Benutzername" />
|
||||||
<div class="input-box">
|
|
||||||
<input id="name" type="text" placeholder="Username" onInput={e => set({ ...val, username: e.target.value })} value={val.username} />
|
|
||||||
<label for="name">Benutzername</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-box">
|
<div className='row'>
|
||||||
<input id="pass" type="password" placeholder="Passwort" onInput={e => set({ ...val, password: e.target.value })} value={val.password} />
|
<TextBox maxlength={25} formdata={val} formchange={set} id="password" label="Passwort" type='password' />
|
||||||
<label for="pass">Password</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class={'button'} onClick={onSubmit} ><input type="submit" value="Anmelden" /></div>
|
<div className='row'>
|
||||||
|
<CheckBox id="permanent" formdata={val} formchange={set} label="Angemeldet bleiben?" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<Button onClick={onSubmit}>Anmelden</Button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import { Breadcrumbs } from "../../components";
|
||||||
|
|
||||||
function System() {
|
function System() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='container'>
|
<div className='container'>
|
||||||
|
<Breadcrumbs items={['Systemeinstellungen']} />
|
||||||
<div className={'contentbox'} >
|
<div className={'contentbox'} >
|
||||||
<h2>System</h2>
|
<h2>System</h2>
|
||||||
<h3>WiFi Setup</h3>
|
<h3>WiFi Setup</h3>
|
||||||
|
@ -2,48 +2,64 @@ import { h } from "preact";
|
|||||||
import { UserList, Pageselector, Breadcrumbs } from "../../components";
|
import { UserList, Pageselector, Breadcrumbs } from "../../components";
|
||||||
import api from '../../api'
|
import api from '../../api'
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
import { useContext, useEffect, useState } from "preact/hooks";
|
import { useContext, useEffect, useState, useMemo } from "preact/hooks";
|
||||||
import AppState, { UserTable } from "../../store";
|
import AppState, { UserTable } from "../../store";
|
||||||
function Users({ pageid }) {
|
function Users({ pageid }) {
|
||||||
const [viewstate, setview] = useState({ limit: 100, page: 1, pages: null })
|
|
||||||
const { usertable, userreducer } = useContext(UserTable);
|
const { usertable, userreducer } = useContext(UserTable);
|
||||||
let [sessiondata,] = useContext(AppState).session;
|
let [sessiondata,] = useContext(AppState).session;
|
||||||
|
const [viewstate, setview] = useState({ limit: 100, page: 1, pages: 0, query: '' });
|
||||||
|
const matchUser = (user, query, fields = ['uid', 'first_name', 'last_name'], exact = false) => {
|
||||||
|
let words = query.toLowerCase().split(' ');
|
||||||
|
let matches = 0;
|
||||||
|
|
||||||
|
for (let word of words) {
|
||||||
|
let match = false;
|
||||||
|
for (let key of fields){
|
||||||
|
let cmp = user[key].toLowerCase();
|
||||||
|
if (exact ? cmp == word : cmp.includes(word)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
matches += 1;
|
||||||
|
if(matches >= words.length) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const results = useMemo(() => (viewstate.query !== '') ? usertable.filter(u => matchUser(u, viewstate.query)) : usertable, [viewstate.query, usertable]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const setPage = (e) => {
|
const setPage = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log(e)
|
console.log(e)
|
||||||
let page = e.target.text;
|
let page = e.target.text;
|
||||||
setview({ ...viewstate, page })
|
setview(state => ({ ...state, page }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (usertable === undefined)
|
setview((state) => {
|
||||||
console.log("TOKEN");
|
let pages = Math.ceil(results.length / state.limit)
|
||||||
console.log(sessiondata.token);
|
if (state.page >= pages) return ({ ...state, pages, page: pages })
|
||||||
api.fetchdb(sessiondata.token).then(imported => {
|
return ({ ...state, pages })
|
||||||
let action = { type: 'import', imported };
|
})
|
||||||
userreducer(action);
|
}, [results.length, viewstate.limit])
|
||||||
});
|
|
||||||
}, [sessiondata]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setview({ ...viewstate, pages: Math.ceil(usertable.length / viewstate.limit) });
|
|
||||||
console.log("New View effect")
|
|
||||||
}, [usertable])
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pageid && !isNaN(pageid))
|
if (pageid && !isNaN(pageid))
|
||||||
setview({ ...viewstate, page: pageid })
|
setview((state) => ({ ...state, page: pageid }))
|
||||||
}, [pageid])
|
}, [pageid])
|
||||||
|
|
||||||
const deleteUser = (user) => {
|
const deleteUser = (user) => {
|
||||||
api.deleteUser(sessiondata.token,user).then(r=>{
|
api.deleteUser(sessiondata.token, user).then(r => {
|
||||||
let action = {
|
let action = {
|
||||||
type: 'delete',
|
type: 'delete',
|
||||||
user, r
|
user, r
|
||||||
}
|
}
|
||||||
userreducer(action)
|
userreducer(action)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
const calculateView = () => {
|
const calculateView = () => {
|
||||||
let start = viewstate.limit * (viewstate.page - 1);
|
let start = viewstate.limit * (viewstate.page - 1);
|
||||||
@ -53,15 +69,22 @@ function Users({ pageid }) {
|
|||||||
const editUser = (user) => {
|
const editUser = (user) => {
|
||||||
route(`/edituser/${user.uid}`);
|
route(`/edituser/${user.uid}`);
|
||||||
}
|
}
|
||||||
const navigation = ["Users"];
|
const setLimit = (e) => {
|
||||||
|
setview(state => ({ ...state, limit: e.target.value }));
|
||||||
|
console.log(e.target.value);
|
||||||
|
console.log(viewstate);
|
||||||
|
}
|
||||||
|
const navigation = ["Benutzerverwaltung",'Übersicht'];
|
||||||
//
|
//
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Breadcrumbs items={navigation} />
|
<Breadcrumbs items={navigation} />
|
||||||
<div className={'contentbox'} >
|
<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'} onInput={(e) => setview(state => ({ ...state, query: e.target.value }))} value={viewstate.query} />
|
||||||
|
<button>Hinzufügen</button>
|
||||||
|
Limit: <select onChange={setLimit} value={viewstate.limit}><optgroup label={'Anzahl'}><option>10</option><option>25</option><option>50</option><option>100</option><option>200</option></optgroup><option>Alle</option></select></div>
|
||||||
</div>
|
</div>
|
||||||
<UserList {...calculateView()} userlist={usertable} deleteUser={deleteUser} editUser={editUser} />
|
<UserList {...calculateView()} userlist={results} 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} />
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,12 +18,12 @@ export const sessionReducer = (state, action) => {
|
|||||||
export const userTableReducer = (state, action) => {
|
export const userTableReducer = (state, action) => {
|
||||||
let user = action.user;
|
let user = action.user;
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'create': return [...state, { line: state.length, ...user }]
|
case 'create': return [...state, { line: state.length, ...user }];
|
||||||
case 'delete': {
|
case 'delete': {
|
||||||
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;
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
text-decoration: none
|
text-decoration: none
|
||||||
border: solid 1px #999
|
border: solid 1px #999
|
||||||
border-radius: .2rem
|
border-radius: .2rem
|
||||||
input[type=submit]
|
button
|
||||||
display: block
|
display: block
|
||||||
background: none
|
background: none
|
||||||
width: 100%
|
width: 100%
|
||||||
@ -32,7 +32,7 @@
|
|||||||
transition: all ease-in-out 250ms
|
transition: all ease-in-out 250ms
|
||||||
&:hover
|
&:hover
|
||||||
color: #fff
|
color: #fff
|
||||||
input[type=submit]
|
button
|
||||||
background: #333
|
background: #333
|
||||||
color: #fff
|
color: #fff
|
||||||
border-radius: .3rem
|
border-radius: .3rem
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
@mixin textfield
|
@mixin textfield
|
||||||
|
margin: 0 1em
|
||||||
|
width: 100%
|
||||||
position: relative
|
position: relative
|
||||||
display: inline-block
|
display: block
|
||||||
margin: 0 .25em 1em .25em
|
margin: 0
|
||||||
background: #fafafa
|
background: #fafafa
|
||||||
border-radius: .3em
|
border-radius: .3em
|
||||||
border-bottom: 1px solid #ccc
|
border-bottom: 1px solid #ccc
|
||||||
@ -35,3 +37,51 @@
|
|||||||
top: 0em
|
top: 0em
|
||||||
left: 0em
|
left: 0em
|
||||||
font-size: 1em
|
font-size: 1em
|
||||||
|
&__info
|
||||||
|
color: #ccc
|
||||||
|
font-size: 0.7em
|
||||||
|
|
||||||
|
@mixin checkbox
|
||||||
|
position: relative
|
||||||
|
font-size: 1.5em
|
||||||
|
label
|
||||||
|
user-select: none
|
||||||
|
margin-left: 1em
|
||||||
|
padding-left: .5em
|
||||||
|
transition: all 250ms ease-in-out
|
||||||
|
&::before
|
||||||
|
position: absolute
|
||||||
|
content: ''
|
||||||
|
left: 0
|
||||||
|
top: 0
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
background: radial-gradient(circle, #e2e2e2 0%, #b8b8b8 100%)
|
||||||
|
border-radius: .3em
|
||||||
|
border: solid 1px #ccc
|
||||||
|
box-shadow: 0 0 .5em #fff, inset 0 0 .3em #fff
|
||||||
|
|
||||||
|
&::after
|
||||||
|
content: ''
|
||||||
|
left: 0
|
||||||
|
top: 0
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
position: absolute
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
transform: scale(0)
|
||||||
|
padding: 0
|
||||||
|
text-shadow: 0 0 .2em #888
|
||||||
|
transition: all 250ms ease-in-out
|
||||||
|
input
|
||||||
|
display: none
|
||||||
|
&:checked + label
|
||||||
|
text-shadow: 0 0 .5em #888
|
||||||
|
&::before
|
||||||
|
box-shadow: 0 0 .5em #fff, inset 0 0 .5em #fff
|
||||||
|
&::after
|
||||||
|
transform: scale(1)
|
||||||
|
content: '✔'
|
||||||
|
color: #fff
|
||||||
|
@ -74,23 +74,30 @@ body
|
|||||||
.page-nav-bar
|
.page-nav-bar
|
||||||
@include pageselector.pageselector
|
@include pageselector.pageselector
|
||||||
.user-list-item
|
.user-list-item
|
||||||
|
position: relative
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
display: flex
|
display: inline-flex
|
||||||
|
flex-shrink: 1
|
||||||
|
min-width: calc(50% - 1em)
|
||||||
|
flex-grow: 1
|
||||||
flex-direction: row
|
flex-direction: row
|
||||||
background: #eee
|
background: #eee
|
||||||
margin: 0 0.5em
|
margin: 0 0.5em
|
||||||
padding: .5em 0
|
padding: .5em 3em 0 0
|
||||||
border-radius: .3em
|
border-radius: .3em
|
||||||
&:not(:last-child)
|
&:not(:last-child)
|
||||||
margin-bottom: .5em
|
margin-bottom: .5em
|
||||||
.user-attributes
|
.user-attributes
|
||||||
padding: .5em
|
padding: .5em
|
||||||
.btn-group
|
.btn-group
|
||||||
margin-left: auto
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
display: inline-flex
|
display: inline-flex
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
align-self: flex-end
|
align-items: center
|
||||||
justify-self: flex-end
|
justify-content: center
|
||||||
@mixin button()
|
@mixin button()
|
||||||
position: relative
|
position: relative
|
||||||
border: none
|
border: none
|
||||||
@ -161,3 +168,21 @@ footer
|
|||||||
.textbox
|
.textbox
|
||||||
@include input.textfield
|
@include input.textfield
|
||||||
font-size: 1.5em
|
font-size: 1.5em
|
||||||
|
.checkbox
|
||||||
|
@include input.checkbox
|
||||||
|
|
||||||
|
.row
|
||||||
|
display: flex
|
||||||
|
justify-content: space-evenly
|
||||||
|
align-items: stretch
|
||||||
|
flex-direction: row
|
||||||
|
flex-wrap: wrap
|
||||||
|
width: 100%
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-bottom: 1em
|
||||||
|
.column
|
||||||
|
padding: .5em
|
||||||
|
flex-grow: 1
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
flex-wrap: wrap
|
Loading…
x
Reference in New Issue
Block a user