1156 Commits

Author SHA1 Message Date
dadaloop82 dcc7e9de42 Fix smart shopping: skip depleted products with equivalent in-stock substitutes
PHP smartShopping():
- Add nameTokens() helper (mirrors JS _nameTokens)
- Build stockByFirstToken map before product loop
- Skip depleted (qty=0) products whose first token has stock elsewhere
  → 'Aglio rosso' depleted but 'Aglio' qty=3 → skip
  → 'Latte Parzialmente Scremato' depleted but 'Latte di Montagna' 4.8 conf → skip
  → 'Muesli Frutta Secca' depleted but 'Muesli multifrutta' 930g → skip
- Result: 13→9 items, no more false critical flagging for covered products

JS cleanupObsoleteBringItems():
- Rewrite with stockByFirstToken approach (aggregate by first token, not product_id)
- urgentMatch logic: if smart item is completely depleted (qty=0) but equivalent
  stock exists via first token → still remove from Bring (need is covered)
- Only keep Bring item if: smart flags it with current_qty>0 (genuinely running low)

Also: removed Milch/Knoblauch/Fruechte/Passata from Bring directly (immediate fix)
2026-04-07 15:20:33 +00:00
dadaloop82 0bca79b8a2 Fix 3 bugs: banana use blocked, cleanup never ran, stale Bring items
1. Use form pz step bug (banana non scalabile):
   - min=0.25 + step=0.5 → solo 0.75, 1.25, 1.75... validi per browser
   - 1 intero (default) era INVALIDO → form bloccato silenziosamente
   - Fix: step='any', min=0.01 (il passo logico resta in adjustUseQty)

2. cleanupObsoleteBringItems mai eseguita:
   - Usava products_list che non ha campo quantity → (qty||0)<=0 sempre vero → skip sempre
   - Fix: usa inventory_list che ha le qty reali per location

3. cleanupObsoleteBringItems troppo rara:
   - sessionStorage → una sola volta per sessione
   - Fix: localStorage con TTL 30 minuti
   - Ora rimuove da Bring qualsiasi item che ha scorte in inventario
     e NON è flaggato come critical/high dalla spesa intelligente
2026-04-07 15:08:03 +00:00
dadaloop82 9b51bb606d Fix smart shopping false positives (prodotti appena comprati/sufficienti)
PHP smartShopping():
- Absolute minimum fallback: requires isRegular + buyCount>=2 + pctLeft<80
  (before: ANY product with buyCount>=1 → triggered for newly bought items)
- Add justRestocked guard: skip item if bought within 3 days AND pctLeft>=50%
  and not expiring (prevents items bought yesterday showing as urgent)
- Add daysSinceLastBuy calculation

JS isLowStock():
- pz threshold: <=1 (was <=2) — 2 pezzi rimasti non è già urgente
2026-04-07 15:02:15 +00:00
dadaloop82 72535ce41c UX: fraction buttons for pz unit in use form + fix qty display
- _pzFractionLabel(): formats 2.5 → '2½', 2.25 → '2¼', etc.
- formatQuantity/formatQuantityParts: use fraction label for pz (was rounding to int)
- loadUseInventoryInfo normal mode: show ¼/½/¾/1 intero quick-select buttons for pz
- setPzFraction(): sets quantity input + syncs active button highlight
- adjustUseQty pz: step = 0.5 (half-piece) instead of 1; syncs frac button on ±
- CSS: .pz-fraction-btns, .fraction-btn-row, .frac-btn, .frac-btn.active
2026-04-07 13:03:32 +00:00
dadaloop82 dc36ce2ae4 fix: MODIFICA button now edits inventory instance (expiry/location/qty/vacuum)
The MODIFICA button on the action page was opening the catalog editor
(name/brand/category). Users expect it to edit the physical item in hand.

Changes:
- MODIFICA button → openInventoryEdit(): edits the inventory row directly.
  If product is in one location → opens editActionInventoryItem directly.
  If multiple locations → shows a location picker modal first.
- editActionInventoryItem modal already has: qty ±, unit, conf size, location
  buttons, expiry date, vacuum toggle — all fields for the instance.
- Catalog editing (name/brand/category) moved to a small secondary link
  '⚙️ Modifica scheda prodotto' shown discreetly below the action buttons.
