Frontend stuff

This commit is contained in:
Leon Meier 2025-02-24 22:55:28 +01:00
parent 611a4a0ead
commit 45cd5b10e6
17 changed files with 330 additions and 142 deletions

View File

@ -1,25 +1,19 @@
import express from 'express';
import config from '../../handlers/config.js';
// Route imports
import dashboardRoute from './dashboard.js';
import testRoute from './test.js';
import contactRoute from './contact.js';
// import itemsRoute from './items.js';
// import manage_routes from './manage/index.js';
import screensaver_Route from './screensaver.js';
import user_select_Route from './user_select.js';
import product_select_Route from './product_select.js';
import test_Route from './test.js';
// Router base is '/'
const Router = express.Router({ strict: false });
// Router.route('/test').get(testRoute.get);
// Router.route('/items').get(itemsRoute.get);
Router.route('/').get(screensaver_Route.get);
Router.route('/user_select').get(user_select_Route.get);
Router.route('/product_select').get(product_select_Route.get);
// Router.route('/:id(\\w{8})').get(skuRoute.get);
// Router.route('/s/:id').get(skuRouteDash.get);
// Router.use('/manage', manage_routes);
Router.route('/').get(dashboardRoute.get);
Router.route('/dbTest').get(testRoute.get);
Router.route('/contact').get(contactRoute.get);
config.global.devmode && Router.route('/test').get(test_Route.get);
export default Router;

View File

@ -1,7 +1,7 @@
import express, { Request, Response } from 'express';
function get(req: Request, res: Response) {
res.render("lockscreen", { message: "Hello world from eta!" })
res.render("product_select")
}
export default { get };

View File

@ -1,7 +1,7 @@
import express, { Request, Response } from 'express';
function get(req: Request, res: Response) {
res.render("contacts")
res.render("screensaver")
}
export default { get };

View File

@ -0,0 +1,7 @@
import express, { Request, Response } from 'express';
function get(req: Request, res: Response) {
res.render("user_select")
}
export default { get };

View File

@ -32,6 +32,9 @@ let _api = {
if (typeof result === 'number') {
return result;
}
if (typeof result === 'boolean') {
return result;
}
console.error('Invalid JSON response');
_testPageFail('Invalid JSON response');
return;

View File

@ -1,3 +1,7 @@
body {
min-height: 100vh;
}
hidden {
display: none;
}

View File

@ -618,3 +618,14 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
});
document.addEventListener('DOMContentLoaded', () => {
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
const $notification = $delete.parentNode;
$delete.addEventListener('click', () => {
$notification.parentNode.removeChild($notification);
});
});
});

View File

@ -0,0 +1,17 @@
console.log('product_select.js loaded');
// Get containers
let mainSelectionDiv = document.getElementById('mainSelect');
const baseStruct = document.getElementById("baseStruct");
let globalData;
// On load
document.addEventListener('DOMContentLoaded', function() {
let data = await returnTableDataByTableName('product');
console.info(`Found ${data.count} products`);
const result = data.result;
globalData = result;
});

134
static/pages/user_select.js Normal file
View File

