Commit Graph

343 Commits

Author SHA1 Message Date
dadaloop82 2980a150e4 Merge branch 'develop' 2026-04-29 17:05:47 +00:00
dadaloop82 8d02e76501 Remove interval% from annual waste info; fix conf whole-qty using package expiry not opened shelf-life 2026-04-29 17:05:38 +00:00
dadaloop82 c4f963dbd8 Merge branch 'develop' 2026-04-29 17:02:20 +00:00
dadaloop82 e71ef3aba3 Dashboard: move waste-chart above expiring; fix opened-items conf split, expiry cache, AI validation, MAX_SHOWN 20; remove DupliClick from README 2026-04-29 17:02:10 +00:00
dadaloop82 abf42059ad Merge branch 'develop' 2026-04-29 16:52:46 +00:00
dadaloop82 3c9fe7dfea Remove all Dupliclick/Spesa integration; merge annual waste info into status line 2026-04-29 16:52:36 +00:00
dadaloop82 0cf64ccca1 Merge branch 'develop' 2026-04-29 16:27:15 +00:00
dadaloop82 9c1346019c Waste section: neutral terminology, drop trend-cards & meals badge, annual/range in bar, 5-min facts, AW facts in screensaver 2026-04-29 16:27:05 +00:00
dadaloop82 8938ae517f Merge branch 'develop' 2026-04-29 16:20:05 +00:00
dadaloop82 67f58172e5 Waste: bigger fonts, auto-fit badge row, 5-min rotation 2026-04-29 16:19:54 +00:00
dadaloop82 6372db6cb6 Merge branch 'develop' 2026-04-29 16:13:45 +00:00
dadaloop82 85274948b4 Waste section: single stacked comparison bar instead of two rows 2026-04-29 16:13:43 +00:00
dadaloop82 c98f5d47bb Merge branch 'develop' 2026-04-29 15:27:02 +00:00
dadaloop82 da46fec174 Fix anomaly banner when expected_qty is negative (untracked initial stock) 2026-04-29 15:26:59 +00:00
dadaloop82 bcf0a8927d Merge branch 'develop' 2026-04-29 06:42:24 +00:00
dadaloop82 22266cb620 Fix sealed/opened expiry; AI shelf-life cache; redesign waste UI 2026-04-29 06:42:21 +00:00
dadaloop82 60e6f3c09c Merge branch 'develop' 2026-04-29 06:28:48 +00:00
dadaloop82 e002955173 Anti-waste: daily food-facts API, 3-badge rotating row with fade 2026-04-29 06:28:46 +00:00
dadaloop82 cd76f5bcdd Merge branch 'develop' 2026-04-29 06:19:38 +00:00
dadaloop82 7c4dd99289 Anti-waste: themed border, rich info badges, fix latte di montagna shelf-life, exclude opened from expiring_soon 2026-04-29 06:19:35 +00:00
dadaloop82 f1129b97f2 Merge branch 'develop' 2026-04-29 06:11:55 +00:00
dadaloop82 0f247a3132 Anti-waste: single-row compare bar, trend cards with arrows, rotating food facts 2026-04-29 06:11:53 +00:00
dadaloop82 3590ac8a77 Merge branch 'develop' 2026-04-29 06:01:17 +00:00
dadaloop82 0163ae11a2 Anti-waste: compact card, live dot, auto-refresh on connectivity 2026-04-29 06:01:14 +00:00
dadaloop82 e912aca219 Merge develop: Anti-Waste Report Card redesign 2026-04-29 05:54:29 +00:00
dadaloop82 ee2c280167 Redesign anti-waste section: report card with grade, comparison vs national avg, savings badges and trend chart
- Replace simple bar chart with full Anti-Waste Report Card
- Grade system (A+ to D) based on user's waste rate
- Dual comparison bars: user waste rate vs national average (IT/DE/US)
- Estimated monthly savings in money, meals saved, CO2 avoided
- 3-month trend mini chart with colour-coded bars
- Backend: getStats() now returns 3×30d buckets (used_30d, used_prev_30d, used_prev_60d, etc.)
- Real-world benchmarks: IT 22%/5.4kg/mo (REDUCE), DE 20%/6.5kg/mo (Eurostat), US 30%/9.2kg/mo (USDA)
- All labels fully i18n: 18 new antiwaste.* keys in it/en/de translation files
- Section is fully JS-rendered; HTML now just an empty container
2026-04-29 05:54:17 +00:00
dadaloop82 0d0b52b048 Merge develop: use-flow UX and finished-banner logic 2026-04-29 05:38:35 +00:00
dadaloop82 2c06be33d4 Improve use-flow UX and suppress redundant finished alerts 2026-04-29 05:38:21 +00:00
dadaloop82 5ec5dc8e4b Merge develop: i18n completion for recipes and meal plan 2026-04-28 17:29:25 +00:00
dadaloop82 8558db1925 Complete i18n pass for recipes and meal plan labels 2026-04-28 17:28:54 +00:00
dadaloop82 8722f15aa0 i18n: Translate all hardcoded Italian labels to English & German
- Convert LOCATIONS labels to use t('locations.*')
- Convert SHOPPING_SECTIONS labels to use t('shopping_sections.*')
- Convert CATEGORY_LABELS to use t('categories.*')
- Convert MEAL_PLAN_TYPES to use t('meal_plan_types.*')
- Convert WEEK_DAYS_SHORT to use t('days.*_short')
- Convert MEAL_TYPES to use t('meal_types.*')
- Convert MEAL_SUB_TYPES to use t('meal_sub.*')
- Convert meal-plan column headers to use translated meal_types
- Replace inline locLabels/LOC_LABELS with translated LOCATIONS object
- Fix shopping action buttons: bring_add_n, bring_add_selected, bring_adding, bring_added_*
- Fix recipe archive empty state
- Fix meal plan reset success toast
- Fix meal plan suggestion hint and screensaver display
- Fix settings save status messages (saved, saved_local, saved_local_error)
- Fix product edit form title
- Fix kiosk session phrases for screensaver counter
- Add cooking.expires_chip translation for expiry date format
- Add meal_plan section (reset_success, suggested_by)
- Add error.select_items for Bring shopping validation
- All strings now properly internationalized for EN/DE languages
2026-04-28 16:03:07 +00:00
dadaloop82 dc25c2fa52 release: v1.5.0 — expired banner, AI fallback, TTS cooking improvements 2026-04-28 12:53:42 +00:00
dadaloop82 105c3298f3 chore: bump version to 1.5.0 2026-04-28 12:53:24 +00:00
dadaloop82 c3b19a6c48 feat: expired banner for opened products, AI model fallback, TTS cooking improvements
- Banner: detect expired opened-products via effective shelf-life (opened_at +
  estimateOpenedExpiryDays), not just raw expiry_date — fixes Fagioli/Panna case
