Feature: User-Feedback, Regen-Uhrzeit im Wetter-Chip, Admin-Karten klickbar (SW by-v833)

- Feedback-Modal im Settings (Kategorie + Text → E-Mail an support@banyaro.app)
- Wetter-Chip (Karte + Gassi-Score): zeigt nächste Regenstunde ab ≥20% Wahrscheinlichkeit
- Gassi-Score-Chip: zweizeilige Wetter-Info, linksbündig, volle Chipbreite
- Admin-Übersicht: Stat-Karten anklickbar → navigiert direkt zum jeweiligen Tab
- ui.js: visualViewport-Listener hebt Modal über Tastatur (alle Modals)
- api.js: Pydantic v2 Array-Detail korrekt als Fehlermeldung extrahiert
- map.js: Wetter-Fallback über watchPosition wenn getCurrentPosition scheitert
- Update-Loop-Fix: index.html ?v= synchron mit APP_VER halten (alle 4 Stellen)
This commit is contained in:
rene 2026-05-10 12:52:55 +02:00
parent d18c592ef0
commit 70af387147
12 changed files with 211 additions and 42 deletions

View file

@ -58,6 +58,7 @@ async def get_weather_for_location(lat: float, lon: float) -> dict:
f"?latitude={lat}&longitude={lon}"
"&current=temperature_2m,apparent_temperature,weathercode,windspeed_10m,is_day"
"&daily=precipitation_probability_max,uv_index_max"
"&hourly=precipitation_probability"
"&timezone=Europe%2FBerlin&forecast_days=1"
)
async with httpx.AsyncClient(timeout=8.0) as client:
@ -65,8 +66,9 @@ async def get_weather_for_location(lat: float, lon: float) -> dict:
resp.raise_for_status()
raw = resp.json()
cur = raw.get('current', {})
daily = raw.get('daily', {})
cur = raw.get('current', {})
daily = raw.get('daily', {})
hourly = raw.get('hourly', {})
temp = cur.get('temperature_2m')
feels_like = cur.get('apparent_temperature')
@ -85,17 +87,36 @@ async def get_weather_for_location(lat: float, lon: float) -> dict:
if temp is not None and temp > 7.0 and 3 <= month <= 10:
zecken = 'hoch' if temp > 20 else ('mittel' if temp > 12 else 'niedrig')
# Nächste Regenstunde: erstes stündliches Fenster (jetzt+1h bis +12h) mit ≥60% Niederschlag
next_rain_time = None
already_raining = wcode >= 51
if not already_raining:
now_h = datetime.now().hour
h_times = hourly.get('time', [])
h_precip = hourly.get('precipitation_probability', [])
for t, p in zip(h_times, h_precip):
try:
entry_h = int(t[11:13])
except Exception:
continue
if entry_h <= now_h or entry_h > now_h + 12:
continue
if p is not None and p >= 20:
next_rain_time = f"{entry_h:02d}:00"
break
data = {
'temp_c': temp,
'feels_like_c': feels_like,
'weathercode': wcode,
'desc': desc,
'icon': icon,
'wind_kmh': wind,
'precip_prob': precip,
'uv_index': uv,
'is_day': bool(is_day),
'zecken_warnung': zecken,
'temp_c': temp,
'feels_like_c': feels_like,
'weathercode': wcode,
'desc': desc,
'icon': icon,
'wind_kmh': wind,
'precip_prob': precip,
'uv_index': uv,
'is_day': bool(is_day),
'zecken_warnung': zecken,
'next_rain_time': next_rain_time,
}
_location_cache[key] = (now, data)
return data