Freezer-aware expired safety + shopping count in dashboard

- getExpiredSafety: items in freezer get bonus days (carne/pesce +3m, verdura/frutta +6m, latticini/pane +2m, altro +4m)
- Expired items show location icon (❄️/🧊) for context
- Dashboard: replaced 'Totale' stat card with 'Spesa' showing Bring! list count
- Added loadShoppingCount() to fetch count from Bring! API
This commit is contained in:
dadaloop82
2026-03-10 16:10:30 +00:00
parent 6e673963c9
commit 9fb2c7ab03
3 changed files with 54 additions and 20 deletions
+50 -16
View File
@@ -110,20 +110,40 @@ function guessCategoryFromName(name) {
function getExpiredSafety(item, daysExpired) {
const cat = mapToLocalCategory(item.category || '', item.name || '');
const loc = (item.location || '').toLowerCase();
const name = (item.name || '').toLowerCase();
// HIGH RISK categories - perishable, can be dangerous
// Latticini freschi, carne, pesce, verdura, frutta
const highRisk = ['latticini', 'carne', 'pesce', 'verdura', 'frutta'];
// MEDIUM RISK - check before consuming
// Pane, surgelati, bevande (fresh juices, milk)
const medRisk = ['pane', 'surgelati'];
// Items in frigo are more perishable
const inFreezer = loc === 'freezer';
const inFrigo = loc === 'frigo';
// === FREEZER: il congelamento allunga molto la vita ===
// Carne/pesce in freezer: +3 mesi. Verdura/frutta: +6 mesi. Pane: +2 mesi.
// Latticini in freezer: +1-2 mesi. Tutto il resto: +3-6 mesi.
if (inFreezer) {
const highRiskFreezer = ['carne', 'pesce'];
const medRiskFreezer = ['latticini', 'pane'];
const produceRiskFreezer = ['verdura', 'frutta'];
let bonusDays;
if (highRiskFreezer.includes(cat)) bonusDays = 90; // +3 mesi
else if (produceRiskFreezer.includes(cat)) bonusDays = 180; // +6 mesi
else if (medRiskFreezer.includes(cat)) bonusDays = 60; // +2 mesi
else bonusDays = 120; // +4 mesi default
const effectiveDays = daysExpired - bonusDays;
if (effectiveDays <= 0) {
return { level: 'ok', icon: '✅', label: 'OK', tip: `In freezer: ancora sicuro (~${bonusDays - daysExpired}g di margine)` };
}
if (effectiveDays <= 30) {
return { level: 'warning', icon: '👀', label: 'Controlla', tip: `In freezer da molto, potrebbe aver perso qualità. Consumare presto` };
}
return { level: 'danger', icon: '🗑️', label: 'Buttare', tip: 'In freezer da troppo tempo, rischio di bruciatura da gelo e degrado' };
}
// === FRIGO e DISPENSA ===
const highRisk = ['latticini', 'carne', 'pesce', 'verdura', 'frutta'];
const medRisk = ['pane', 'surgelati'];
if (highRisk.includes(cat)) {
if (daysExpired <= 2 && inFrigo) {
if (inFrigo && daysExpired <= 2) {
return { level: 'warning', icon: '👀', label: 'Controlla', tip: 'Scaduto da poco, controlla odore e aspetto prima di consumare' };
}
return { level: 'danger', icon: '🗑️', label: 'Buttare', tip: 'Prodotto deperibile scaduto: da buttare per sicurezza' };
@@ -139,9 +159,7 @@ function getExpiredSafety(item, daysExpired) {
return { level: 'danger', icon: '🗑️', label: 'Buttare', tip: 'Troppo tempo dalla scadenza, meglio buttare' };
}
// LOW RISK - long shelf life items
// Pasta, conserve, condimenti, cereali, snack, bevande confezionate
// "Da consumarsi preferibilmente entro" = TMC, safe well past expiry
// LOW RISK - lunga conservazione (pasta, conserve, condimenti, cereali, snack)
if (daysExpired <= 30) {
return { level: 'ok', icon: '✅', label: 'OK', tip: 'Prodotto a lunga conservazione, ancora sicuro da consumare' };
}
@@ -367,7 +385,8 @@ async function loadDashboard() {
total += s.product_count;
}
});
document.getElementById('stat-total').textContent = total || summary.reduce((a, s) => a + s.product_count, 0);
// Load shopping list count from Bring!
loadShoppingCount();
// Expiring items
const expiringSection = document.getElementById('alert-expiring');
@@ -407,10 +426,11 @@ async function loadDashboard() {
else if (days === 1) daysText = 'Da ieri';
else daysText = `Da ${days}g`;
const safety = getExpiredSafety(item, days);
const locIcon = item.location === 'freezer' ? '❄️' : item.location === 'frigo' ? '🧊' : '';
return `
<div class="alert-item expired-item alert-item-clickable" onclick="showAlertItemDetail(${item.id}, ${item.product_id})">
<div class="alert-item-info">
<span class="alert-item-name">${escapeHtml(item.name)}</span>
<span class="alert-item-name">${locIcon ? locIcon + ' ' : ''}${escapeHtml(item.name)}</span>
${item.brand ? `<span class="alert-item-brand">${escapeHtml(item.brand)}</span>` : ''}
</div>
<div class="alert-item-badges">
@@ -2180,6 +2200,20 @@ let shoppingListUUID = '';
let shoppingItems = [];
let suggestionItems = [];
// Load just the shopping count for dashboard stat card
async function loadShoppingCount() {
try {
const data = await api('bring_list');
if (data.success && data.items) {
document.getElementById('stat-spesa').textContent = data.items.length;
} else {
document.getElementById('stat-spesa').textContent = '-';
}
} catch {
document.getElementById('stat-spesa').textContent = '-';
}
}
async function loadShoppingList() {
const statusEl = document.getElementById('bring-status');
const currentEl = document.getElementById('shopping-current');
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -46,10 +46,10 @@
<span class="stat-value" id="stat-freezer">0</span>
<span class="stat-label">Freezer</span>
</div>
<div class="stat-card" onclick="showPage('inventory', '')">
<span class="stat-icon">📦</span>
<span class="stat-value" id="stat-total">0</span>
<span class="stat-label">Totale</span>
<div class="stat-card" onclick="showPage('shopping')">
<span class="stat-icon">🛒</span>
<span class="stat-value" id="stat-spesa">-</span>
<span class="stat-label">Spesa</span>
</div>
</div>