Commit Graph

243 Commits

Author SHA1 Message Date
dadaloop82 ffb0341eb6 feat: screensaver configurable timeout + fix gitignore (exclude kiosk build artifacts) 2026-05-06 15:00:04 +00:00
dadaloop82 1c890c66ea feat: kiosk manual update check + install from settings
KioskActivity.kt:
- checkForUpdates(forceCheck, jsCallback): accepts force flag + optional
  JS callback to deliver result as JSON to the WebView
- New @JavascriptInterface checkForUpdates(): bypasses 6h throttle,
  calls back window._kioskUpdateResult({has_update, current, latest, apk_url})
- New @JavascriptInterface installUpdate(apkUrl): triggers APK download+install
- Improved error handling: HTTP status check + network error JSON response

app.js:
- window._kioskUpdateResult(): callback receives update check JSON,
  shows green/amber status box with version info, shows install button
- _kioskCheckForUpdates(): triggers native check, shows spinner while waiting
- _kioskInstallUpdate(): passes apk_url to native installUpdate()
- loadSettings(): shows #kiosk-update-panel when in kiosk WebView

index.html:
- #kiosk-update-panel: version label, 'Cerca aggiornamenti' button,
  status box, 'Installa aggiornamento' button (hidden until update found)
- CSS cache bump ?v=20260506e

build.gradle.kts: version 1.6.0 → 1.7.0 (versionCode 10 → 11)
2026-05-06 14:17:31 +00:00
dadaloop82 891733aa8c fix: scale dot white+glow, kiosk reconfigure fallback, live weight in settings
style.css:
- Connected dot: white fill + green border/glow (was green-on-green, invisible)

app.js:
- _kioskReconfigureScale(): show #kiosk-needs-update-notice + toast when
  kiosk APK is too old and reconfigureScale() method is missing
- _scaleUpdateStatus(): show/hide #scale-live-diag panel, update device + battery
- _scaleOnMessage weight: update #scale-diag-weight in real time
- _scaleOnMessage status: update #scale-diag-proto with BLE protocol

index.html:
- #kiosk-needs-update-notice: amber warning + download link inside kiosk panel
- #scale-live-diag: device name, battery, live weight readout, reconnect info
- CSS cache bump ?v=20260506d
2026-05-06 14:07:29 +00:00
dadaloop82 e002cc4483 feat: nutrition analysis section + screensaver animated pie charts
app.js:
- _buildNutritionData(): category distribution, health/variety/freshness scores
- _renderNutritionSection(): animated 3D conic-gradient pie + legend + score bars
- _startInsightAlternation(): waste <-> nutrition fade-swap every hour
- _startScreensaverRotation(): facts and nutrition panel alternate every 5 min
- _renderScreensaverNutrition(): 3D animated pie + donut ring scores on screensaver
- _ssDonut(): CSS-only ring donut helper
- Removed two generic filler screensaver facts
- Cleaned up time-of-day screensaver facts (content-aware, no empty greetings)

index.html:
- Wrap waste/nutrition sections in #dashboard-insight-wrap
- Add #screensaver-nutrition slot in screensaver overlay
- Bump CSS cache ?v=20260506c

style.css:
- .ss-pie3d: 3D perspective + cubic-bezier spring + continuous slow spin
- .ss-donut-ring: CSS conic-gradient donut with bobbing animation
- .nutr-card, .nutr-pie-3d: dashboard nutrition card with 3D pie spin
- Score bars with fill transition
2026-05-06 14:00:13 +00:00
dadaloop82 115c966322 fix: scale dot contrast + kiosk scale config panel + download banner in kiosk
style.css:
- Scale connected dot: bright #4ade80 fill + white border + double shadow
  so it pops on the dark green header (was white on green = invisible)

index.html:
- Scale settings tab: add kiosk panel with 'Riconfigura bilancia BLE'
  button (hidden in browser, shown in kiosk mode)
- Wrap gateway download section and WebSocket URL section with IDs
  so JS can hide them in kiosk mode
- CSS cache bust ?v=20260506b

app.js:
- syncSettingsFromDB: in kiosk mode hide scale gateway download section,
  WebSocket URL section and test button; show kiosk BLE panel instead;
  auto-set URL to ws://localhost:8765
