diff --git a/assets/css/style.css b/assets/css/style.css index 50f4b39..4d67d64 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -499,6 +499,14 @@ body { } .aw-cmp-legend-you { color: #16a34a; } .aw-cmp-legend-avg { color: #f97316; } +/* Annual totals + range note */ +.aw-cmp-range { + font-size: 0.67rem; + color: #6b7280; + margin: 1px 0 4px; + padding: 0; + text-align: center; +} /* Inline status below bars */ .aw-status-inline { font-size: 0.75rem; diff --git a/assets/js/app.js b/assets/js/app.js index 44c4c57..3a86a66 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2076,9 +2076,9 @@ function showPage(pageId, param = null) { // ===== ANTI-WASTE SECTION ===== const WASTE_BENCHMARKS = { - it: { avgWasteRate: 22, avgKgMonth: 5.4, costPerKg: 8.2, currency: 'โ‚ฌ', countryKey: 'antiwaste.country_it' }, - de: { avgWasteRate: 20, avgKgMonth: 6.5, costPerKg: 7.7, currency: 'โ‚ฌ', countryKey: 'antiwaste.country_de' }, - en: { avgWasteRate: 30, avgKgMonth: 9.2, costPerKg: 8.5, currency: '$', countryKey: 'antiwaste.country_en' }, + it: { avgWasteRate: 22, avgKgMonth: 5.4, costPerKg: 8.2, currency: 'โ‚ฌ', countryKey: 'antiwaste.country_it', rangeMin: 8, rangeMax: 36 }, + de: { avgWasteRate: 20, avgKgMonth: 6.5, costPerKg: 7.7, currency: 'โ‚ฌ', countryKey: 'antiwaste.country_de', rangeMin: 7, rangeMax: 34 }, + en: { avgWasteRate: 30, avgKgMonth: 9.2, costPerKg: 8.5, currency: '$', countryKey: 'antiwaste.country_en', rangeMin: 12, rangeMax: 50 }, }; const _AW_KG_PER_EVENT = 0.5; let _awRefreshTimer = null; @@ -2291,7 +2291,6 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60, const savedEvents = Math.max(0, avgWastedEvents - wasted30); const savedKg = +(savedEvents * _AW_KG_PER_EVENT).toFixed(1); const savedMoney = Math.round(savedKg * bm.costPerKg); - const savedMeals = Math.round(savedKg * 2.5); const savedCO2 = +(savedKg * 2.5).toFixed(1); // Status @@ -2313,21 +2312,16 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60, const youPct = +((myRate / scale) * 88).toFixed(1); // your bar, same scale const youLabel = t('antiwaste.you').split(' ')[0]; // "Tu" / "You" / "Du" - // Trend cards - const totals = [usedP60 + wastedP60, usedP30 + wastedP30, total30]; - const rates = totals.map((tot, i) => { - const w = [wastedP60, wastedP30, wasted30][i]; - return tot > 0 ? Math.round((w / tot) * 100) : null; - }); - const labels = [t('antiwaste.months_ago_2'), t('antiwaste.months_ago_1'), t('antiwaste.this_month')]; - const maxTrend = Math.max(...rates.filter(r => r !== null), 5); - const hasTrend = rates[0] !== null || rates[1] !== null; + // Annual totals for comparison bar + const myAnnualKg = Math.round(wasted30 * _AW_KG_PER_EVENT * 12); + const avgAnnualKg = Math.round(bm.avgKgMonth * 12); + const annualInfo = t('antiwaste.annual_info') + .replace('{you}', myAnnualKg) + .replace('{avg}', avgAnnualKg) + .replace('{min}', bm.rangeMin) + .replace('{max}', bm.rangeMax); - const arr1 = _awTrendArrow(rates[0], rates[1]); - const arr2 = _awTrendArrow(rates[1], rates[2]); - const arrowHtml = a => a ? `${a.sym}` : ''; - - // Build all badge objects (shown 4 at a time, rotated every hour) + // Build all badge objects (shown 4 at a time, rotated every 5 min) const diffPct = avgRate - myRate; const allBadges = []; allBadges.push(` @@ -2342,10 +2336,6 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60, ๐Ÿ’ฐ ${bm.currency}${savedMoney}/m${t('antiwaste.badge_saved_money')} `); - if (savedMeals > 0) allBadges.push(` - ๐Ÿฅ— - ${savedMeals}${t('antiwaste.badge_meals')} - `); if (savedCO2 > 0) allBadges.push(` ๐ŸŒ โˆ’${savedCO2} kgCOโ‚‚ @@ -2384,19 +2374,12 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60, โ–ฎ ${youLabel} ${myRate}% ${country} ${avgRate}% โ–ฎ +

${annualInfo}

${statusMsg}

${allBadges.length > 0 ? `
${initBadges}
` : ''} - ${hasTrend ? `
- ${_awTrendCard(rates[0], labels[0], maxTrend)} - ${arrowHtml(arr1)} - ${_awTrendCard(rates[1], labels[1], maxTrend)} - ${arrowHtml(arr2)} - ${_awTrendCard(rates[2], labels[2], maxTrend)} -
` : ''} -
๐Ÿ’ก ${facts[factIdx]} @@ -2453,7 +2436,7 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60, el.textContent = facts[idx]; el.classList.remove('aw-fact-fade'); }, 420); - }, 6000); + }, 5 * 60_000); } } @@ -11178,6 +11161,10 @@ function generateScreensaverFact() { }); } + // --- Anti-waste knowledge facts --- + const awFacts = _awGetFacts(); + for (const f of awFacts) { facts.push(() => f); } + // Pick a random fact if (facts.length === 0) { return `${greeting}! La tua Dispensa ti aspetta.`; diff --git a/translations/de.json b/translations/de.json index 60c8089..ba5a540 100644 --- a/translations/de.json +++ b/translations/de.json @@ -683,8 +683,8 @@ "grade_label": "Note", "you": "Du", "avg_label": "ร˜", - "better": "๐ŸŽ‰ Du verschwendest {diff}% weniger als der {country}!", - "worse": "โš ๏ธ Du verschwendest mehr als der {country}. Verbesserungspotenzial!", + "better": "๐ŸŽ‰ Du verlierst {diff}% weniger als der {country}!", + "worse": "โš ๏ธ Du verlierst mehr als der {country}. Verbesserungspotenzial!", "on_par": "โ†’ Du liegst beim {country}. Du kannst noch besser werden!", "saved_money": "~{amount}/Monat gespart", "saved_meals": "~{n} Mahlzeiten gerettet", @@ -700,10 +700,10 @@ "live_on": "Live-Daten", "live_off": "Offline", "meals": "Mahlzeiten", - "badge_rate": "Abfallquote", + "annual_info": "๐Ÿ“… Du ~{you} kg/Jahr ยท ร˜ ~{avg} kg/Jahr ยท Spanne {min}โ€“{max}%", + "badge_rate": "Verlustquote", "badge_saved_money": "gespart vs ร˜", - "badge_meals": "Mahlzeiten gerettet", - "badge_wasted": "verschwendet", + "badge_wasted": "verloren", "badge_better": "weniger als ร˜" }, "error": { diff --git a/translations/en.json b/translations/en.json index b1bb40f..cdef7ce 100644 --- a/translations/en.json +++ b/translations/en.json @@ -682,8 +682,8 @@ "grade_label": "Grade", "you": "You", "avg_label": "Avg", - "better": "๐ŸŽ‰ You waste {diff}% less than the {country}!", - "worse": "โš ๏ธ You waste more than the {country}. Let's improve!", + "better": "๐ŸŽ‰ You lose {diff}% less than the {country}!", + "worse": "โš ๏ธ You lose more than the {country}. Room for improvement!", "on_par": "โ†’ You're at the {country}. You can do better!", "saved_money": "~{amount}/month saved", "saved_meals": "~{n} meals saved", @@ -699,10 +699,10 @@ "live_on": "Live data", "live_off": "Offline", "meals": "meals", - "badge_rate": "waste rate", + "annual_info": "๐Ÿ“… You ~{you} kg/yr ยท avg ~{avg} kg/yr ยท range {min}โ€“{max}%", + "badge_rate": "loss rate", "badge_saved_money": "saved vs avg", - "badge_meals": "meals saved", - "badge_wasted": "items wasted", + "badge_wasted": "items lost", "badge_better": "less than avg" }, "error": { diff --git a/translations/it.json b/translations/it.json index 728cb5f..77e22b5 100644 --- a/translations/it.json +++ b/translations/it.json @@ -682,8 +682,8 @@ "grade_label": "Voto", "you": "Tu", "avg_label": "Media", - "better": "๐ŸŽ‰ Sprechi il {diff}% in meno della {country}!", - "worse": "โš ๏ธ Sprechi piรน della media {country}. Puoi migliorare!", + "better": "๐ŸŽ‰ Perdi il {diff}% in meno della {country}!", + "worse": "โš ๏ธ Perdi piรน della media {country}. Puoi migliorare!", "on_par": "โ†’ Sei nella media {country}. Prova a fare ancora meglio!", "saved_money": "~{amount}/mese risparmiati", "saved_meals": "~{n} pasti salvati", @@ -699,10 +699,10 @@ "live_on": "Dati in tempo reale", "live_off": "Offline", "meals": "pasti", - "badge_rate": "tasso spreco", + "annual_info": "๐Ÿ“… Tu ~{you} kg/anno ยท media ~{avg} kg/anno ยท intervallo {min}โ€“{max}%", + "badge_rate": "tasso perdita", "badge_saved_money": "risparmio vs media", - "badge_meals": "pasti salvati", - "badge_wasted": "prod. sprecati", + "badge_wasted": "prod. persi", "badge_better": "in meno vs media" }, "error": {