Commit Graph

70 Commits

Author SHA1 Message Date
dadaloop82 4db8882dbd feat: ottimizza prompt Gemini ricette (riduzione ~60% token) e migliora stabilità bilancia
Prompt ricette:
- Lista ingredienti compatta: skip staples, no brand, flag brevi (🔴3gg)
- Cap gruppo 4 a 40 items, gruppo 6 a 20
- Regole condensate da 10 verbose a 6 concise
- Testi condizionali (varietà, regen, opzioni) abbreviati
- Aggiunto detail errore API Gemini nel toast

Bilancia:
- Ignora oscillazioni sub-grammo (jitter 0.5g)
- Confronto integer-gram prima di dichiarare instabile
2026-04-20 14:43:05 +00:00
dadaloop82 c115f83879 fix(bring): avoid ambiguous fallback match on generic words like 'dolce'
- italianToBring(): pass-2 whole-word fallback now ignores generic qualifiers
  (dolce, light, classico, originale, etc.)
- when multiple single-word matches exist, choose the longest/specific token
  instead of first catalog iteration hit
- prevents wrong mappings like 'Pancetta Dolce' being interpreted via generic
  adjective rather than the core product token
2026-04-19 09:07:44 +00:00
dadaloop82 1021f04735 fix: smarter proactive shopping list urgency
- PHP: predictive urgency block now scales by imminence:
  round(days_left) <= 3 → high, <= 7 → medium, <= 14 → low
  (was flat 'low' for any days_left <= 14)
- PHP: also upgrades existing 'low' urgency to 'high' when
  imminent depletion detected (round(days_left) <= 3, isFrequent)
- JS: autoAddCriticalItems now also adds:
  - high urgency items with pct_left < 20% (nearly empty)
  - high urgency items with days_left <= 3 (imminent)
  - any item with days_left <= 2 and uses_per_month >= 5

Result: Latte di Montagna (27.8x/mo, 3 days left) now appears
on shopping list before running out, as do Lenticchie/Riso
Basmati at 1% stock and Sandwich at 1 day left.
2026-04-19 06:06:18 +00:00
dadaloop82 07bdfe6b87 fix: kiosk overlay, preferred use-location, scale reconnect, Bring! translation, smart cache invalidation
- Kiosk: replace header-inject overlay with position:fixed div appended to <html>
  so buttons appear regardless of SPA init timing
- Kiosk: bump versionCode 3→4, versionName 1.2.0→1.3.0
- Kiosk: add explicit signingConfigs block (debug keystore) to avoid signature
  mismatch on updates; update banner now shows uninstall instruction + 12s timeout
- Web: v1.4.0 → v1.5.0
- Preferred use-location: remember last N location choices per product; after 3+
  consistent picks auto-select and collapse location picker (with 'cambia' link)
- Scale: call updateScaleReadButtons() on every status change so live-box and
  read button appear instantly on reconnect without manual refresh
- Smart shopping cache: invalidate JSON cache file on every inventory_add and
  inventory_use so next shopping-page load always sees current stock
- isLowStock: conf threshold changed <= 1 → < 1 (1 full pack is not low stock)
- italianToBring: replace substring matching with whole-word matching (min 4 chars)
  to prevent 'gin' matching 'original', 'rum' matching 'crumble', etc.
  Philadelphia original was silently mapped to Gin and skipped as duplicate
- Storico: add undo support (transaction_undo endpoint, undone column, JS undo btn)
- LOG → Storico rename in UI, nav, translations
- Bring! sync: urgency-aware purchased blocklist TTL (critical 30m, high 90m, others 4h)
- forceSyncBring() button to clear all guards and re-sync from scratch
- Scale live-box: position:fixed CSS class, 1.6rem/800 value, direct ml display
- Recipe use modal: scale live-box with 10s stability + 5s auto-confirm countdown
- Recipe use modal: show recipe quantity as highlighted row in Usa popup
2026-04-18 18:50:15 +00:00
dadaloop82 9e2a24def4 feat: v1.3.0 — banner notifications, quick-access, swipe navigation, bug fixes
Added:
- Expired/expiring product banner alerts with use, throw, edit, dismiss actions
- Priority-sorted notifications (expired > expiring > suspicious qty > predictions)
- Touch swipe navigation for banner with dot indicators and arrow buttons
- Quick-access buttons on inventory (4 recent + 8 popular products)
- Auto-refresh banner every 5 min on dashboard
- Edit expiry dates directly from expired/expiring notifications

