diff --git a/assets/css/style.css b/assets/css/style.css index 70cdc66..85db746 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -879,6 +879,89 @@ body { flex-shrink: 0; } +.quick-name-entry { + margin-bottom: 12px; +} + +.quick-name-divider { + text-align: center; + margin: 10px 0 8px; + position: relative; +} + +.quick-name-divider::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + right: 0; + height: 1px; + background: var(--border); +} + +.quick-name-divider span { + background: var(--bg-main); + padding: 0 12px; + position: relative; + font-size: 0.85rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.quick-name-results { + margin-top: 8px; + display: flex; + flex-direction: column; + gap: 6px; + max-height: 200px; + overflow-y: auto; +} + +.quick-name-result-item { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + background: var(--bg-card); + border-radius: var(--radius); + box-shadow: var(--shadow); + cursor: pointer; + transition: transform 0.1s; +} + +.quick-name-result-item:active { + transform: scale(0.98); +} + +.quick-name-result-item .qnr-icon { + font-size: 1.5rem; + flex-shrink: 0; +} + +.quick-name-result-item .qnr-info { + flex: 1; + min-width: 0; +} + +.quick-name-result-item .qnr-name { + font-weight: 600; + font-size: 0.95rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.quick-name-result-item .qnr-detail { + font-size: 0.8rem; + color: var(--text-muted); +} + +.quick-name-result-item.qnr-new { + border: 1px dashed var(--accent); + background: rgba(124, 58, 237, 0.06); +} + .scan-hint { text-align: center; font-size: 0.85rem; diff --git a/assets/js/app.js b/assets/js/app.js index 18eec58..2a15b75 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -328,7 +328,7 @@ function showPage(pageId, param = null) { } loadInventory(); break; - case 'scan': initScanner(); break; + case 'scan': initScanner(); clearQuickNameResults(); break; case 'products': loadAllProducts(); break; case 'ai': initAICamera(); break; } @@ -1019,6 +1019,151 @@ function submitManualBarcode() { onBarcodeDetected(barcode); } +// ===== QUICK NAME ENTRY (for loose/unpackaged products) ===== +async function submitQuickName() { + const input = document.getElementById('quick-product-name'); + const name = (input.value || '').trim(); + if (!name || name.length < 2) { + showToast('Scrivi almeno 2 caratteri', 'error'); + input.focus(); + return; + } + + stopScanner(); + showLoading(true); + + try { + // Search local products DB + const localData = await api('products_search', { q: name }); + const localProducts = (localData.products || []).slice(0, 5); + + showLoading(false); + + if (localProducts.length > 0) { + // Show results to pick from + option to create new + showQuickNameResults(name, localProducts); + } else { + // No local results โ€” create new product directly + await createQuickProduct(name); + } + } catch (err) { + showLoading(false); + console.error('Quick name search error:', err); + showToast('Errore nella ricerca', 'error'); + } +} + +function showQuickNameResults(searchName, products) { + const container = document.querySelector('.quick-name-entry'); + + // Remove any previous results + const oldResults = container.querySelector('.quick-name-results'); + if (oldResults) oldResults.remove(); + + const resultsDiv = document.createElement('div'); + resultsDiv.className = 'quick-name-results'; + + // Existing products + products.forEach(p => { + const catIcon = CATEGORY_ICONS[mapToLocalCategory(p.category, p.name)] || '๐Ÿ“ฆ'; + const item = document.createElement('div'); + item.className = 'quick-name-result-item'; + item.innerHTML = ` + ${catIcon} +
+
${escapeHtml(p.name)}
+
${p.brand ? escapeHtml(p.brand) + ' ยท ' : ''}${p.barcode ? '๐Ÿ“Š ' + p.barcode : 'Senza barcode'}
+
+ `; + item.onclick = () => selectQuickProduct(p); + resultsDiv.appendChild(item); + }); + + // "Create new" button + const newItem = document.createElement('div'); + newItem.className = 'quick-name-result-item qnr-new'; + newItem.innerHTML = ` + โž• +
+
Crea "${escapeHtml(searchName)}"
+
Nuovo prodotto senza barcode
+
+ `; + newItem.onclick = () => createQuickProduct(searchName); + resultsDiv.appendChild(newItem); + + container.appendChild(resultsDiv); +} + +function selectQuickProduct(product) { + currentProduct = { + id: product.id, + barcode: product.barcode || '', + name: product.name, + brand: product.brand || '', + category: product.category || '', + image_url: product.image_url || '', + unit: product.unit || 'pz', + default_quantity: product.default_quantity || 1, + }; + // Extract weight_info from notes if available + if (product.notes) { + const pesoMatch = product.notes.match(/Peso:\s*([^ยท]+)/); + if (pesoMatch) currentProduct.weight_info = pesoMatch[1].trim(); + } + clearQuickNameResults(); + showProductAction(); +} + +async function createQuickProduct(name) { + showLoading(true); + + // Auto-detect category from name + const category = guessCategoryFromName(name); + + try { + const result = await api('product_save', {}, 'POST', { + name: name, + brand: '', + category: category, + unit: 'pz', + default_quantity: 1, + }); + + if (result.success || result.id) { + currentProduct = { + id: result.id, + name: name, + brand: '', + category: category, + unit: 'pz', + default_quantity: 1, + }; + showLoading(false); + clearQuickNameResults(); + showToast('Prodotto creato!', 'success'); + showProductAction(); + } else { + showLoading(false); + showToast(result.error || 'Errore nel salvataggio', 'error'); + } + } catch (err) { + showLoading(false); + console.error('Quick product creation error:', err); + showToast('Errore di connessione', 'error'); + } +} + +function clearQuickNameResults() { + const container = document.querySelector('.quick-name-entry'); + if (container) { + const results = container.querySelector('.quick-name-results'); + if (results) results.remove(); + } + const input = document.getElementById('quick-product-name'); + if (input) input.value = ''; +} + function startManualEntry(barcode = '') { stopScanner(); // Reset form diff --git a/data/dispensa.db b/data/dispensa.db index d4793b8..43d77c1 100644 Binary files a/data/dispensa.db and b/data/dispensa.db differ diff --git a/index.html b/index.html index 2ddd009..d080a16 100644 --- a/index.html +++ b/index.html @@ -123,6 +123,13 @@ +
+
oppure scrivi il nome
+
+ + +
+
-

Inquadra il codice a barre del prodotto oppure inseriscilo manualmente

+

Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo