Commit Graph

257 Commits

Author SHA1 Message Date
dadaloop82 bd5d4bcac6 fix: dispensa.db auto-delete, zerowaste save, vacuum expiry, DB retention
- api/index.php: auto-delete legacy dispensa.db when evershelf.db exists
  and dispensa.db is empty (<1KB); vacuum-sealed items only show as
  expired after VACUUM_EXPIRY_EXTENSION_DAYS (default 30) past printed
  date; add dbCleanup() function; add recipe/tx/vacuum params to
  getServerSettings + saveSettings intMap; add 'db_cleanup' action
- api/cron_smart_shopping.php: run dbCleanup() each cron cycle
- app.js: add zerowaste_tips_enabled + screensaver_timeout + retention
  days to saveSettings POST payload (were missing, causing reset on sync);
  asset version bumped to v=20260518b
- .env: added ZEROWASTE_TIPS_ENABLED, RECIPE_RETENTION_DAYS=7,
  TRANSACTION_RETENTION_DAYS=7, VACUUM_EXPIRY_EXTENSION_DAYS=30
2026-05-18 07:16:26 +00:00
dadaloop82 c9a859463c feat: Generali tab, time-based auto theme, AI cost from real data
- index.html: new Generali tab (first, active) with Language/Currency/
  Theme/Screensaver/ZeroWaste/Export; old tab-language removed;
  screensaver timeout select uses form-input style; asset v=20260518a
- app.js: auto theme = time-based (20:00-07:00 dark, not system pref);
  removed matchMedia listener; added 5min setInterval for auto re-check;
  removed Bring! token row from Info tab (internal implementation detail)
- api/index.php: gemini_usage - removed all cache-estimation code;
  month/year_stats from ai_usage.json only
- data/ai_usage.json: data-driven baseline estimate for 2026-05:
  ~4.4M in + ~1.3M out from 8374 inferred historical calls (102 recipes,
  555 price lookups, getStats loop pre-fix, smart cron runs, etc.)
  = ~EUR 1.32 at 2.5-flash rates; new calls tracked precisely from now
- translations: settings.tab_general added; theme.auto updated to
  'Automatico (orario)' / 'Automatic (time of day)' / 'Automatisch (Tageszeit)'
2026-05-18 07:07:47 +00:00
dadaloop82 56e68b72f8 feat: Info tab v3 — clean month/year stats, currency to Info tab, Gemini costs from .env
- .env: GEMINI_COST_* rates configurable (4 new vars, defaults to current Google pricing)
- api/index.php: GEMINI_COST defines read from env() with fallback; added SHOPPING_NAME_CACHE_PATH
- api/index.php: gemini_usage output — clean month_stats/year_stats (no tracked/retro split)
  updated token estimates: price 700/250, shelf 650/120, cat 280/40, shopping_name 250/40
  added 'pricing' key to response (current rates); removed food_facts from estimate
- index.html: currency selector moved from tab-api to tab-info as first card (global setting)
- app.js: _renderInfoTab() rewritten — just month + year sections, no retro framing
  cost displayed in user's currency (price_currency) with expanded multi-currency conversion
- translations: settings.info.currency_title/hint/year_label added; retro/tracked keys removed
2026-05-18 06:45:56 +00:00
dadaloop82 cc0d9763ed feat: Info tab enriched — retroactive AI estimate, annual totals, inventory & activity stats
api/index.php:
- gemini_usage: retroactive AI call estimate from cache files (price/shelf/category)
  with per-entry token estimates (price ~475tok, shelf ~580tok, category ~230tok)
- yearly totals: sum tracked months + retro estimate for full 2026 view
- DB activity stats: products, inventory, transactions, expired, expiring_soon
- cache stats: price (255), shelf (30), category (7), foodfacts (10)
- system info: last backup timestamp+size, Bring! token expiry
- new constants: SHELF_CACHE_PATH, FOODFACTS_CACHE_PATH, BRING_TOKEN_PATH

assets/js/app.js:
- _renderInfoTab(): full rewrite — 4 cards (AI, Inventory, Activity, System)
- month displayed as localized name via Intl.DateTimeFormat (es. 'maggio 2026')
- tracked section shown when calls > 0; retro estimate always shown if gap exists
- year section: tracked + retro combined total
- pill() helper for consistent stat display