Fixed:
- Ignore negative BLE scale readings
- Banner re-appearing after edit (confirmation now persisted)
- False consumption predictions when inventory was manually edited
- Kiosk overlay no longer blocks web app header
2026-04-18 05:37:03 +00:00
dadaloop82 3e25fcd5df feat: banner alerts, consumption predictions, scale improvements, kiosk app
- Banner notification system: suspicious quantities + consumption prediction alerts
- Consumption predictions API: tracks 90-day usage patterns, flags >30% deviations
- Scale stability timeout: 5s → 10s, auto-confirm remains 5s
- Scale integration in edit form: weigh button with inline live display
- Banner edit/weigh actions open edit form directly with scale activation
- Cooking mode: Italian aliases + stem-prefix matching for ingredients
- Recipe regeneration: tracks rejected ingredients for diversity
- Settings migration: localStorage → .env server-side storage
- Expiry priority: mandatory ≤3 days, recommended ≤7 days in recipes
- Scale bug fixes: clear stale weight, double-submit guard, cap deduction
- Android kiosk app (evershelf-kiosk): WebView + embedded BLE scale gateway
- Version bump to 1.4.0
2026-04-16 14:46:30 +00:00
dadaloop82 4d972b824e fix: vacuum state in move-after-use modal + show all recipe ingredients in cooking mode
- showMoveAfterUseModal: add '🫙 Torna sotto vuoto' checkbox (default=previous state)
  only shown if item was vacuum sealed; confirmMoveAfterUse passes vacuum_sealed to API
- showRecipeMoveModal: same vacuum checkbox with wasVacuum default passed from ingredient
- confirmRecipeMove: passes vacuum_sealed to inventory_update
- PHP API: add vacuum_sealed to recipe ingredient enrichment
- renderCookingStep: remove step-text word filter; show ALL unused from_pantry
  ingredients at every cooking step (AI uses pronouns like 'tagliarla' instead of
  repeating the ingredient name, causing them to be invisible)
2026-04-15 11:05:23 +00:00
dadaloop82 0893742f05 feat: add smart scale BLE gateway integration
- Add evershelf-scale-gateway/ Android app (Kotlin):
  - BLE scanning and GATT connection to smart scales
  - Supports BT SIG Weight Scale (0x181D), Body Composition (0x181B), and generic heuristic parser
  - WebSocket server on port 8765 (local LAN)
  - Real-time weight broadcasting to EverShelf browser client
- Add scale status indicator in header (green/orange/grey dot)
- Add Settings tab for scale configuration (URL, enable toggle, test, APK download link)
- Add 'Read from scale' button in Add/Use forms when unit is g or ml
- Add scale WebSocket client logic in app.js with auto-reconnect
- Fix recipe suggestion: expiry-prioritized ingredients now only injected into
  AI prompt when user explicitly selects 'Priorità Scadenze' or 'Zero Sprechi'
- Update README with smart scale section and website link
- Update all translations (it, en, de) with scale strings
2026-04-14 15:59:40 +00:00
dadaloop82 20f734d54a rebrand: rename project from Dispensa Manager to EverShelf
- Update app name across all files (manifest, index.html, README, docs)
- Update contact email to evershelfproject@gmail.com
- Rename Docker service/container/volume to evershelf
- Rename localStorage keys: dispensa_* → evershelf_*
- Rename SQLite DB reference: dispensa.db → evershelf.db
- Update SSH remote to dadaloop82/EverShelf
- Update Apache conf file name to evershelf.conf
- Update CI workflow Docker image/container names
- Update cron job example path
- Add data/dispensa.db to .gitignore to prevent accidental commit
2026-04-13 10:09:33 +00:00
dadaloop82 d13f744aea feat: v1.1.0 - Docker, i18n, setup wizard, rate limiting, OpenAPI
New features:
- Docker support (Dockerfile + docker-compose.yml)
- GitHub Actions CI pipeline (PHP lint, JS lint, Docker build, i18n validation)
- Internationalization system with 3 languages (it, en, de) and 347 translation keys
- First-run setup wizard (4-step configuration)
- File-based API rate limiting (120/15/5 req/min tiers)
- OpenAPI 3.1.0 specification for all 43 API endpoints
- CONTRIBUTING.md with translation and development guide
- Screenshots directory placeholder