- Add _kioskReconfigureScale() helper that calls _kioskBridge.reconfigureScale()

KioskActivity.kt:
- Add reconfigureScale() @JavascriptInterface: stops GatewayService,
  clears saved scale device prefs, launches SetupActivity at step 4
- Import GatewayService
2026-05-06 13:44:50 +00:00
dadaloop82 f48fb02589 fix: center header title + bump to v1.7.3 for update detection
- CSS: header-title-wrap uses position:absolute with left:0/right:0
  + justify-content:center so title is truly centered regardless of
  button count on each side (kiosk has 2 left / 3 right)
- header-actions gets margin-left:auto to push right; both sides z-index:1
- Bump version 1.7.2 → 1.7.3 so already-open kiosk tabs will see
  the update badge once the new version is deployed to the server
- CSS cache bust: ?v=20260506a
2026-05-06 05:19:37 +00:00
dadaloop82 af9bae3093 fix: remove skeleton shimmer from dashboard stat cards 2026-05-06 05:15:44 +00:00
dadaloop82 14a7bbccbe fix: bust CSS cache (v20260505b) + bump webapp to v1.7.2
style.css was still served with ?v=20260421a so all CSS changes since
April 21 (scale indicator redesign, banner close button fix, etc.) were
invisible in the kiosk WebView — it used the cached old file.

Bumping manifest.json version 1.7.1→1.7.2 causes the auto-update
detector (_checkWebappUpdate) to fire on the kiosk: it compares the
loaded page version (1.7.1) with the server version (1.7.2), detects
a change, and shows the '⬆️ Aggiorna' badge so the user can reload.
2026-05-05 18:39:35 +00:00
dadaloop82 7ea5505a0d fix: scale indicator, logo crop, gateway LAN IP, setup spacing
webapp:
- Scale indicator: replace plain green dot with ⚖️ emoji + colored
  status badge (green/amber/grey/red); icon fades out when disconnected;
  tap shows a toast with device name + battery level
- Logo images: crop excess transparent padding from logo.png and
  logo_icon.png so content fills the frame at small display sizes
- style.css: reworked .scale-status-indicator CSS for emoji+badge

kiosk:
- SetupActivity: use device's real LAN IP for scale_gateway_url
  (was hardcoded 127.0.0.1 — only worked if server and kiosk run on
  the same machine); added getDeviceLanIp() helper (prefers wlan/eth)
- activity_setup.xml: reduce welcome step padding/margins so step 1
  fits on screen without scrolling; text sizes slightly reduced
- activity_setup.xml: fix feature bullet 'Bilancia Bluetooth via
  Gateway app' → 'Bilancia BLE integrata (nessuna app esterna)'
- strings.xml (en + it): rewrite all wizard_gateway_* strings to
  reflect integrated BLE service instead of external gateway APK
- ic_logo.png: regenerated at all densities from cropped source
2026-05-05 17:47:54 +00:00
dadaloop82 9cb29de1f0 kiosk: integrate BLE scale gateway + fix logo/branding
- Kiosk v1.6.0 (versionCode 10)
  - Integrate BLE scale gateway directly into kiosk app (no external app needed)
    - New scale/ package: BleScaleManager, GatewayWebSocketServer, ScaleProtocol, GatewayService
    - GatewayService: foreground service, runs BLE scan + WebSocket :8765 server
    - Auto-reconnect on BLE disconnect; protocol compatible with old gateway app
  - Setup step 4: replace gateway install flow with BLE device scan + selection (mandatory)
  - Permissions: added BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION (pre-S),
    FOREGROUND_SERVICE, FOREGROUND_SERVICE_CONNECTED_DEVICE
  - KioskActivity: replace launchGatewayInBackground() with startGatewayService()
  - checkForUpdates: remove gateway APK check (gateway is now internal)
  - Remove GATEWAY_PACKAGE / GATEWAY_DOWNLOAD_URL constants

