From 3391106010ad442ff388f7857f3a355653695f42 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Mon, 11 May 2026 17:11:07 +0000 Subject: [PATCH] feat: banner opened items show 'aperto da X giorni in frigo' instead of 'scaduto' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- assets/js/app.js | 56 ++++++++++++++++++++++++++++++++++---------- index.html | 4 ++-- translations/de.json | 6 +++++ translations/en.json | 6 +++++ translations/it.json | 6 +++++ 5 files changed, 63 insertions(+), 15 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 8b95bb9..f6a108b 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -3746,10 +3746,32 @@ function renderBannerItem() { if (entry.type === 'expired') { const item = entry.data; const qtyDisplay = formatQuantity(item.quantity, item.unit, item.default_quantity, item.package_unit); - const daysText = item.days_expired === 0 - ? t('expiry.expired_today_long') - : t('expiry.expired_ago_long').replace('{n}', item.days_expired); + const isOpenedExpiry = !!item.opened_at; const safety = getExpiredSafety(item, item.days_expired); + + let daysText, suffix; + if (isOpenedExpiry) { + const todayMs = new Date(); todayMs.setHours(0, 0, 0, 0); + const daysSinceOpened = Math.round((todayMs - new Date(item.opened_at)) / 86400000); + daysText = daysSinceOpened === 0 + ? t('expiry.opened_today_long') + : t('expiry.opened_ago_long').replace('{n}', daysSinceOpened); + suffix = safety.level === 'ok' + ? t('expiry.opened_suffix_ok') + : safety.level === 'warning' + ? t('expiry.opened_suffix_warning') + : t('expiry.opened_suffix'); + } else { + daysText = item.days_expired === 0 + ? t('expiry.expired_today_long') + : t('expiry.expired_ago_long').replace('{n}', item.days_expired); + suffix = safety.level === 'ok' + ? t('expiry.expired_suffix_ok') + : safety.level === 'warning' + ? t('expiry.expired_suffix_warning') + : t('expiry.expired_suffix'); + } + if (safety.level === 'danger') { banner.className = 'alert-banner banner-expired banner-expired-danger'; iconEl.textContent = '🚫'; @@ -3760,16 +3782,24 @@ function renderBannerItem() { 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); - const locationTag = item.location ? ` · ${escapeHtml(item.location)}` : ''; - const expiryTag = item.expiry_date ? ` · scade il ${escapeHtml(item.expiry_date)}` : ''; - detailEl.innerHTML = `${baseDetail}${locationTag}${expiryTag} `; + titleEl.textContent = `${item.name}${item.brand ? ' (' + item.brand + ')' : ''} ${suffix}`; + + let baseDetail; + if (isOpenedExpiry) { + const locLabel = (LOCATIONS[item.location] + ? LOCATIONS[item.location].icon + ' ' + LOCATIONS[item.location].label + : (item.location || '')); + baseDetail = t('dashboard.banner_opened_detail') + .replace('{when}', daysText) + .replace('{location}', escapeHtml(locLabel)) + .replace('{qty}', qtyDisplay); + } else { + baseDetail = t('dashboard.banner_expired_detail').replace('{when}', daysText).replace('{qty}', qtyDisplay); + const locationTag = item.location ? ` · ${escapeHtml(item.location)}` : ''; + const expiryTag = item.expiry_date ? ` · ${escapeHtml(item.expiry_date)}` : ''; + baseDetail += locationTag + expiryTag; + } + detailEl.innerHTML = `${baseDetail} `; let btns = ''; if (safety.level !== 'danger') { btns += ``; diff --git a/index.html b/index.html index 322a21e..379634c 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ EverShelf - + @@ -1469,6 +1469,6 @@ - + diff --git a/translations/de.json b/translations/de.json index 9a14f74..a1e6457 100644 --- a/translations/de.json +++ b/translations/de.json @@ -142,6 +142,7 @@ "wasted": "Weggeworfen: {n} ({pct}%)", "more_opened": "und {n} weitere geöffnet...", "banner_expired_detail": "{when} · du hast noch {qty}.", + "banner_opened_detail": "{when} in {location} · du hast noch {qty}.", "banner_explain_title": "Gemini um eine Erklärung bitten", "banner_explain_btn": "Erklären", "banner_analyzing": "🤖 Analysiere…" @@ -693,6 +694,11 @@ "expired_suffix": "— Abgelaufen!", "expired_suffix_ok": "— Abgelaufen (noch ok)", "expired_suffix_warning": "— Abgelaufen (erst prüfen)", + "opened_ago_long": "Seit {n} Tagen geöffnet", + "opened_today_long": "Heute geöffnet", + "opened_suffix": "— Zu lange geöffnet!", + "opened_suffix_ok": "— Geöffnet (noch ok)", + "opened_suffix_warning": "— Geöffnet (erst prüfen)", "days_compact": "{n}T" }, "status": { diff --git a/translations/en.json b/translations/en.json index 05a1364..98f40f6 100644 --- a/translations/en.json +++ b/translations/en.json @@ -142,6 +142,7 @@ "wasted": "Wasted: {n} ({pct}%)", "more_opened": "and {n} more opened...", "banner_expired_detail": "{when} · you still have {qty}.", + "banner_opened_detail": "{when} in {location} · you still have {qty}.", "banner_explain_title": "Ask Gemini for an explanation", "banner_explain_btn": "Explain", "banner_analyzing": "🤖 Analyzing…" @@ -693,6 +694,11 @@ "expired_suffix": "— Expired!", "expired_suffix_ok": "— Expired (still ok)", "expired_suffix_warning": "— Expired (check first)", + "opened_ago_long": "Opened {n} days ago", + "opened_today_long": "Opened today", + "opened_suffix": "— Opened too long!", + "opened_suffix_ok": "— Opened (still ok)", + "opened_suffix_warning": "— Opened (check first)", "days_compact": "{n}d" }, "status": { diff --git a/translations/it.json b/translations/it.json index 496f05f..a9dd886 100644 --- a/translations/it.json +++ b/translations/it.json @@ -142,6 +142,7 @@ "wasted": "Buttati: {n} ({pct}%)", "more_opened": "e altri {n} prodotti aperti...", "banner_expired_detail": "{when} · hai ancora {qty}.", + "banner_opened_detail": "{when} in {location} · hai ancora {qty}.", "banner_explain_title": "Chiedi a Gemini una spiegazione", "banner_explain_btn": "Spiega", "banner_analyzing": "🤖 Analizzo…" @@ -693,6 +694,11 @@ "expired_suffix": "— Scaduto!", "expired_suffix_ok": "— Scaduto (ancora ok)", "expired_suffix_warning": "— Scaduto (controlla)", + "opened_ago_long": "Aperto da {n} giorni", + "opened_today_long": "Aperto oggi", + "opened_suffix": "— Aperto da troppo tempo!", + "opened_suffix_ok": "— Aperto (ancora ok)", + "opened_suffix_warning": "— Aperto (controlla)", "days_compact": "{n}gg" }, "status": {