diff --git a/attached_assets/image_1772315386258.png b/attached_assets/image_1772315386258.png new file mode 100644 index 0000000..6f89287 Binary files /dev/null and b/attached_assets/image_1772315386258.png differ diff --git a/client/src/components/weather-widget.tsx b/client/src/components/weather-widget.tsx new file mode 100644 index 0000000..6bfdb2b --- /dev/null +++ b/client/src/components/weather-widget.tsx @@ -0,0 +1,134 @@ +import { useState, useEffect } from "react"; +import { Cloud, Sun, CloudRain, CloudSnow, CloudLightning, CloudDrizzle, Wind, Droplets, Thermometer, MapPin } from "lucide-react"; + +interface WeatherData { + temperature: number; + weatherCode: number; + windSpeed: number; + humidity: number; + city: string; +} + +function getWeatherIcon(code: number) { + if (code === 0 || code === 1) return ; + if (code === 2 || code === 3) return ; + if (code >= 51 && code <= 57) return ; + if (code >= 61 && code <= 67) return ; + if (code >= 71 && code <= 77) return ; + if (code >= 80 && code <= 82) return ; + if (code >= 95) return ; + return ; +} + +function getWeatherText(code: number): string { + if (code === 0) return "Klar"; + if (code === 1) return "Überwiegend klar"; + if (code === 2) return "Teilweise bewölkt"; + if (code === 3) return "Bewölkt"; + if (code >= 51 && code <= 57) return "Nieselregen"; + if (code >= 61 && code <= 65) return "Regen"; + if (code === 66 || code === 67) return "Gefrierender Regen"; + if (code >= 71 && code <= 75) return "Schneefall"; + if (code === 77) return "Schneegriesel"; + if (code >= 80 && code <= 82) return "Regenschauer"; + if (code === 85 || code === 86) return "Schneeschauer"; + if (code >= 95) return "Gewitter"; + return "Unbekannt"; +} + +async function reverseGeocode(lat: number, lon: number): Promise { + try { + const res = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json&zoom=10&accept-language=de`); + const data = await res.json(); + return data.address?.city || data.address?.town || data.address?.village || data.address?.municipality || "Unbekannt"; + } catch { + return "Unbekannt"; + } +} + +export function WeatherWidget() { + const [weather, setWeather] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + const defaultLat = 46.55; + const defaultLon = 14.55; + const defaultCity = "Klagenfurt"; + + async function fetchWeather(lat: number, lon: number, city: string) { + try { + const res = await fetch( + `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,weather_code,wind_speed_10m,relative_humidity_2m&timezone=auto` + ); + const data = await res.json(); + setWeather({ + temperature: Math.round(data.current.temperature_2m), + weatherCode: data.current.weather_code, + windSpeed: Math.round(data.current.wind_speed_10m), + humidity: data.current.relative_humidity_2m, + city, + }); + } catch { + setError(true); + } finally { + setLoading(false); + } + } + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + async (pos) => { + const city = await reverseGeocode(pos.coords.latitude, pos.coords.longitude); + fetchWeather(pos.coords.latitude, pos.coords.longitude, city); + }, + () => { + fetchWeather(defaultLat, defaultLon, defaultCity); + }, + { timeout: 5000 } + ); + } else { + fetchWeather(defaultLat, defaultLon, defaultCity); + } + }, []); + + if (loading) { + return ( +
+
+ +

Wetter

+
+
+
+ ); + } + + if (error || !weather) return null; + + return ( +
+
+ +

Wetter

+
+
+
+ {getWeatherIcon(weather.weatherCode)} +
+
+
{weather.temperature}°C
+
{getWeatherText(weather.weatherCode)}
+
+
+
+ + {weather.city} +
+
+ {weather.windSpeed} km/h + {weather.humidity}% +
+
+ ); +} diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index 22d35e6..b3e1cd1 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -12,6 +12,7 @@ import { PhotoGalleryWidget } from "@/components/photo-gallery"; import { HoroscopeWidget } from "@/components/horoscope-widget"; import { RecipeWidget } from "@/components/recipe-widget"; import { NewsWidget } from "@/components/news-widget"; +import { WeatherWidget } from "@/components/weather-widget"; import { useState, useEffect, useCallback, useRef } from "react"; function useFocalPoints() { @@ -408,6 +409,16 @@ export default function Home() {
+ +
+ +