81 Commits

Author SHA1 Message Date
dadaloop82 cf65e79010 Release v1.7.36: recipe stock hints, ghost products, and shopping total fix.
Adds pantry stock/remainder lines on recipe ingredients with zero-waste use-all on sealed package leftovers, ghost product restore in the dashboard, unified shopping totals, i18n sync, and maintenance scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 17:22:59 +00:00
dadaloop82 d33b0ca2fe Harden security, modularize API bootstrap, and fix scale SSE auth.
Block web access to sensitive paths, require API_TOKEN for mutations, encrypt GitHub issue credentials in .env, auto-provision tokens for same-origin clients, and pass api_token in scale relay URLs since EventSource cannot send headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 18:04:19 +00:00
dadaloop82 7104483dac fix: barcode EAN checksum validation + recipe persons dialog conflict
- Manual barcode input now blocks on invalid EAN checksum (was warning-only)
- Native BarcodeDetector now validates EAN/UPC checksum before confirming
- Renamed duplicate adjustRecipePersons (rescaler) to scaleRecipePersons
  to restore +/- buttons in the recipe generation dialog
- Added error.barcode_checksum translation key (all 5 languages)
- Bump version to v1.7.35
2026-06-02 08:58:48 +00:00
dadaloop82 b1bcf9e714 fix: AI visual barcode fallback fires only once per scanner session
If Gemini cannot identify the product visually, mark _aiFallbackExhausted=true
for the current scanner session so the 5s timer never fires again. The scanner
restarts normally (user can keep trying with the barcode reader) and a persistent
status message is shown: 'AI: product not recognized — try scanning the barcode'.
_aiFallbackExhausted resets to false in stopScanner() so the next camera session
starts fresh.
2026-05-29 17:43:55 +00:00
dadaloop82 98c38f017e feat: AI visual barcode fallback after 5s with settings toggle
When the barcode scanner cannot read a code within 5 seconds and Gemini
is available, a camera frame is automatically captured and sent to the
new gemini_barcode_visual endpoint for visual product identification.
The result pre-fills the product form identically to a barcode scan.

- PHP: new geminiBarcodeVisual() function + router case + aiActions entry
- PHP: barcode_ai_fallback setting in getServerSettings() + saveSettings() boolMap
- JS: _aiFallbackTimer (cleared on detection/stop), 5s timer in initScanner()
- JS: _tryGeminiVisualBarcode() — captures JPEG frame, calls API, saves product
- JS: barcode_ai_fallback wired into serverKeys, applyUI, collectUI, POST body
- HTML: AI fallback toggle in Settings → Camera card
- Translations: ai_fallback_* strings in scan + settings.camera (it/en/de/fr/es)

Feature is disabled by default (BARCODE_AI_FALLBACK=false).
2026-05-29 17:37:37 +00:00
dadaloop82 1637cc1020 feat: HA sensor enrichment, inventory edit guard, recipe ingredient fix, bread shelf-life
- HA sensor: expiring_list now includes full product details (location, brand,
  category, days_remaining, opened_at, vacuum_sealed, default_quantity, etc.)
- HA sensor: new expired_list attribute with full product details per expired item
- HA sensor: new low_stock_list attribute (items with quantity ≤ 1, full details)
- HA sensor: new sensor=product endpoint (?action=ha_sensor&sensor=product)
  with optional filters: &id=, &name=, &location=
- HA cron webhook: expiry alert items now carry full product details
- Inventory edit: confirm dialog when quantity exceeds unit-specific threshold
  (prevents data loss from unit-confusion typos, e.g. 183 conf instead of 0.183)
- Recipe AI: explicit rule against ingredient form substitution
  (fresh tomatoes ≠ passata, fresh milk ≠ UHT ≠ cream, etc.)
- Shelf-life: opened bread rules (piadina 2d, bauletto/pancarrè 4d, pane 3d)
- docs/wiki: HA page updated with new schema, examples, product endpoint

Closes #125
2026-05-29 05:40:25 +00:00
dadaloop82 bc39361246 feat: barcode scan visual feedback + EAN checksum validation
- Add scan-status-bar overlay inside scanner viewport showing:
  - Active scan method (Native API / Quagga / Native + Quagga)
  - Scanning state: ready, scanning, partial read, invalid, confirmed
- Validate EAN-8/EAN-13/UPC checksums in Quagga path before confirming
  (native BarcodeDetector validates internally; Quagga can return false positives)