- Removed redundant 'Tocca una riga per modificare' hint from status bar.
- Added .btn-link-small CSS class for the secondary catalog-edit link.
2026-04-07 12:52:31 +00:00
dadaloop82 4e576559a9 feat: barcode scan button + reminder in manual product form
- Add 📷 scan button next to the barcode field in product form
  Opens a camera modal (BarcodeDetector if available, manual fallback)
  Detects barcode after 2 consistent frames, fills field and closes modal
- Show ⚠️ hint below the barcode field when it's empty (new products only):
  'Aggiungi il barcode così al prossimo acquisto basta scansionarlo!'
  Hint hides automatically when a code is entered or scanned
- Hint is hidden in edit-product mode (barcode already saved)
- scanBarcodeForForm() reuses the modal overlay; handles camera permission
  errors gracefully (shows manual input only)
2026-04-07 12:17:18 +00:00
dadaloop82 b7ed9899fa feat+fix: Bring removal, multi-expiry batches, FIFO in cooking steps
BRING! REMOVAL FIX (latte/aglio not removed after shopping):
- PHP addToInventory: replace exact strcasecmp with token-based fuzzy
  matching (same logic as _productOnBring) so custom Bring item names
  and translated catalog keys both match correctly
- JS submitAdd: add client-side fallback — if PHP removal missed the item,
  use _findSimilarItem against the loaded shoppingItems and call bring_remove

MULTI-EXPIRY BATCHES (when buying conf with different expiry dates):
- Add form (unit=conf): shows '+ Lotto con scadenza diversa' button
- Each extra batch has its own qty + expiry date input with +/- controls
- On submitAdd, extra batches are submitted as additional inventory_add calls
  (separate DB rows, separate expiry dates)
- Multi-batch section hidden in 'Ce l'avevo già' mode and for non-conf units
- Re-shown/hidden when switching unit via onAddUnitChange

RECIPE COOKING STEPS - FIFO ingredient display:
- renderCookingStep: each ingredient row now shows brand chip, location chip,
  and expiry date chip (color-coded: red ≤3d, yellow ≤7d)
- PHP already selected earliest-expiry inventory entry (ORDER BY days_left ASC
  with > not >= ensures first/earliest match wins)
- CSS: .cooking-ing-meta, .cooking-ing-chip, .exp-soon, .exp-close,
  .multi-batch-row, .multi-batch-qty, .multi-batch-date, .btn-icon-sm
2026-04-07 12:10:14 +00:00
dadaloop82 22ae3abf47 📦 Backup database automatico - 2026-04-07 03:00 2026-04-07 03:00:01 +00:00
dadaloop82 5be62cfbfd fix: low stock detection for rarely-used items
- PHP smart_shopping: add absolute stock fallback that flags conf/pz items
  with <=2 units (medium) or <=1 unit (high) and g/ml at <=20% of default,
  regardless of usage frequency. Fixes products like Panna da cucina that
  are rarely used but running low and were invisible to the frequency-based
  urgency logic (pctLeft was 66% since last purchase was 3 at once).
- JS isLowStock(): return true (not false) when totalRemaining <= 0.
  A fully depleted item is definitely low-stock; the Bring! add prompt
  should fire when you use the very last unit.
2026-04-06 10:53:15 +00:00
dadaloop82 6424f381af fix: TTS only on Rileggi; use-all deducts all locations; fix DB permissions 2026-04-06 10:28:24 +00:00
dadaloop82 b47dcb4fac fix: TTS only on Rileggi btn; use-all deducts from all locations
Cooking mode TTS:
- Removed auto-speak from renderCookingStep() entirely
- TTS now fires ONLY when user presses 'Rileggi' (replayCookingTTS)
- Timer-expiry TTS unchanged (still speaks when a cooking timer expires)

submitUseAll fix:
- Changed location from selected-location to '__all__'
- 'Usato TUTTO / Finito' means the product is completely consumed;
  using a specific location could fail with a 404 if the async
  loadUseInventoryInfo() hadn't yet updated the selector (race condition)
- The __all__ path in PHP removes inventory across every location
2026-04-06 10:23:03 +00:00
dadaloop82 4e8b586201 feat: AI photo identification from product form
When creating a new product (manual entry), a '📷 Scatta foto e identifica con AI'
button appears at the top of the form. Tapping it:
1. Opens a camera modal (same pattern as expiry scanner)
2. User takes photo of product/label
3. Sends to gemini_identify — returns name, brand, category + OpenFoodFacts matches
4. User can pick a specific OFF match (fills barcode + full details via lookup_barcode)
   or tap 'Usa dati AI' to fill just name/brand/category from Gemini
