From d69c5a04131b5c93da2b3f8c00e56931a1eb36a1 Mon Sep 17 00:00:00 2001 From: Ulas Date: Thu, 26 Mar 2026 15:20:55 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Wetter-Widget=20responsive=20=C3=BCber?= =?UTF-8?q?=20volle=20Breite=20im=20Desktop-Dashboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auf Desktop wird das Wetter-Widget über allen anderen Widgets platziert mit horizontalem Layout (aktuelles Wetter links, Vorhersage rechts). Vorhersagezeitraum skaliert mit Bildschirmbreite: 3 Tage (Mobil), 4 Tage (Tablet), 5 Tage (Desktop/Wide). Co-Authored-By: Claude Opus 4.6 --- public/pages/dashboard.js | 27 +++++----- public/styles/dashboard.css | 100 +++++++++++++++++++++++++++++++++++- server/routes/weather.js | 6 +-- 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js index b8b6960..2d179e8 100644 --- a/public/pages/dashboard.js +++ b/public/pages/dashboard.js @@ -269,11 +269,12 @@ function renderWeatherWidget(weather) { const { city, current, forecast } = weather; - const forecastHtml = forecast.map((d) => { + const forecastHtml = forecast.map((d, i) => { const date = new Date(d.date + 'T12:00:00'); const label = date.toLocaleDateString('de-DE', { weekday: 'short' }); + const extraCls = i >= 3 ? ' weather-forecast__day--extended' : ''; return ` -
+
${label}
${d.desc} @@ -286,19 +287,21 @@ function renderWeatherWidget(weather) { return `
-
-
-
${current.temp}°C
-
${current.desc}
-
${city}
-
- Gefühlt ${current.feels_like}° · ${current.humidity}% · Wind ${current.wind_speed} km/h +
+
+
+
${current.temp}°C
+
${current.desc}
+
${city}
+
+ Gefühlt ${current.feels_like}° · ${current.humidity}% · Wind ${current.wind_speed} km/h +
+ ${current.desc}
- ${current.desc} + ${forecast.length ? `
${forecastHtml}
` : ''}
- ${forecast.length ? `
${forecastHtml}
` : ''}
`; } diff --git a/public/styles/dashboard.css b/public/styles/dashboard.css index 9073e1a..a069b47 100644 --- a/public/styles/dashboard.css +++ b/public/styles/dashboard.css @@ -507,7 +507,9 @@ * Wetter-Widget * * Eigener Gradient, überschreibt .widget Hintergrund. - * Kompakt: Icon kleiner, Forecast enger. + * Mobile: kompakt, 3-Tage-Vorhersage. + * Desktop: volle Breite über allen Widgets, horizontales + * Layout mit bis zu 5-Tage-Vorhersage. * -------------------------------------------------------- */ .weather-widget { background: linear-gradient(135deg, var(--color-accent) 0%, #1E5CB3 100%); @@ -569,6 +571,11 @@ flex: 1; } +/* Tage 4+5 nur auf breiteren Bildschirmen sichtbar */ +.weather-forecast__day--extended { + display: none; +} + .weather-forecast__label { font-size: var(--text-xs); font-weight: var(--font-weight-semibold); @@ -595,6 +602,97 @@ opacity: 0.6; } +/* Tablet: Wetter über volle Breite, 4 Tage */ +@media (min-width: 768px) { + .weather-widget { + grid-column: 1 / -1; + } + + .weather-widget__inner { + display: flex; + align-items: stretch; + } + + .weather-widget__main { + flex-shrink: 0; + } + + .weather-forecast { + border-top: none; + border-left: 1px solid rgba(255, 255, 255, 0.15); + flex: 1; + align-items: center; + padding: var(--space-3) var(--space-4); + } + + .weather-forecast__day--extended:nth-child(-n+4) { + display: flex; + } +} + +/* Desktop: 5-Tage-Vorhersage, größere Elemente */ +@media (min-width: 1024px) { + .weather-widget { + grid-column: 1 / -1; + } + + .weather-widget__main { + padding: var(--space-4) var(--space-5); + } + + .weather-widget__temp { + font-size: 2.5rem; + } + + .weather-widget__icon { + width: 80px; + height: 80px; + } + + .weather-forecast { + gap: var(--space-4); + padding: var(--space-3) var(--space-5); + } + + .weather-forecast__day--extended { + display: flex; + } + + .weather-forecast__icon { + width: 36px; + height: 36px; + } + + .weather-forecast__temps { + font-size: var(--text-sm); + } + + .weather-forecast__label { + font-size: var(--text-sm); + } +} + +/* Wide Desktop: noch mehr Platz */ +@media (min-width: 1440px) { + .weather-widget { + grid-column: 1 / -1; + } + + .weather-widget__main { + padding: var(--space-4) var(--space-6); + } + + .weather-forecast { + gap: var(--space-5); + padding: var(--space-3) var(--space-6); + } + + .weather-forecast__icon { + width: 40px; + height: 40px; + } +} + /* -------------------------------------------------------- * Skeleton-Zustände (pro Widget) * Kompakt: gleiches Spacing wie echte Widgets. diff --git a/server/routes/weather.js b/server/routes/weather.js index dd26807..a555662 100644 --- a/server/routes/weather.js +++ b/server/routes/weather.js @@ -15,7 +15,7 @@ const CACHE_TTL_MS = 30 * 60 * 1000; // -------------------------------------------------------- // GET /api/v1/weather -// Gibt aktuelles Wetter + 3-Tage-Vorschau zurück. +// Gibt aktuelles Wetter + 5-Tage-Vorschau zurück. // Erfordert OPENWEATHER_API_KEY + OPENWEATHER_CITY in .env // Response: { data: { current, forecast } } | { data: null } // -------------------------------------------------------- @@ -49,7 +49,7 @@ router.get('/', async (req, res) => { const currentJson = await currentRes.json(); // 5-Tage-Forecast (3h-Intervalle → wir nehmen Mittags-Werte für Tagesvorschau) - const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?q=${encodeURIComponent(city)}&appid=${apiKey}&units=${units}&lang=${lang}&cnt=24`; + const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?q=${encodeURIComponent(city)}&appid=${apiKey}&units=${units}&lang=${lang}&cnt=40`; const forecastRes = await fetch(forecastUrl, { signal: AbortSignal.timeout(8000) }); let forecastDays = []; if (forecastRes.ok) { @@ -67,7 +67,7 @@ router.get('/', async (req, res) => { icon: item.weather[0]?.icon, desc: item.weather[0]?.description, }); - if (forecastDays.length >= 3) break; + if (forecastDays.length >= 5) break; } }