Commit Graph

451 Commits

Author SHA1 Message Date
dadaloop82 e3975b7d2e fix: editBannerNoExpiry called undefined openEditInventoryModal
Replace with correct editInventoryItem() call — same function used
by all other banner edit handlers.
2026-05-11 16:55:26 +00:00
dadaloop82 38c6c5aac3 fix: auto-create data dir on first Docker run (HY000[14])
When a Docker named volume is first mounted at /var/www/html/data,
the directory may be owned by root (the volume is created empty before
the image's chown step applies). This caused PDO::__construct to throw:
  SQLSTATE[HY000][14] unable to open database file

Fix: _ensureDataDir() checks/creates the directory and attempts chmod
before every getDB() call. On subsequent calls the is_dir+is_writable
checks are O(1) stat calls with no overhead.

Fixes #34 (also closes #28 #29 #30 #31 #32 #33)
2026-05-11 15:54:40 +00:00
dadaloop82 a21b54deaa feat: i18n — translate all hardcoded Italian strings (nutrition, facts, kiosk, gemini, scanner, shopping)
- Added 106 new translation keys across all 3 languages (it/en/de):
  - nutrition.* (11 keys): card title, score labels, health/variety/fresh bars, source
  - facts.* (70 keys): screensaver facts — greetings, expiry, shopping, categories, tips
  - kiosk.* (12 keys): update check, install flow, exit/refresh button titles
  - update.* (2 keys): badge label and button
  - gemini.* (2 keys): chat button title, not-configured tooltip
  - dashboard.banner_explain_title/btn/analyzing (3 keys): anomaly explain button
  - add.history_badge_tip (1 key): history badge tooltip
  - shopping.smart_last_update, names_already_updated (2 keys)
  - appliances.empty (1 key)
  - scanner.save_new_btn (1 key)
- app.js: replaced all remaining hardcoded Italian strings with t() calls
- api/index.php: fixed Frutta/Früchte Bring! loop (Pass 2 genericQualifiers)
- index.html: asset version bumped to v=20260511b
2026-05-11 15:49:55 +00:00
dadaloop82 da62647089 feat: v1.7.9 — category badges, category search, AI guards
- Category badge on every inventory item (icon + label); 'altro' items
  refined asynchronously via new guess_category Gemini endpoint
  (data/category_ai_cache.json) — no AI call when key not configured
- Category search: inventory search now matches by macro-category key
  and translated label (e.g. 'biscotti' finds all cookie items)
- Brand fast-path in guessCategoryFromName (Oreo, Barilla, Lavazza…)
- Fix: duplicate banner alerts — _bannerLoading guard + _queuedItemIds Set
- Fix: mapToLocalCategory with en:dairies (dairi stem added)
- Fix: mapToLocalCategory no longer blocks on 'altro' — falls back to
  guessCategoryFromName(productName) before returning 'altro'
- Fix: 'Tonno all'olio' was resolving to condimenti — moved tonno\b
  check before olio\b in conserve regex block
- AI guards: _refineCategoryBadgesAsync and fetchAllPrices now check
  _geminiAvailable (JS); getShoppingPrice returns no_api_key (PHP)
  when GEMINI_API_KEY is not set — all AI functions are now explicit
2026-05-11 05:53:15 +00:00
dadaloop82 763b7fd057 fix: bilancia ricette attende ≥5g di variazione; sale spurio in Bring!
- Recipe use modal: reset _scaleLastConfirmedGrams al peso attuale prima
  di aprire il modale, così la tara ha tempo; soglia ridotta 10→5g
- PHP useFromInventory: prima di auto-aggiungere a Bring! un prodotto esaurito,
  controlla se la famiglia shopping_name ha scorte da altri prodotti (es.
  'Sale marino iodato' esaurito ma 3kg di altri sali in dispensa → non aggiunge)
  JS, così il cron bringCleanupObsolete può auto-rimuovere
- Rimosso manualmente 'Sale' da Bring! (aggiunto senza marker dalla vecchia logica)
2026-05-10 15:45:56 +00:00
dadaloop82 d1139a7e4b fix: falso alert burro; JSON traduzioni corrotte; allineamento inventario
- Smart shopping: aggiungi family-coverage check per prodotti 'quasi finiti'.
  Se il shopping_name family ha scorte da altri prodotti (es. Burro conf)
  con unità diff (g/ml vs conf), l'alert 'sta finendo' viene soppresso.
- Corretto bug traduzioni: sezione 'action' duplicata in de/en/it.json
  causava JSONDecodeError in CI/CD (line 944 column 2).
- DB: allineamento inventario burro — rimossi 30g residui (usati),
  pulito opened_at da pacco nuovo Burro conf (comprato 2026-05-08).
2026-05-10 15:34:29 +00:00
dadaloop82 5fccb5309c feat: Crea una ricetta per ingrediente + fix bottone Apri ricetta + meal non categorizzato
- Bottone 'Apri la ricetta': il transfer btn si trasforma direttamente in
  '📖 Apri la ricetta' dopo il successo (invece di aggiungere un elemento DOM separato)
- meal null: chatToRecipe e recipe_from_ingredient non auto-categorizzano il pasto;
  renderRecipe mostra il tag meal solo se presente
- Nuovo endpoint recipe_from_ingredient: genera una ricetta con l'ingrediente
  selezionato come protagonista, stessa pipeline di chatToRecipe (Gemini + fuzzy-match)
- Bottone '👨‍🍳 Crea una ricetta con questo' nel pannello azione degli alimenti
  (span-2 sotto la griglia 2x2), apre overlay Ricette in loading state
2026-05-10 15:21:21 +00:00
dadaloop82 63ede4fb53 fix: increase maxOutputTokens to 8192 in chatToRecipe; add 'Apri la ricetta' button after transfer
Fixes parse_error on complex recipes (JSON was truncated at 2048 tokens).
After successful transfer, shows 'Apri la ricetta' button inline in chat
alongside the ' Aggiunta alle Ricette!' button.
Closes #27
2026-05-10 15:08:24 +00:00
dadaloop82 370a5a62b0 fix: robust JSON extraction in chatToRecipe — handles Gemini preamble text + nested fences 2026-05-10 15:02:58 +00:00
dadaloop82 ac7368e49d fix: button outside chat bubble + showToast on success/error in chatTransferToRecipes 2026-05-10 15:00:19 +00:00
dadaloop82 2f04543de3 fix: use 'persons' field (not 'servings') in chatToRecipe for renderRecipe compatibility 2026-05-10 14:54:29 +00:00
dadaloop82 06cba1ea71 fix: add missing chatTransferToRecipes function to app.js 2026-05-10 14:50:32 +00:00
dadaloop82 073b4b9cfa v1.7.8: Trasferisci a Ricette dalla chat (refactor)
- Sostituisce 'Usa ingredienti' inline con 'Trasferisci a Ricette'
- Nuovo endpoint chat_to_recipe: Gemini restituisce JSON completo
  (title, meal, servings, ingredients, steps, nutrition_note),
  PHP arricchisce tutti gli ingredienti con product_id/location
  via fuzzy-match identico a generateRecipe
- La ricetta viene salvata in archivio e si apre nell'overlay Ricette
  con tutti i pulsanti Usa, modalità cottura, salvataggio intatto
- Rimossi: chatExtractIngredients, _buildChatIngredientPanelHTML,
  _chatRecipeTitle, chat_extract_recipe, chat-recipe-panel CSS
2026-05-10 14:49:08 +00:00
dadaloop82 9973edf463 v1.7.8: usa ingredienti dalla chat
- Nuovo endpoint chat_extract_recipe: Gemini estrae solo nomi+quantità
  con prompt minimo (nessun inventario nel prompt → niente troncamento),
  poi PHP fuzzy-match contro l'inventario completo identico a generateRecipe
- Frontend: _looksLikeRecipe() rileva risposte chat con ricetta;
  bottone '🥄 Usa ingredienti' appare sotto la bubble, chiama chatExtractIngredients()
  che mostra pannello inline con pulsanti '📦 Usa' per ogni ingrediente in dispensa
- useRecipeIngredient() riusato 1:1 con fallback _chatRecipeTitle per le note
- Stili CSS: btn-chat-use-recipe, chat-recipe-panel, chat-recipe-panel-container
- Chiavi i18n: use_ingredients_btn, recipe_ingredients_from_pantry (it/en/de)
2026-05-10 14:40:25 +00:00
dadaloop82 5462879783 fix: chat response truncated at 'Ingredienti:' (MAX_TOKENS)
- Move system prompt to systemInstruction API field instead of injecting
  it as a fake user/model turn, saving the full turn's token count from
  the context window used for generation
- Increase maxOutputTokens from 1500 to 4096 so full recipes (with
  ingredients + instructions) can complete without being cut off
- Increase API timeout from 60 to 90 seconds for longer responses
finish_reason changes from MAX_TOKENS → STOP, reply goes from 265 to 2108 chars
2026-05-10 14:19:41 +00:00
dadaloop82 7de556e25c fix: bread machine support in chat + appliances prompt
- Add 'macchina del pane' to multiFunction list and capabilityMap with
  bread-specific instructions (ingredient order: liquids → flour → salt →
  sugar → yeast on top; programs: Base, Integrale, Francese, Rapido, Dolce)
- Fix compact appliances prompt: when multiple specialized appliances exist,
  list each with capabilities instead of forcing 'PREFERISCI Cookeo' (which
  caused Gemini to ignore the user's explicit bread machine request)
- Add chat rule #10: when user asks for a specific appliance recipe, always
  provide instructions tailored to that device only
2026-05-10 14:06:35 +00:00
dadaloop82 93684c5842 ux: merge vacuum sealed question into move-after-use modal
Instead of a separate floating prompt after use, the vacuum sealed checkbox
is now shown directly inside the 'where to put the rest?' modal:
- Always shown for container-type units (conf/g/kg/ml/l) or if previously sealed
- Pre-checked when the item was already vacuum sealed (semi-automatic)
- Saving on 'rimani qui' button also persists the vacuum state
- Saves one step: user answers location + vacuum in a single interaction
2026-05-10 13:23:40 +00:00
dadaloop82 75ca49ac4e fix: smart shopping family suppression, shelf life pre-warming (v1.7.7)
- Remove recentlyExhausted bypass from shopping_name family suppression:
  products recently exhausted (<14d) were incorrectly flagged as critical
  even when the same family had ample stock (Yogurt 2002g, Affettato 1022g,
  Pane 400g). recentlyExhausted now only bypasses loose token-based coverage.
- Add prewarmShelfLifeCache() in cron: pre-warms opened shelf life via
  Gemini AI (max 5 items/cycle) so the UI never blocks on first load.
2026-05-10 13:18:41 +00:00
dadaloop82 ed447d5811 fix: codebase audit fixes — indexes, daily_rate, anomaly key, CSRF, chat pruning, shopping_name
## v1.7.6

- DB: fix shopping_name Pi→Piadina, Grana→Formaggio, Prosciutto cotto→Affettato, Panna acida→Panna
- DB: composite indexes idx_transactions_type_date + idx_transactions_pid_type_undone (+ migration)
- PHP: daily_rate uses first_in→last_activity window (not first_in→now)
- PHP: anomaly dismiss key uses product_id+direction (stable, not product_id+round(expected))
- PHP: smart shopping — products exhausted within 14 days bypass token/family suppression
- PHP: chat pruning — DELETE messages beyond 200 after each chatSave()
- PHP: getStats() — 5 queries → 1 consolidated query with subselects
- PHP: bringCleanupObsolete — 300ms delay between bulk removals
- PHP: CSRF guard — POST write actions require X-EverShelf-Request:1 or Content-Type:application/json
- JS: api() — sends X-EverShelf-Request:1 on all POST requests
- JS: _opLog — prunes entries older than 30 days in addition to 200-entry cap
2026-05-10 11:26:10 +00:00
dadaloop82 f65fb4365c fix: shopping list accuracy, Bring! cleanup server-side, vacuum prompt, recipe appliances
## v1.7.5

### Added
- Vacuum sealed prompt after item use (conf/weighted units, auto-dismiss 8s)
- Multi-function appliance awareness in Gemini recipe prompts (Cookeo/Bimby/Thermomix)
- Server-side Bring! cleanup in cron (no client page load required)
- shopping_name field in inventory_list API response

### Fixed
- Bring! cleanup: false token match (Succo/Frutta from product name tokens)
- Bring! cleanup: expired item with fresh family stock no longer flagged critical (Verdure)
- Bring! remove: catalog items now removed via German key fallback (Formaggio→Käse)
- Shopping list: isExpiringSoon false positives (requires pctLeft < 50%)
- Shopping list: expired batch suppressed when fresh restock >= 50%
- Cross-device cleanup: detect app-added items via spec markers not localStorage
- API fetch: cache: 'no-store' on all api() calls
- Shopping page: 45s polling for multi-client sync
2026-05-10 10:54:35 +00:00
dadaloop82 10114dae50 errors: report EVERY server/gateway error to GitHub Issues
PHP api/index.php:
- DB connection failure (500) now calls _phpErrorReport()
- Main router catch-all (500) now calls _phpErrorReport()
- undoTransaction DB error (500) now calls _phpErrorReport()

PHP api/cron_smart_shopping.php:
- cron Throwable catch now calls _phpErrorReport() before exit(1)
  (fires even in CRON_MODE since _phpErrorReport() has its own guard)

Scale Gateway GatewayWebSocketServer.kt:
- onError() now calls ErrorReporter.report(ex, ...) in addition to Log.e

Combined with previous kiosk commit, every error path in the entire
EverShelf stack now sends an automatic GitHub Issue.
2026-05-08 11:34:06 +00:00
dadaloop82 96d3514c38 kiosk: report ALL crashes via ErrorReporter
- onRenderProcessGone: WebView renderer crash/OOM kill now reported
  and Activity is recreated automatically (no more silent crash)
- onReceivedHttpError: HTTP 5xx from server now reported
- onLowMemory: system low-memory event reported
- onTrimMemory: moderate+ memory trim events reported

Every error path in the kiosk now sends an auto-report issue.
2026-05-08 11:27:28 +00:00
dadaloop82 336d9091be fix: pkgUnit fallback for /kg+/L, fuzzy smart lookup by word-prefix 2026-05-08 06:21:35 +00:00
dadaloop82 3e6e8dc0c7 fix: pz×container multiplication, approx badge for null-total items 2026-05-08 06:11:09 +00:00
dadaloop82 be8dfe9e1e fix: shopping price calc — null for unconvertible /kg items, resolved qty in response/badge 2026-05-08 06:02:40 +00:00
dadaloop82 7cb1dfe285 fix: no HTML in use-all confirm dialog; rework disambiguation UX flow 2026-05-08 05:48:00 +00:00
dadaloop82 f4dbd151a8 fix: getAllShoppingPrices TypeError on null estimated_total for /kg items; fallback to 1-unit price 2026-05-07 20:56:03 +00:00
dadaloop82 4f98a63414 fix: refresh btn busts only total cache (fast); fix _pricesFetching button lock 2026-05-07 20:43:26 +00:00
dadaloop82 d2e5eea05b fix: extend PHP timeout for batch price fetch; refresh btn forces recompute 2026-05-07 20:39:44 +00:00
dadaloop82 b412cc0ebe fix: server is single source of truth for prices — no sessionStorage preload 2026-05-07 20:35:37 +00:00
dadaloop82 7a51a44b86 perf: batch Gemini price fetch — 1 call for all missing items instead of N 2026-05-07 20:28:58 +00:00
dadaloop82 c02a2fc632 feat: screensaver shopping panel with item count and estimated total 2026-05-07 19:29:53 +00:00
dadaloop82 dfbdbc6efb fix: use timestamp-based price cache validity; invalidate stale per-item cache 2026-05-07 18:58:11 +00:00
dadaloop82 6c342a412b fix: centralize price totals server-side; batch API call; 5-min total cache 2026-05-07 18:55:37 +00:00
dadaloop82 a01ca583ea fix: skip price fetch on first render when smart data empty; bump header to v1.7.4 2026-05-07 18:50:04 +00:00
dadaloop82 13b55104a1 chore: release v1.7.4 — AI price estimation, dashboard badge, real-time total 2026-05-07 17:55:05 +00:00
dadaloop82 bcd7580729 fix: show price total on dashboard via sessionStorage fallback 2026-05-07 17:48:18 +00:00
dadaloop82 1584d402e4 fix: replace stat-urgent with green price total badge top-right 2026-05-07 17:46:20 +00:00
dadaloop82 20192f902d fix: prices cached on tab switch; background price fetch every 2min; stat-price-total bigger 2026-05-07 17:44:56 +00:00
dadaloop82 b9082eae52 fix: log.title emoji; add price estimate total to shopping stat card 2026-05-07 17:41:41 +00:00
dadaloop82 0de9a62058 fix: price estimate for all items, including manually-added ones
- AI prompt: always return a best-guess price (never null/price_not_found)
  for unrecognised items returns generic package estimate with '~' prefix
- Cache key bumped to v3 to invalidate old null-returning cache entries
- JS: manually-added items (no smart match, no spec) default to qty=1/conf
  instead of qty=1/pz so _calcEstimatedTotal treats them as a single pack
- Price badge: shows '~€X.XX' prefix when source_note starts with '~'
  so user knows the price is a rough estimate
2026-05-07 17:38:05 +00:00
dadaloop82 3a9f0ccf79 fix: bump asset versions to force cache bust; price rate limit own bucket
- app.js and style.css versioned to 20260507a so browsers load new code
- get_shopping_price / get_all_shopping_prices moved to dedicated 'price'
  rate-limit bucket (60 req/min) separate from general (120 req/min)
  to avoid false 429s during sequential per-item price loading
2026-05-07 17:34:04 +00:00
dadaloop82 5f510c0451 feat: AI price estimation for shopping list with per-item real-time display
- Add get_shopping_price / get_all_shopping_prices API endpoints
- AI (Gemini) estimates retail price per natural unit (pack, piece, bunch)
  instead of always per-kg — avoids absurd totals like €1609
- _calcEstimatedTotal: proper g/ml→package conversion using defQty + regex
  on unit_label; only 'kg'/'l' labels trigger weight/volume math
- Cache key bumped to v2 to invalidate old per-kg cached entries
- Suggested quantity cap lowered from 20 to 10 conf/pz
- Unit mismatch guard: if totalUsed >> buyCount*5 for unit=conf, use
  purchase frequency instead of raw consumption rate
- JS _buildPricePayload: use smartShoppingItems for qty/unit (not Bring! spec)
- JS _cachedPrices: persist in sessionStorage (survives navigation);
  validated by _qty/_unit metadata so stale totals auto-invalidate
- Price display redesigned: right-side column per row (price-col-main +
  price-col-unit) instead of small inline badge
- fetchAllPrices: buttons disabled immediately before guard check;
  running total uses only current shoppingItems (not Object.values cache)
- Background refresh: always silent (removed 90s interaction condition)
- visibilitychange: sets _bgCall=true for shopping before refreshCurrentPage
- .gitignore: add runtime data files (bring_migrate_ts, shopping_price_cache,
  anomaly_dismissed, opened_shelf_cache, shopping_name_cache)
- Remove bring_catalog.json and bring_migrate_ts.json from tracking
2026-05-07 17:31:23 +00:00
dadaloop82 4196130835 feat: AI suggestions, smart shopping qty, shelf life fixes, UX polish
- bringSuggestItems(): Gemini AI for seasonal/complementary suggestions (6h cache)
- renderSuggestions(): AI badge (🤖 AI) for AI-sourced items + CSS .priority-ai
- smartShopping(): suggested_qty/unit/approx with package-aware tiers
- autoSyncUrgencySpecs(): sync suggested quantities to Bring! spec field
- estimateOpenedExpiryDays(): dairy-outside-fridge rules (panna 3d, yogurt 2d, latte 1d)
- AI shelf-life upper bound tightened to max(rule×4, 30) days
- Opened section: fix 0g display (remainderAmt >= 0.5 threshold, pkgSize guard)
- guessCategoryFromName(): expanded with 50+ new patterns (uova, herbs, vegetables...)
- Suggestions panel: excludes already-added Bring! items
- Shopping list: no re-render while suggestions panel is open
- Translations: remove duplicate 🍳 from dashboard.quick_recipe (all 3 langs)
- Scale icon: always white via filter:brightness(0)invert(1)
- opened_shelf_cache.json: remove 3 bad dairy entries (60d outside fridge)
2026-05-07 06:19:07 +00:00
dadaloop82 ffb0341eb6 feat: screensaver configurable timeout + fix gitignore (exclude kiosk build artifacts) 2026-05-06 15:00:04 +00:00
dadaloop82 725e2ee5ee docs: update README with kiosk v1.7.0 features, OTA signing fix, Actions CI, nutrition dashboard 2026-05-06 14:53:07 +00:00
dadaloop82 8535f4d4b9 fix: update check ignores non-semver tags + GitHub Actions builds versioned releases 2026-05-06 14:41:36 +00:00
dadaloop82 cbb3f6b288 fix: use project keystore for consistent APK signing (fixes OTA update signature conflict) 2026-05-06 14:25:59 +00:00
dadaloop82 484b378be9 fix: kiosk update button works in old APK, shows manual steps if installUpdate missing 2026-05-06 14:22:01 +00:00
dadaloop82 8d0ffef600 fix: kiosk update panel shows manual download link when APK too old 2026-05-06 14:18:29 +00:00