5. All matching fields are auto-filled: name, brand, category, barcode, image, unit/qty
6. Button hidden when editing an existing product (not needed)
2026-04-06 09:23:41 +00:00
dadaloop82 a6bc05cd2d feat: spesa mode stats banner + scan zoom x1/x2 toggle
Spesa mode banner:
- Tracks each added product in _spesaSession[]
- Shows a rotating stat/phrase below the title: count, top category,
  duplicates, fun milestone messages (primo prodotto, ottimo ritmo, spesa epica…)
- Banner gains two-line layout (title + stat)

Scan zoom:
- Small pill button 'x1'/'x2' overlaid top-right of the camera viewport
- On hardware-zoom capable devices (Android Chrome) uses track.applyConstraints zoom
- Falls back to CSS scale(2) on video element for all other browsers
- Zoom resets to x1 on stopScanner()
2026-04-06 09:16:50 +00:00
dadaloop82 7782eb1519 fix: pre-fill conf size from product's weight/volume unit when switching to 'confezioni'
If a product was created with unit='g' (or ml/kg/l) and a default_quantity,
that value already IS the package size — no need to ask again.
Applied in both showAddForm() initial render and onAddUnitChange() toggle.
2026-04-06 09:10:29 +00:00
dadaloop82 50da545c72 feat: predict expiry date from product history when adding items
- PHP: new 'expiry_history' action computes avg shelf life (expiry_date - added_at)
  from inventory table for the same product_id (last 730 days, valid entries only)
- JS: _fetchExpiryHistoryAndUpdate() fires async after showAddForm() renders
  and replaces the rule-based estimate with the historical average if available
- Labeled with '📊 storico' badge on the estimate line (tooltip shows sample count)
- recalculateAddExpiry() and selectPurchaseType('new') both honour window._historyExpiryDays
- Vacuum-sealed multiplier still applied on top of historical base
- Falls back silently to rule-based estimateExpiryDays when no history exists
2026-04-06 09:09:04 +00:00
dadaloop82 568cc1e6fa fix: don't re-add items to Bring after user removes them (purchased blocklist, 4h TTL) 2026-04-06 08:53:14 +00:00
dadaloop82 854bc37709 📦 Backup database automatico - 2026-04-06 03:00 2026-04-06 03:00:01 +00:00
dadaloop82 fedb7c50e2 📦 Backup database automatico - 2026-04-05 03:00 2026-04-05 03:00:02 +00:00
dadaloop82 57677fa0d0 fix: keep previous settings (meal, persons, options) on regenerate 2026-04-04 15:35:07 +00:00
dadaloop82 e233dcef6d fix: remove duplicate const meal declaration in regenerateRecipe 2026-04-04 15:31:31 +00:00
dadaloop82 da5552e992 fix: hide meal-plan banner on chip uncheck; fix recipe variety (variation counter, temp scaling, client-side title tracking) 2026-04-04 15:29:07 +00:00
dadaloop82 bd6f92f2f3 fix: route TTS through PHP proxy to bypass mixed-content/CORS 2026-04-04 14:44:11 +00:00
dadaloop82 475d482184 feat: TTS generic API builder, remove HA refs, pre-fill credentials 2026-04-04 14:40:48 +00:00
dadaloop82 7bc1c87d5c feat: TTS via Home Assistant API, settings panel, remove browser speechSynthesis 2026-04-04 14:37:00 +00:00
dadaloop82 63db7cc114 feat: bring urgency sync, background auto-sync, recipe mealplan chip, screensaver fix 2026-04-04 14:32:25 +00:00
dadaloop82 6e3e451a39 📦 Backup database automatico - 2026-04-04 03:00 2026-04-04 03:00:01 +00:00
dadaloop82 3bbf093857 📦 Backup database automatico - 2026-04-03 03:00 2026-04-03 03:00:01 +00:00
dadaloop82 20e7d2cbfc 📦 Backup database automatico - 2026-04-02 03:00 2026-04-02 03:00:01 +00:00
dadaloop82 6f81846942 Smart shopping: timestamp ultimo aggiornamento, CSS progress dots e timer bar; fix layout modalità cucina 2026-04-01 05:52:46 +00:00
dadaloop82 e18fb5839a Smart shopping: cron ogni 5min pre-calcola cache server-side, API serve da cache (risposta istantanea) 2026-04-01 05:52:17 +00:00
dadaloop82 200ec145d9 📦 Backup database automatico - 2026-04-01 03:00 2026-04-01 03:00:01 +00:00
dadaloop82 fb7bb4d675 Modalità cucina: timer multipli persistenti con etichetta, riprendi dal passo salvato, progress dots, pulsante Ricomincia; priorità ricette basata su scadenze con ingredienti obbligatori 2026-03-31 15:55:35 +00:00
dadaloop82 2be6643104 📦 Backup database automatico - 2026-03-31 03:00 2026-03-31 03:00:01 +00:00
dadaloop82 bcddba46d4 Remove kg/l units everywhere — only g (grammi) and ml (millilitri)
- HTML: removed kg/l options from all unit selector dropdowns
- JS detectUnitAndQuantity(): auto-converts kg→g (*1000) and l→ml (*1000)
- JS unit labels: removed all kg/l entries from unitLabels maps
- JS category defaults: frutta 1000g, verdura 500g, bevande 1000ml
- JS step/min logic: simplified for g/ml only (no more 0.01 steps)
- JS getSubUnitStep(): removed kg/l cases
- JS isLowStock(): removed kg/l threshold
- JS spec parser: labels now show g/ml instead of kg/L
- PHP recipe parser: converts kg→g and l→ml immediately on parse
- PHP AI prompt: updated to specify only g/ml/pz/conf units
- PHP migration endpoint available at ?action=migrate_units (no-op if DB already clean)
2026-03-30 14:13:11 +00:00
dadaloop82 c4938457ac Fix min quantity for kg/l units in use forms
- Normal mode (non-conf) now sets min=0.01 for kg/l, min=1 for g/ml
- +/- buttons use unit-aware steps: 0.01 for small kg/l values, 0.1 for
  values <1, 0.5 for values >=1 (instead of fixed 0.5)