- Logo / branding
  - logo.png + logo_icon.png: transparent background (no more black)
  - ic_logo.png regenerated in all densities
  - Removed house emoji (🏠) from web UI: favicon, bottom nav, setup wizard header
  - Removed 🏠 prefix from all translations (it/en/de) and manifest
  - Setup wizard: logo shown in language + welcome steps
  - Setup wizard: footer with credits ('Creato da Stimpfl Daniel • Open Source')
  - CSS: .nav-logo-icon for bottom nav logo sizing

- Scale Gateway v2.1.1 (versionCode 8)
  - Fix false update notification: replace == comparison with proper semverNewer()
    (was reporting 'update available' whenever tag != current, e.g. v2.1.0 != 2.1.0)
2026-05-05 17:24:24 +00:00
dadaloop82 30aa2db0b9 branding: add EverShelf logo to kiosk splash + web header/preloader
- assets/img/logo/logo.png: trimmed full logo (icon + text, transparent bg)
- assets/img/logo/logo_icon.png: icon-only crop (no text, for header)
- drawable-*/ic_logo.png: multi-density PNGs for Android splash (mdpi→xxxhdpi)
- activity_kiosk.xml: replace ic_launcher_foreground with ic_logo at 260dp,
  remove redundant 'EverShelf' text row (already in logo image)
- index.html: add logo_icon.png in header title, logo.png in preloader
- style.css: add .app-preloader-logo and .header-logo-icon rules
2026-05-05 16:37:57 +00:00
dadaloop82 d02e48543f chore: release v1.7.1
- Bump header-version in index.html to v1.7.1
- Bump manifest.json version to 1.7.1
- Update CHANGELOG with v1.7.1 release notes

Includes:
- Destructive confirm modal with 5s auto-countdown (throwAll, submitUseAll)
- Undo button visibility fix in history log
- undoTransactionEntry() uses custom modal instead of native confirm()
2026-05-04 19:50:39 +00:00
dadaloop82 2e46090adc fix(webapp): confirmation dialog + undo button visibility
- Add _showDestructiveConfirm() helper: shows modal with 5-second
  auto-confirm countdown bar; user can confirm early or cancel
- throwAll(): now shows confirmation before discarding all stock
- submitUseAll(): same confirmation before marking all as used
- undoTransactionEntry(): replace native confirm() with modal
- Rename core logic to _doSubmitUseAll() / _doUndoTransaction()
- btn-log-undo: more visible (red tint + larger font) so user can
  easily undo accidental operations from the history log
- Bump app.js version to v=20260505a
2026-05-04 18:30:30 +00:00
dadaloop82 bff22d43b4 fix(i18n): translate antiwaste title in IT/DE + use quantity validation
- translations: antiwaste.title = 'Rapporto Anti-Spreco' (IT), 'Anti-Verschwendungs-Bericht' (DE)
- translations: add use.error_exceeds_stock in IT/EN/DE
- submitUse(): block submit if qty > available at selected location + shake animation
- adjustUseQty(): cap '+' button at max available at selected location (incl. sub-unit mode)
- style.css: add @keyframes inputShake + .input-shake class

Bump app.js cache buster to v=20260504c
2026-05-04 16:56:37 +00:00
dadaloop82 874ae90afa fix(settings): fix screensaver toggle (add toggle-slider, correct layout)
- Replace checkbox-label with toggle-row pattern matching other toggles
- Add missing <span class="toggle-slider"></span> inside toggle-switch
- Add data-i18n attributes to card title and hint
- Add screensaver.card_title / card_hint translations in all 3 locales

feat(demo): full demo mode implementation
- _applyDemoModeUI(): set _geminiAvailable=true + call _updateGeminiButtonState()
- api(): no-op all bring_add/bring_remove/bring_set_spec calls in demo mode
- api(): return in-memory shoppingItems for bring_list in demo mode
- loadShoppingList(): show placeholder list in demo mode, skip all Bring! calls

fix(shopping): graceful Bring! missing credentials handling
- Show friendly message with link to settings instead of raw PHP error
- Add shopping.bring_not_configured i18n key in IT/EN/DE

