From 14cf8af14bd9962be8a68d69789bdfaa8ccdddbf Mon Sep 17 00:00:00 2001 From: grey <pnh@thegreydiamond.de> Date: Wed, 19 Mar 2025 20:45:05 +0100 Subject: [PATCH] restock mode --- static/pages/admin_products.js | 132 ++++++++++++++++++++++++++++++++- static/pages/product_select.js | 8 +- views/admin/products.eta | 84 ++++++++++++++++++++- 3 files changed, 214 insertions(+), 10 deletions(-) diff --git a/static/pages/admin_products.js b/static/pages/admin_products.js index e3dd290..00909f6 100644 --- a/static/pages/admin_products.js +++ b/static/pages/admin_products.js @@ -1,6 +1,26 @@ let uploadFileInput = document.getElementById('imgUpload'); let fileName = document.getElementById('fileName'); let imgUploadForm = document.getElementById('imgUploadForm'); +let scannerField = document.getElementById('scannerField'); +let btn_restock = document.getElementById('btn_restock'); + +let btn_save_2 = document.getElementById('btn_save_2'); + +let form_gtin = document.getElementById('form_gtin'); + +let modal_stage_1 = document.getElementById('modal-stage-1'); +let modal_stage_2 = document.getElementById('modal-stage-2'); +let modal_stage_3 = document.getElementById('modal-stage-3'); + +let modal_stage_2_result = document.getElementById("stage-2-result"); + +let modal_stage_2_amount = document.getElementById("stage-2-amount"); + +let globalData; + +waitingForScan = false; +let currentRestockProduct = null; + function handleImagePresence(row) { // Check if /api/v1/image?id=row&check returns true @@ -55,6 +75,10 @@ function silentFormSubmit() { }; } +function enableScanner() { + waitingForScan = true; + scannerField.focus(); +} uploadFileInput.addEventListener('change', function() { fileName.innerHTML = this.files[0].name; @@ -62,4 +86,110 @@ uploadFileInput.addEventListener('change', function() { setTimeout(() => { refreshTableByName('products'); }, 1000); -}); \ No newline at end of file +}); + +scannerField.style.fontSize = '1px'; +scannerField.style.height = '1px'; +scannerField.style.width = '1px'; +scannerField.style.opacity = '0'; +scannerField.style.position = 'relative'; + +// Make sure text fields is always centerd vertically +window.addEventListener('scroll', function(event) { + if(!waitingForScan) { + return; + } + scannerField.y = document.documentElement.scrollTop + 20; + scannerField.style.top = document.documentElement.scrollTop + 20 + "px"; +}); + +setInterval(() => { + if(!waitingForScan) { + return; + } + scannerField.focus(); +}, 1000); + +btn_restock.addEventListener('click', function() { + modal_stage_1.classList.remove('is-hidden'); + modal_stage_2.classList.add('is-hidden'); + modal_stage_3.classList.add('is-hidden'); + waitingForScan = true; +}); +// 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-info'); + + + // Check if barcode is in the database + let product = globalData.find(p => p.gtin == barcode); + if(product) { + console.log('Product found:', product); + currentRestockProduct = product; + modal_stage_2_amount.innerHTML = "Aktuelle Menge: " + product.stock; + modal_stage_1.classList.add('is-hidden'); + modal_stage_2.classList.remove('is-hidden'); + modal_stage_3.classList.add('is-hidden'); + createTemporaryNotification(`<i class="bi bi-upc-scan"></i> Barcode scan: GTIN ${barcode} gefunden`, 'is-success'); + } else { + modal_stage_1.classList.add('is-hidden'); + modal_stage_2.classList.add('is-hidden'); + modal_stage_3.classList.remove('is-hidden'); + form_gtin.value = barcode; + } + // modal_stage_2_result.innerHTML = product ? `<i class="bi bi-check"></i> Produkt gefunden: ${product.name}` : `<i class="bi bi-x"></i> Produkt nicht gefunden`; + waitingForScan = false; + + + // 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'); + // } +}); + +function restock(amount) { + currentRestockProduct.stock += amount; + modal_stage_2_amount.innerHTML = "Aktuelle Menge: " + currentRestockProduct.stock; +} + +function applyStock() { + let result = _api.patch('products', { + "id": currentRestockProduct.id, + "stock": currentRestockProduct.stock + }) + if(result) { + createTemporaryNotification('Bestand erfolgreich aktualisiert', 'is-success'); + modal_stage_2.classList.add('is-hidden'); + modal_stage_1.classList.remove('is-hidden'); + modal_stage_3.classList.add('is-hidden'); + enableScanner(); + } else { + createTemporaryNotification('Fehler beim Aktualisieren des Bestands', 'is-danger'); + } + + +} + +document.addEventListener('DOMContentLoaded', async function() { + let data = await returnTableDataByTableName('products'); + console.info(`Found ${data.count} products`); + const result = data.result; + globalData = result; +}); + +// btn_save_2.addEventListener('click', async function() { +// // Assume submission is valid +// // Get the form data +// // reload table +// // close modal +// }); \ No newline at end of file diff --git a/static/pages/product_select.js b/static/pages/product_select.js index b5daf2a..3825dc8 100644 --- a/static/pages/product_select.js +++ b/static/pages/product_select.js @@ -186,13 +186,7 @@ function confirmedCart() { }); } -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 diff --git a/views/admin/products.eta b/views/admin/products.eta index e72bca6..acfddc8 100644 --- a/views/admin/products.eta +++ b/views/admin/products.eta @@ -1,11 +1,13 @@ <%~ include("partials/base_head.eta", {"title": "Admin - Benutzer"}) %> <%~ include("partials/nav.eta") %> - +<input id="scannerField" type="text"/> <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> + </button><button class="js-modal-trigger button" data-target="modal-restock" id="btn_restock"> + Lager nachfüllen + </button><br></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"> @@ -148,6 +150,84 @@ <button class="modal-close is-large" aria-label="close"></button> </div> +<div id="modal-restock" class="modal"> + <div class="modal-background"></div> + + <div class="modal-content"> + <div class="box" id="modal-stage-1"> + <h2 class="title">Nachfüllen</h1> + <center><h1 class="title"><i class="bi bi-upc-scan"></i></h1></center> + Warten auf Scan.... + </div> + <div class="box" id="modal-stage-2"> + <h2 class="title">Scan erfolgreich - Produktmenge eingeben</h1> + <h3 class="subtitle" id="stage-2-amount">Aktuelle Menge: 0</h3> + <div class="buttons"> + <button class="button is-info" onclick="restock(6)">+6</button> + <button class="button is-info" onclick="restock(10)">+10</button> + <button class="button is-info" onclick="restock(12)">+12</button> + </div> + <button class="button is-success" onclick="applyStock()">Änderungen speichern</button> + <div id="stage-2-result"></div> + </div> + <div class="box" id="modal-stage-3"> + <h2 class="title">Scan erfolgreich - Produkt erstellen</h1> + <form data-targetTable="products"> + <div class="field"> + <label class="label">Bezeichner</label> + <div class="control has-icons-left"> + <input class="input" type="text" placeholder="Schokolade" 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 id="form_gtin" class="input" type="number" placeholder="" value="" name="gtin" readonly> + <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 is-grouped"> + <div class="control"> + <input type="submit" class="button is-link" value="Save" data-actionBtn="save" data-extTable="productTable" id="btn_save_2"> + </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") %>