Redesign main app: slim top bar, dynamic sky hero, nowcast, scrub chart, unified palette
- One slate/blue palette across the app, dashboard, account/login pages (data colours stay orange = warm, blue = cool on the dashboard) - Slim sticky top bar with location chip, gear (settings) and ? (learn); 4-tab nav (Now, Station, Map, Alerts) with a thumb-reach bottom bar on phones - Hero sky gradient by condition/time with rain, storm, snow and star effects (respects reduced motion); keeps the searched city and badges when temps come from your station - One-line summary, 'Next 2 hours' bars and rain countdown from 15-minute precipitation, rain/cold-aware 'Bring' chips; yesterday + 'vs usual' context from the station - Scrubbable hourly chart (temp, rain chance, wind), 7-day range bars, AQI gauge, details tiles instead of drawers, taller radar with location marker + alert polygons - Skeleton loading instead of a full-screen spinner, sticky section headers, safe areas - Server: minutely_15 precipitation, public /api/station/summary Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1830fb2e2b
commit
fd61e35b56
16 files changed
+846
-221
No files matched your search
@@ -706,6 +706,8 @@ app.get('/api/weather', async (req, res) => {
|
||||
'precipitation_sum','precipitation_probability_max',
|
||||
'wind_speed_10m_max','sunrise','sunset','uv_index_max'
|
||||
].join(','),
|
||||
minutely_15: 'precipitation',
|
||||
forecast_minutely_15: 24,
|
||||
forecast_days: 16,
|
||||
wind_speed_unit: useMetric ? 'kmh' : 'mph',
|
||||
temperature_unit: useMetric ? 'celsius' : 'fahrenheit',
|
||||
@@ -1574,6 +1576,30 @@ app.get('/api/station/records', requireDashboardAuth, (_req, res) => {
|
||||
res.json(deriveRecords());
|
||||
});
|
||||
|
||||
// Public, minimal station context for the weather app (no raw readings): yesterday / today
|
||||
// extremes and the station's own average high/low for this date from earlier years.
|
||||
app.get('/api/station/summary', (_req, res) => {
|
||||
const days = recordsStore.days;
|
||||
const fmtDay = (ts) => new Intl.DateTimeFormat('en-CA', { timeZone: stationTzFallback }).format(new Date(ts));
|
||||
const todayKey = fmtDay(Date.now());
|
||||
const yKey = fmtDay(Date.now() - 86400000);
|
||||
const pick = (k) => {
|
||||
const d = days[k];
|
||||
return d ? { day: k, hi: d.hi?.[0] ?? null, lo: d.lo?.[0] ?? null, rain: d.rain ?? null, gust: d.gust?.[0] ?? null } : null;
|
||||
};
|
||||
// Normals: same calendar date ±3 days in earlier years (needs a prior year of data)
|
||||
const [ty, tm, td] = todayKey.split('-').map(Number);
|
||||
const his = [], los = [];
|
||||
for (const [k, d] of Object.entries(days)) {
|
||||
const [y, m, dd] = k.split('-').map(Number);
|
||||
if (y >= ty) continue;
|
||||
const diff = Math.abs(Date.UTC(2001, m - 1, dd) - Date.UTC(2001, tm - 1, td)) / 86400000;
|
||||
if (Math.min(diff, 365 - diff) <= 3) { if (d.hi) his.push(d.hi[0]); if (d.lo) los.push(d.lo[0]); }
|
||||
}
|
||||
const avg = (a) => (a.length >= 3 ? +(a.reduce((x, y) => x + y, 0) / a.length).toFixed(1) : null);
|
||||
res.json({ today: pick(todayKey), yesterday: pick(yKey), normal: { hi: avg(his), lo: avg(los), samples: his.length }, daysRecorded: Object.keys(days).length });
|
||||
});
|
||||
|
||||
// Per-day summaries (extremes, average temp, rain, wind-direction histogram) for charts/insights
|
||||
app.get('/api/station/daily', requireDashboardAuth, (req, res) => {
|
||||
const limit = Math.min(Math.max(parseInt(req.query.days) || 0, 0), 5000);
|
||||
|
||||
Reference in new issue
Block a user