Bump app.js cache buster to v=20260504b
2026-05-04 16:52:22 +00:00
dadaloop82 8d56d4a221 fix(settings): use correct toggle-switch markup for screensaver checkbox 2026-05-04 16:40:47 +00:00
dadaloop82 ab647f38d6 fix(cache): bump app.js version to force browser cache refresh 2026-05-04 16:38:54 +00:00
dadaloop82 108f3ef283 feat(settings): add screensaver toggle in Language tab (default off)
Toggle appears in the Language settings tab, below the language selector.
Default: disabled. When disabled, initInactivityWatcher() exits early so
the screensaver never activates. i18n added for it/en/de.
2026-05-04 16:17:36 +00:00
dadaloop82 bc0beea090 fix(header): full redesign — left-aligned title, uniform buttons, update badge
- Title always left-aligned (was centered via 3-col flex trick)
- In kiosk mode: exit/refresh buttons appear left of title via header-left
- All action buttons unified as .header-btn (42×42px, consistent style)
- Scan button: 48×48px + pulse animation to stand out from others
- Gemini button: no longer misuses header-scan-btn class; own indigo tint
- Scale status: same 42×42px .header-btn shape with colored .scale-dot
  inside instead of a tiny 22px standalone circle
- Update notification: uses #header-update-badge beside the title instead
  of replacing title innerHTML (title never disappears anymore)
- Fixed _scaleUpdateStatus() to preserve header-btn class on className reset
2026-05-04 16:12:43 +00:00
dadaloop82 f625e55526 fix: header 3-col layout, shopping_name migration, demo mode UI, kiosk buttons left
Header layout:
- Redesign header-content as 3-column flex (left / center / right)
- Add #header-left div: dedicated slot for kiosk buttons (empty by default)
- header-title: flex-shrink auto, no more position:absolute centering hack
- header-actions: flex:1 1 0 + justify-content:flex-end (right)
- header-left: flex:1 1 0 (left) — equal width balances the title visually

Kiosk exit/refresh buttons:
- _injectKioskOverlay() now appends to #header-left instead of
  insertBefore(firstChild) — buttons appear on LEFT, not mixed with center

DB migration:
- Add shopping_name TEXT DEFAULT '' to CREATE TABLE products schema
- Add ALTER TABLE migration in migrateDB() for existing databases
- Avoids repeated ALTER TABLE in seed code on every request

Demo mode UI:
- _applyDemoModeUI(): hides ⚙️ settings nav button in demo mode
- Suppresses first-run setup wizard when _demoMode === true
- Shows a small DEMO badge in header-left
- Called from both syncSettingsFromDB() and _initApp()
2026-05-04 12:52:29 +00:00
dadaloop82 bf27469228 security: fix 3 critical vulnerabilities
1. Remove raw API key from get_settings response
   - getServerSettings() no longer returns gemini_key in plain text
   - Only gemini_key_set (boolean) and settings_token_set (boolean)
   - JS updated to only check gemini_key_set (removes stale gemini_key fallback)

2. Protect save_settings with SETTINGS_TOKEN
   - If SETTINGS_TOKEN is set in .env, all save_settings calls must
     include matching X-Settings-Token header (uses hash_equals)
   - Empty token = no protection (backwards-compatible default)
   - Settings UI (Security tab) has a token input field
   - Wrong/missing token returns HTTP 403 with error 'unauthorized'
   - JS shows '🔒 Token non valido o mancante' on 403

3. DEMO_MODE native blocking in PHP
   - DEMO_MODE=false added to .env (default off)
   - When DEMO_MODE=true, all write actions return HTTP 403 before routing
   - Blocked: save_settings, product_save/delete/merge, inventory_add/use/update/remove,
     dismiss_anomaly, bring_add/remove/sync
   - demo_mode flag exposed via get_settings so JS can adapt UI
2026-05-04 06:20:23 +00:00
dadaloop82 6756b16ecb fix(ui): center header title on tablet + add skeleton loader to spesa stat card
Header title centering:
- .header-content: remove max-width:600px, use position:relative + justify-content:center
- .header-title: position:absolute; left:50%; transform:translateX(-50%)
  so the title is always at the exact center of the header regardless of
  screen width or how many action buttons are on the right
- Added max-width:calc(100% - 200px) to prevent overlap with action buttons
  on narrow screens

