From 8359b14931d0996b86c56461170900d6cc126813 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Thu, 30 Apr 2026 05:21:50 +0000 Subject: [PATCH] Banner: adapt expired icon/color/title to safety level (non-alarmist) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ok level (long-life/freezer safe): green banner, βœ… icon, 'Scaduto (ancora ok)' - warning level: amber banner, πŸ‘€ icon, 'Scaduto (controlla)' - danger level: unchanged red 🚫 banner - Added banner-expired-ok / banner-expired-warning CSS variants - Added expiry.expired_suffix_ok / expired_suffix_warning i18n keys (IT/EN/DE) - Updated README and CHANGELOG --- CHANGELOG.md | 10 ++++++++++ README.md | 5 +++-- assets/css/style.css | 22 ++++++++++++++++++++++ assets/js/app.js | 21 ++++++++++++++++----- translations/de.json | 2 ++ translations/en.json | 2 ++ translations/it.json | 2 ++ 7 files changed, 57 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b7bb16..4654f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to EverShelf will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] - 2026-04-30 + +### Changed +- **Non-alarmist expired banner** β€” Banner icon, CSS class, and title suffix now adapt to the `getExpiredSafety()` level: + - `ok` (long-life products, freezer within margin): green banner, βœ… icon, "β€” Scaduto (ancora ok)" + - `warning` (items that should be inspected): amber/yellow banner, πŸ‘€ icon, "β€” Scaduto (controlla)" + - `danger` (raw meat, dairy, fish, etc.): unchanged red 🚫 banner and "β€” Scaduto!" title +- Added `expiry.expired_suffix_ok` and `expiry.expired_suffix_warning` i18n keys to all three language files (IT/EN/DE) +- Added `banner-expired-ok` and `banner-expired-warning` CSS variants (green / amber) in `style.css` + ## [1.5.0] - 2026-04-28 ### Added diff --git a/README.md b/README.md index 746282e..797ac7e 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ --- -## 🌍 Recent i18n Updates +## 🌍 Recent Updates +- **Non-alarmist expired banner** β€” The expired-product banner now adapts its icon, colour, and title to the actual safety level: green βœ… for long-life products that are still safe, amber πŸ‘€ for items that should be checked, and the original red 🚫 only for genuinely dangerous items (raw meat, dairy, fish). Low-risk products like canned tomatoes or pasta are no longer shown with a scary red banner. - Recipe and meal-plan labels now resolve at runtime from translations, preventing raw placeholders like `meal_types.*` and `meal_plan_types.*` from appearing in the UI. - Recipe generation now receives the selected app language (`it`/`en`/`de`) and enforces localized output in both streaming and non-streaming API flows. - Added missing shared error keys (`error.network`, `error.no_api_key`) across all language files to keep fallback/error toasts fully translated. @@ -64,7 +65,7 @@ - **Opened products panel** β€” Tracks partially-used items; expiry is recalculated from the opening date using AI (Gemini) + per-category rule fallback; whole sealed packages always keep their original manufacturer expiry; conf items with mixed whole + fractional units are shown as two separate entries - **Freezer shelf-life** β€” Granular per-product estimates (USDA/EFSA): fish 120 d, poultry 270 d, whole red-meat cuts 365 d, mince 120 d, vegetables/fruit 270 d, generic 180 d; AI + cache still take priority over rules - **Safety ratings** β€” Smart assessment of expired product safety (by category and location); expired unsafe items shown with a red danger banner and "L'ho buttato" as the primary action -- **Expired product banner** β€” Products that have passed their effective shelf-life (including opened-product reduced expiry) appear in the top notification banner with safety tip, danger styling for high-risk items, and a prominent discard action +- **Expired product banner** β€” Products that have passed their effective shelf-life (including opened-product reduced expiry) appear in the top notification banner; icon, colour and title adapt to the actual safety level (βœ… green for safe, πŸ‘€ amber to check, 🚫 red for danger); high-risk items get a prominent discard action - **Quick recipe bar** β€” One-tap recipe suggestion using expiring products - **Anomaly banner** β€” Scrollable banner with suspicious quantities and consumption prediction mismatches, with one-tap correction or inline edit - **Expired/expiring alerts** β€” Priority-sorted banner notifications for expired and soon-to-expire products with use, throw, edit, and dismiss actions diff --git a/assets/css/style.css b/assets/css/style.css index 5ebdf2a..5b8cefe 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -5641,6 +5641,28 @@ body { background: #fee2e2; color: #dc2626; } +.alert-banner.banner-expired-ok { + background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); + border-color: #16a34a; +} +.banner-expired-ok .alert-banner-title { + color: #14532d; +} +.banner-expired-ok .alert-banner-counter { + color: #15803d; +} +.banner-expired-ok .banner-dot.active { background: #16a34a; } +.alert-banner.banner-expired-warning { + background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%); + border-color: #d97706; +} +.banner-expired-warning .alert-banner-title { + color: #78350f; +} +.banner-expired-warning .alert-banner-counter { + color: #92400e; +} +.banner-expired-warning .banner-dot.active { background: #d97706; } .alert-banner.banner-expired-danger { background: linear-gradient(135deg, #fca5a5 0%, #f87171 100%); border-color: #b91c1c; diff --git a/assets/js/app.js b/assets/js/app.js index 2f1a433..f0096aa 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2840,11 +2840,22 @@ function renderBannerItem() { ? t('expiry.expired_today_long') : t('expiry.expired_ago_long').replace('{n}', item.days_expired); const safety = getExpiredSafety(item, item.days_expired); - banner.className = safety.level === 'danger' - ? 'alert-banner banner-expired banner-expired-danger' - : 'alert-banner banner-expired'; - iconEl.textContent = '🚫'; - titleEl.textContent = `${item.name}${item.brand ? ' (' + item.brand + ')' : ''} ${t('expiry.expired_suffix')}`; + if (safety.level === 'danger') { + banner.className = 'alert-banner banner-expired banner-expired-danger'; + iconEl.textContent = '🚫'; + } else if (safety.level === 'warning') { + banner.className = 'alert-banner banner-expired banner-expired-warning'; + iconEl.textContent = 'πŸ‘€'; + } else { + banner.className = 'alert-banner banner-expired banner-expired-ok'; + iconEl.textContent = 'βœ…'; + } + const expiredSuffix = safety.level === 'ok' + ? t('expiry.expired_suffix_ok') + : safety.level === 'warning' + ? t('expiry.expired_suffix_warning') + : t('expiry.expired_suffix'); + titleEl.textContent = `${item.name}${item.brand ? ' (' + item.brand + ')' : ''} ${expiredSuffix}`; const baseDetail = t('dashboard.banner_expired_detail').replace('{when}', daysText).replace('{qty}', qtyDisplay); detailEl.innerHTML = `${baseDetail} `; let btns = ''; diff --git a/translations/de.json b/translations/de.json index 81221ca..11b66fd 100644 --- a/translations/de.json +++ b/translations/de.json @@ -641,6 +641,8 @@ "expired_today_long": "Heute abgelaufen", "expired_ago_long": "Seit {n} Tagen abgelaufen", "expired_suffix": "β€” Abgelaufen!", + "expired_suffix_ok": "β€” Abgelaufen (noch ok)", + "expired_suffix_warning": "β€” Abgelaufen (erst prΓΌfen)", "days_compact": "{n}T" }, "status": { diff --git a/translations/en.json b/translations/en.json index 3b4cb5f..f05e281 100644 --- a/translations/en.json +++ b/translations/en.json @@ -640,6 +640,8 @@ "expired_today_long": "Expired today", "expired_ago_long": "Expired {n} days ago", "expired_suffix": "β€” Expired!", + "expired_suffix_ok": "β€” Expired (still ok)", + "expired_suffix_warning": "β€” Expired (check first)", "days_compact": "{n}d" }, "status": { diff --git a/translations/it.json b/translations/it.json index 2f8d458..9f2efd9 100644 --- a/translations/it.json +++ b/translations/it.json @@ -640,6 +640,8 @@ "expired_today_long": "Scaduto oggi", "expired_ago_long": "Scaduto da {n} giorni", "expired_suffix": "β€” Scaduto!", + "expired_suffix_ok": "β€” Scaduto (ancora ok)", + "expired_suffix_warning": "β€” Scaduto (controlla)", "days_compact": "{n}gg" }, "status": {