diff --git a/api/index.php b/api/index.php index 427423f..a4c3351 100644 --- a/api/index.php +++ b/api/index.php @@ -764,7 +764,24 @@ function useFromInventory(PDO $db): void { } } - $response = ['success' => true, 'remaining' => $remaining, 'added_to_bring' => $addedToBring]; + // Calculate total remaining across ALL locations + $stmt = $db->prepare("SELECT SUM(quantity) as total FROM inventory WHERE product_id = ? AND quantity > 0"); + $stmt->execute([$productId]); + $totalRemaining = round((float)($stmt->fetchColumn() ?: 0), 6); + + // Get product info for low-stock prompt + $stmt = $db->prepare("SELECT name, unit, default_quantity, package_unit FROM products WHERE id = ?"); + $stmt->execute([$productId]); + $prodInfo = $stmt->fetch(); + + $response = ['success' => true, 'remaining' => $remaining, 'added_to_bring' => $addedToBring, + 'total_remaining' => $totalRemaining]; + if ($prodInfo) { + $response['product_name'] = $prodInfo['name']; + $response['product_unit'] = $prodInfo['unit']; + $response['product_default_qty'] = (float)($prodInfo['default_quantity'] ?: 0); + $response['product_package_unit'] = $prodInfo['package_unit'] ?: ''; + } if ($openedId) $response['opened_id'] = $openedId; echo json_encode($response); } diff --git a/assets/js/app.js b/assets/js/app.js index 159409b..06a77e4 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -3482,6 +3482,88 @@ function selectUseLocation(btn, loc) { document.getElementById('use-location').value = loc; } +// ===== LOW STOCK → BRING! PROMPT ===== +function isLowStock(totalRemaining, unit, defaultQty) { + if (totalRemaining <= 0) return false; // already fully depleted → auto-added + if (unit === 'pz') return totalRemaining <= 2; + if (unit === 'conf') return totalRemaining <= 1; + // Weight/volume: use percentage of default_qty or fixed threshold + if (defaultQty > 0) return totalRemaining <= defaultQty * 0.25; + // Fallback fixed thresholds + if (unit === 'g' || unit === 'ml') return totalRemaining <= 100; + if (unit === 'kg' || unit === 'l') return totalRemaining <= 0.15; + return false; +} + +function showLowStockBringPrompt(result, afterCallback) { + const name = result.product_name || currentProduct?.name || ''; + const unit = result.product_unit || currentProduct?.unit || 'pz'; + const defaultQty = result.product_default_qty || parseFloat(currentProduct?.default_quantity) || 0; + const totalRemaining = result.total_remaining; + + if (!isLowStock(totalRemaining, unit, defaultQty)) { + if (afterCallback) afterCallback(); + return; + } + + // Format remaining for display + let remainLabel = ''; + if (unit === 'conf' && result.product_package_unit) { + const subTotal = Math.round(totalRemaining * defaultQty); + remainLabel = `${subTotal}${result.product_package_unit}`; + } else { + const unitLabels = { pz: 'pz', g: 'g', kg: 'kg', ml: 'ml', l: 'L', conf: 'conf' }; + remainLabel = `${Number.isInteger(totalRemaining) ? totalRemaining : totalRemaining.toFixed(1)} ${unitLabels[unit] || unit}`; + } + + // Store callback for after user responds + window._lowStockAfterCallback = afterCallback; + + document.getElementById('modal-content').innerHTML = ` + +
+

${escapeHtml(name)} sta per finire — rimangono solo ${remainLabel}.

+

Vuoi aggiungerlo alla lista della spesa?

+ + +
+ `; + document.getElementById('modal-overlay').style.display = 'flex'; +} + +async function addLowStockToBring(productName) { + closeModal(); + try { + const payload = { items: [{ name: productName }] }; + if (shoppingListUUID) payload.listUUID = shoppingListUUID; + const data = await api('bring_add', {}, 'POST', payload); + if (data.success && data.added > 0) { + showToast('🛒 Aggiunto alla lista della spesa!', 'success'); + } else if (data.success && data.skipped > 0) { + showToast('ℹ️ Già nella lista della spesa', 'info'); + } + } catch (e) { + showToast('Errore nell\'aggiunta a Bring!', 'error'); + } + const cb = window._lowStockAfterCallback; + window._lowStockAfterCallback = null; + if (cb) cb(); +} + +function closeLowStockPrompt() { + closeModal(); + const cb = window._lowStockAfterCallback; + window._lowStockAfterCallback = null; + if (cb) cb(); +} + function showMoveAfterUseModal(product, fromLoc, remaining, openedId) { const otherLocs = Object.entries(LOCATIONS).filter(([k]) => k !== fromLoc); const locButtons = otherLocs.map(([k, v]) => @@ -3555,7 +3637,8 @@ async function submitUseAll() { if (result.added_to_bring) { setTimeout(() => showToast('🛒 Prodotto finito → aggiunto a Bring!', 'info'), 1500); } - showPage('dashboard'); + // Check low stock (product may exist at other locations) + showLowStockBringPrompt(result, () => showPage('dashboard')); } else { showToast(result.error || 'Errore', 'error'); } @@ -3595,11 +3678,11 @@ async function submitUse(e) { } // If there's remaining quantity, offer to move to another location const usedFrom = document.getElementById('use-location').value; - if (result.remaining > 0) { - showMoveAfterUseModal(currentProduct, usedFrom, result.remaining, result.opened_id); - } else { - showPage('dashboard'); - } + const moveCallback = result.remaining > 0 + ? () => showMoveAfterUseModal(currentProduct, usedFrom, result.remaining, result.opened_id) + : () => showPage('dashboard'); + // Check low stock → Bring! prompt + showLowStockBringPrompt(result, moveCallback); } else { showToast(result.error || 'Errore', 'error'); } @@ -5149,10 +5232,11 @@ async function submitRecipeUse(useAll) { setTimeout(() => showToast('🛒 Prodotto finito → aggiunto a Bring!', 'info'), 1500); } - // Offer to move opened package (stays on recipe page, modal over recipe overlay) - if (result.remaining > 0) { - setTimeout(() => showRecipeMoveModal(productId, location, result.remaining, result.opened_id), 400); - } + // Check low stock → Bring! prompt, then offer move + const moveCallback = result.remaining > 0 + ? () => setTimeout(() => showRecipeMoveModal(productId, location, result.remaining, result.opened_id), 300) + : null; + setTimeout(() => showLowStockBringPrompt(result, moveCallback), 300); } else { btn.disabled = false; btn.textContent = '📦 Usa'; diff --git a/data/dispensa.db b/data/dispensa.db index fd85752..86a1119 100644 Binary files a/data/dispensa.db and b/data/dispensa.db differ diff --git a/index.html b/index.html index d46c342..53fbdce 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ Dispensa Manager - + @@ -911,6 +911,6 @@ - +