Spesa skeleton preloader:
- index.html: add stat-loading class to stat-spesa (was missing, other 3 had it)
- app.js showPage('dashboard'): add 'spesa' to the skeleton init array
- app.js loadShoppingCount(): remove stat-loading class after data loads
  (like loadDashboard() does for the other 3 locations)
2026-05-04 05:26:38 +00:00
dadaloop82 9e4a8323c3 chore: bump versions + update CHANGELOG/README for v1.6.0
Webapp:    v1.5.0 → v1.6.0
Kiosk:     v1.3.0 → v1.4.0 (versionCode 4→5)
Scale GW:  v2.0.0 → v2.1.0 (versionCode 6→7)

CI: build-scale-gateway.yml now also triggers on develop branch
    (was main-only, causing APK builds to not run on feature branches)

CHANGELOG: added [1.6.0] entry covering PackageInstaller OTA fixes,
  dashboard skeleton, update banners, cooking mode z-index, XOR token
README: updated 'Recent Updates' section with 1.6.0 highlights
2026-05-03 18:00:46 +00:00
dadaloop82 73fbb73974 fix: APK install conflict (PackageInstaller) + dashboard stat skeleton
APK install conflict:
- Replace ACTION_VIEW-based install with PackageInstaller.Session API (API 21+)
- PackageInstaller gives us the actual install status via BroadcastReceiver:
  STATUS_PENDING_USER_ACTION → launch system confirmation dialog automatically
  STATUS_SUCCESS → success toast
  STATUS_FAILURE_CONFLICT/INCOMPATIBLE → show AlertDialog offering to
    uninstall the old version (ACTION_DELETE) so user can re-download and install
- FileProvider no longer needed for install (still kept for other uses)
- Kiosk: derive target package from filename (gateway vs kiosk self-update)

Dashboard 0-flash:
- Replace hardcoded 0 in HTML stat-value spans with ... placeholder
- Add .stat-loading CSS class: shimmer skeleton animation (gradient sweep)
- showPage(dashboard): set ... + stat-loading before API call
- loadDashboard: remove stat-loading class and set real count after data arrives
2026-05-03 17:51:18 +00:00
dadaloop82 58e69625bd fix: preloader + update notification robustness
- Add full-screen CSS preloader to webapp (fades out when _initApp completes)
- Defer _checkWebappUpdate() to 6s after app init so it does not compete
  with startup API calls (fixes perceived slowness on first load)
- Switch update-check throttle from sessionStorage to localStorage (6h TTL);
  use release published_at instead of version string for comparison, so the
  banner correctly appears when a new release is published regardless of whether
  the tag is a semver or the rolling "latest" tag
- PHP _isLatestVersion(): return true (do not suppress error reports) when
  tag_name is non-semver (e.g. "latest") — was incorrectly blocking ALL reports
- Kiosk checkForUpdates(): show banner only when the release asset actually
  contains an APK for the component; handle non-semver tag by treating it
  as always-update (prevents silent no-op with rolling "latest" tag)
- Scale gateway checkForUpdates(): same non-semver fix; apkUrl now defaults
  to empty and bails out if no matching APK found in assets (prevents 404 install)
2026-05-03 17:46:42 +00:00
dadaloop82 a6c2fb93cf feat: offline OCR (Tesseract) + embedding category classifier (@xenova/transformers)
Tesseract OCR (PHP, server-side):
- Dockerfile: adds tesseract-ocr + tesseract-ocr-ita + libgd-dev (gd extension)
- api/index.php: new tesseractReadExpiry() — decodes base64 image, pre-processes with GD (2× upscale, greyscale, auto-contrast, sharpen), runs tesseract CLI with ita+eng PSM-6, extracts date with multi-pattern regex (DD/MM/YYYY, MM/YYYY, ISO, named-month), returns YYYY-MM-DD + confidence
- geminiReadExpiry() now: (1) tries Tesseract first; (2) falls back to Gemini Vision if OCR returns null or no date found; (3) passes source ('ocr'|'gemini') in response

