feat: uuroverzicht per dag (temperatuur, regen, wind)

Tik op een dag in de verwachtingsstrook voor een uuroverzicht van
temperatuur, regen en wind (richting + snelheid), om de 2 uur, 12
punten per dag. Sluit na 20 seconden vanzelf of bij een tik op de
overlay zelf.

Backend: Open-Meteo levert hourly-data in dezelfde call als daily
(geen extra verzoek, dezelfde 60min-cache). Nieuwe HourlyForecastMapper
groepeert per kalenderdatum en bemonstert om het andere uur, robuust
voor de zomertijddag (23/25 uur). OpenMeteoClient::dailyForecast()
hernoemd naar forecast(), geeft nu {daily, hourly} terug. Elke dag in
de forecast-array krijgt een hourly-lijst.

Frontend: temperatuurlijn hergebruikt sparklinePath (nu ook als
sparklinePoints voor exacte uitlijning met de 12-koloms grid), regen
als kleine staafjes, windpijl (nieuw icoon) wijst waarheen de wind
waait. 20s-balk via CSS transition met reflow-restart. Servicewerker
naar v5.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 10:18:11 +02:00
co-authored by Claude Sonnet 5
parent 90e979f562
commit 8e90eb1129
20 changed files with 1420 additions and 62 deletions
+13 -5
View File
@@ -122,11 +122,19 @@ final class DashboardService
$this->addError(self::ERR_FORECAST);
return ['days' => [], 'stale' => true];
}
$daily = $this->fetch('forecast', fn() => $this->source->forecast($lat, $lon, $tz->getName()), self::ERR_FORECAST);
return [
'days' => $daily['data'] === null ? [] : ForecastMapper::map($daily['data']),
'stale' => $daily['stale'],
];
$result = $this->fetch('forecast', fn() => $this->source->forecast($lat, $lon, $tz->getName()), self::ERR_FORECAST);
if ($result['data'] === null) {
return ['days' => [], 'stale' => $result['stale']];
}
$days = ForecastMapper::map($result['data']['daily'] ?? []);
$hourlyByDate = HourlyForecastMapper::mapByDate($result['data']['hourly'] ?? [], $tz);
foreach ($days as &$day) {
$day['hourly'] = $hourlyByDate[$day['date']] ?? [];
}
unset($day);
return ['days' => $days, 'stale' => $result['stale']];
}
/**