- Show 'invalid barcode, retrying' message with invalid code highlighted
- Reset invalid barcode confidence counter on invalid read so scanner retries
- Spawn parallel Quagga scan with 'combined scan active' status message
- Add 6 translation keys (scan.status_*) in all 5 language files
2026-05-27 05:26:47 +00:00
dadaloop82 f46b12e3ad feat: recipe favorites (#124), portion rescaler (#123), financial waste report (#117), macronutrient panel (#118)
- #124: star toggle on recipe view + favorites shown first in archive with gold border
- #123: +/- persons buttons on recipe to scale ingredient quantities
- #117: wasted value in EUR displayed in monthly stats section
- #118: macronutrient breakdown panel (P/C/F/fiber bars) with 4th insight rotation phase
- DB: is_favorite column on recipes, nutriments_json on products (auto-migrated)
- OFF API: nutriments fields fetched and stored per product
- Translations: it/en/de/fr/es updated with new keys
2026-05-26 18:09:32 +00:00
dadaloop82 b985247b95 feat: monthly stats panel in rotating insight banner (closes #100)
- Add PHP action 'monthly_stats' + getMonthlyStats() function:
  items consumed/added/wasted this month, trend vs prev month,
  top 5 categories and top 3 products by transaction count
- Expand insight rotation from 2 → 3 phases (waste → nutrition → monthly)
- Add _renderMonthlyStatsSection() following nutrition section styling:
  large indigo number, trend arrow with %, horizontal category bars
  animated on show, badges for added/wasted/top-product
- Add ms-* CSS classes (bar chart layout matching nutr-* design)
- Add stats_monthly translations in it/en/de/fr/es (10 keys each)
2026-05-26 17:19:54 +00:00
dadaloop82 50660f634f fix: TTS test ask user if heard + PHP 8 end() ref bug + DB migration guard for fresh volumes
- app.js: TTS kiosk timeout 4s → 10s; fires interactive 'Hai sentito?' YES/NO
  prompt instead of showing error (TTS can take 6-8s; UtteranceProgressListener
  may not fire on all firmware); YES → success, NO → troubleshooting steps
- translations: add heard_question/heard_yes/heard_no/test_ok_kiosk/test_fail_steps
  to all 5 languages (it/en/de/fr/es) under settings.tts
- api/index.php: fix end() PHP 8.0+ reference error in _offFetchProduct()
  (categories_hierarchy stored in temp var before calling end())  (fixes #130)
- api/database.php: migrateDB() now checks sqlite_master for 'products' table;
  if missing, calls initializeDB() and returns — no ALTER on nonexistent table
  (fixes #133, covers #131)
- api/index.php: health_check db_row_count query guarded against missing
  inventory table (fixes #131)
2026-05-26 14:59:17 +00:00
dadaloop82 be3dceeebb feat: add separate sound beep test button + always show move modal on open
- Add testSound() function: plays AudioContext beep independently of TTS
- Add '🔔 Esegui Test Suono' button in TTS settings (above voice test btn)
- Add translations: settings.tts.test_sound_btn (it/en/de)
- Remove hasOtherLocs guard in showMoveAfterUseModal: always show
  location-choice modal when opening a package, regardless of whether
  the product already exists in other locations (e.g. peas only in pantry
  → user can now choose to move opened portion to fridge)
2026-05-26 12:17:18 +00:00
dadaloop82 4e4a736dba feat: ask replace vs save-to-archive on regenerate recipe
When user clicks 'Generate another', show a choice:
- Replace (discard current, generate new) — former behavior
- Save to archive & generate new — saves current recipe first

All 5 languages (it/en/de/fr/es) with regen_choice_title,
regen_replace, regen_save_new keys.
2026-05-25 10:11:56 +00:00
dadaloop82 eddb622c85 feat(offline): full offline mode — cache sync, write queue, startup recovery
- Full-screen network error overlay (z-index 300000, above screensaver)
- 'Continue offline' button after 3s, auto-enter after 8s
- Inventory + settings synced to localStorage at startup (during health check)
- inventory_summary and stats computed from local cache while offline
- Write queue (add/use/update/delete): optimistic UI + sync on reconnect
- Pending ops survive page refresh — detected and re-synced at next startup
- Buffered remoteLog/reportError flushed to server (GitHub issues) on restore
- AI/network sections hidden in offline mode (CSS body.offline-mode)
- Banner: pulsing dot while loading cache, item count when ready
- Broken external images replaced with grey SVG placeholder
- Fix: opened items marked is_edible:true offline (was flooding banner)
- Fix: _semverGt() prevents update badge for older GitHub releases
- Bump version to v1.7.25
2026-05-25 09:05:19 +00:00
dadaloop82 965a672abe feat: full Home Assistant integration
- PHP: _fireHaWebhook(), _sendHaNotify(), haInventorySensor(), haTestConnection()
- PHP: ha_sensor + ha_test routing actions
- PHP: getServerSettings() exposes ha_token (consistent with tts_token)
- PHP: saveSettings() handles all HA_* env keys (url, token, tts_entity, webhook_id, events, notify_service, expiry_days)
- PHP: bringAddItems(), shoppingAdd(), updateInventory() fire shopping_add / stock_update webhooks
- Cron: daily HA expiry/expired webhook + push notify with flag-file guard
- HTML: 🏠 Settings tab button + full HA panel (connection, TTS, webhook, notify, sensor cards)
- JS: serverKeys + loadSettingsUI extended with HA fields
- JS: _applyHaSettingsUI(), _loadHaTab(), _renderHaSensorYaml()
- JS: onHaEnabledChange(), testHaConnection(), applyHaTtsPreset()
- JS: saveHaSettings(), copyHaSensorYaml(), showHaWebhookHelp()
- JS: _buildHaTtsRequest() for HA media_player TTS
- JS: speakCookingStep() now supports HA TTS as first-priority path
- JS: onTtsEngineChange() fixed to show server section for both 'server' and 'custom'
- Translations: settings.ha.* (52 keys) in all 5 languages (it/en/de/fr/es)
- .env.example: HA_ENABLED/URL/TOKEN/TTS_ENTITY/WEBHOOK_ID/EVENTS/NOTIFY_SERVICE/EXPIRY_DAYS
- docs/wiki/Home-Assistant.md: new wiki page (REST sensors, webhooks, TTS, push notify, troubleshooting)
- README: HA integration highlighted as first feature block
2026-05-23 12:28:09 +00:00
dadaloop82 cc0fa09219 fix: recipe errors now show specific cause instead of generic 'connection error'
- PHP generateRecipeStream: wrap entire body in try/catch(\Throwable) to catch
  any PHP fatal/exception mid-stream and send it as a proper SSE error event
- PHP: curl timeout raised 60s→90s; capture curl errno/errmsg on failure
- PHP: HTTP error messages now include a human-readable status label
  (e.g. 'Quota API esaurita (429)', 'Nessuna risposta da Gemini (cURL: ...)')
- JS catch block: show err.message alongside error.connection so the actual
  JS network error (NetworkError, AbortError, etc.) is visible
- JS no-recipe+no-error path: show recipes.stream_interrupted instead of
  generic error.connection
- Translation: added recipes.stream_interrupted in it/en/de
2026-05-23 11:45:26 +00:00
dadaloop82 6a41b53174 feat: shopping list pantry hints, barcode multi-API fallback (OPF/beauty/Gemini), README disclaimer
- Shopping list: each item now shows 'Hai già Xg in dispensa' for same-family inventory stock
  - Lazy-loads inventory once per shopping page visit (_getShoppingInventoryCache)
  - Matches by first significant token (same logic as related-stock on action page)
  - Green hint below item badges, dark-mode aware (.shopping-pantry-hint)
- Barcode lookup: added Open Products Facts + Open Beauty Facts as step 3;
  Gemini AI (_barcodeLookupGemini) as final step 4 fallback
- Added stockForName PHP endpoint (stock_for_name action) for future use
- Restored missing function signatures for _offFetchProduct() and saveProduct()
  that were accidentally lost when stockForName was added in a previous session
- Translation: added shopping.pantry_hint in it/en/de
2026-05-23 09:53:17 +00:00
dadaloop82 6320b575e0 v1.7.25 — partial throw from banner, barcode fallback, related stock, Bring! re-add fix
- Fix: Bring! items re-appearing after manual removal (missing _markBringPurchased call in removeBringItem / confirmShoppingItemFound; autoAddCriticalItems now respects blocklist for qty=0 items)
- Fix: barcode false 'not found' — new _offFetchProduct() helper tries UPC-A↔EAN-13 candidates × 2 locales with auto-retry; UPCItemDB fallback also iterates candidates
- Fix: bannerThrowAway() now opens partial-throw modal (location + qty input + throw-all button) instead of immediately discarding everything
- Add: related stock card on action page — shows same-family inventory items when scanning a branded product
2026-05-23 08:17:20 +00:00
dadaloop82 fa0442e2f6 feat: native shopping list — decouple from Bring! (#105)
- New shopping_list SQLite table (migration in migrateDB)
- shoppingGetList/Add/Remove — delegates to Bring! or internal DB
  based on SHOPPING_MODE env var (default: internal)
- isShoppingBringMode() guard: requires mode=bring + BRING credentials
- bringQuickSyncProduct updated to support both modes
- All bring_* JS calls replaced with shopping_* (bring_migrate_names kept)
- New settings tab 'Lista spesa' (tab-bring) with:
  - Enable/disable shopping list toggle
  - Provider radio: internal vs Bring!
  - Bring! sub-section (shown only when mode=bring)
  - AI smart suggestions toggle
  - Forecast toggle
  - Auto-add threshold (qty slider)
  - Price estimation section
- _applyShoppingSettingsUI, onShoppingEnabledChange, onShoppingModeChange
- SHOPPING_* env vars documented in .env.example
- cron_smart_shopping respects SHOPPING_MODE and SHOPPING_SMART_SUGGESTIONS
- Translations: 12 new keys in all 5 languages (it/en/de/fr/es)
- DB busy_timeout=5000ms + WAL pragma in getDB() (fixes #95)
2026-05-19 16:05:49 +00:00
dadaloop82 7364e75881 feat: Google Drive OAuth via http://localhost redirect (no public domain required)
- Switch redirect URI from server IP to http://localhost (works everywhere)
- Add manual code exchange flow: user copies URL from browser, pastes in app
- New PHP action gdrive_oauth_exchange to exchange auth code for refresh token
- Fix  null bug in gdrive_oauth_exchange (was read before initialization)
- Add #gdrive-code-section UI with input + submit button in index.html
- Update _gdriveAuthorize() to show code section and store redirect_uri
- Add _gdriveSubmitCode() JS function for manual code submission
- Update setup wizard and backup tab to show http://localhost as redirect URI
- Add 5 new translation keys (gdrive_redirect_uri_hint, gdrive_code_title,
  gdrive_code_hint, gdrive_code_submit, gdrive_code_empty) in all 5 languages
- Update gdrive_oauth_steps in all translations to reflect new flow
- Document Google Drive OAuth setup in README.md
- Dark mode: comprehensive fix for 30+ components with hardcoded light colors
2026-05-18 18:41:56 +00:00
dadaloop82 4515ff7246 i18n: replace all hardcoded Italian strings with English
- api/index.php: health check hints (disk space, DB, Gemini, TTS, scale,
  internet) translated to English; Bring! error strings (credentials,
  list not found, fetch error, missing params) translated; Gemini chat
  and identify_product error strings translated; transaction note
  [Correzione manuale] -> [Manual correction]
- assets/js/app.js: expiry scanner result strings now use t() keys
  (scanner.expiry_found, scanner.expiry_read_fail, scanner.expiry_raw_label);
  removed Italian fallback from kiosk native_update_hint toast
- translations/{it,en,de}.json: added scanner.expiry_found,
  scanner.expiry_read_fail, scanner.expiry_raw_label keys
- README.md: 'Generali' tab label -> 'General' (2 occurrences)
2026-05-18 07:32:41 +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 da4aa5a1ae fix: shelf life formaggio, banner vacuum/modifica, preloader redesign, ricetta da ingrediente, porzioni, modal ricetta testo tradotto, use_btn semplificato 2026-05-17 18:19:13 +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 5f4c29bd5a feat: in-app bug report form (replaces GitHub link) 2026-05-16 13:25:51 +00:00
dadaloop82 8a596cb7d8 fix: openNativeSettings uses try/catch instead of fragile typeof check 2026-05-16 13:18:07 +00:00
dadaloop82 61a2372caa feat(kiosk): add native settings shortcut in webapp settings page 2026-05-16 13:07:29 +00:00
dadaloop82 8928c75a9d feat(recipes): add tools_needed field — appliances shown as chips above ingredients 2026-05-16 09:56:10 +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 850c5047b8 Fix noisy consumption alerts and make predictions adaptive 2026-05-15 11:41:29 +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 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 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 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 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 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