@xenova/transformers embedding classifier (browser-side):
- index.html: ES-module bootstrap that lazy-loads 'Xenova/all-MiniLM-L6-v2' quantized (~23 MB, cached in browser) via window._getCategoryPipeline(); pre-warms on first scan page visit
- assets/js/app.js: classifyCategoryByEmbedding(name) — embeds product name + 16 category anchor descriptions, cosine similarity, threshold 0.30; results cached in _embeddingCache Map
- autoDetectCategory(): after keyword map misses, fires classifyCategoryByEmbedding async and updates select when resolved (respects manuallySet flag)
- createQuickProduct(): if regex returned 'altro', silently patches category with embedding result via a background api call
2026-05-03 13:17:14 +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 3c9fe7dfea Remove all Dupliclick/Spesa integration; merge annual waste info into status line 2026-04-29 16:52:36 +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 8558db1925 Complete i18n pass for recipes and meal plan labels 2026-04-28 17:28:54 +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 37299e60c9 fix: TTS voices — retry 10s, message on failure, refresh button
- Retry loop extended to 10s (50×200ms) for slow Android WebViews
- Show 'Nessuna voce disponibile' after timeout instead of infinite loader
- Show 'Voce non supportata dal browser' if speechSynthesis missing
- Reset to loading state on each settings open (fixes stale empty select)
- Add refresh button ↺ to force-reload voices manually
2026-04-26 17:28:05 +00:00
dadaloop82 546d4afd59 feat: SSE streaming recipe generation with live agent feedback
- Add generateRecipeStream() endpoint with real-time SSE status events
- Frontend generateRecipe() uses ReadableStream for live step updates
- Fix gemini-2.5-flash thinking model: disable thinkingBudget, raise maxOutputTokens to 4096
- Passo 2 is pure PHP heuristic (zero extra AI calls)
- Retry logic with live countdown on 429, fallback chain: 2.5-flash → 2.0-flash
- Pass all ingredients when meal plan is active (no limits)
- Add recipe-loading-msg element with CSS transition
2026-04-23 15:16:50 +00:00
dadaloop82 f4a62ef496 feat: anomaly detection banner - notifica incongruenze inventario/transazioni
- New API endpoint 'inventory_anomalies': detects items where stored qty
  differs from tx history by >20% AND >50 units (phantom qty or missing qty)
- New API endpoint 'dismiss_anomaly': persists dismissal in anomaly_dismissed.json
- Banner system: new 'anomaly' type shown in dashboard alert banner
  with 'Correggi' (opens edit) and 'Ok, ignora' (dismisses) buttons
- CSS: banner-anomaly style (orange gradient)
- Fix: lo zucchero azzerato (175g fantasma rimossi), aggiunto a Bring!
2026-04-21 12:34:54 +00:00
dadaloop82 5e01c0656c fix: bump cache-buster JS/CSS (v=20260420a) 2026-04-20 17:29:56 +00:00
dadaloop82 3901113e76 fix: Storico nav icon, recipe title in transaction notes and log display
- index.html: replace broken byte with 📋 emoji in Storico nav button
- submitRecipeUse: pass 'Ricetta: <title>' as notes to inventory_use API
  so every ingredient use is linked to the recipe in the DB
- loadLog: render recipe note as small italic line 🍳 below log-detail row
- style.css: add .log-recipe-note style (0.75rem, muted, italic)
2026-04-18 19:12:07 +00:00
dadaloop82 07bdfe6b87 fix: kiosk overlay, preferred use-location, scale reconnect, Bring! translation, smart cache invalidation
- Kiosk: replace header-inject overlay with position:fixed div appended to <html>
  so buttons appear regardless of SPA init timing
- Kiosk: bump versionCode 3→4, versionName 1.2.0→1.3.0
- Kiosk: add explicit signingConfigs block (debug keystore) to avoid signature
  mismatch on updates; update banner now shows uninstall instruction + 12s timeout
- Web: v1.4.0 → v1.5.0
- Preferred use-location: remember last N location choices per product; after 3+
  consistent picks auto-select and collapse location picker (with 'cambia' link)
- Scale: call updateScaleReadButtons() on every status change so live-box and
  read button appear instantly on reconnect without manual refresh