@ -0,0 +1,134 @@
console.log('user_select.js loaded');
// Get containers
let mainSelectionDiv = document.getElementById('mainSelect');
let pinPadModal = document.getElementById('pinPadModal');
let numpad = document.getElementById('numpad');
let pinInput1 = document.getElementById('pinInput1');
let pinInput2 = document.getElementById('pinInput2');
let pinInput3 = document.getElementById('pinInput3');
let pinInput4 = document.getElementById('pinInput4');
let pinError = document.getElementById('pinError');
let currentUser = null;
// Attach event listeners to all numpad buttons
let numpadButtons = numpad.getElementsByTagName('button');
for(let i = 0; i < numpadButtons.length; i++) {
let button = numpadButtons[i];
button.addEventListener('click', handleNumberClick);
}
// On keyboard input for pinInputX jump to next input field
pinInput1.addEventListener('input', function() {
if(pinInput1.value) {
pinInput2.focus();
// Update pinValue
pinValue = pinInput1.value;
}
});
pinInput2.addEventListener('input', function() {
if(pinInput2.value) {
pinInput3.focus();
pinValue = pinInput1.value + pinInput2.value;
}
}
);
pinInput3.addEventListener('input', function() {
if(pinInput3.value) {
pinInput4.focus();
pinValue = pinInput1.value + pinInput2.value + pinInput3.value;
}
});
pinInput4.addEventListener('input', function() {
if(pinInput4.value) {
pinValue = pinInput1.value + pinInput2.value + pinInput3.value + pinInput4.value;
validatePin();
}
});
let baseStruct = document.getElementById("baseStruct");
let globalData;
let pinValue = "";
// On load
document.addEventListener('DOMContentLoaded', async function() {
let data = await returnTableDataByTableName('user');
console.info(`Found ${data.count} users`);
const result = data.result;
globalData = result;
for(let i = 0; i < result.length; i++) {
let user = result[i];
let userDiv = baseStruct.cloneNode(true);
userDiv.id = `user_${user.id}`;
userDiv.innerHTML = user.name;
userDiv.addEventListener('click', handleUserClick);
mainSelectionDiv.appendChild(userDiv);
}
});
function handleUserClick(e) {
let userDiv = e.target;
let userId = userDiv.id.split('_')[1];
console.log(`Clicked on user with id ${userId}`);
let user = globalData.find(u => u.id == userId);
currentUser = user;
if(user.code) {
pinPadModal.classList.add('is-active');
// Automatically focus on the first input field
pinInput1.focus();
}
}
function validatePin() {
let pin = pinValue;
let userId = currentUser.id;
console.log(`Validating pin ${pinValue} for user ${userId}`);
_api.get("/user/codecheck?code=" + pinValue + "&id=" + userId).then((response) => {
if(response) {
console.log("Pin is correct");
pinPadModal.classList.remove('is-active');
window.location.href = `/product_select?user=${userId}`;
} else {
console.log("Pin is incorrect");
pinValue = "";
updatePinFields();
pinError.classList.remove('is-hidden');
}
});
}
function handleNumberClick(e) {
let number = e.target.innerHTML;
if(number == '<i class="bi bi-backspace-fill"></i>' || e.target.classList.contains('bi-backspace-fill')) {
pinValue = pinValue.slice(0, -1);
updatePinFields();
return;
}
if(number == '<i class="bi bi-check"></i>' || e.target.classList.contains('bi-check')) {
validatePin();
return;
}
if(pinValue.length >= 4) {
return;
}
pinValue += number;
updatePinFields();
}
function updatePinFields() {
let pin = pinValue;
pinInput1.value = pin[0] || "";
pinInput2.value = pin[1] || "";
pinInput3.value = pin[2] || "";
pinInput4.value = pin[3] || "";
}

View File

@ -1,119 +0,0 @@
<%~ include("partials/base_head.eta", {"title": "Kontakte"}) %>
<%~ include("partials/nav.eta") %>
<section class="hero is-primary" id="heroStatus">
<div class="hero-body">
<p class="title" data-tK="start-hero-header-welcome">Kontaktverwaltung</p>
<p class="subtitle" data-tK="start-hero-header-subtitle-default" id="heroExplainer">Erklärungstext</p>
</div>
</section>
<section class="section">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">Kontakte</p>
<p class="title"><span data-dataSource="AlertContacts" data-dataAction="COUNT" class="is-skeleton">Load.</span></p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading"><button class="js-modal-trigger button" data-target="modal-js-example">
Neuen Konakt anlegen
</button></p>
</div>
</div>
</nav>
</section>
<!-- TODO: Mark required fields as required; add handling for validation -->
<div id="modal-js-example" class="modal">
<div class="modal-background"></div>
<div class="modal-content">
<div class="box entryPhase is-hidden">
<h2 class="title">Neuer Kontakt</h1>
<i class="bi bi-arrow-clockwise title"></i>
</div>
<div class="box entryPhase">
<form data-targetTable="AlertContacts">
<h2 class="title">Neuer Kontakt</h1>
<div class="field">
<label class="label">Name</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="John Doe" value="" name="name">
<span class="icon is-small is-left">
<i class="bi bi-file-earmark-person-fill"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">Telefonummer</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="+49 1234 5678912" value="" name="phone">
<span class="icon is-small is-left">
<i class="bi bi-telephone-fill"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">Anmerkung</label>
<div class="control has-icons-left">
<input class="input" type="text" placeholder="" value="" name="comment">
<span class="icon is-small is-left">
<i class="bi bi-chat-fill"></i>
</span>
</div>
</div>
<br>
<div class="field is-grouped">
<div class="control">
<input type="submit" class="button is-link" value="Save" data-actionBtn="save">
</div>
<!--<div class="control">
<button type="button" class="button is-link is-light" data-actionBtn="cancel">Cancel</button>
</div>-->
</div>
</form>
</div>
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
<section class="section">
<h1 class="title" data-tK="start-recent-header">Kontaktübersicht</h1>
<input class="input" type="text" data-searchTargetId="contactTable" placeholder="Search..." />
<table class="table is-striped is-fullwidth is-hoverable" data-dataSource="AlertContacts" id="contactTable" data-pageSize="5">
<thead>
<tr>
<th data-dataCol = "id"><abbr title="Position">Pos</abbr></th>
<th data-dataCol = "name">Name</th>
<th data-dataCol = "phone">Telefonnummer</th>
<th data-dataCol = "comment">Kommentar</th>
<th data-fnc="actions" data-actions="edit,delete">Aktionen</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<nav class="pagination is-hidden" role="navigation" aria-label="pagination" data-targetTable="contactTable">
<ul class="pagination-list">
</ul>
</nav>
</section>
<%~ include("partials/footer.eta") %>
<%~ include("partials/base_foot.eta") %>