index.html: 4 cards with ids info-ai-content, info-inv-content, info-act-content, info-system-content

translations: updated settings.info.* keys in it/en/de (overview subtitle, retro labels, inv/act/system keys)
2026-05-18 06:33:59 +00:00
dadaloop82 9f554c6e22 feat: Gemini token usage counter (#82) + smarter qty suggestions 90-day EWMA (#70)
Backend (api/index.php):
- callGemini() now extracts usageMetadata (tokens_in/tokens_out) from response
- _recordAiUsage() persists monthly token data to data/ai_usage.json
- callGeminiWithFallback() accepts $usageAction param; all 15 call sites labeled
- gemini_usage endpoint: returns token stats, cost estimate, log info, DB size
- smartShopping(): rolling 90-day EWMA (70% last-30d / 30% days-31-90)
  with fallback to all-time rate when <14 days of history

Frontend (index.html + app.js):
- New Info tab (ℹ️) in Settings with Gemini usage and System cards
- _loadInfoTab() / _renderInfoTab(): loads on click, auto-refreshes every 30s
- switchSettingsTab() stops auto-refresh when leaving Info tab

Translations (it/en/de): settings.info.* keys
2026-05-18 06:23:42 +00:00
dadaloop82 9541e3a385 feat: preloader smooth fade ticker; fix asiago shelf life; kiosk 5-lang wizard (ES/FR + Gemini/Bring steps)
Preloader:
- Replace 3D wheel with smooth fade-in ticker queue
- Bigger text (clamp 1.1–1.35rem), green/amber/red per check state
- Previous items fade upward at decreasing opacity
- Wider container (min(96vw,860px)) — no more awkward line-wrapping
- JS already used ticker-item/state-ok/warn/error classes (CSS was missing)

Shelf life — Asiago sottovuoto fix:
- estimateSealedExpiryDaysPHP() and estimateExpiryDays() JS:
  asiago/fontina/emmental/gruyere/scamorza now grouped with hard cheeses (60d base)
  vacuum sealed: 60 × 2.5 = 150 days — correct for fridge + sottovuoto
- Cleared stale opened_shelf_cache entry for 'Formaggio Asiago fresco'

Kiosk wizard:
- 5 languages: values-es/ and values-fr/ created (97 strings each)
- values/, values-it/, values-de/: complete rewrite with new keys
  (ble_connecting, ble_connecting_to, summary_scale_ok/warn, Gemini/Bring step strings)
  stepDone hardcoded Italian → @string refs; screensaver nav → @string/setup_step_back/next
- SetupActivity.kt: steps 0-8 fully implemented; ES/FR language selection;
  auto-skip Gemini/Bring if already configured; buildSummary() localised;
  finishSetup() sends gemini_api_key + bring_email/password; BLE connecting
  strings localised; scale summary lines use R.string
2026-05-17 16:23:22 +00:00
dadaloop82 47ce849311 fix(ux): banner aperto senza 'Usa comunque'/'Ignora'; preloader ruota 3D; config default non bloccante
Banner prodotti aperti:
- Rimosse le opzioni 'Usa comunque' e 'Ignora' (non hanno senso
  se il prodotto è solo aperto — rimangono solo 'L\''ho finito!',
  'L\''ho buttato', 'Correggi data')
- Per prodotti scaduti non aperti il comportamento rimane invariato

Preloader startup check:
- Sostituito il mini-label monospace con una ruota 3D (stile cooking wheel)
- Testo grande, colorato: VERDE=ok, ARANCIONE=warning, ROSSO=errore
- Il check precedente sale in cima (rotateX tilt, dimmed) mentre il
  nuovo entra dal basso con animazione 3D
- setProgress() ora guida la ruota; slowAnim() aggiorna solo la barra

Defaults / non-bloccante:
- Gemini API key non impostata → ok:true 'non configurata' (verde)
- Bring! token non ancora generato → ok:true (verde, auto-generato al 1° accesso)
- La configurazione mancante mostra  informativo, non ⚠️ warning
2026-05-17 15:47:57 +00:00
dadaloop82 a690d2e7cf fix: conditional checks, evershelf.db fix, warning popup 5s, error modal (v1.7.22)
- health_check: use evershelf.db (not dispensa.db); auto-migrate if needed
- removed dispensa.db (legacy, obsolete)
- backups check: verify files exist (not dir writability, cron writes as root)
- bring_token: read data/bring_token.json (not env var)
- warning popup: 5s countdown bar with label+hint per warning, auto-closes
- error popup: blocking panel with title + hint per critical failure
- db_legacy check: warns if old dispensa.db still present
- 32 total checks (added db_legacy, tts_url, scale_gateway)
- hint messages on every check explaining cause and fix
- translations: added check_db_legacy, check_tts, check_scale,
  critical_error_intro, error_network_detail in it/en/de
2026-05-17 10:00:38 +00:00
dadaloop82 78f499205c feat: progress bar startup check with 29 diagnostics (v1.7.21)
- Replace banner checklist with real-time progress bar + per-check label
  Bar fills smoothly (0→100%) as each check runs; label shows current check.
  On success: bar stays green briefly then fades. On warnings: amber badges
  shown for 2.2s. On critical error: bar turns red + error block + Retry.
- Extend health_check to 29 comprehensive checks:
  PHP 8.0+ version, 4 critical extensions (pdo_sqlite/curl/json/mbstring),
  4 optional extensions (openssl/fileinfo/zip/intl), PHP memory/timeout/upload,
  data/ writable, rate_limits/ dir, backups/ dir, actual file-write test,
  free disk space, SQLite connect, required tables, PRAGMA quick_check integrity,
  WAL mode, DB file size, inventory row count, .env file, Gemini AI key,
  Bring! credentials + token, cURL SSL version, internet reachability (Gemini API)
- Fresh-install detection: if dispensa.db not found + data/ writable → OK (auto-create)
- Translations: startup.* expanded to 28 keys in IT, EN, DE, FR, ES
- CSS: new .preloader-progress-wrap, .preloader-bar-track, .preloader-bar,
  .preloader-check-label, .preloader-warn-badge; removed old .preloader-checks
- Version: v1.7.21, assets v=20260520b
2026-05-17 09:50:42 +00:00
dadaloop82 d3b119c7fe feat: startup health check during splash screen (v1.7.20)
- Add ?action=health_check PHP endpoint (early-exit, before rate-limiter)
  Checks: PHP version, required extensions, data/ writability, SQLite DB
  connection + table integrity, .env file, Gemini AI key, Bring! token
- Display animated checklist in splash screen with per-item icons
  (ok/warn/error); critical failures block app launch with clear error
  message and Retry button; optional warnings shown but don't block
- New JS: _runStartupCheck(), _startupRetry(); called first in _initApp()
- New HTML elements in #app-preloader: #preloader-checks, #preloader-error-msg,
  #preloader-retry-btn (hidden until startup check completes)
- New CSS: .preloader-checks, .preloader-check-row, .preloader-error-msg,
  .preloader-retry-btn with state colors (ok=green, warn=amber, error=red)
- Translations: startup.* keys (10 per language) in IT, EN, DE, FR, ES
- Asset version bump: v=20260520a
2026-05-17 09:40:11 +00:00
dadaloop82 a602726531 feat: zero-waste tips during cooking mode (#76) 2026-05-17 09:16:48 +00:00
dadaloop82 0a6e653692 feat: dark mode (Off/On/Auto) + export inventory CSV/PDF (#78, #64) 2026-05-17 08:59:40 +00:00
dadaloop82 3ba4f7eaad feat: add French and Spanish translations (#77)
- Complete fr.json (1049 keys, 52 sections)
- Complete es.json (1049 keys, 52 sections)
- Language selector updated with Francais and Espanol
- Setup wizard localized for fr/es
- Default fallback language changed from 'it' to 'en'
- Version bump to 1.7.17
2026-05-17 08:36:46 +00:00
dadaloop82 473d3f59a4 v1.7.15 — i18n audit, splash min 3s, decimal precision, demo GIF, README fixes
- Complete i18n audit: 25+ new translation keys (en/it/de) — vacuum toast,
  TTS voices, timer steps, product notes, error prefixes, form placeholders,
  barcode hints, recipe/cooking ingredient labels, unit variants
- pz/conf unit labels now use t('units.pz') / t('units.conf') throughout
- Splash screen: minimum 3-second display (_splashStart recorded at parse
  time, fade delayed by remaining ms if app loads faster)
- Quantity decimal precision: qtyNum in recipe/cooking buttons and conf
  fallback display capped to 1 decimal (was showing 7+ from raw AI output)
- Recipe/cooking buttons: removed Italian fallback strings from t() calls
- README: translated remaining Italian phrases; added demo.gif to Screenshots
- CHANGELOG: updated 1.7.15 entry with all session changes
- assets/img/demo.gif: EverShelf.gif processed at 2x speed (~36s)
2026-05-16 15:36:31 +00:00
dadaloop82 195c3d3bfa fix(i18n): comprehensive translation pass — inventory tabs, product form, page-ai, nav, settings (recipe/mealplan/TTS/security/camera/scale/kiosk), setup wizard, screensaver timeouts; add 25+ missing i18n keys across all 3 languages 2026-05-16 13:56:41 +00:00
dadaloop82 698eb721f2 fix(i18n): add data-i18n to all static page-use/page-add/page-action labels; fix common.cancel → btn.cancel 2026-05-16 13:43:11 +00:00
dadaloop82 834d8efab4 chore: bump to v1.7.14 — kiosk versionCode 15, CHANGELOG 2026-05-16 13:31:31 +00:00
dadaloop82 5f4c29bd5a feat: in-app bug report form (replaces GitHub link) 2026-05-16 13:25:51 +00:00
dadaloop82 61a2372caa feat(kiosk): add native settings shortcut in webapp settings page 2026-05-16 13:07:29 +00:00
dadaloop82 e19c2564f6 feat(cooking): show required tools/appliances bar in cooking mode 2026-05-16 09:58:39 +00:00
dadaloop82 9b9a196f73 fix(ux): skip move-after-use modal after 2 consistent choices; hide single-location picker 2026-05-16 09:32:46 +00:00
dadaloop82 24954cb893 fix: kiosk settings button position + opened-item expiry badge consistency
- kiosk: add gear button (⚙) to the left overlay (between ✕ and ↻)
  so settings are reachable from within kiosk mode without a native
  Android button. The web button calls showPage('settings').
- kiosk: permanently hide the native Android settings button via
  setNativeSettingsVisible(false) after overlay injection. Removes the
  touch bleed-through that caused the camera button tap to open kiosk
  settings instead of the scan page.
- kiosk: closeModal() no longer restores native settings visibility
  (native button is replaced, must stay hidden)
- dashboard opened-items panel: items expired by opened shelf-life but
  classified as safe by getExpiredSafety (level='ok', e.g. jam,
  condiments) now show a gentler amber 'Check soon' badge instead of
  the red  'Scaduto!' that was misleading users. Red  is now
  reserved for warning/danger safety levels only, consistent with the
  top banner which already filtered out safe-level expired items.
- header: version label corrected to v1.7.13
- translations: added expiry.badge_check_soon (it/en/de)
2026-05-16 06:10:01 +00:00
dadaloop82 da4bd635db feat: professional repo cleanup + community infrastructure
- README: remove Recent Updates section, clean roadmap (pending only),
  replace Screenshots with demo link, add 6 new badges (stars, last
  commit, contributors, discussions, CI), invite GIF contributions
- CHANGELOG: translate all Italian entries to English, add v1.7.13
  (DB fresh-install crash fix)
- database.php: add missing 'undone' column to transactions schema;
  wrap ALTER TABLE calls in try/catch to prevent race-condition errors
  on concurrent first requests
- Wiki: Android-Kiosk v1.5.0 → v1.6.0, Step 5 rewritten (BLE scan,
  no external APK), removed gateway troubleshooting section
- Wiki: Scale-Gateway promoted to deprecated with redirect banner
- Wiki: Home What's New updated to v1.7.12 / v1.7.13
- Wiki: Features.md, kiosk README Italian UI strings translated
- .github: add bug_report.yml, feature_request.yml issue templates,
  config.yml (blank issues off, links to FAQ/Discussions/Security),
  PULL_REQUEST_TEMPLATE.md with checklist
- .github: FUNDING.yml (Ko-fi), dependabot.yml (monthly action updates)
- .github/workflows/security.yml: Trivy docker + fs scan, SARIF upload
- .github/workflows/build-scale-gateway.yml: disabled (deprecated)
- SECURITY.md: responsible disclosure policy, supported versions, scope
- CODE_OF_CONDUCT.md: Contributor Covenant 2.1
- Settings UI: About section with version display, Report Bug button,
  Changelog and GitHub links; reportBugManual() + _loadAboutSection()
- Translations: added 'about' key group (9 keys × 3 languages)
2026-05-16 06:02:18 +00:00
dadaloop82 2d70e7a688 chore: release v1.7.12 — banner aperti, fix ricette pz, fix usa-tutto 2026-05-13 11:40:05 +00:00
dadaloop82 bba0f4715a fix: remove datalist suggestions from quick-name input (scan page) 2026-05-12 15:29:40 +00:00
dadaloop82 696a9c6d11 feat: scan page redesign — fixed 2x zoom, torch, camera flip, tabs, recents, AI number OCR
- Always-on 2x hardware zoom (CSS scale fallback)
- Torch button with toggle + visual feedback
- Camera flip (front/back) with settings persistence
- 3-tab input panel: Barcode / Name / AI
- Recent products chips (last 6 scanned, from localStorage)
- Live barcode code overlay during partial detection
- Confirm overlay (checkmark + name) on successful scan
- AI number OCR (Gemini reads barcode digits from image, shown after 4s)
- Guide corners frame in viewport
- PHP: gemini_number_ocr action + rate-limited
- Translations: new scan.* keys in it/en/de
2026-05-12 14:55:14 +00:00
dadaloop82 d056a6a116 fix: expired section hides items with quantity=0
Query was missing AND i.quantity > 0, so thrown-away items (qty=0)
with a past expiry_date kept appearing in the expired list.
Also cleaned up the orphan row for Aglio in the DB.
2026-05-11 17:35:53 +00:00
dadaloop82 cb39b63997 fix: drastically reduce false-positive consumption anomaly banners
Two changes:
1. Skip prediction when expected_qty=0 — model says 'should be finished'
   but user simply restocked or consumed less. Not actionable.
2. Raise 'more than expected' threshold to 400% (was 30%).
   Having more than expected almost always means a restock the model
   doesn't know about yet — only truly extreme cases (>4x) are flagged.
   'Less than expected' stays at 30% (still actionable: unregistered use).
2026-05-11 17:31:41 +00:00
dadaloop82 5b401f8d5f fix: consumption predictions false positives after restocking
Root cause: baseline was 'restockQty' (only the new items added) but
actualQty = pre-existing stock + new items → always looked like 'more than expected'.

New approach: baseline = current_qty + consumed_since_restock.
This correctly reflects the true starting point regardless of pre-existing stock,
eliminating all false positives after shopping trips.
2026-05-11 17:24:44 +00:00
dadaloop82 20c16401d2 fix: inventory_update now records compensating transactions
When a user manually edits quantity (e.g. after restocking), the diff
is recorded as 'in' or 'out' transaction with note '[Correzione manuale]'.
This prevents the anomaly detector from flagging manual edits as phantom
or missing consumption.
2026-05-11 17:21:57 +00:00
dadaloop82 3391106010 feat: banner opened items show 'aperto da X giorni in frigo' instead of 'scaduto'
When inventory item has opened_at set, the expired banner now shows:
- Title: '[Nome] — Aperto da troppo tempo!' (instead of '— Scaduto!')
- Detail: 'Aperto da N giorni in [icon] [location] · hai ancora X'
Also removed hardcoded Italian 'scade il' string from non-opened expired detail.
2026-05-11 17:11:07 +00:00
dadaloop82 85090ecc9f fix: generic 'latte' opened shelf life 4→7 days (UHT default)
Fresh milk is explicitly matched by 'latte fresco/intero/parzial/scremato' (3 days).
Generic 'Latte' without qualifier is almost always UHT in Italian households — 7 days.
2026-05-11 17:07:59 +00:00
dadaloop82 8407dea781 fix: editBannerNoExpiry load inventory before opening edit modal
currentInventory is empty on dashboard. Fetch inventory_list first
(same pattern as editReviewItem and weighBannerItem).
2026-05-11 16:57:00 +00:00
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 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 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 ac7368e49d fix: button outside chat bubble + showToast on success/error in chatTransferToRecipes 2026-05-10 15:00:19 +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 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 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 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