Aggiunta ricerca rapida per nome prodotto (frutta/verdura sfusa)
This commit is contained in:
@@ -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;
|
||||
|
||||
+146
-1
@@ -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 = `
|
||||
<span class="qnr-icon">${catIcon}</span>
|
||||
<div class="qnr-info">
|
||||
<div class="qnr-name">${escapeHtml(p.name)}</div>
|
||||
<div class="qnr-detail">${p.brand ? escapeHtml(p.brand) + ' · ' : ''}${p.barcode ? '📊 ' + p.barcode : 'Senza barcode'}</div>
|
||||
</div>
|
||||
`;
|
||||
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 = `
|
||||
<span class="qnr-icon">➕</span>
|
||||
<div class="qnr-info">
|
||||
<div class="qnr-name">Crea "${escapeHtml(searchName)}"</div>
|
||||
<div class="qnr-detail">Nuovo prodotto senza barcode</div>
|
||||
</div>
|
||||
`;
|
||||
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
|
||||
|
||||
Binary file not shown.
+8
-1
@@ -123,6 +123,13 @@
|
||||
<button class="btn btn-primary" onclick="submitManualBarcode()">🔍 Cerca</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quick-name-entry">
|
||||
<div class="quick-name-divider"><span>oppure scrivi il nome</span></div>
|
||||
<div class="barcode-input-row">
|
||||
<input type="text" id="quick-product-name" class="form-input" placeholder="Es: Mele, Zucchine, Pane..." list="common-products" autocomplete="off" onkeydown="if(event.key==='Enter')submitQuickName()">
|
||||
<button class="btn btn-accent" onclick="submitQuickName()">✅ Vai</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scan-actions">
|
||||
<button class="btn btn-large btn-secondary" onclick="startManualEntry()">
|
||||
✏️ Inserimento Manuale
|
||||
@@ -131,7 +138,7 @@
|
||||
🤖 Identifica con AI
|
||||
</button>
|
||||
</div>
|
||||
<p class="scan-hint">Inquadra il codice a barre del prodotto oppure inseriscilo manualmente</p>
|
||||
<p class="scan-hint">Scansiona il barcode, scrivi il nome del prodotto, oppure usa l'AI per identificarlo</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user