diff --git a/api/index.php b/api/index.php index a239ce4..db28f13 100644 --- a/api/index.php +++ b/api/index.php @@ -141,17 +141,37 @@ function lookupBarcode(): void { $p = $data['product']; // Prefer Italian name, fall back to generic + // Also request localized name via abbreviated_product_name $name = ''; if (!empty($p['product_name_it'])) { $name = $p['product_name_it']; - } elseif (!empty($p['product_name'])) { - $name = $p['product_name']; } elseif (!empty($p['generic_name_it'])) { $name = $p['generic_name_it']; + } elseif (!empty($p['product_name'])) { + $name = $p['product_name']; } elseif (!empty($p['generic_name'])) { $name = $p['generic_name']; } + // If the name looks like it's in a non-Latin script (Arabic, Chinese, Thai, etc.) + // try to use a fallback from brands + generic category + if (!empty($name) && preg_match('/[\x{0600}-\x{06FF}\x{0E00}-\x{0E7F}\x{4E00}-\x{9FFF}\x{3040}-\x{30FF}\x{AC00}-\x{D7AF}\x{0400}-\x{04FF}]/u', $name)) { + // Try other name fields that might be in Latin script + $latinName = ''; + foreach (['generic_name_it', 'generic_name', 'product_name_it', 'product_name'] as $field) { + if (!empty($p[$field]) && !preg_match('/[\x{0600}-\x{06FF}\x{0E00}-\x{0E7F}\x{4E00}-\x{9FFF}\x{3040}-\x{30FF}\x{AC00}-\x{D7AF}\x{0400}-\x{04FF}]/u', $p[$field])) { + $latinName = $p[$field]; + break; + } + } + // If still no Latin name, construct from brand + category + if (empty($latinName)) { + $brand = $p['brands'] ?? ''; + $latinName = !empty($brand) ? $brand : 'Prodotto sconosciuto'; + } + $name = $latinName; + } + // Get Italian ingredients, fall back to generic $ingredients = ''; if (!empty($p['ingredients_text_it'])) { diff --git a/assets/js/app.js b/assets/js/app.js index 216b45c..83ee710 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -490,7 +490,7 @@ function editInventoryItem(id) {
- +
@@ -746,6 +746,15 @@ function startManualEntry(barcode = '') { document.getElementById('pf-image-preview').style.display = 'none'; document.getElementById('product-form-title').textContent = 'Nuovo Prodotto'; + // Reset manual-edit tracking flags + document.getElementById('pf-category').dataset.manuallySet = 'false'; + document.getElementById('pf-defqty').dataset.manuallySet = 'false'; + + // Track if user manually changes the quantity field + const qtyInput = document.getElementById('pf-defqty'); + qtyInput.removeEventListener('input', markQtyManuallySet); + qtyInput.addEventListener('input', markQtyManuallySet); + // Auto-detect name → category when typing const nameInput = document.getElementById('pf-name'); nameInput.removeEventListener('input', autoDetectCategory); @@ -754,6 +763,10 @@ function startManualEntry(barcode = '') { showPage('product-form'); } +function markQtyManuallySet() { + document.getElementById('pf-defqty').dataset.manuallySet = 'true'; +} + function autoDetectCategory() { const name = document.getElementById('pf-name').value.toLowerCase(); if (name.length < 3) return; @@ -796,23 +809,26 @@ function autoDetectCategory() { for (const [keyword, cat] of Object.entries(keyword2cat)) { if (name.includes(keyword)) { catSelect.value = cat; - onCategoryChange(); + onCategoryChange(true); return; } } } -function onCategoryChange() { +function onCategoryChange(fromAutoDetect = false) { const cat = document.getElementById('pf-category').value; const unitSelect = document.getElementById('pf-unit'); const qtyInput = document.getElementById('pf-defqty'); - // Mark as manually set if triggered by user click - if (event && event.isTrusted) { - document.getElementById('pf-category').dataset.manuallySet = 'true'; + // If user manually changed category via dropdown, don't auto-fill qty/unit + if (!fromAutoDetect) { + // Mark qty as "set" so future auto-detects won't overwrite either + qtyInput.dataset.manuallySet = 'true'; + return; } - // Suggest default unit/qty based on category + // Auto-detect from name: suggest default unit/qty based on category + // BUT only if user hasn't manually changed the quantity field const catDefaults = { 'latticini': { unit: 'pz', qty: 1 }, 'carne': { unit: 'g', qty: 500 }, @@ -832,8 +848,11 @@ function onCategoryChange() { }; if (catDefaults[cat]) { - unitSelect.value = catDefaults[cat].unit; - qtyInput.value = catDefaults[cat].qty; + // Only auto-fill unit/qty if user hasn't manually touched them + if (qtyInput.dataset.manuallySet !== 'true') { + unitSelect.value = catDefaults[cat].unit; + qtyInput.value = catDefaults[cat].qty; + } } } @@ -1002,6 +1021,12 @@ function showAddForm() { unitSelect.value = unit; document.getElementById('add-quantity').value = currentProduct.default_quantity || 1; + document.getElementById('add-quantity').dataset.manuallySet = 'false'; + + // Track manual edits to quantity in add form + const addQtyInput = document.getElementById('add-quantity'); + addQtyInput.removeEventListener('input', markAddQtyManuallySet); + addQtyInput.addEventListener('input', markAddQtyManuallySet); // Show weight info if product has it const weightInfoEl = document.getElementById('add-weight-info'); @@ -1061,8 +1086,11 @@ function showAddForm() { function onAddUnitChange() { updateAddQtyStep(); // If switching units, suggest a sensible quantity + // BUT only if the user hasn't manually changed the quantity in this form const unit = document.getElementById('add-unit').value; const qtyInput = document.getElementById('add-quantity'); + if (qtyInput.dataset.manuallySet === 'true') return; // User already edited qty, don't overwrite + const currentQty = parseFloat(qtyInput.value) || 1; // Convert between related units if logical @@ -1077,28 +1105,38 @@ function onAddUnitChange() { function updateAddQtyStep() { const qtyInput = document.getElementById('add-quantity'); const unit = document.getElementById('add-unit').value; + qtyInput.step = 'any'; if (unit === 'g' || unit === 'ml') { - qtyInput.step = '25'; qtyInput.min = '1'; } else if (unit === 'kg' || unit === 'l') { - qtyInput.step = '0.25'; qtyInput.min = '0.1'; } else { - qtyInput.step = '1'; qtyInput.min = '1'; } } +function markAddQtyManuallySet() { + document.getElementById('add-quantity').dataset.manuallySet = 'true'; +} + function adjustAddQty(delta) { const qtyInput = document.getElementById('add-quantity'); + qtyInput.dataset.manuallySet = 'true'; // +/- buttons count as manual edit const unit = document.getElementById('add-unit').value; - let step; - if (unit === 'g' || unit === 'ml') step = 25; - else if (unit === 'kg' || unit === 'l') step = 0.25; - else step = 1; let val = parseFloat(qtyInput.value) || 0; + let step; + if (unit === 'kg' || unit === 'l') { + step = val < 1 ? 0.1 : 0.5; + } else if (unit === 'g' || unit === 'ml') { + step = val < 50 ? 1 : (val < 500 ? 10 : 50); + } else { + step = 1; + } val = Math.max(parseFloat(qtyInput.min) || 0.1, val + delta * step); - qtyInput.value = Math.round(val * 100) / 100; + // Round nicely + if (step >= 1) val = Math.round(val); + else val = Math.round(val * 10) / 10; + qtyInput.value = val; } function selectPurchaseType(btn, type, estimatedDate, estimateLabel) { @@ -1107,6 +1145,9 @@ function selectPurchaseType(btn, type, estimatedDate, estimateLabel) { const detailDiv = document.getElementById('expiry-detail'); + // Save current quantity before switching, so we can preserve it + const currentQty = document.getElementById('add-quantity').value; + if (type === 'new') { detailDiv.innerHTML = `
@@ -1119,6 +1160,8 @@ function selectPurchaseType(btn, type, estimatedDate, estimateLabel) {

📝 Puoi modificare la data o scansionarla con la fotocamera

`; + // Restore quantity - switching purchase type should NOT change it + document.getElementById('add-quantity').value = currentQty; } else { detailDiv.innerHTML = `
@@ -1134,14 +1177,13 @@ function selectPurchaseType(btn, type, estimatedDate, estimateLabel) {

Quanto è rimasto approssimativamente?

- +
`; - // Pre-set to 75% - setRemainingPct(0.75); + // DON'T auto-set remaining percentage - keep the quantity the user already entered } } @@ -1526,16 +1568,17 @@ async function scanExpiryWithAI() {
-
- +
+ +
-