View File

@ -1,5 +1,5 @@
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
<%~ include("partials/nav.eta") %>
<!--<%~ include("partials/nav.eta") %>-->
<section class="hero is-primary">
<div class="hero-body">

View File

@ -1,4 +1,2 @@
<script src="/static/apiWrapper.js"></script>
<script src="/static/pageDriver.js"></script>
</body>
</html>

View File

@ -7,3 +7,5 @@
</p>
</div>
</footer>
<script src="/static/apiWrapper.js"></script>
<script src="/static/pageDriver.js"></script>

View File

@ -18,9 +18,11 @@
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="/">Home</a>
<a class="navbar-item" href="/dbTest">API Integration <span class="tag is-info">Dev</span></a>
<a class="navbar-item" href="/contact">Kontakte <span class="tag is-primary">Neu!</span></a>
<a class="navbar-item" href="/">Screensaver</a>
<a class="navbar-item" href="/user_select">user_select</a>
<a class="navbar-item" href="/product_select">product_select</a>
<a class="navbar-item" href="/test">Test <span class="tag is-info">Dev</span></a>
<!--<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">More</a>
<div class="navbar-dropdown">

38
views/product_select.eta Normal file
View File

@ -0,0 +1,38 @@
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
<%~ include("partials/nav.eta") %>
<section class="section">
<div class="container columns" id="mainSelect">
</div>
</section>
<hidden>
<!-- Base Button -->
<div class="column" id="baseStruct">
<div class="card" >
<div class="card-image">
<figure class="image is-4by3">
<img
src="https://bulma.io/assets/images/placeholders/1280x960.png"
alt="Placeholder image"
/>
</figure>
</div>
<div class="card-content">
<p class="title is-4">CocaCola lol</p>
<p class="subtitle is-6">0123456789</p>
</div>
<footer class="card-footer">
<p class="card-footer-item">
<span> 9.99€ </span>
</p>
</footer>
</div>
</div>
</hidden>
<%~ include("partials/footer.eta") %>
<script src="/static/pages/product_select.js"></script>
<%~ include("partials/base_foot.eta") %>

97
views/user_select.eta Normal file
View File

@ -0,0 +1,97 @@
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
<%~ include("partials/nav.eta") %>
<section class="section buttons container" id="mainSelect">
</section>
<hidden>
<!-- Base Button -->
<button class="button is-link is-large m-2" id="baseStruct">Username</button>
</hidden>
<div class="modal" id="pinPadModal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Bitte PIN eingeben</p>
<div class="notification is-danger is-hidden" id="pinError">
<button class="delete"></button>
Ein fehlerhafter PIN wurde eingegeben.
</div>
</header>
<section class="modal-card-body">
<!--
<input class="input" type="password" placeholder="1234" id="pinInput" maxlength="4" />
-->
<!-- Use 4 fields for the pin, similar to a 2FA fields -->
<div class="field has-addons is-centered">
<div class="control">
<input class="input is-large has-text-centered" type="password" placeholder="1" id="pinInput1" maxlength="1" />
</div>
<div class="control">
<input class="input is-large has-text-centered" type="password" placeholder="2" id="pinInput2" maxlength="1" />
</div>
<div class="control">
<input class="input is-large has-text-centered" type="password" placeholder="3" id="pinInput3" maxlength="1" />
</div>
<div class="control">
<input class="input is-large has-text-centered" type="password" placeholder="4" id="pinInput4" maxlength="1" />
</div>
</div>
<section class="section buttons container is-centered are-centered" id="numpad">
<!-- Num pad with 3 keys per row -->
<div class="columns is-grouped">
<div class="column">
<button class="button is-link is-large m-2">1</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">2</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">3</button>
</div>
</div>
<div class="columns is-grouped">
<div class="column">
<button class="button is-link is-large m-2">4</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">5</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">6</button>
</div>
</div>
<div class="columns is-grouped">
<div class="column">
<button class="button is-link is-large m-2">7</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">8</button>
</div>
<div class="column">
<button class="button is-link is-large m-2">9</button>
</div>
</div>
<div class="columns is-grouped">
<div class="column">
<button class="button is-link is-large m-2">0</button>
</div>
<div class="column">
<button class="button is-warning is-large m-2"><i class="bi bi-backspace-fill"></i></button>
</div>
<div class="column">
<button class="button is-success is-large m-2"><i class="bi bi-check"></i></button>
</div>
</div>
</section>
</section>
</div>
<button class="modal-close is-large" aria-label="close"></button>
</div>
<%~ include("partials/footer.eta") %>
<script src="/static/pages/user_select.js"></script>
<%~ include("partials/base_foot.eta") %>