- Smart shopping cache: invalidate JSON cache file on every inventory_add and
  inventory_use so next shopping-page load always sees current stock
- isLowStock: conf threshold changed <= 1 → < 1 (1 full pack is not low stock)
- italianToBring: replace substring matching with whole-word matching (min 4 chars)
  to prevent 'gin' matching 'original', 'rum' matching 'crumble', etc.
  Philadelphia original was silently mapped to Gin and skipped as duplicate
- Storico: add undo support (transaction_undo endpoint, undone column, JS undo btn)
- LOG → Storico rename in UI, nav, translations
- Bring! sync: urgency-aware purchased blocklist TTL (critical 30m, high 90m, others 4h)
- forceSyncBring() button to clear all guards and re-sync from scratch
- Scale live-box: position:fixed CSS class, 1.6rem/800 value, direct ml display
- Recipe use modal: scale live-box with 10s stability + 5s auto-confirm countdown
- Recipe use modal: show recipe quantity as highlighted row in Usa popup
2026-04-18 18:50:15 +00:00
dadaloop82 9e2a24def4 feat: v1.3.0 — banner notifications, quick-access, swipe navigation, bug fixes
Added:
- Expired/expiring product banner alerts with use, throw, edit, dismiss actions
- Priority-sorted notifications (expired > expiring > suspicious qty > predictions)
- Touch swipe navigation for banner with dot indicators and arrow buttons
- Quick-access buttons on inventory (4 recent + 8 popular products)
- Auto-refresh banner every 5 min on dashboard
- Edit expiry dates directly from expired/expiring notifications

Fixed:
- Ignore negative BLE scale readings
- Banner re-appearing after edit (confirmation now persisted)
- False consumption predictions when inventory was manually edited
- Kiosk overlay no longer blocks web app header
2026-04-18 05:37:03 +00:00
dadaloop82 383ef1113d feat: kiosk APK download banner in settings page
- Green-themed banner between settings panels and Save button
- Direct download link to kiosk-latest release APK
- Auto-hidden when running inside Android WebView (kiosk mode)
- i18n: translations added for it/en/de
2026-04-16 16:00:40 +00:00
dadaloop82 3e25fcd5df feat: banner alerts, consumption predictions, scale improvements, kiosk app
- Banner notification system: suspicious quantities + consumption prediction alerts
- Consumption predictions API: tracks 90-day usage patterns, flags >30% deviations
- Scale stability timeout: 5s → 10s, auto-confirm remains 5s
- Scale integration in edit form: weigh button with inline live display
- Banner edit/weigh actions open edit form directly with scale activation
- Cooking mode: Italian aliases + stem-prefix matching for ingredients
- Recipe regeneration: tracks rejected ingredients for diversity
- Settings migration: localStorage → .env server-side storage
- Expiry priority: mandatory ≤3 days, recommended ≤7 days in recipes
- Scale bug fixes: clear stale weight, double-submit guard, cap deduction
- Android kiosk app (evershelf-kiosk): WebView + embedded BLE scale gateway
- Version bump to 1.4.0
2026-04-16 14:46:30 +00:00
dadaloop82 3ff91b3018 fix(scale): progress-bar restart loop + low-weight warning + gateway auto-reconnect cycle
- Fix progress-bar restarting continuously when weight is stable:
  add _cancelScaleTimersOnly() that stops timers/animations without
  _cancelScaleTimersOnly() so the same value resumes counting when
  stability returns instead of always restarting the 5-s wait.
  Add 'else if' branch in _scaleAutoFillUse / _scaleAutoFillRecipeUse
  to restart stability wait after brief instability for the same value.

- Show red blinking warning in scale-live-box when weight < 10 g:
  adds scale-low-weight CSS class with pulsing border/shadow animation,
  the label shows '< 10 g · inserisci manualmente' instead of the
  stability progress bar.  No auto-confirm fires below 10 g.

- Gateway Android app: scale auto-reconnect now retries indefinitely.
  isAutoReconnecting flag keeps the scan→wait→scan cycle running until
  the scale is found again; onScanStopped schedules a new scan after
  10 s whenever autoReconnect is active and scale is still offline.