Modified:
- README.md: Docker badges, install instructions, translations section
- api/index.php: rate limiting middleware
- assets/js/app.js: i18n system, setup wizard, t() function
- assets/css/style.css: setup wizard styles
- index.html: data-i18n attributes, setup wizard overlay, language settings
- .gitignore: rate_limits exclusion
2026-04-10 06:03:11 +00:00
dadaloop82 e0956c6043 Prepare for public distribution v1.0.0
- Remove all personal data from source code (HA IP, JWT tokens)
- Move secrets to .env configuration (gitignored)
- Create .env.example template for new installations
- Add centralized env() helper, eliminate code duplication (~120 lines removed)
- Add input validation on inventory operations (quantity bounds, location whitelist)
- Remove sensitive credential exposure in API responses
- Remove database and runtime files from Git tracking
- Disable database push-to-GitHub backup (local-only backup now)
- Update .gitignore for distribution
- Add comprehensive README with installation guide
- Add CHANGELOG.md for version tracking
- Add MIT LICENSE
- Add author/license headers to all source files
- TTS defaults now empty (configured per-installation via .env)
2026-04-10 05:24:27 +00:00
dadaloop82 6531765921 Priorità verdura/frutta aperta in ricette + stime corrette
Stime opened (PHP+JS):
- Avocado -> 2d; fragole/banane/pesca/mango -> 2d
- Mela/pera/kiwi/ananas/melone/uva -> 3d
- Zucchina/melanzana/pomodoro/peperone -> 3d (era 7d)
- Broccolo/cavolfiore/sedano/finocchio -> 3d (era 7d)
- Cipolla/cipollotto/scalogno/porro -> 4d (era 7d)
- Carota -> 5d (era 7d); Aglio -> 10d
- Insalata/rucola/spinaci -> 2d (era 4d)

Recipe generator (index.php):
- SQL include i.opened_at
- getItemPriority: rileva opened_at su QUALSIASI unità (non solo conf)
- Elementi [APERTO] con scadenza <=5gg promossi in 'fortemente consigliati'
- Label '📦 PRODOTTI APERTI' aggiornata
- [APERTO] mostrato nel testo ingredienti del prompt AI
- Stesso fix nel contesto chat (opened_at + [APERTO] label)

DB migration: ricalcolate scadenze aperti con nuove stime
2026-04-09 12:56:10 +00:00
dadaloop82 48543ee8c4 Fix opened expiry: estimation rewrite + 0ml display + HTTPS info
- Restructure estimateOpenedExpiryDays (PHP+JS): check 'eternal' items FIRST
  before any location check, so they never get 5/30d regardless of shelf:
  - Salt/sugar/honey/vinegar/bicarbonato -> 9999d (never expires)
  - Spirits -> 730d, aroma/extracts/tea -> 730d, olio/coffee -> 365d
  - Pasta/rice/dry legumes (non-frigo) -> 365d; Polenta/flour -> 180d
  - Maionese/ketchup/senape -> 90d, soy sauce -> 90d anywhere
  - Dispensa fallback: 60d (was 30d); pantry salsa di pomodoro -> 5d
  - Fruit/veg in fridge: specific rules (banana 10d, citrus 14d, etc.)
  - burro 30d, panna 4d, mortadella/wurstel 5d (improved from old values)
- Fix dashboard (getStats): remove min() with stored expiry_date (was from old
  wrong estimation); use opened_at + new estimate directly
- Filter opened list: skip items with days_to_expiry > 365 (non-perishables)
- Fix useFromInventory (both paths): min(opened_estimate, sealed_expiry)
  so original sealed expiry is respected if it expires sooner
- DB migration: re-compute expiry_date for all 22 opened inventory rows
  (maionese: +90d, salt/sugar/aceto: +9999d, muesli/biscotti: +60d, etc.)
- Clear opened_at for 'inchusa' birra (user confirmed: not actually opened)
- Fix '4 conf + 0ml' display: only show remainder if remainderAmt >= 1
- Add ' Stabile' expiry badge for days > 365 (JS)
- Add dispensa-ca.crt to /data/ for browser import (HTTPS trust)
2026-04-08 14:29:44 +00:00
dadaloop82 19489a0265 Smart opened-product expiry: days countdown, edibility, correct sort
PHP getStats() opened section:
- Primary detection: opened_at IS NOT NULL (reliable, set by useFromInventory)
  Fallback: fractional-qty pattern (legacy items)
- Per-item compute opened_expiry = min(opened_at + estimateOpenedExpiryDaysPHP, original_expiry)
  → vacuum_sealed items get 1.5× multiplier
  → always take sooner of 'opened shelf life' vs 'original sealed expiry'
- Add days_to_expiry, opened_expiry, is_edible, has_opened_at to each item
- Filter legacy items (no opened_at) with expiry > 14 days (too much noise)
- Sort by days_to_expiry ASC (soonest/spoiled first) instead of updated_at DESC

