From 3f0f27e9385bb7fe928a262d35f2a1e6f0827b1e Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Sun, 15 Mar 2026 17:58:11 +0000 Subject: [PATCH] =?UTF-8?q?Multipack=E2=86=92conf=20pre-fill=20(2x200g?= =?UTF-8?q?=E2=86=922=20conf=20da=20200g)=20+=20modifica=20inventario=20da?= =?UTF-8?q?=20action=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/css/style.css | 8 +++ assets/js/app.js | 122 +++++++++++++++++++++++++++++++++++++++++-- data/dispensa.db | Bin 167936 -> 167936 bytes index.html | 4 +- 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 60e76f3..850fbea 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -2808,6 +2808,14 @@ body { font-weight: 700; } +.inventory-status-bar .inv-status-item-clickable { + cursor: pointer; + transition: background 0.15s; +} +.inventory-status-bar .inv-status-item-clickable:active { + background: rgba(59, 130, 246, 0.15); +} + /* ===== ACTION BUTTONS GRID ===== */ .action-buttons-3col { display: grid; diff --git a/assets/js/app.js b/assets/js/app.js index 2b4528c..aee7666 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -232,15 +232,14 @@ const CATEGORY_LABELS = { function detectUnitAndQuantity(quantityInfo) { if (!quantityInfo) return { unit: 'pz', quantity: 1, weightInfo: '' }; const q = quantityInfo.toLowerCase().trim(); - // Match multi-pack patterns like "6 x 1l", "4 x 125g" → total weight + // Match multi-pack patterns like "6 x 1l", "4 x 125g" → confezioni const multiMatch = q.match(/(\d+)\s*x\s*([\d.,]+)\s*(ml|l|g|kg|cl)/i); if (multiMatch) { const count = parseInt(multiMatch[1]); let perUnitVal = parseFloat(multiMatch[2].replace(',', '.')); let perUnitUnit = multiMatch[3].toLowerCase(); if (perUnitUnit === 'cl') { perUnitUnit = 'ml'; perUnitVal *= 10; } - const totalVal = count * perUnitVal; - return { unit: perUnitUnit, quantity: totalVal, weightInfo: quantityInfo }; + return { unit: 'conf', quantity: perUnitVal, packageUnit: perUnitUnit, confCount: count, weightInfo: quantityInfo }; } // Match single package patterns like "500 g", "1 l", "750 ml", "1.5 kg" const match = q.match(/([\d.,]+)\s*(kg|g|l|ml|cl)/i); @@ -349,6 +348,7 @@ function guessLocation(product) { // ===== STATE ===== let currentProduct = null; let currentInventory = []; +let _actionInventoryItems = []; let currentLocation = ''; let scannerStream = null; let quaggaRunning = false; @@ -1786,6 +1786,8 @@ async function onBarcodeDetected(barcode) { currentProduct.unit = detected.unit; currentProduct.default_quantity = detected.quantity; currentProduct.weight_info = weightStr; + if (detected.packageUnit) currentProduct.package_unit = detected.packageUnit; + if (detected.confCount) currentProduct._confCount = detected.confCount; // Update product in DB for future scans api('product_save', {}, 'POST', { id: currentProduct.id, @@ -1796,6 +1798,7 @@ async function onBarcodeDetected(barcode) { image_url: currentProduct.image_url || '', unit: detected.unit, default_quantity: detected.quantity, + package_unit: detected.packageUnit || '', notes: currentProduct.notes, }); } @@ -1806,6 +1809,11 @@ async function onBarcodeDetected(barcode) { const pesoMatch = currentProduct.notes.match(/Peso:\s*([^·]+)/); if (pesoMatch) currentProduct.weight_info = pesoMatch[1].trim(); } + // Detect confCount from weight_info for multipack pre-fill + if (currentProduct.weight_info && currentProduct.unit === 'conf' && !currentProduct._confCount) { + const detected = detectUnitAndQuantity(currentProduct.weight_info); + if (detected.confCount) currentProduct._confCount = detected.confCount; + } showLoading(false); stopScanner(); showProductAction(); @@ -1837,6 +1845,7 @@ async function onBarcodeDetected(barcode) { image_url: p.image_url || '', unit: detected.unit, default_quantity: detected.quantity, + package_unit: detected.packageUnit || '', notes: notesParts.join(' · '), }); @@ -1850,6 +1859,8 @@ async function onBarcodeDetected(barcode) { image_url: p.image_url || '', unit: detected.unit, default_quantity: detected.quantity, + package_unit: detected.packageUnit || '', + _confCount: detected.confCount || 0, weight_info: p.quantity_info || '', nutriscore: p.nutriscore || '', ingredients: p.ingredients || '', @@ -2379,6 +2390,7 @@ function showProductAction() { // === CHECK INVENTORY FOR THIS PRODUCT === checkInventoryForProduct(currentProduct.id).then(inventoryItems => { + _actionInventoryItems = inventoryItems; const statusBar = document.getElementById('action-inventory-status'); const btnsContainer = document.getElementById('action-buttons-container'); @@ -2402,7 +2414,7 @@ function showProductAction() { else if (d <= 7) expiryStr = ` · 🟡 Scade tra ${d}g`; else expiryStr = ` · 📅 ${formatDate(inv.expiry_date)}`; } - return `
${locInfo.icon} ${locInfo.label}${expiryStr}${qtyStr}${pkgF ? ' ' + pkgF : ''}
`; + return `
${locInfo.icon} ${locInfo.label}${expiryStr}${qtyStr}${pkgF ? ' ' + pkgF : ''} ✏️
`; }).join(''); const totalStr = formatQuantity(totalQty, unit, defQty, pkgUnit); @@ -2417,6 +2429,7 @@ function showProductAction() {
${invHtml}
+

Tocca una riga per modificare

`; btnsContainer.className = 'action-buttons-4col'; @@ -2505,6 +2518,105 @@ function editProductFromAction() { showPage('product-form'); } +// === EDIT INVENTORY ITEM FROM ACTION PAGE === +function editActionInventoryItem(inventoryId) { + const item = _actionInventoryItems.find(i => i.id === inventoryId); + if (!item) return; + + const isConf = (item.unit || 'pz') === 'conf'; + const confSizeVal = (isConf && item.default_quantity > 0) ? item.default_quantity : ''; + const confUnitVal = (isConf && item.package_unit) ? item.package_unit : 'g'; + + document.getElementById('modal-content').innerHTML = ` + +
+
+ +
+ + + +
+
+
+ + +
+
+ +
+ + +
+
+
+ +
+ ${Object.entries(LOCATIONS).map(([k, v]) => ` + + `).join('')} +
+ +
+
+ + +
+ +
+ `; + document.getElementById('modal-overlay').style.display = 'flex'; +} + +function onActionEditUnitChange() { + const unit = document.getElementById('action-edit-unit').value; + const confGroup = document.getElementById('action-edit-conf-group'); + if (confGroup) confGroup.style.display = unit === 'conf' ? 'block' : 'none'; +} + +async function submitActionEditInventory(e, id, productId) { + e.preventDefault(); + const qty = parseFloat(document.getElementById('action-edit-qty').value); + const loc = document.getElementById('action-edit-loc').value; + const expiry = document.getElementById('action-edit-expiry').value || null; + const unit = document.getElementById('action-edit-unit').value; + + const payload = { id, quantity: qty, location: loc, expiry_date: expiry, unit, product_id: productId }; + + if (unit === 'conf') { + payload.package_unit = document.getElementById('action-edit-conf-unit')?.value || ''; + payload.package_size = parseFloat(document.getElementById('action-edit-conf-size')?.value) || 0; + } else { + payload.package_unit = ''; + payload.package_size = 0; + } + + await api('inventory_update', {}, 'POST', payload); + closeModal(); + showToast('Aggiornato!', 'success'); + showProductAction(); // Refresh the action page +} + +async function deleteActionInventoryItem(id) { + if (confirm('Vuoi davvero rimuovere questo prodotto dall\'inventario?')) { + await api('inventory_delete', {}, 'POST', { id }); + closeModal(); + showToast('Prodotto rimosso', 'success'); + showProductAction(); // Refresh the action page + } +} + // === THROW AWAY FORM === function showThrowForm() { // Open a modal to ask how much to throw away @@ -2700,7 +2812,7 @@ function showAddForm() { const unitSelect = document.getElementById('add-unit'); unitSelect.value = unit; - document.getElementById('add-quantity').value = unit === 'conf' ? (currentProduct.last_qty || 1) : (currentProduct.default_quantity || 1); + document.getElementById('add-quantity').value = unit === 'conf' ? (currentProduct._confCount || currentProduct.last_qty || 1) : (currentProduct.default_quantity || 1); document.getElementById('add-quantity').dataset.manuallySet = 'false'; // Show/hide conf size row and pre-fill diff --git a/data/dispensa.db b/data/dispensa.db index 4461de66ec044ebff2e41ce8156c32278d3a1c75..010997be58093b455f8d1bcfe6f65f95391bfcfe 100644 GIT binary patch delta 2460 zcmdT_-EY%Y6u2@R?(2np?jufoFw4`Z4pHW*_Y3~3;kCW?l3uamaW_8-{O zvCld8ch3FYdw-|ru&d{=>oXfinfA4YgR3mI%Pzp~b+@>$vP+%1`$PLKBgid6_798{ z7jxN@eHEKH;2H$NVeu~d5Kw6Kvg3xB_-2R zR8!Ps8eB56Ew%8#$j>d+h15mY?GZ&uJTd zmmW3F_Jo}D4f-m5nm&OsKXn>oeXd0ZTvQo~rIfe|`WdPMZC27x4uW!FD~AKT#H=0E?! zg#|b(C}eA&z-t}U`?gJ{qaEGy+8)||w0&bcYCB|m*R}~C*k;OPUO}@mj zJU87p+zM=UEumYTJ&uTdS3x251=>BL({o{QZ)=7HwTtI;=wd0-R|^7b{;r@S#-S57 zvwthvZMWEoXaDL%zd5rd+t3b}y_rB&)C-017+@EBdzm4R$L*p|(1NqU;k7pwJWt)B zcB0pj10KUQ_Q1>ry^I01b<7;5$jJx-^Ng~#Vdaa;+lAM+kl9thwsFGCTlTRam6+)3|*OIt; zEA&##Np)SVF0x2pp6hs0@?VqUQcB{sD{?|!iOX~+<4Qb%m0OmRk+)K^v@Is3we%KE z>qcWo*F(fHhi&^n$+Hmr5Z!V@!MtQsWp%@N4KKlOg;A!?qBXs zsa2+?^OEBXN{<1tV&&;QFU2=3ArzE-cgnC8A zdx&?ZH5C_0YFnhuN>YtVsiYi-`4>I$1jXr3khpq9eHJDxTwSGyS*QNCMc7l*uYHS> zkT%PSt}TkhsaR8$q?9mEm%`=7^)t3QJSKg3PWs58%=4mXjp3$7JbN3rcE#iEa$1dW z0nxnD0q2>AVu&^0n2qP*N5{SH4RT!8q?95hRpZGY&n?Tzr4Zr8DNGTq=ymADxY5cv zjCI==?}~em^P87DV2Syqgxtng zx441ZbLZvS@J9rGN*hFe;@QS`&{dlAGN%B0mHmX(*;iSfb$YLRKlk=~JH27AoB4$~ zg;i&mDXi94VSy`xWegq@N|HA39YiIiM22egl_Y{lWLQbU6;`N-2vMEB!U|R3(2&{r zJuEd_Zoq=smYmn<%O}-1$4>x{{DMIw=;8hxn~z#&AN$=q&f delta 469 zcmZozz}2vTYXge{^F+4oli3yI`M4PPJGkw+ukpKbG;+_JY@uK`xk4e0jh~;7k*|L8 z3k8kMLW&No6BiV2-mdmWLS&*KE3+z7Nl{{6abj{wW`15V!{N!>?bR9EH{Z8c6<|KZ zt+1IrAc1jmW}xb3MS&@tn-!v;u#jV0nlC4FC9m*g_JYqqgCu!23)g;RVy@);HJQC( z@8lB=W{VXCIGcZUZvWNE$d$${$r&=8{R5*hP^}c__K=^9i&z-HO#l9uQF}W7KStK= z>i-z;1~M11@=j+DV0sEvrN}ZpE`&*VyFw__LoQXGJ_de<_Y8dJcrWs%@jT}4;TGY% z%CUic4ci;mWh}3lYnYT6-cN4iJh{C&iRl@KP?JqPKZClnWMHEyKU1Txu`%!TphTu6 zldE^hPWHboJz3!*=Va>>+>;evb59RQV$x+bvam8V+Mb!sRK>``z`zhXabn>1$2m-j z%o1E|dJO!|d`-Mld3dDispensa Manager - + @@ -884,6 +884,6 @@ - +