Updated At A Glance - re-order and add category colour shades
Pets image
This commit is contained in:
fraxle
2026-07-09 20:16:10 +01:00
parent aee039ad61
commit fb8547c8b8
4 changed files with 68 additions and 20 deletions
+5 -5
View File
@@ -1694,8 +1694,8 @@ export function UTCIForecast() {
${(() => {
const heatEvents = eventGlanceItems.filter(e => /heat caution|heat warning|extreme heat/i.test(e.label)).map(e => ({ ...e, alert: true }));
const otherEvents = eventGlanceItems.filter(e => !/heat caution|heat warning|extreme heat/i.test(e.label));
const renderRow = ({ icon, label, value, alert }, keyPrefix = '') => html`
<div class=${`insight-row${alert ? ' insight-row--alert' : ''}`} key=${`${keyPrefix}${label}`}>
const renderRow = ({ icon, label, value, alert, grp }, keyPrefix = '') => html`
<div class=${`insight-row${alert ? ' insight-row--alert' : ''}${grp ? ` insight-row--${grp}` : ''}`} key=${`${keyPrefix}${label}`}>
<span class="insight-icon">${icon}</span>
<span class="insight-label">${label}</span>
<span class="insight-value">${value}</span>
@@ -1705,8 +1705,8 @@ export function UTCIForecast() {
const hasFeltAnchor = glanceSummary.some(i => i.label === 'Peak felt temp');
const comfortItem = hasFeltAnchor ? glanceSummary.find(i => i.label === 'Comfortable window') : null;
const drivingItem = hasFeltAnchor ? glanceSummary.find(i => i.label === 'Good driving time') : null;
const climateRows = hasFeltAnchor ? glanceSummary.filter(i => i.label === '1991-2020 High' || i.label === '1991-2020 Average') : [];
const restSummary = glanceSummary.filter(i => !(hasFeltAnchor && (i.label === 'Comfortable window' || i.label === 'Good driving time' || i.label === '1991-2020 High' || i.label === '1991-2020 Average')));
const climateRows = glanceSummary.filter(i => i.label === '1991-2020 High' || i.label === '1991-2020 Average');
const restSummary = glanceSummary.filter(i => i.label !== '1991-2020 High' && i.label !== '1991-2020 Average' && !(hasFeltAnchor && (i.label === 'Comfortable window' || i.label === 'Good driving time')));
return [
...restSummary.flatMap(item => [
renderRow(item),
@@ -1715,11 +1715,11 @@ export function UTCIForecast() {
? [
...heatEvents.map(e => renderRow(e, 'ev-')),
...(drivingItem ? [renderRow(drivingItem)] : []),
...climateRows.map(item => renderRow(item)),
...(comfortItem ? [renderRow(comfortItem)] : []),
]
: []),
]),
...climateRows.map(item => renderRow(item)),
...otherEvents.map(e => renderRow(e, 'ev-')),
];
})()}
+39 -15
View File
@@ -479,6 +479,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Lightning risk',
value: `${lightningRisk} at ${hhmm(peakLightningRow.iso)}`,
alert: peakLightning >= 5,
grp: 'precip',
}] : [];
const moistureLabel = (v) => {
@@ -513,22 +514,20 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
if (taVals.length === 0) return [];
const dayHigh = Math.max(...taVals);
const dayMean = taVals.reduce((a, b) => a + b, 0) / taVals.length;
const fmt = (anom) => {
const mag = Math.abs(anom);
return mag < 0.5
? 'About average'
: `${anom >= 0 ? '+' : ''}${mag.toFixed(1)}° ${anom >= 0 ? 'warmer' : 'cooler'}`;
};
const out = [];
const nHigh = normals.high?.[doy];
if (nHigh != null) {
const a = dayHigh - nHigh;
out.push({ icon: '🌡', label: '1991-2020 High', value: fmt(a), alert: a >= 5 });
if (Math.abs(a) >= 3) {
out.push({ icon: '🌡', label: '1991-2020 High', value: `${a >= 0 ? '+' : ''}${Math.abs(a).toFixed(1)}° ${a >= 0 ? 'warmer' : 'cooler'}`, alert: a >= 5, grp: 'ambient' });
}
}
const nMean = normals.mean?.[doy];
if (nMean != null) {
const a = dayMean - nMean;
out.push({ icon: '🌡', label: '1991-2020 Average', value: fmt(a), alert: a >= 5 });
if (Math.abs(a) >= 3) {
out.push({ icon: '🌡', label: '1991-2020 Average', value: `${a >= 0 ? '+' : ''}${Math.abs(a).toFixed(1)}° ${a >= 0 ? 'warmer' : 'cooler'}`, alert: a >= 5, grp: 'ambient' });
}
}
return out;
})();
@@ -557,6 +556,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Good driving time',
value: formatWindows(drivingWins) ?? 'Drive with care',
alert: drivingWins.length === 0,
grp: 'felt',
};
// ── Farming ────────────────────────────────────────────────────────────
@@ -575,12 +575,14 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Best field work',
value: formatWindows(fieldWins) ?? 'No suitable window',
alert: fieldWins.length === 0,
grp: 'surface',
},
...(show('soilT') ? [{
icon: '🌱',
label: 'Soil warms to 10°C by',
value: soilWarmRow ? (hhmm(soilWarmRow.iso) === '00:00' ? 'All Day' : hhmm(soilWarmRow.iso)) : 'Not today',
alert: !soilWarmRow,
grp: 'surface',
}] : []),
...(show('precipProb') ? [rainItem] : []),
...lightningItem,
@@ -589,6 +591,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Soil moisture',
value: moistureLabel(soilM) ?? '-',
alert: soilM != null && (soilM < 0.10 || soilM > 0.50),
grp: 'surface',
}] : []),
...(weekDays ? computeCropAdvice(weekDays, lat) : []),
...(show('pollen') && maxPollen >= 10 ? [{
@@ -596,6 +599,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Grass pollen',
value: pollenLabel(maxPollen),
alert: maxPollen >= 50,
grp: 'airqual',
}] : []),
];
}
@@ -612,11 +616,13 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Peak cabin temp',
value: peakCabin ? `${Math.round(peakCabin.vehicleT)}° at ${hhmm(peakCabin.iso)}` : '-',
alert: !!(peakCabin && peakCabin.vehicleT > 31.5),
grp: 'felt',
}, {
icon: '🧒',
label: 'Children/pets in car',
value: dangerFrom ? `Unsafe from ${hhmm(dangerFrom.iso)}` : 'Safe all day',
alert: !!dangerFrom,
grp: 'felt',
}] : []),
...(show('precipProb') ? [rainItem] : []),
...lightningItem,
@@ -630,6 +636,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
return best ? `${hhmm(best.iso)} (${Math.round(best.utciAdj)}° felt)` : '-';
})(),
alert: false,
grp: 'felt',
},
];
}
@@ -651,18 +658,21 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Peak indoor (unmanaged)',
value: peakIndoor ? `${Math.round(peakIndoor.indoorT)}° at ${hhmm(peakIndoor.iso)}` : '-',
alert: !!(peakIndoor && peakIndoor.indoorT >= 28),
grp: 'felt',
}] : []),
...(show('managedT') ? [{
icon: '🌡',
label: 'Peak indoor (managed)',
value: peakManaged ? `${Math.round(peakManaged.managedT)}°` : '-',
alert: !!(peakManaged && peakManaged.managedT >= 28),
grp: 'felt',
}] : []),
...(show('indoorT') ? [{
icon: '🪟',
label: 'Open windows',
value: formatWindows(ventWins) ?? 'Keep closed',
alert: false,
grp: 'felt',
}] : []),
...lightningItem,
...(show('aqi') ? [{
@@ -676,6 +686,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Pollen',
value: pollenLabel(maxPollen),
alert: maxPollen >= 50,
grp: 'airqual',
}] : []),
];
}
@@ -695,24 +706,28 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Sunsoak for pet',
value: peakFur ? `${Math.round(peakFur.furSurfaceT)}° at ${hhmm(peakFur.iso)}` : '-',
alert: !!(peakFur && peakFur.furSurfaceT >= 45),
grp: 'surface',
}] : []),
...(show('petShadeT') ? [{
icon: '🌳',
label: 'Shade for pet',
value: peakShade ? `${Math.round(peakShade.shadeT)}° at ${hhmm(peakShade.iso)}` : '-',
alert: !!(peakShade && peakShade.shadeT >= 26),
grp: 'ambient',
}] : []),
...(show('petHomeT') ? [{
icon: '🏠',
label: 'Pet at home',
value: peakIndoor ? `${Math.round(peakIndoor.indoorT)}° at ${hhmm(peakIndoor.iso)}` : '-',
alert: !!(peakIndoor && peakIndoor.indoorT >= 26),
grp: 'felt',
}] : []),
...(show('vehicleT') ? [{
icon: '🧒',
label: 'Kids/pets in car',
value: carDanger ? `Unsafe from ${hhmm(carDanger.iso)}` : 'Safe all day',
alert: !!carDanger,
grp: 'felt',
}] : []),
...lightningItem,
...(show('aqi') ? [{
@@ -720,12 +735,14 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Air quality',
value: peakAqi > 0 ? aqiLabel(peakAqi) : '-',
alert: peakAqi >= 60,
grp: 'airqual',
}] : []),
...(show('pollen') && maxPollen >= 10 ? [{
icon: '🌼',
label: 'Pollen',
value: pollenLabel(maxPollen),
alert: maxPollen >= 50,
grp: 'airqual',
}] : []),
];
}
@@ -749,6 +766,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: `Best ${variant} window`,
value: formatWindows(coolWins) ?? 'No cool window today',
alert: coolWins.length === 0,
grp: 'felt',
},
...(show('precipProb') ? [rainItem] : []),
...lightningItem,
@@ -757,6 +775,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'UV burn time (peak)',
value: burnMins,
alert: !!(peakUvRow && peakUvRow.uv >= 6),
grp: 'solar',
}] : []),
...(show('aqi') ? [{
icon: '💨',
@@ -793,18 +812,21 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
label: 'Comfortable window',
value: formatWindows(comfortWins) ?? 'No comfortable window',
alert: comfortWins.length === 0,
grp: 'felt',
},
{
icon: '🌡',
label: 'Peak felt temp',
value: peakFelt ? `${Math.round(peakFelt.utciAdj)}° at ${hhmm(peakFelt.iso)}` : '-',
alert: !!(peakFelt && (peakFelt.utciAdj >= 32 || peakFelt.utciAdj < 0)),
grp: 'felt',
},
...((show('burn') || show('uvA')) && burnMins ? [{
icon: '☀',
label: 'UV burn time (peak)',
value: burnMins,
alert: !!(peakUvRow && peakUvRow.uv >= 6),
grp: 'solar',
}] : []),
...(show('precipProb') ? [rainItem] : []),
...lightningItem,
@@ -812,13 +834,15 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
icon: '💨',
label: 'Air quality',
value: peakAqi > 0 ? aqiLabel(peakAqi) : '-',
alert: peakAqi >= 60,
}] : []),
...(show('pollen') && maxPollen >= 10 ? [{
alert: peakAqi >= 60,
grp: 'airqual',
}] : []),
...(show('pollen') && maxPollen >= 10 ? [{
icon: '🌿',
label: 'Pollen',
value: pollenLabel(maxPollen),
alert: maxPollen >= 50,
grp: 'airqual',
}] : []),
];
}
@@ -894,11 +918,11 @@ export function computeCropAdvice(weekDays, lat) {
const ready = sowSeason.filter(c =>
weekSoilT != null && weekSoilT >= c.sow.minSoilT && !groundWet);
out.push(ready.length ? {
icon: '🌱', label: 'Good to sow', value: listCrops(ready), alert: false,
icon: '🌱', label: 'Good to sow', value: listCrops(ready), alert: false, grp: 'surface',
} : {
icon: '🌱', label: 'Good to sow',
value: groundWet ? 'Hold off — ground too wet' : 'Hold off — soil still cold',
alert: true,
alert: true, grp: 'surface',
});
}
@@ -911,10 +935,10 @@ export function computeCropAdvice(weekDays, lat) {
out.push(ready.length ? {
icon: '🌾', label: 'Good to harvest',
value: dryDriven ? `${listCrops(ready)} (${dryRange()})` : listCrops(ready),
alert: false,
alert: false, grp: 'surface',
} : {
icon: '🌾', label: 'Good to harvest',
value: 'Hold — too wet to harvest grain', alert: true,
value: 'Hold — too wet to harvest grain', alert: true, grp: 'surface',
});
}