JS dashboard opened render:
- Expiry badge:  Scaduto / ⚠️ Scade oggi /  Xgg (urgent≤2, soon≤5, ok>5)
- 🔒 icon added when vacuum_sealed=1
- Spoiled items shown with strikethrough name + muted styling (.alert-item-spoiled)
- Cap display at 10 items; 'e altri N prodotti aperti...' note if more
- Sort comes from server (removed JS openedFraction sort)

CSS:
- .opened-expiry-{ok,soon,urgent,today,spoiled} badge classes
- .alert-item-spoiled strikethrough styling
- .alert-more-note
2026-04-08 12:30:36 +00:00
dadaloop82 dccda8ebc9 Fix: any-token product family grouping + auto timer reset on cache change
Root cause: autoAddCriticalItems used stale in-memory cache (old critical items)
and re-added items to Bring right after manual removal, because on_bring was
now false but urgency was still 'critical' in the old cache.

PHP smartShopping():
- Rename stockByFirstToken → stockByAnyToken (indexes ALL significant tokens)
- 'Passata di pomodoro' depleted + 'Polpa di pomodoro' in stock → share token
  'pomodoro' → passata no longer flagged as critical (COVERS: passata/polpa/pelato
  and any future tomato product variant)
- Same logic: 'aglio'/'aglio rosso', 'latte'/'latte di montagna', etc.

JS loadSmartShopping():
- When critical item set changes (items added OR removed), immediately reset
  _autoAddedCriticalTs and _bringCleanupTs so next shopping load uses fresh data
  instead of debounced old data

JS cleanupObsoleteBringItems():
- Use any-token matching (like PHP) for both stockByAnyToken and urgentSmartByToken
  → 'Passata di pomodoro' in Bring, 'polpa' in stock → share 'pomodoro' → removed
2026-04-07 15:26:35 +00:00
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 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 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 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 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 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 63db7cc114 feat: bring urgency sync, background auto-sync, recipe mealplan chip, screensaver fix 2026-04-04 14:32:25 +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 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 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 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 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 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 11a703d274 20260319a: Auto-refresh recipe archive after save, Bring spec uses product name not brand 2026-03-19 13:18:35 +00:00
dadaloop82 7cc557d4df 20260317c: Torta->Dolce, Bring specification with brand for product variants 2026-03-17 13:55:30 +00:00
dadaloop82 d1df6a6862 20260317b: Replace Comfort Food with Priorita cose aperte in recipe options 2026-03-17 12:06:14 +00:00
dadaloop82 b4b68d6579 20260316i: Low-stock Bring prompt after use
- API returns total_remaining, product_name, unit, default_qty after use
- isLowStock() detects when inventory is running low (pz<=2, conf<=1, weight/vol<=25% of default)
- After using a product, if low stock detected, shows prompt asking to add to Bring shopping list
- Works in main USE form, USE ALL, and recipe ingredient USE
- Prompt chains properly: low-stock then move-after-use (if applicable)
- Skips prompt when product is fully depleted (already auto-added to Bring)
2026-03-16 14:59:55 +00:00
dadaloop82 134e5dfa4e 20260316g: Fix conf anomaly detection + auto-split opened packages
- Fixed isSuspiciousDefaultQty: for conf products, checks package_unit thresholds
  (375g is fine for g-max=10000, not checked against conf-max=50)
- Auto-split on use: when using from a conf product with whole+fractional qty,
  automatically separates whole confs from opened part
  e.g. Panna 2.6conf → use 5g → 2conf (sealed) + 0.56conf (opened)
- Move modal now moves only the opened row (via opened_id)
- Use query prefers fractional rows (opened packages) first
- Non-conf products still get standard move-after-use behavior
2026-03-16 08:07:17 +00:00
dadaloop82 c6a3ae6e63 20260316c: Dashboard quick recipe button + waste chart
- Added 'Ricetta veloce' button between stat cards and expired section
  (navigates to chat and auto-asks Gemini for a recipe with expiring products)
- Added waste vs consumption mini chart between expiring and opened sections
  (horizontal bar showing used/wasted ratio from last 30 days)
- API: getStats() now returns used_30d and wasted_30d counts
- Cache busters updated to 20260316c
2026-03-16 07:49:10 +00:00
dadaloop82 eb5ee60dd7 Sposta prodotto dopo l'uso + fix prodotti aperti
- Dopo aver usato un prodotto, se rimane quantità mostra modal con opzione
  di spostarlo in un'altra posizione (es. dispensa→frigo dopo apertura)