- Same fix applied to recipe use form
- Allows inputting e.g. 0.07kg (70g) when product is tracked in kg
2026-03-30 13:45:02 +00:00
dadaloop82 c63faf56e4 Conservative Bring! cleanup + operations log
- cleanupObsoleteBringItems() now much more conservative:
  * Only removes items matching a known DB product (preserves manual additions)
  * Only removes if the product has current_qty > 0 (has stock)
  * AND item is no longer flagged by smart shopping
- Added logOperation() — stores all Bring! operations in localStorage '_opLog'
  (bring_auto_add, bring_cleanup, bring_found, bring_manual_remove)
  Capped at 200 entries, each with timestamp + action + details
- All Bring! add/remove paths now log their operations
2026-03-30 13:36:51 +00:00
dadaloop82 b954be4cac Fix smart shopping: skip one-time purchases, better frequency for short history
- Products bought only once (buy_count=1) and out of stock are skipped entirely
  (not enough history to predict repurchase need, e.g. piadelle)
- usesPerMonth for items tracked 7-30 days now normalized instead of using raw count
- usesPerMonth for items tracked <7 days halved to avoid inflation
- Critical urgency now requires buy_count >= 2
2026-03-29 19:58:43 +00:00
dadaloop82 4b3e5f2ce4 Cleanup obsolete Bring! items after smart shopping algorithm fix
- cleanupObsoleteBringItems(): one-time per session, removes items from Bring!
  that the updated smart shopping algorithm no longer considers relevant
- Cross-references shoppingItems vs smartShoppingItems using exact + token match
- Shows toast with count of removed items
- Called alongside autoAddCriticalItems after loading smart shopping data
2026-03-29 19:54:05 +00:00
dadaloop82 df32fa3441 fix: spesa intelligente - filtro frequenza e recency per urgenze
L'algoritmo ora calcola:
- usesPerMonth: utilizzi al mese (non piu solo useCount totale)
- daysSinceLastUse: giorni dall'ultimo utilizzo
- isFrequent: >= 1.5 utilizzi/mese
- isRegular: >= 0.5 utilizzi/mese (almeno 1 ogni 2 mesi)
- isRecent: usato/comprato negli ultimi 60 giorni