- Banner: expired items show safety tip inline; danger-level items (fridge dairy,
  meat, fish) get red banner + 'L'ho buttato' as primary button, 'Usa comunque'
  demoted to grey; safety-ok/warning items keep original button order
- Banner: anomaly dismiss button now shows current inventory qty ('La quantità è
  giusta (2 pz)') so the action is unambiguous
- AI: add callGeminiWithFallback() helper — tries gemini-2.5-flash first (separate
  quota), falls back to gemini-2.0-flash; applied to all endpoints (expiry, chat,
  identify, recipe non-streaming, shopping name classifier)
- AI: show friendly 'Quota AI esaurita' message instead of raw Gemini error string
- Cooking TTS: fix auto-speak broken since 'auto-speak removed' comment — each step
  is now read automatically on navigate and on first step when entering cooking mode
- Cooking TTS: remove incorrect s.tts_enabled gate — _cookingTTS toggle is the only
  gate; browser Web Speech API used by default without requiring Settings config
- Cooking TTS: timer fires '10 secondi rimanenti' warning at T-10s
- Cooking TTS: announce recipe completion ('Buon appetito!') on last step confirm
- i18n: add timer_warning_tts, recipe_done_tts, error.ai_quota keys (IT/EN/DE)
- CSS: add banner-expired-danger, banner-safety-* styles for unsafe expired items
2026-04-28 12:46:00 +00:00
dadaloop82 8a16307b39 i18n: translate all hardcoded Italian strings in app.js
Added 49 new translation keys to all 3 language files (IT/EN/DE)
and wired every hardcoded Italian label/toast/hint in app.js to use
the t() translation function.

Sections covered:
- scale: density_hint, ml_hint, weight_detected, weight_too_low,
         stable, auto_confirm
- dashboard: banner review titles/details, prediction rate/days/
             direction texts, finished-zero/expected/check,
             anomaly phantom/ghost titles and details
- action: have_title, add_more_sub, use_qty_sub, throw_btn/sub, edit_sub
- add: purchase_type_label, new_btn, existing_btn,
       remaining_label/hint/full/half
- use: throw_title, throw_all, throw_qty_label/hint, throw_partial_btn
- shopping: bring_badge, add_urgent_toast, migration_done,
            added_to_bring, added_to_bring_skip, all_on_bring,
            removed_sufficient (was a complex plural, now uses key)
- toast: product_updated, thrown_away, thrown_away_partial
- confirm: kiosk_exit
- WEEK_DAYS array now uses t('days.*') keys
2026-04-28 06:36:30 +00:00
dadaloop82 1606cb3a90 docs: add v1.4.0 CHANGELOG and README updates for all features since 1.3.0 2026-04-28 06:20:50 +00:00
dadaloop82 608afb086d fix: bringMigrateNamesInternal — use PUT/remove and German catalog keys
Two bugs in the migration function:
1. DELETE endpoint does not exist in Bring! API — must use PUT with
   'remove' param (same as the remove-from-list flow elsewhere in the code)
2. Items were added using the Italian shopping_name as the 'purchase' field
   instead of the German catalog key via italianToBring(shoppingName).
   This created Italian/German duplicates (e.g. both 'Affettato' and
   'Aufschnitt' in the list at the same time).

Also add a pre-add duplicate check so existing catalog-key items are not
double-added when the old specific item is removed.

Manual cleanup run: removed 25 stale/duplicate items, added 8 correct
German-key items, ran migration (1 more migrated). List is now clean.
2026-04-27 18:14:27 +00:00
dadaloop82 d1478245da fix: add 24 missing shopping_name aliases to Bring! catalog (100% coverage)
All shopping_name values now resolve to a German Bring! catalog key:
  aroma / ingredienti spezie -> Zutaten & Gewürze
  bevande / liquore          -> Getränke & Tabak
  camomilla                  -> Tee
  cioccolata calda           -> Kakao
  cipolla                    -> Zwiebeln
  cracker / taralli / snack  -> Snacks & Süsswaren
  farina integrale           -> Mehl
  fette biscottate           -> Toast
  filetto                    -> Fleisch
  muesli                     -> Corn Flakes
  panna da cucina            -> Rahm
  passata / polpa pomodoro   -> Pelati
  piatti pronti/purè/sfornat -> Fertig- & Tiefkühlprodukte
  salsa                      -> Zutaten & Gewürze
  succo                      -> Fruchtsaft
  vino                       -> Rotwein
  zucchero di canna          -> Zucker
2026-04-27 17:37:01 +00:00
dadaloop82 cb75558581 fix: auto-migrate Bring! names to generic on every list load (throttled 10min)
- bringGetList() now runs bringMigrateNamesInternal() silently after
  returning the response, max once per 10 minutes (bring_migrate_ts.json flag)
- Refactored migration into bringMigrateNamesInternal() reusable function
- Removed manual migrate button from UI (not needed, it's automatic now)
- One-shot migration already executed via curl: 16 items updated in Bring!
2026-04-27 17:33:49 +00:00
dadaloop82 8258591e44 feat: migrate existing Bring! items to generic shopping names
- New API action bring_migrate_names: reads current Bring! list, matches
  items against products DB, replaces specific names with shopping_name
  (e.g. 'Mortadella IGP' → 'Affettato' with spec 'Mortadella IGP · Brand')
- New button in Bring! settings: 'Generalizza nomi lista Bring!'
  with live status feedback (migrated / skipped / errors count)
- Auto-refreshes shopping list view after migration
2026-04-27 17:29:55 +00:00
dadaloop82 28a8c938bd fix: prevent scale double-deduction (duplicate inventory_use)
Root cause: after scale auto-confirm fires submitUse(), the old code called
_cancelScaleAutoConfirm(false) which reset _scaleLastConfirmedGrams to null.
This allowed the scale (still showing the same reading) to start a new 10-second
stability+confirm cycle and trigger a second identical deduction.

JS fix:
- submitUse() now calls _cancelScaleTimersOnly() instead of
  _cancelScaleAutoConfirm(false), preserving _scaleLastConfirmedGrams so the
  same weight is rejected until the product is removed from the plate.
- _scaleStabilityVal reset to null so a genuinely new weight starts fresh.
- Duplicate API response (result.duplicate) silently ignored in the UI.

PHP fix (server-side safety net):
- useFromInventory() rejects a second 'out' transaction for the same product
  within 12 seconds with { success: false, duplicate: true }.
  This catches any client-side edge cases regardless of scale timing.
2026-04-27 17:01:11 +00:00
dadaloop82 d269f919b9 ci: trigger kiosk rebuild — include native TTS bridge (95389eb) 2026-04-27 14:49:53 +00:00
dadaloop82 679b3f16a8 ci: force kiosk APK rebuild with TTS bridge fix 2026-04-27 14:49:30 +00:00
dadaloop82 97f6681e24 ci: trigger kiosk APK build on develop branch too 2026-04-27 14:49:19 +00:00
dadaloop82 a5a6e80b31 fix: use product_shopping_name in all Bring! add paths from low-stock flow
- inventory_use API now returns product_shopping_name in response
- showLowStockBringPrompt: uses generic shopping name (e.g. Affettato) as
  Bring! item name, specific product name + brand as specification field
- addLowStockToBring: reads from window._lowStockName instead of arg
- Auto-add on depletion JS fallback: same generic-name pattern
- Deduplication check now tries both shoppingName and raw name
2026-04-27 13:45:10 +00:00
dadaloop82 fd5ff00d82 fix: comprehensive shopping name audit + README update
Shopping name system hardening (api/index.php):
- phraseMap: added sugar subtypes (zucchero a velo), passato di verdure/patate
  → Verdure, aroma phrases → Ingredienti Spezie, farina 00 → Farina,
  explicit brodo subtypes; added ordering comment on 'farina integrale'
- keywordMap: added 'lievito' → Lievito, 'aroma' → Ingredienti Spezie
  single-token fallbacks

DB migration (sqlite3 direct):
- farina integrale / Farina integrale di grano tenero → Farina integrale (3 rows)
- Prodotto sconosciuto (Belbake, it:farina-integrale) → Farina integrale
- Zucchero di canna → Zucchero di canna
- Passato di patate e carote → Verdure (it's a blended veg purée)
- Aroma mandorla per dolci → Ingredienti Spezie (consistent with other aromas)
Total: 11 products re-classified

README.md:
- Shopping List: added generic shopping name feature + auto-add on depletion
- Cooking Mode: updated TTS description (browser / native Android / REST)
- Kiosk: added native TTS bridge bullet
- Roadmap: checked off 3 new completed items
2026-04-27 12:17:11 +00:00
dadaloop82 1a73ed91dd fix: compound shopping names + auto-Bring on depletion + panna da cucina
1. shopping_name compound-phrase map (computeShoppingName)
   Add phraseMap checked against the full product name BEFORE the single-token
   keyword loop.  Prevents 'pane grattugiato' → 'Pane', 'panna da cucina' → 'Panna', etc.
   Key new phrases:
   - pane/pan grattugiato → Pangrattato
   - panna da cucina / panna cucina / panna chef → Panna da cucina
   - fette biscottate → Fette biscottate
   - aceto balsamico / glassa balsamico → Aceto balsamico
   - latte condensato/evaporato/vegetale/di soia/mandorla/avena/riso/cocco → specific
   - prosciutto cotto → Prosciutto cotto
   - farina di riso/mais/integrale → specific
   - pasta fresca, zucchero di canna, acqua minerale/frizzante/gassata, brodo, …
   Also added single-token safety-net entries: 'grattugiato'/'grattato'/'pangrattato'
   → 'Pangrattato', 'biscottate' → 'Fette biscottate'.

2. DB migration (sqlite3 UPDATE)
   Re-classified 10 products that had wrong shopping_name:
   Pane grattugiato → Pangrattato
   Panna da cucina (×4) → Panna da cucina
   Fette biscottate (×2) → Fette biscottate
   Aceto balsamico (×3) → Aceto balsamico
   Cleared 2 stale Gemini cache entries.

3. showLowStockBringPrompt (app.js)
   When totalRemaining <= 0 (product fully depleted), skip the modal entirely.
   The backend already auto-adds to Bring! on depletion; the JS only asks as a
   fallback if that failed (fire-and-forget async, never blocks the UI).
   The afterCallback (e.g. move-remainder modal, navigate to dashboard) is called
   immediately without user interaction.
2026-04-27 12:04:48 +00:00
dadaloop82 95389ebe87 fix: native Android TTS bridge in kiosk — bypass Web Speech API voice issues
On Android WebView, window.speechSynthesis.getVoices() often returns empty
because the Web Speech API cannot enumerate the device's TTS voices.
This caused the kiosk to show 'nessuna voce offline è supportata'.

Changes:
- KioskActivity.kt: initialise Android TextToSpeech engine on startup;
  expose speak(text, rate, pitch), stopSpeech() and isTtsReady() via
  the existing _kioskBridge JavascriptInterface; release TTS in onDestroy.
- app.js (_speakBrowser): when _kioskBridge.speak is available, delegate
  to it instead of using speechSynthesis — works even without offline voice
  packs installed.
- app.js (_initBrowserTtsVoices): show 'Voce nativa Android (kiosk)'
  in the voice dropdown when running inside the kiosk WebView.
- app.js (testTTS): use the bridge path when testing TTS inside the kiosk.
2026-04-27 11:52:30 +00:00
dadaloop82 8b5985dc80 feat: improve computeShoppingName — expanded keyword map + Gemini AI fallback
- Extended keyword map: 100+ entries covering bread (bauletto->Pane),
  cheese (bel->Formaggio, casatella->Formaggio), wine (vesoletto/trebbiano->Vino),
  foreign brand names (kaffee->Caffe, risofrolle->Cracker, zuppalatte->Biscotti,
  inchusa->Birra, apfelsaft->Succo, kartoffelpüree->Purè, ciobar->Cioccolata calda,
  ovomaltine->Bevande), desserts (gelato->Gelato), herbs (camomilla->Camomilla),
  liquors (sambuca->Liquore), sugar variants (zuccheri->Zucchero), foreign words
  (jus/zumo/arome->Italian equivalents)
- Add _geminiClassifyProduct(): calls Gemini 2.0 Flash for ambiguous product names,
  with persistent cache in data/shopping_name_cache.json (never re-queries same product)
- computeShoppingName() now calls Gemini when keyword map and Bring! catalog both fail
  and the product name is multi-token or has a brand/category hint
- saveProduct() auto-computes shopping_name on every create/update (already in prev commit)
- DB migration: all 210 products re-classified with new rules
- shopping list: 38->33 groups (Formaggio +4v, Affettato +5v, Biscotti +1v, Pane +1v...)
- Final SQL fixes for edge cases: Gelato, Camomilla, brand name single tokens
2026-04-27 11:40:14 +00:00
dadaloop82 430f9e7854 feat: generic shopping names — group products by shopping_name
- Add shopping_name column to products table
- Add computeShoppingName() PHP auto-assign function:
  * Curated keyword map: all salumi/cold cuts → 'Affettato'
  * Bring! catalog back-translation: 'Latte di Montagna' → 'Latte'
  * Fallback: first significant token capitalized
- Migrate all 210 existing products with auto-computed shopping_name
- saveProduct() auto-computes shopping_name on every create/update
- smartShopping() groups items by shopping_name: most urgent item is
  representative, others listed as variants (e.g. 'Affettato' shows
  Mortadella, Speck, Nduja, Salame, Prosciutto, Schinkenspeck as one row)
- _productOnBring() also checks shopping_name for Bring! match detection
- addToInventory auto-remove: uses shopping_name-based Bring! key
- useFromInventory auto-add: sends shopping_name to Bring! (not raw name),
  specific product name goes into specification field
- Frontend renderSmartItem: shows shopping_name as title, specific
  product name(s) in italic subtitle line below
- _syncOnBringFlags: matches on both name and shopping_name
2026-04-27 08:16:44 +00:00