2026-04-16 06:25:40 +00:00
dadaloop82 951ef1d64f fix(scale): auto-fill broken for conf products (e.g. latte)
Two root causes:
1. _useNormalUnit was stale ('pz') for conf products because it's only
   updated in normal mode — fix: resolve effective unit from
   _useConfMode.packageUnit when in sub mode
2. Food scales in liquid mode send unit='ml' directly — was falling
   through to raw value, skipping density; fix: detect scaleAlreadyMl
   flag to use ml directly for ml target, or apply density for g target
Also: add scale pre-fill call after switchUseUnit('sub') in conf mode
2026-04-15 21:12:37 +00:00
dadaloop82 7144ec7386 feat(scale): g→ml density conversion for liquid products
- _scaleDensityForProduct(): returns g/ml density based on product name/category
  olio oliva 0.91, girasole 0.92, spirits 0.94, aceto/panna 1.01, latte 1.03,
  yogurt 1.05, succo 1.04, miele/sciroppo 1.40, default 1.00 (water)
- _scaleAutoFillUse(): normalises all scale units to grams first, then converts
  to target unit; when unit=ml applies density; never touches pz/conf
2026-04-15 21:08:41 +00:00
dadaloop82 d26229800c feat(scale): auto-fill use-quantity live from scale reading
- _scaleAutoFillUse(): converts and fills use-quantity on stable readings
- _scaleLatestWeight pre-fills when use page opens (after unit is known)
- oninput on use-quantity pauses auto-fill when user types manually
- showUseForm() resets paused flag so next opening resumes auto-fill
- Small 'live' hint text shown while auto-filling, hidden when overridden
2026-04-15 21:06:12 +00:00
dadaloop82 6f5bc15734 fix: use relative API paths (leading / broke /dispensa/ subdir routing) 2026-04-15 21:00:34 +00:00
dadaloop82 55c5b34381 feat(scale): auto-discover gateway on local network
- api/scale_discover.php: async TCP scan of whole /24 subnet on port 8765,
  confirms with WebSocket handshake, returns found ws:// URLs in ~1.5s
- index.html: '🔍 Auto' button next to gateway URL field
- app.js: discoverScaleGateway() — calls relay, fills URL field and
  auto-saves settings + reconnects on success
2026-04-15 20:56:54 +00:00
dadaloop82 9523b68fea ci: add GitHub Actions workflow to build & release Scale Gateway APK
- Add .github/workflows/build-scale-gateway.yml
  Triggers on push to main (evershelf-scale-gateway/** path filter)
  Builds debug APK with Gradle/JDK 17, renames to evershelf-scale-gateway.apk
  Creates/updates 'latest' GitHub Release so the direct download URL resolves
- Bump web app version v1.2.0 -> v1.3.0 (index.html)
- Bump Android versionCode 1->2, versionName 1.0.0->1.3.0 (app/build.gradle.kts)
2026-04-14 16:29:00 +00:00
dadaloop82 0893742f05 feat: add smart scale BLE gateway integration
- Add evershelf-scale-gateway/ Android app (Kotlin):
  - BLE scanning and GATT connection to smart scales
  - Supports BT SIG Weight Scale (0x181D), Body Composition (0x181B), and generic heuristic parser
  - WebSocket server on port 8765 (local LAN)
  - Real-time weight broadcasting to EverShelf browser client
- Add scale status indicator in header (green/orange/grey dot)
- Add Settings tab for scale configuration (URL, enable toggle, test, APK download link)
- Add 'Read from scale' button in Add/Use forms when unit is g or ml
- Add scale WebSocket client logic in app.js with auto-reconnect
- Fix recipe suggestion: expiry-prioritized ingredients now only injected into
  AI prompt when user explicitly selects 'Priorità Scadenze' or 'Zero Sprechi'
- Update README with smart scale section and website link
- Update all translations (it, en, de) with scale strings
2026-04-14 15:59:40 +00:00
dadaloop82 0c6a07ee95 fix: preserve version badge during i18n translatePage
Move data-i18n to inner <span> so translatePage() with textContent
does not overwrite the sibling .header-version span.
2026-04-13 10:23:26 +00:00