Add new quick summary panels and update layout to make space for them
This commit is contained in:
fraxle
2026-06-02 00:17:18 +01:00
parent 6734422673
commit 099f2924d3
6 changed files with 648 additions and 63 deletions
+110 -56
View File
@@ -33,6 +33,7 @@ import { getCellTagEvents, getUpcomingEvents } from './events.js';
import { POLLEN_TYPES, COL_DESCRIPTIONS, UTCI_ENVIRONMENTS } from './config.js';
import { useAppState } from './hooks/useAppState.js';
import { DayTabs } from './components/DayTabs.js';
import { computeWhyFeelsLike, computeGlanceSummary } from './compute.js';
const html = htm.bind(h);
@@ -166,6 +167,16 @@ export function UTCIForecast() {
return raw.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66)));
};
// ─── 3b. PANEL COMPUTATIONS ──────────────────────────────────────────
// Derived from currentRow and the active day - no side effects.
const whyFeelsLike = computeWhyFeelsLike(currentRow ?? null, UTCI_ENVIRONMENTS[utciEnv]);
const glanceSummary = computeGlanceSummary(
days[selectedDay]?.rows ?? [],
activeProfile,
outdoorsVariant,
skinType,
);
// ─── 4. JSX RETURN ───────────────────────────────────────────────────
// Everything below is the actual page markup, written as one big HTM
// template. Search tips:
@@ -272,43 +283,13 @@ export function UTCIForecast() {
</div>
<div class="utci-header">
<div>
<div class="header-left">
<h1 class="utci-title">
<span class="title-sun">SUN</span><span class="title-scope">Scope</span>
</h1>
<div class="utci-tagline">See the sun the way your skin does.</div>
</div>
<div class="scope-col">
<${ScopeReticle}
value=${currentRow?.utciAdj ?? null}
cat=${currentCat}
loading=${loading}
elev=${currentRow?.elev ?? 0}
dt=${currentRow?.dt ?? new Date()}
glob=${currentRow?.glob ?? 0}
activeEvent=${lensEvent}
utciEnvShort=${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ?? null}
/>
<div class="scope-env-row" data-env=${utciEnv}>
<span class="scope-env-label">Environment</span>
<${CustomSelect}
value=${utciEnv}
isOn=${true}
noHide=${true}
grpClass="grp-felt"
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
value: k,
label: v.label,
}))}
onChange=${(v) => setUtciEnv(v)}
/>
</div>
</div>
<div class="header-right">
<div class="utci-search-wrap">
<input
<input
class="utci-search"
type="text"
placeholder="Search any town or city…"
@@ -350,7 +331,63 @@ export function UTCIForecast() {
${fetchedAt && !loading && html`
<div class="utci-fetch-time">Last update: ${fetchedAt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>`}
</div>
</div>
<div class="scope-col">
<${ScopeReticle}
value=${currentRow?.utciAdj ?? null}
cat=${currentCat}
loading=${loading}
elev=${currentRow?.elev ?? 0}
dt=${currentRow?.dt ?? new Date()}
glob=${currentRow?.glob ?? 0}
activeEvent=${lensEvent}
utciEnvShort=${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ?? null}
/>
</div>
<div class="scope-env-bar">
<div class="scope-env-row" data-env=${utciEnv}>
<span class="scope-env-label">Environment</span>
<${CustomSelect}
value=${utciEnv}
isOn=${true}
noHide=${true}
grpClass="grp-felt"
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
value: k,
label: v.label,
}))}
onChange=${(v) => setUtciEnv(v)}
/>
</div>
</div>
<div class="header-right">
${forecast && currentRow && whyFeelsLike && html`
<div class="insight-panel insight-panel--why">
<div class="insight-panel-title">Why it feels like this</div>
${[
{ label: 'Sun and sky', icon: '☀', value: whyFeelsLike.sunAndSky },
{ label: 'Wind', icon: '🌬', value: whyFeelsLike.wind },
{ label: 'Humidity', icon: '💧', value: whyFeelsLike.humidity },
...(whyFeelsLike.environment !== 0 ? [
{ label: UTCI_ENVIRONMENTS[utciEnv]?.label ?? 'Environment', icon: '🌍', value: whyFeelsLike.environment }
] : []),
...(whyFeelsLike.precipitation !== 0 ? [
{ label: 'Precipitation', icon: '🌧', value: whyFeelsLike.precipitation }
] : []),
].map(({ label, icon, value }) => html`
<div class="insight-row" key=${label}>
<span class="insight-icon">${icon}</span>
<span class="insight-label">${label}</span>
<span class=${`insight-delta ${value >= 0 ? 'delta-pos' : 'delta-neg'}`}>
${value >= 0 ? '+' : ''}${value.toFixed(1)}°
</span>
</div>
`)}
</div>`}
</div>
</div>
${error && html`
<div class="utci-status" style=${{ borderColor: '#3a1010', color: '#c44a3a' }}>
@@ -385,32 +422,34 @@ export function UTCIForecast() {
/>`}
<div class="table-with-rail">
<div class="table-main">
<div class="col-toggles">
<span class="col-toggles-label">Columns:</span>
${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['utciP']) : activeCols['utciP']) && html`
<span class=${'col-toggle-group' + (visibleCols.utciP ? ' col-toggle-group--expanded' : '')}>
<${CustomSelect}
value=${utciEnv}
isOn=${visibleCols.utciP}
grpClass="grp-felt"
hideLabel="SunSoak"
hidingLabel="Hide SunSoak"
groupedLeft=${true}
isLastChild=${true}
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
value: k,
label: v.label,
}))}
onChange=${(v) => {
if (v === 'off') {
setVisibleCols(prev => ({ ...prev, utciP: false }));
} else {
setUtciEnv(v);
setVisibleCols(prev => ({ ...prev, utciP: true }));
}
}}
/>
</span>`}
<span class=${'col-toggle-group' + (visibleCols.utciP ? ' col-toggle-group--expanded' : '')}>
<${CustomSelect}
value=${utciEnv}
isOn=${visibleCols.utciP}
grpClass="grp-felt"
hideLabel="SunSoak"
hidingLabel="Hide SunSoak"
groupedLeft=${true}
isLastChild=${true}
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
value: k,
label: v.label,
}))}
onChange=${(v) => {
if (v === 'off') {
setVisibleCols(prev => ({ ...prev, utciP: false }));
} else {
setUtciEnv(v);
setVisibleCols(prev => ({ ...prev, utciP: true }));
}
}}
/>
</span>`}
${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['vehicleT']) : activeCols['vehicleT']) && html`
<span class=${'col-toggle-group' + (visibleCols.vehicleT ? ' col-toggle-group--expanded' : '')}>
<${CustomSelect}
@@ -862,6 +901,21 @@ export function UTCIForecast() {
</table>
</div>
</div>
</div>
${forecast && glanceSummary && glanceSummary.length > 0 && html`
<aside class="glance-rail">
<div class="insight-panel insight-panel--glance">
<div class="insight-panel-title">Today at a glance</div>
${glanceSummary.map(({ icon, label, value, alert }) => html`
<div class=${`insight-row${alert ? ' insight-row--alert' : ''}`} key=${label}>
<span class="insight-icon">${icon}</span>
<span class="insight-label">${label}</span>
<span class="insight-value">${value}</span>
</div>
`)}
</div>
</aside>`}
</div>
${colPopup && COL_DESCRIPTIONS[colPopup.key] && html`
<div