From aaf9323ba5a4ecde46c305a2df8afeae7d45ab20 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Sat, 16 May 2026 20:32:51 +0000 Subject: [PATCH] fix: opened-but-not-edible items missing from banner + remove confusing strikethrough MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Banner (loadBannerAlerts): add step 1b — any item from statsData.opened with is_edible=false is queued as expired even when client-side getExpiredSafety would consider it 'ok' (e.g. conserve <30d past expiry). Applies to all product types, not just conserve. Requires fetching stats in the same Promise.all (no extra round-trip since loadDashboard already calls stats separately). - CSS: remove text-decoration:line-through from .alert-item-spoiled .alert-item-name. The badge (⛔/⚠️) already communicates the state; strikethrough added no information and confused users into thinking the item had been deleted. --- assets/css/style.css | 1 - assets/js/app.js | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 28f963a..9372da4 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -5751,7 +5751,6 @@ body.cooking-mode-active .app-header { opacity: 0.75; } .alert-item-spoiled .alert-item-name { - text-decoration: line-through; color: var(--text-light); } diff --git a/assets/js/app.js b/assets/js/app.js index effcc47..ac6ec7d 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -3926,11 +3926,12 @@ async function loadBannerAlerts() { if (!banner) { _bannerLoading = false; console.warn('[Banner] #alert-banner not found'); return; } try { - const [invData, predData, anomalyData, finishedData] = await Promise.all([ + const [invData, predData, anomalyData, finishedData, statsData] = await Promise.all([ api('inventory_list'), api('consumption_predictions').catch(err => { console.warn('[Banner] predictions fetch failed:', err); return { predictions: [] }; }), api('inventory_anomalies').catch(err => { console.warn('[Banner] anomalies fetch failed:', err); return { anomalies: [] }; }), api('inventory_finished_items').catch(err => { console.warn('[Banner] finished_items fetch failed:', err); return { finished: [] }; }), + api('stats').catch(() => ({ opened: [] })), ]); const items = invData.inventory || []; const confirmed = getReviewConfirmed(); @@ -3971,6 +3972,18 @@ async function loadBannerAlerts() { _queuedItemIds.add(item.id); }); + // 1b. Opened items the SERVER considers not edible (is_edible=false from stats). + // The client-side getExpiredSafety check above uses conservative thresholds (e.g. + // conserve are 'ok' for 30 days past), but the server uses product-specific AI shelf + // life. Trust the server: any opened item with is_edible=false that isn't already + // queued goes into the banner as expired. + const openedNotEdible = (statsData.opened || []).filter(oi => !oi.is_edible && !_queuedItemIds.has(oi.id) && !confirmed['exp_' + oi.id]); + openedNotEdible.forEach(oi => { + const daysOI = Math.abs(oi.days_to_expiry ?? 0); + _bannerQueue.push({ type: 'expired', data: { ...oi, days_expired: daysOI } }); + _queuedItemIds.add(oi.id); + }); + // 2. Suspicious quantities ("expiring soon" shown only in dashboard sections, not in banner) // Group items by product identity to detect sibling entries in other locations. // A "low quantity" alert is suppressed when other stock of the same product exists