implemented ui functions for managing products, uploading images and editiing users
This commit is contained in:
parent
fa7f3004fa
commit
bd43f03507
7
src/routes/frontend/admin/dashboard.ts
Normal file
7
src/routes/frontend/admin/dashboard.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render("admin/dashboard")
|
||||
}
|
||||
|
||||
export default { get };
|
18
src/routes/frontend/admin/index.ts
Normal file
18
src/routes/frontend/admin/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import express from 'express';
|
||||
|
||||
// Route imports
|
||||
import dashboard_Route from './dashboard.js';
|
||||
import users from './users.js';
|
||||
import products from './products.js';
|
||||
|
||||
// Router base is '/admin'
|
||||
const Router = express.Router({ strict: false });
|
||||
|
||||
Router.route('/').get(dashboard_Route.get);
|
||||
Router.route('/users').get(users.get);
|
||||
Router.route('/products').get(products.get);
|
||||
// Router.route('/user_select').get(user_select_Route.get);
|
||||
// Router.route('/product_select').get(product_select_Route.get);
|
||||
// Router.route('/pay_up').get(pay_up_Route.get);
|
||||
|
||||
export default Router;
|
7
src/routes/frontend/admin/products.ts
Normal file
7
src/routes/frontend/admin/products.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render("admin/products")
|
||||
}
|
||||
|
||||
export default { get };
|
7
src/routes/frontend/admin/users.ts
Normal file
7
src/routes/frontend/admin/users.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render("admin/users")
|
||||
}
|
||||
|
||||
export default { get };
|
@ -5,14 +5,20 @@ import config from '../../handlers/config.js';
|
||||
import screensaver_Route from './screensaver.js';
|
||||
import user_select_Route from './user_select.js';
|
||||
import product_select_Route from './product_select.js';
|
||||
import pay_up_Route from './pay_up.js';
|
||||
import test_Route from './test.js';
|
||||
|
||||
import adminRouter from './admin/index.js';
|
||||
|
||||
// Router base is '/'
|
||||
const Router = express.Router({ strict: false });
|
||||
|
||||
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('/pay_up').get(pay_up_Route.get);
|
||||
|
||||
Router.use('/admin', adminRouter);
|
||||
|
||||
config.global.devmode && Router.route('/test').get(test_Route.get);
|
||||
|
||||
|
7
src/routes/frontend/pay_up.ts
Normal file
7
src/routes/frontend/pay_up.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
|
||||
function get(req: Request, res: Response) {
|
||||
res.render("payup")
|
||||
}
|
||||
|
||||
export default { get };
|
@ -19,6 +19,12 @@ let _api = {
|
||||
headers: new Headers({ 'content-type': 'application/json' })
|
||||
};
|
||||
const response = await fetch(_apiConfig.basePath + path, options);
|
||||
if(response.status == 404) {
|
||||
return {
|
||||
count: 0,
|
||||
result: []
|
||||
}
|
||||
}
|
||||
// Handle the response
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch:', response.statusText);
|
||||
@ -190,13 +196,14 @@ function returnTableDataByTableName(tableName, search="", orderBy="asc", sort=""
|
||||
}
|
||||
|
||||
async function getCountByTable(tableName, search="") {
|
||||
let baseString = tableName + '?count=true';
|
||||
let baseString = tableName + '';
|
||||
if (search && search.length > 0) {
|
||||
baseString += '&search=' + search;
|
||||
baseString += '?search=' + search;
|
||||
}
|
||||
// Stored in `data:count:${tableName}`
|
||||
let result = await _api.get(baseString);
|
||||
console.debug('Count result:', result);
|
||||
result = result.count;
|
||||
if (typeof result !== 'number') {
|
||||
_testPageWarn('Count was not a number, was: ' + result);
|
||||
console.warn('Count was not a number, was: ' + result);
|
||||
@ -214,6 +221,8 @@ function _testPageFail(reason) {
|
||||
}
|
||||
|
||||
function _testPageWarn(reason) {
|
||||
console.warn('API Wrapper Test Warning, reason: ' + reason);
|
||||
return;
|
||||
document.getElementById('heroStatus').classList.remove('is-success');
|
||||
document.getElementById('heroStatus').classList.add('is-warning');
|
||||
|
||||
|
@ -29,3 +29,21 @@ flex-wrap: wrap;
|
||||
font-size: 50px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader {
|
||||
height: 200px;
|
||||
aspect-ratio: 2/3;
|
||||
--c:no-repeat linear-gradient(#fff 0 0);
|
||||
background: var(--c), var(--c), var(--c), var(--c);
|
||||
background-size: 50% 33.4%;
|
||||
animation: l8 1.5s infinite linear;
|
||||
}
|
||||
@keyframes l8 {
|
||||
0%,
|
||||
5% {background-position:0 25%,100% 25%,0 75%,100% 75%}
|
||||
33% {background-position:0 50%,100% 0,0 100%,100% 50%}
|
||||
66% {background-position:0 50%,0 0,100% 100%,100% 50%}
|
||||
95%,
|
||||
100% {background-position:0 75%,0 25%,100% 75%,100% 25%}
|
||||
}
|
||||
|
@ -1,22 +1,26 @@
|
||||
// Image Handler
|
||||
const baseUrl = "https://api.unsplash.com/photos/random?client_id=[KEY]&orientation=landscape&topics=nature";
|
||||
const apiKey = "tYOt7Jo94U7dunVcP5gt-kDKDMjWFOGQNsHuhLDLV8k"; // Take from config
|
||||
const fullUrl = baseUrl.replace("[KEY]", apiKey);
|
||||
const baseUrl = 'https://api.unsplash.com/photos/random?client_id=[KEY]&orientation=landscape&topics=nature';
|
||||
const apiKey = 'tYOt7Jo94U7dunVcP5gt-kDKDMjWFOGQNsHuhLDLV8k'; // Take from config
|
||||
const fullUrl = baseUrl.replace('[KEY]', apiKey);
|
||||
|
||||
const showModeImage = "/static/media/showModeLockscreen.jpg"
|
||||
const showModeImage = '/static/media/showModeLockscreen.jpg';
|
||||
|
||||
let credits = document.getElementById("credits");
|
||||
let credits = document.getElementById('credits');
|
||||
|
||||
let currentImageHandle;
|
||||
|
||||
document.body.addEventListener('click', () => {
|
||||
window.location.href = '/user_select';
|
||||
});
|
||||
|
||||
// Lock screen or show mode
|
||||
let screenState = "lock";
|
||||
let screenState = 'lock';
|
||||
|
||||
function handleImage() {
|
||||
if(screenState === "lock") {
|
||||
fetch("https://staging.thegreydiamond.de/projects/photoPortfolio/api/getRand.php?uuid=01919dec-b2cd-7adc-8ca2-a071d1169cbc&unsplash=true")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (screenState === 'lock') {
|
||||
fetch('https://staging.thegreydiamond.de/projects/photoPortfolio/api/getRand.php?uuid=01919dec-b2cd-7adc-8ca2-a071d1169cbc&unsplash=true&orientation=landscape')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// data = {
|
||||
// urls: {
|
||||
// regular: "https://imageproxy.thegreydiamond.de/ra5iqxlyve6HpjNvC1tzG50a14oIOgiWP95CxIvbBC8/sm:1/kcr:1/aHR0cHM6Ly9zdGFn/aW5nLnRoZWdyZXlk/aWFtb25kLmRlL3By/b2plY3RzL3Bob3Rv/UG9ydGZvbGlvL2Rl/bW9IaVJlcy9QMTE5/MDgzMC1zY2hpbGQu/anBn.webp"
|
||||
@ -28,29 +32,29 @@ function handleImage() {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if(!currentImageHandle) {
|
||||
if (!currentImageHandle) {
|
||||
// Create a page filling div which contains the image
|
||||
currentImageHandle = document.createElement("div");
|
||||
currentImageHandle.style.position = "absolute";
|
||||
currentImageHandle.style.top = "0";
|
||||
currentImageHandle.style.left = "0";
|
||||
currentImageHandle.style.width = "100%";
|
||||
currentImageHandle.style.height = "100%";
|
||||
currentImageHandle = document.createElement('div');
|
||||
currentImageHandle.style.position = 'absolute';
|
||||
currentImageHandle.style.top = '0';
|
||||
currentImageHandle.style.left = '0';
|
||||
currentImageHandle.style.width = '100%';
|
||||
currentImageHandle.style.height = '100%';
|
||||
currentImageHandle.style.backgroundImage = `url(${data.urls.regular})`;
|
||||
currentImageHandle.style.backgroundSize = "cover";
|
||||
currentImageHandle.style.backgroundSize = 'cover';
|
||||
currentImageHandle.style.opacity = 1;
|
||||
} else {
|
||||
// Create a new div behind the current one and delete the old one when the new one is loaded
|
||||
let newImageHandle = document.createElement("div");
|
||||
newImageHandle.style.position = "absolute";
|
||||
newImageHandle.style.top = "0";
|
||||
newImageHandle.style.left = "0";
|
||||
newImageHandle.style.width = "100%";
|
||||
newImageHandle.style.height = "100%";
|
||||
let newImageHandle = document.createElement('div');
|
||||
newImageHandle.style.position = 'absolute';
|
||||
newImageHandle.style.top = '0';
|
||||
newImageHandle.style.left = '0';
|
||||
newImageHandle.style.width = '100%';
|
||||
newImageHandle.style.height = '100%';
|
||||
newImageHandle.style.backgroundImage = `url(${data.urls.regular})`;
|
||||
newImageHandle.style.backgroundSize = "cover";
|
||||
newImageHandle.style.backgroundSize = 'cover';
|
||||
newImageHandle.style.opacity = 1;
|
||||
newImageHandle.style.transition = "1s";
|
||||
newImageHandle.style.transition = '1s';
|
||||
newImageHandle.style.zIndex = 19999;
|
||||
document.body.appendChild(newImageHandle);
|
||||
|
||||
@ -61,33 +65,31 @@ function handleImage() {
|
||||
currentImageHandle = newImageHandle;
|
||||
}, 1000);
|
||||
|
||||
|
||||
|
||||
// Set the credits
|
||||
credits.innerHTML = `Photo by <a href="${data.user.links.html}" target="_blank">${data.user.name}</a> on <a href="https://unsplash.com" target="_blank">Unsplash</a>`;
|
||||
credits.style.zIndex = 300000;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching image: ", error);
|
||||
.catch((error) => {
|
||||
console.error('Error fetching image: ', error);
|
||||
});
|
||||
} else {
|
||||
if(currentImageHandle) {
|
||||
if (currentImageHandle) {
|
||||
// Check if the image is already loaded
|
||||
if(currentImageHandle.style.backgroundImage === `url("${showModeImage}")`) {
|
||||
if (currentImageHandle.style.backgroundImage === `url("${showModeImage}")`) {
|
||||
return;
|
||||
}
|
||||
// Create a new div behind the current one and delete the old one when the new one is loaded
|
||||
let newImageHandle = document.createElement("div");
|
||||
newImageHandle.style.position = "absolute";
|
||||
newImageHandle.style.top = "0";
|
||||
newImageHandle.style.left = "0";
|
||||
newImageHandle.style.width = "100%";
|
||||
newImageHandle.style.height = "100%";
|
||||
let newImageHandle = document.createElement('div');
|
||||
newImageHandle.style.position = 'absolute';
|
||||
newImageHandle.style.top = '0';
|
||||
newImageHandle.style.left = '0';
|
||||
newImageHandle.style.width = '100%';
|
||||
newImageHandle.style.height = '100%';
|
||||
newImageHandle.style.backgroundImage = `url(${showModeImage})`;
|
||||
newImageHandle.style.backgroundSize = "cover";
|
||||
newImageHandle.style.backgroundSize = 'cover';
|
||||
newImageHandle.style.opacity = 1;
|
||||
newImageHandle.style.transition = "1s";
|
||||
newImageHandle.style.transition = '1s';
|
||||
document.body.appendChild(newImageHandle);
|
||||
|
||||
setTimeout(() => {
|
||||
@ -107,37 +109,22 @@ function handleTimeAndDate() {
|
||||
month += 1;
|
||||
let year = time.getFullYear();
|
||||
|
||||
let timeHandle = document.getElementById("time");
|
||||
let dateHandle = document.getElementById("date");
|
||||
let timeHandle = document.getElementById('time');
|
||||
let dateHandle = document.getElementById('date');
|
||||
|
||||
timeHandle.innerHTML = `${hours < 10 ? "0" + hours : hours}:${minutes < 10 ? "0" + minutes : minutes}:${time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds()}`;
|
||||
timeHandle.innerHTML = `${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}:${time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()}`;
|
||||
// Datum in format Montag, 22.12.2024
|
||||
dateHandle.innerHTML = `${getDay(time.getDay())}, ${day < 10 ? "0" + day : day}.${month < 10 ? "0" + month : month}.${year}`;
|
||||
dateHandle.innerHTML = `${getDay(time.getDay())}, ${day < 10 ? '0' + day : day}.${month < 10 ? '0' + month : month}.${year}`;
|
||||
}
|
||||
|
||||
function getDay(day) {
|
||||
switch(day) {
|
||||
case 0:
|
||||
return "Sonntag";
|
||||
case 1:
|
||||
return "Montag";
|
||||
case 2:
|
||||
return "Dienstag";
|
||||
case 3:
|
||||
return "Mittwoch";
|
||||
case 4:
|
||||
return "Donnerstag";
|
||||
case 5:
|
||||
return "Freitag";
|
||||
case 6:
|
||||
return "Samstag";
|
||||
}
|
||||
return ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'][day];
|
||||
}
|
||||
|
||||
// Set the image handler to run every 10 minutes
|
||||
setInterval(handleImage, 60 * 1000 * 10);
|
||||
handleImage();
|
||||
handleImage()
|
||||
handleImage();
|
||||
|
||||
// Set the time and date handler to run every minute
|
||||
setInterval(handleTimeAndDate, 500);
|
||||
|
@ -5,3 +5,11 @@ body {
|
||||
hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.notification-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
margin: 20px;
|
||||
}
|
@ -26,6 +26,13 @@ var searchFields = document.querySelectorAll('input[data-searchTargetId]');
|
||||
// Find all modalForms
|
||||
var modalForms = document.querySelectorAll('form[data-targetTable]');
|
||||
|
||||
|
||||
// Create a floating container for notifications
|
||||
const notificationContainer = document.createElement('div');
|
||||
notificationContainer.classList.add('notification-container');
|
||||
document.body.appendChild(notificationContainer);
|
||||
let notifications = [];
|
||||
|
||||
console.info('Processing single values');
|
||||
console.info(singleValues);
|
||||
|
||||
@ -71,8 +78,6 @@ tables.forEach(async (table) => {
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
async function writeSingelton(element) {
|
||||
const table = element.getAttribute('data-dataSource');
|
||||
console.log('Table: ', table, ' Action: ', element.getAttribute('data-dataAction'), ' Element: ', element);
|
||||
@ -181,6 +186,10 @@ modalForms.forEach((modalForm) => {
|
||||
console.log('Type: ', rule['args']['type']);
|
||||
break;
|
||||
}
|
||||
case 'email': {
|
||||
field.setAttribute('type', 'email');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (flags) {
|
||||
@ -301,7 +310,7 @@ async function refreshTable(table) {
|
||||
if (searchField) {
|
||||
const value = searchField.value;
|
||||
const dbTable = table.getAttribute('data-dataSource');
|
||||
const result = await returnTableDataByTableName(dbTable, value, order, column, take= maxLinesPerPage, skip= start);
|
||||
const result = await returnTableDataByTableName(dbTable, value, order, column, take=maxLinesPerPage, skip= start);
|
||||
const totalResultCount = await getCountByTable(dbTable, value);
|
||||
paginationPassOnPre['dataLength'] = totalResultCount;
|
||||
var magMiddl = managePaginationMiddleware(result, paginationPassOnPre);
|
||||
@ -355,6 +364,7 @@ function writeDataToTable(table, data, paginationPassOn) {
|
||||
if(data == undefined || data == null || data.length == 0) {
|
||||
return;
|
||||
}
|
||||
data = data.result
|
||||
console.log('Writing data to table: ', table, data);
|
||||
// Get THEAD and TBODY elements
|
||||
const thead = table.querySelector('thead');
|
||||
@ -377,6 +387,9 @@ function writeDataToTable(table, data, paginationPassOn) {
|
||||
actionFields.push(column);
|
||||
return;
|
||||
}
|
||||
if(column.getAttribute('data-type') == 'hidden') {
|
||||
return;
|
||||
}
|
||||
requiredCols.push(column.getAttribute('data-dataCol'));
|
||||
});
|
||||
|
||||
@ -472,9 +485,30 @@ function writeDataToTable(table, data, paginationPassOn) {
|
||||
const row = data[resultIndex];
|
||||
const tr = document.createElement('tr');
|
||||
requiredCols.forEach((column) => {
|
||||
// console.log('Column: ', column, ' Index: ', columnIndices[column]);
|
||||
const td = document.createElement('td');
|
||||
td.innerText = row[column];
|
||||
// Grab attribute from header
|
||||
const header = columns[columnIndices[column]];
|
||||
if(header.getAttribute('data-dataCol') == "FUNC:INLINE") {
|
||||
try {
|
||||
// Call data-ColHandler as a function
|
||||
const handler = window[header.getAttribute('data-ColHandler')];
|
||||
const result = handler(row);
|
||||
row[column] = result;
|
||||
|
||||
} catch (e) {
|
||||
console.error('Error in ColHandler: ', e);
|
||||
}
|
||||
}
|
||||
|
||||
if(header.getAttribute('data-type') == "bool") {
|
||||
td.innerHTML = row[column] ? '<i class="bi bi-check"></i>' : '<i class="bi bi-x"></i>';
|
||||
|
||||
} else {
|
||||
td.innerHTML = row[column];
|
||||
}
|
||||
tr.appendChild(td);
|
||||
|
||||
});
|
||||
|
||||
// Add action fields
|
||||
@ -539,6 +573,9 @@ function writeDataToTable(table, data, paginationPassOn) {
|
||||
if(field.getAttribute('type') == 'submit') {
|
||||
return;
|
||||
}
|
||||
if(field.getAttribute('data-edit-transfer') == 'disable') {
|
||||
return;
|
||||
}
|
||||
field.value = data[field.getAttribute('name')];
|
||||
});
|
||||
form.closest('.modal').classList.add('is-active');
|
||||
@ -556,9 +593,11 @@ function writeDataToTable(table, data, paginationPassOn) {
|
||||
if(resp['status'] == 'DELETED') {
|
||||
refreshTable(table);
|
||||
updateSingeltonsByTableName(table.getAttribute('data-dataSource'));
|
||||
createTemporaryNotification('Entry deleted successfully', 'is-success');
|
||||
} else {
|
||||
// Show error message
|
||||
// TODO: Show error message
|
||||
createTemporaryNotification('Error while deleting entry', 'is-danger');
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -605,6 +644,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Add a click event on various child elements to close the parent modal
|
||||
(document.querySelectorAll('.modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
|
||||
const $target = $close.closest('.modal');
|
||||
if($target.data && $target.data.dissmiss == "false") {
|
||||
return;
|
||||
}
|
||||
|
||||
$close.addEventListener('click', () => {
|
||||
closeModal($target);
|
||||
@ -620,10 +662,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
|
||||
function createTemporaryNotification(message, type = 'is-success', timeout = 5000) {
|
||||
const notification = document.createElement('div');
|
||||
notification.classList.add('notification');
|
||||
notification.classList.add(type);
|
||||
notification.innerHTML = message;
|
||||
notificationContainer.appendChild(notification);
|
||||
setTimeout(() => {
|
||||
$(notification).fadeOut(500);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||
const $notification = $delete.parentNode;
|
||||
|
||||
$delete.addEventListener('click', () => {
|
||||
$notification.parentNode.removeChild($notification);
|
||||
});
|
||||
|
65
static/pages/admin_products.js
Normal file
65
static/pages/admin_products.js
Normal file
@ -0,0 +1,65 @@
|
||||
let uploadFileInput = document.getElementById('imgUpload');
|
||||
let fileName = document.getElementById('fileName');
|
||||
let imgUploadForm = document.getElementById('imgUploadForm');
|
||||
|
||||
function handleImagePresence(row) {
|
||||
// Check if /api/v1/image?id=row&check returns true
|
||||
// Needs to be sync
|
||||
|
||||
let isThere = false;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `/api/v1/image?id=${row.id}&check=true`, false);
|
||||
xhr.send();
|
||||
if (xhr.status === 200) {
|
||||
try {
|
||||
isThere = JSON.parse(xhr.responseText);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
isThere = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let pretty = isThere ? '<i class="bi bi-check"></i>' : '<i class="bi bi-x"></i></a>';
|
||||
const template = `<a href="/api/v1/image?id=${row.id}" target="_blank">${pretty}</a> <i class="bi bi-dot"></i> <button class="btn btn-primary" onclick="uploadImage(${row.id})">Upload</button>`;
|
||||
return template;
|
||||
}
|
||||
|
||||
function uploadImage(id) {
|
||||
// Open a file picker
|
||||
uploadFileInput.click();
|
||||
// // Open a modal to upload an image
|
||||
// // Use a form
|
||||
// const modal = document.getElementById('imageModal');
|
||||
// modal.style.display = 'block';
|
||||
imgUploadForm.action = `/api/v1/image?id=${id}`;
|
||||
}
|
||||
|
||||
function silentFormSubmit() {
|
||||
// Submit the form silently (without reloading the page or redirecting)
|
||||
// Grab the form and do a POST request (dont forget to prevent default)
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', imgUploadForm.action, true);
|
||||
xhr.send(new FormData(imgUploadForm));
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
console.log(xhr.responseText);
|
||||
//location.reload();
|
||||
createTemporaryNotification('Bild hochgeladen', 'is-success');
|
||||
// Close the modal
|
||||
document.getElementById('imageModal').style.display = "none";
|
||||
|
||||
// Empty the input
|
||||
uploadFileInput.value = '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
uploadFileInput.addEventListener('change', function() {
|
||||
fileName.innerHTML = this.files[0].name;
|
||||
silentFormSubmit();
|
||||
setTimeout(() => {
|
||||
refreshTableByName('products');
|
||||
}, 1000);
|
||||
});
|
2
static/pages/admin_users.js
Normal file
2
static/pages/admin_users.js
Normal file
@ -0,0 +1,2 @@
|
||||
let elm_table_users = document.getElementById('table_users');
|
||||
|
4
static/pages/payup.js
Normal file
4
static/pages/payup.js
Normal file
@ -0,0 +1,4 @@
|
||||
const tableContent = document.querySelector('.table-content');
|
||||
const tableSum = document.querySelector('.table-sum');
|
||||
|
||||
alert("NYI: Endpoint is not yet implemented. This demo ends here.");
|
@ -2,11 +2,35 @@ console.log('product_select.js loaded');
|
||||
|
||||
// Get containers
|
||||
let mainSelectionDiv = document.getElementById('mainSelect');
|
||||
let checkoutTable = document.getElementById('selectedProducts');
|
||||
|
||||
let sumField = document.getElementById('TableSum');
|
||||
|
||||
let toCheckoutButton = document.getElementById('checkout');
|
||||
let confirmCartButton = document.getElementById('confirmCheckout');
|
||||
|
||||
let loadingModal = document.getElementById('loadingModal');
|
||||
|
||||
let scannerField = document.getElementById('scannerField');
|
||||
|
||||
const baseStruct = document.getElementById("baseStruct");
|
||||
|
||||
let globalData;
|
||||
|
||||
let shoppingCart = [];
|
||||
|
||||
toCheckoutButton.addEventListener('click', finalizeTransaction);
|
||||
confirmCartButton.addEventListener('click', confirmedCart);
|
||||
|
||||
// Get user from url (and cookie)
|
||||
let userFCookie = getCookie('user');
|
||||
let userFUrl = new URLSearchParams(window.location.search).get('user');
|
||||
|
||||
if(userFCookie != userFUrl) {
|
||||
createTemporaryNotification('Fehler: User nicht korrekt gesetzt!', 'is-danger');
|
||||
window.location.href = '/user_select';
|
||||
}
|
||||
|
||||
// On load
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
let data = await returnTableDataByTableName('products');
|
||||
@ -17,7 +41,7 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
|
||||
for(let i = 0; i < result.length; i++) {
|
||||
let product = result[i];
|
||||
if(product.visible) {
|
||||
if(product.visible && product.stock > 0) {
|
||||
let newDiv = baseStruct.cloneNode(true);
|
||||
newDiv.id = `product_${product.id}`;
|
||||
newDiv.style.display = 'block';
|
||||
@ -27,9 +51,186 @@ document.addEventListener('DOMContentLoaded', async function() {
|
||||
|
||||
newDiv.querySelector('.product_price').innerText = price + " €";
|
||||
newDiv.querySelector('.product_ean').innerText = product.gtin;
|
||||
newDiv.querySelector('.product_image').src = product.image || "https://bulma.io/assets/images/placeholders/1280x960.png";
|
||||
newDiv.querySelector('.product_image').src = "/api/v1/image?id=" + product.id;
|
||||
newDiv.querySelector('.product_image').alt = product.name;
|
||||
|
||||
newDiv.addEventListener('click', selectProductEvent);
|
||||
mainSelectionDiv.appendChild(newDiv);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function canIAddProduct(product, shoppingCart) {
|
||||
let stock = product.stock;
|
||||
let count = shoppingCart.filter(p => p.id == product.id).length;
|
||||
return count < stock;
|
||||
}
|
||||
|
||||
function selectProductEvent(e) {
|
||||
console.log('selectProductEvent', e);
|
||||
let id = e.currentTarget.id.split('_')[1];
|
||||
let product = globalData.find(p => p.id == id);
|
||||
if(!canIAddProduct(product, shoppingCart)) {
|
||||
createTemporaryNotification('Nicht genug Lagerbestand mehr vorhanden!', 'is-danger');
|
||||
return;
|
||||
}
|
||||
let price = parseFloat(product.price).toFixed(2);
|
||||
let row = checkoutTable.insertRow();
|
||||
row.id = `product_${product.id}`;
|
||||
let cell1 = row.insertCell(0); // Name
|
||||
let cell2 = row.insertCell(1); // Price
|
||||
let cell3 = row.insertCell(2); // Actions
|
||||
|
||||
shoppingCart.push(product);
|
||||
|
||||
cell1.innerText = product.name;
|
||||
cell2.innerText = price + " €";
|
||||
let deleteButton = document.createElement('button');
|
||||
deleteButton.innerHTML = '<i class="bi bi-trash"></i>';
|
||||
deleteButton.onclick = deleteProductEvent;
|
||||
deleteButton.className = 'button is-danger';
|
||||
deleteButton.style.color = 'white';
|
||||
cell3.appendChild(deleteButton);
|
||||
|
||||
|
||||
sumField.innerText = calculateSum(shoppingCart);
|
||||
}
|
||||
|
||||
function calculateSum(cart) {
|
||||
let sum = 0;
|
||||
for(let i = 0; i < cart.length; i++) {
|
||||
sum += parseFloat(cart[i].price);
|
||||
}
|
||||
return sum.toFixed(2) + " €";
|
||||
}
|
||||
|
||||
function deleteProductEvent(e) {
|
||||
let row = e.target.parentElement.parentElement;
|
||||
// Check if icon was clicked instead of button
|
||||
if(row.tagName != 'TR') {
|
||||
row = e.target.parentElement.parentElement.parentElement;
|
||||
}
|
||||
let id = row.id.split('_')[1];
|
||||
let product = shoppingCart.find(p => p.id == id);
|
||||
let index = shoppingCart.indexOf(product);
|
||||
shoppingCart.splice(index, 1);
|
||||
row.remove();
|
||||
sumField.innerText = calculateSum(shoppingCart);
|
||||
}
|
||||
|
||||
function finalizeTransaction() {
|
||||
if(shoppingCart.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show confirmation dialog (id-> checkoutModal)
|
||||
let modal = document.getElementById('checkoutModal');
|
||||
modal.classList.add('is-active');
|
||||
let modalContent = document.getElementById('modalContent');
|
||||
|
||||
// Grab table in modal
|
||||
let modalTable = document.getElementById('selectedProductsModal');
|
||||
modalTable.innerHTML = "";
|
||||
for(let i = 0; i < shoppingCart.length; i++) {
|
||||
let product = shoppingCart[i];
|
||||
let row = modalTable.insertRow();
|
||||
let cell1 = row.insertCell(0); // Name
|
||||
let cell2 = row.insertCell(1); // Price
|
||||
cell1.innerText = product.name;
|
||||
cell2.innerText = parseFloat(product.price).toFixed(2) + " €";
|
||||
}
|
||||
|
||||
let modalSum = document.getElementById('ModalSum');
|
||||
modalSum.innerText = calculateSum(shoppingCart);
|
||||
}
|
||||
|
||||
function confirmedCart() {
|
||||
// Close modal
|
||||
let modal = document.getElementById('checkoutModal');
|
||||
modal.classList.remove('is-active');
|
||||
|
||||
// Show loading modal
|
||||
loadingModal.classList.add('is-active');
|
||||
|
||||
// Send data to server
|
||||
// alert('NYI: Send data to server. This demo ends here.');
|
||||
|
||||
let listOfIds = shoppingCart.map(p => p.id);
|
||||
let data = {
|
||||
products: listOfIds,
|
||||
user_id: getCookie('user')
|
||||
};
|
||||
|
||||
// Send data to server
|
||||
fetch('/api/v1/transaction', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(async (response) => {
|
||||
let json = await response.json();
|
||||
if(response.ok) {
|
||||
createTemporaryNotification('<i class="bi bi-check-lg"></i> Erfolgreich abgeschlossen', 'is-success');
|
||||
setTimeout(() => {
|
||||
window.location.href = '/user_select';
|
||||
}, 1000);
|
||||
} else {
|
||||
createTemporaryNotification('Fehler: ' + json.error, 'is-danger');
|
||||
}
|
||||
loadingModal.classList.remove('is-active');
|
||||
}).catch((error) => {
|
||||
createTemporaryNotification('Fehler: ' + error, 'is-danger');
|
||||
loadingModal.classList.remove('is-active');
|
||||
});
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
let value = "; " + document.cookie;
|
||||
let parts = value.split("; " + name + "=");
|
||||
if(parts.length == 2) {
|
||||
return parts.pop().split(";").shift();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle barcode scanner
|
||||
// Force the cursor to the scanner field
|
||||
scannerField.focus();
|
||||
// Do so in an interval
|
||||
setInterval(() => {
|
||||
scannerField.focus();
|
||||
}, 1000);
|
||||
|
||||
// Make it tiny
|
||||
scannerField.style.fontSize = '1px';
|
||||
scannerField.style.height = '1px';
|
||||
scannerField.style.width = '1px';
|
||||
scannerField.style.opacity = '0';
|
||||
scannerField.style.position = 'relative';
|
||||
|
||||
// Handle barcode scanner input
|
||||
scannerField.addEventListener('keydown', async function(event) {
|
||||
if(event.key != 'Enter') {
|
||||
return;
|
||||
}
|
||||
let barcode = scannerField.value;
|
||||
console.log('Barcode scanned:', barcode);
|
||||
scannerField.value = "";
|
||||
// createTemporaryNotification(`Barcode ${barcode} gescannt`, 'is-link');
|
||||
|
||||
let product = globalData.find(p => p.gtin == barcode);
|
||||
if(product) {
|
||||
let event = new Event('click');
|
||||
createTemporaryNotification(`<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} gefunden`, 'is-success');
|
||||
document.getElementById(`product_${product.id}`).dispatchEvent(event);
|
||||
} else {
|
||||
createTemporaryNotification( `<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} nicht gefunden`, 'is-danger');
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure text fields is always centerd vertically
|
||||
window.addEventListener('scroll', function(event) {
|
||||
scannerField.y = document.documentElement.scrollTop + 20;
|
||||
scannerField.style.top = document.documentElement.scrollTop + 20 + "px";
|
||||
});
|
@ -100,12 +100,15 @@ function validatePin() {
|
||||
if(response) {
|
||||
console.log("Pin is correct");
|
||||
pinPadModal.classList.remove('is-active');
|
||||
// Write a cookie
|
||||
document.cookie = `user=${userId}`;
|
||||
document.cookie = `name=${currentUser.name}`;
|
||||
window.location.href = `/product_select?user=${userId}`;
|
||||
} else {
|
||||
console.log("Pin is incorrect");
|
||||
pinValue = "";
|
||||
updatePinFields();
|
||||
pinError.classList.remove('is-hidden');
|
||||
createTemporaryNotification('Fehlerhafte PIN Eingabe!', 'is-danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
24
views/admin/dashboard.eta
Normal file
24
views/admin/dashboard.eta
Normal file
@ -0,0 +1,24 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<section class="section container" id="mainSelect">
|
||||
<h1 class="title">Administration</h1>
|
||||
<!-- Big buttons linking to the different admin pages (Produkte, Benutzer, Bericht) -->
|
||||
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-4">
|
||||
<a href="/admin/products" class="button is-large is-fullwidth is-primary">Produkte</a>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<a href="/admin/users" class="button is-large is-fullwidth is-primary">Benutzer</a>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<a href="/admin/reports" class="button is-large is-fullwidth is-primary">Berichte</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<!-- <script src="/static/pages/admin_.js"></script>-->
|
||||
<%~ include("partials/base_foot.eta") %>
|
153
views/admin/products.eta
Normal file
153
views/admin/products.eta
Normal file
@ -0,0 +1,153 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Admin - Benutzer"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<section class="section container" id="mainSelect">
|
||||
<h1 class="title">Produktverwaltung</h1>
|
||||
<p class="heading"><button class="js-modal-trigger button" data-target="modal-js-example">
|
||||
Neues Produkt anlegen
|
||||
</button></p>
|
||||
|
||||
<input class="input" type="text" data-searchTargetId="productTable" placeholder="Nach Produkt suchen.." />
|
||||
<table class="table is-striped is-fullwidth is-hoverable" data-dataSource="products" id="productTable" data-pageSize="10">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-dataCol = "id">Id</th>
|
||||
<th data-dataCol = "name">Name</th>
|
||||
<th data-dataCol = "gtin">GTIN</th>
|
||||
<th data-dataCol = "price">Preis</th>
|
||||
<th data-dataCol = "stock">Lagermenge</th>
|
||||
<th data-dataCol = "visible" data-type="bool">Sichtbarkeit</th>
|
||||
<th data-dataCol = "FUNC:INLINE" data-ColHandler=handleImagePresence>Bild hinterlegt</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="productTable">
|
||||
<ul class="pagination-list">
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<!-- Image upload modal -->
|
||||
<div id="imageModal" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="box">
|
||||
<form id="imgUploadForm" enctype="multipart/form-data" method="post" action="/api/v1/image">
|
||||
<h2 class="title">Bild hochladen</h1>
|
||||
<div class="file has-name">
|
||||
<label class="file-label">
|
||||
<input id="imgUpload" class="file-input" type="file" name="image" />
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
</span>
|
||||
<span class="file-label"> Datei wählen… </span>
|
||||
</span>
|
||||
<span class="file-name" id="fileName"></span>
|
||||
</label>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
</form>
|
||||
<div class="control">
|
||||
<input type="button" class="button is-link" value="Hochladen" onclick="silentFormSubmit()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 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="products">
|
||||
<h2 class="title">Neuer Benutzer</h1>
|
||||
<div class="field">
|
||||
<label class="label">Bezeichner</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">GTIN</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="number" placeholder="" value="" name="gtin">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="bi bi-upc"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Lagermenge</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="number" placeholder="" value="" name="stock">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="bi bi-archive-fill"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Preis</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="number" placeholder="" value="" step=0.01 name="price">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="bi bi-currency-euro"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" value="" name="visible">
|
||||
In der Liste anzeigen</a>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
<script src="/static/pages/admin_products.js"></script>
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<%~ include("partials/base_foot.eta") %>
|
96
views/admin/users.eta
Normal file
96
views/admin/users.eta
Normal file
@ -0,0 +1,96 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Admin - Benutzer"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<section class="section container" id="mainSelect">
|
||||
<h1 class="title">Benutzerverwaltung</h1>
|
||||
<p class="heading"><button class="js-modal-trigger button" data-target="modal-js-example">
|
||||
Neuen Konakt anlegen
|
||||
</button></p>
|
||||
<table class="table is-striped is-fullwidth is-hoverable" data-dataSource="user" id="userTable" data-pageSize="10">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-dataCol = "id">Id</th>
|
||||
<th data-dataCol = "name">Name</th>
|
||||
<th data-dataCol = "email">E-Mail</th>
|
||||
<th data-dataCol = "code" data-type="bool" data-edit-transfer="disable">Code</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="userTable">
|
||||
<ul class="pagination-list">
|
||||
|
||||
</ul>
|
||||
</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="user">
|
||||
<h2 class="title">Neuer Benutzer</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">E-Mail</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="email" placeholder="test@example.org" value="" name="email">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="bi bi-envelope"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Pin</label>
|
||||
<div class="control has-icons-left">
|
||||
<input class="input" type="text" placeholder="" value="" name="code">
|
||||
<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>
|
||||
|
||||
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<script src="/static/pages/admin_users.js"></script>
|
||||
<%~ include("partials/base_foot.eta") %>
|
@ -33,15 +33,43 @@
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<% /* <div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary">
|
||||
<strong>Sign up</strong>
|
||||
</a>
|
||||
<a class="button is-light">Log in</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item is-hidden" id="showOnLogin">
|
||||
<strong>Hey, <span id="nav_username"></span></strong>
|
||||
<button class="button" onclick="window.location='/pay_up'" >Zur Abrechnung</button>
|
||||
</div>
|
||||
</div> */ %>
|
||||
<div class="navbar-item is-hidden" id="onlyShowRoot">
|
||||
<button class="button" onclick="window.location='/admin/'" >Zur Administration</button>
|
||||
</div>
|
||||
<div class="navbar-item is-hidden" id="onlyShowAdmin">
|
||||
<button class="button" onclick="window.location='/admin/'" >Zur Administration</button>
|
||||
<button class="button" onclick="window.location='/'" >Abmelden</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Check if ?user is set
|
||||
if (window.location.search.includes('user')) {
|
||||
// Show the sign up button
|
||||
document.querySelector('#showOnLogin').classList.remove('is-hidden');
|
||||
// Get the username from the cookie
|
||||
username = document.cookie.split('; ').find(row => row.startsWith('name')).split('=')[1];
|
||||
// Set the username in the nav
|
||||
document.getElementById('nav_username').innerText = username;
|
||||
}
|
||||
|
||||
// Check if /user_select is the current page
|
||||
if (window.location.pathname == '/user_select') {
|
||||
// Show the sign up button
|
||||
document.querySelector('#onlyShowRoot').classList.remove('is-hidden');
|
||||
}
|
||||
|
||||
// If admin is contained in url
|
||||
if (window.location.pathname.includes('admin')) {
|
||||
// Show the sign up button
|
||||
document.querySelector('#onlyShowAdmin').classList.remove('is-hidden');
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</nav>
|
||||
|
32
views/payup.eta
Normal file
32
views/payup.eta
Normal file
@ -0,0 +1,32 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<section class="section container" id="mainSelect">
|
||||
<h1 class="title">Abrechnung</h1>
|
||||
<h2 class="subtitle">Ausstehend</h2>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Bezeichner">Bez.</abbr></th>
|
||||
<th>Preis</th>
|
||||
<th></th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th id="table-sum"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody id="table-content">
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<script src="/static/pages/payup.js"></script>
|
||||
<%~ include("partials/base_foot.eta") %>
|
@ -1,6 +1,7 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<input id="scannerField" type="text"/>
|
||||
<section class="section main-content">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
@ -11,6 +12,27 @@
|
||||
<!-- Empty sidebar on the right -->
|
||||
<div class="column is-one-quarter">
|
||||
<h2 class="title is-4" >Ausgewählte Produkte</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><abbr title="Bezeichner">Bez.</abbr></th>
|
||||
<th>Preis</th>
|
||||
<th></th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th id="TableSum"></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody id="selectedProducts">
|
||||
</tbody>
|
||||
</table>
|
||||
<button class="button is-primary" id="checkout">Zur Kasse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -43,6 +65,57 @@
|
||||
</div>
|
||||
</hidden>
|
||||
|
||||
<!-- Confirmation modal -->
|
||||
<div class="modal" id="checkoutModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Bestellung abschließen</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="content">
|
||||
<p>
|
||||
Sind Sie sicher, dass Sie die ausgewählten so Produkte bestellen möchten?
|
||||
</p>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bezeichner</th>
|
||||
<th>Preis</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th id="ModalSum"></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody id="selectedProductsModal">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success" id="confirmCheckout">Bestellen</button>
|
||||
<button class="button" id="cancelCheckout">Abbrechen</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading modal -->
|
||||
<div class="modal" id="loadingModal" data-dissmiss="false">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content">
|
||||
<div class="box">
|
||||
<p>Bitte warten...</p>
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<script src="/static/pages/product_select.js"></script>
|
||||
<%~ include("partials/base_foot.eta") %>
|
@ -1,5 +1,4 @@
|
||||
<%~ include("partials/base_head.eta", {"title": "Dashboard"}) %>
|
||||
<%~ include("partials/nav.eta") %>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/lockscreen.css">
|
||||
|
||||
@ -14,5 +13,4 @@
|
||||
|
||||
<script src="/static/js/lockscreenBgHandler.js"></script>
|
||||
|
||||
<%~ include("partials/footer.eta") %>
|
||||
<%~ include("partials/base_foot.eta") %>
|
||||
|
Loading…
x
Reference in New Issue
Block a user