Nuova logica urgenze:
- CRITICAL (esaurito): solo se frequente E recente
- HIGH (esaurito): regolare E recente E storico consistente
- Quasi finito/scorta bassa: solo per prodotti regolari/recenti
- Previsione esaurimento: solo per frequenti E recenti
- Prodotti usati raramente o non di recente vengono esclusi
2026-03-29 19:50:04 +00:00
dadaloop82 7be6ae8cd7 feat: timer integrato nella modalita cucina
- Rileva automaticamente durate nel testo dello step (minuti, ore, secondi,
  mezz'ora, un quarto d'ora, qualche minuto, un paio di minuti, ecc.)
- Mostra countdown grande con Avvia/Pausa/Reset
- Ultimi 30 secondi in arancione, scaduto in rosso pulsante
- Allo scadere: vibrazione + TTS 'Tempo scaduto!'
- Timer continua a contare in overtime (+00:XX) dopo lo zero
- Timer si resetta automaticamente cambiando step o chiudendo
2026-03-29 16:09:12 +00:00
dadaloop82 2d754526a5 fix: TTS primo step con voce corretta, bottone Rileggi sempre visibile
- startCookingMode: attende voiceschanged prima di parlare il primo step
  cosi la voce Google (donna) viene usata subito, non il fallback robot
- _cookingTTS parte sempre true (attivato), icona 🔊
- Bottone Rileggi visibile di default senza display:none
- Cache buster aggiornato a v=20260329c
2026-03-29 16:00:24 +00:00
dadaloop82 23e8d9a6b8 fix: voce TTS migliore (Google/online) + cache buster aggiornato 2026-03-29 15:50:03 +00:00
dadaloop82 227c31d9f9 fix: TTS sincrono + pulsante Rileggi nella modalità cucina 2026-03-29 15:46:43 +00:00
dadaloop82 b4ee70862e fix: screensaver disabilitato durante la modalità cucina 2026-03-29 15:44:36 +00:00
dadaloop82 bba6551b37 fix: TTS cucina - gestione asincrona voci e delay dopo cancel() 2026-03-29 15:43:46 +00:00
dadaloop82 f87e2204a9 fix: cooking overlay fuori dallo screensaver (era nascosto da display:none) 2026-03-29 15:38:33 +00:00
dadaloop82 c7439cc858 fix: deduplicazione lista spesa quando prodotto sta finendo
- Aggiunta helper _nameTokens() + _findSimilarItem() per confronto
  per token tra nomi prodotto (gestisce marche diverse / varianti)
  1. Se un prodotto simile e' gia' nella lista Bring! (shoppingItems)
     → mostra solo un toast, non apre il modale
  2. Se la spesa intelligente prevede gia' il prodotto (smartShoppingItems)
     → mostra nota contestuale nel modale con urgenza
2026-03-29 15:34:20 +00:00
dadaloop82 917aa56001 feat: sezioni reparto lista spesa, gradient urgenza, modalità cucina con TTS
- renderShoppingItems: raggruppamento per reparto (sezioni), ordinamento
  per urgenza+frequenza, sfondo con gradiente colore urgenza
- renderSmartShopping: stesso raggruppamento per reparto in tab previsione
- Modalità Cucina: overlay fullscreen nero, step per step con navigazione,
  TTS italiano via Web Speech API, pulsante 'Usa' ingredienti per step
- CSS: modal z-index 600 in cooking-mode-active per sovrapposizione corretta
2026-03-29 15:30:55 +00:00
dadaloop82 a38a5d670f feat: lista spesa con tab Da comprare/In previsione, tag, frequenza, tap-to-scan
- Counter nei tab aggiornati dinamicamente
- Auto-aggiunta prodotti CRITICI a Bring! al caricamento (1x per sessione)
- Badge urgenza e frequenza sugli item in lista (cross-ref smart shopping)
- Tag locali per item (Urgente/Priorità/Verificare) con menu dropdown
- Ordinamento automatico per frequenza utilizzo (item più usati in cima)
- Tap su un item → scanner barcode, con banner 'Trovato! Rimuovi dalla lista'
- Fix pctLeft: usa max(1, qty) come fallback refQty per evitare falsi alert
- Fix daysLeft capped a 365gg per pulire stringhe di previsione
- Back button on action page → torna a shopping se aperto da lista
2026-03-29 14:12:37 +00:00
dadaloop82 5b11ab6493 📦 Backup database automatico - 2026-03-28 03:00 2026-03-28 03:00:01 +00:00