- La scadenza viene ricalcolata per la nuova posizione
- Fix marmellata: default_quantity era 0 → non appariva tra prodotti aperti
- Auto-set default_quantity al primo add per prodotti g/ml/kg/l senza pkg size
- Versione: 20260316b
2026-03-16 06:48:48 +00:00
dadaloop82 dc9785c160 Estendi 'Prodotti Aperti' a tutti i tipi (g, ml, l, pz, non solo conf)
- Query SQL ora rileva prodotti aperti per tutte le unità dove la quantità
  non è multiplo della dimensione confezione (soglia 2% per evitare errori arrot.)
- Frontend: rendering differente per conf (X conf + rimanente) e altri (Xg / Yg)
- Versione: 20260315i
2026-03-15 19:05:03 +00:00
dadaloop82 4239e0b204 🫙 Sotto vuoto: flag per estendere scadenza prodotti conservati sotto vuoto 2026-03-15 18:09:38 +00:00
dadaloop82 067a4acb5c Add: mostra totale aggiornato nel toast + auto-rimuovi da Bring dopo acquisto 2026-03-15 17:47:29 +00:00
dadaloop82 17fdc993a5 Bring: non inviare marca/quantità, usa solo nomi generici catalogo. Puliti 27 articoli. 2026-03-15 17:19:36 +00:00
dadaloop82 bd1e5d950f Prodotti aperti: mostra solo conf con capacità nota (latte, panna ecc.) 2026-03-13 15:15:46 +00:00
dadaloop82 f64423296b Prodotti aperti: includi anche non-conf con quantità parziale (19 prodotti) 2026-03-13 15:10:17 +00:00
dadaloop82 948e851b71 Dashboard: sezione Prodotti Aperti (conf parzialmente usate) 2026-03-13 15:02:27 +00:00
dadaloop82 53fb85b42b Weekly recipe variety + soften fridge priority (suggestion, not mandatory) 2026-03-12 18:20:47 +00:00
dadaloop82 f2b090f107 Move recipes, chat, settings to shared DB — sync across devices, 1 recipe per meal per day 2026-03-12 18:06:50 +00:00
dadaloop82 a7dabbce87 Fix: prevent save_settings from overwriting .env with empty values 2026-03-12 17:57:59 +00:00
dadaloop82 c5f22fdf42 feat: BarcodeDetector nativo + camera selector + recipe dedup + remote debug logging
- Scanner: usa BarcodeDetector API nativa (Chrome Android) come primario, Quagga come fallback
- Settings: aggiunta tab Fotocamera per scegliere posteriore/anteriore/specifica
- Scanner feedback: barra verde (scansione attiva), gialla (barcode rilevato)
- Ricette: invio titoli ricette del giorno per evitare duplicati nello stesso giorno
- Debug: sistema di logging remoto (client_debug.log) per diagnostica da dispositivi chioscati
- Fix: permessi .env per scrittura da Apache
2026-03-12 17:32:54 +00:00
dadaloop82 3a7fce49a0 Shopping list improvements: dedup Bring items, sync removals, server-side DupliClick token check, redesigned price layout, recipe archive with CSS, fridge/opened priority in recipes, better search with spec in query 2026-03-12 12:06:02 +00:00
dadaloop82 bf2e137502 feat: confezioni (conf) chiede dimensione singola confezione
- Nuovo campo package_unit in DB (migrazione automatica)
- Form aggiungi/modifica: quando si seleziona 'conf', appare campo per
  specificare il contenuto della singola confezione (es. 300g, 2L)
- Visualizzazione: '3 conf (da 300g)' in inventario, dettaglio, butta
- formatQuantity aggiornato con supporto package_unit
- API: salva/restituisce package_unit in tutti gli endpoint
- Ricette e chat: contesto arricchito con info confezione
- CSS: stili per il nuovo campo conf-size
2026-03-11 15:43:44 +00:00
dadaloop82 af3b5941a0 Add Gemini Chat: conversational AI assistant for kitchen help
- Gemini star icon button next to camera in header
- Full chat page with message bubbles, typing indicator
- Conversation history persisted in localStorage (last 50 messages)
- System context includes: full inventory with expiry dates, appliances, dietary restrictions
- Multi-turn conversation with Gemini 2.0 Flash
- Pre-built suggestion chips: snack, juice/smoothie, light meal, use expiring items
- Clear chat button for fresh conversations
- Indigo/purple themed UI matching Gemini branding
- PHP gemini_chat API endpoint with inventory context injection
2026-03-11 15:26:19 +00:00