From a8b4fd781f46c28192e6c1eefaee7838385a4e33 Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 7 May 2026 19:04:31 +0200 Subject: [PATCH 01/16] =?UTF-8?q?Fix:=20Wetter=20=E2=80=94=20windspeed=5Fm?= =?UTF-8?q?ax=20=E2=86=92=20wind=5Fkmh=20(Feldname-Mismatch,=20Wind=20zeig?= =?UTF-8?q?te=20immer=200)=20(SW=20by-v761)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 2 +- backend/static/js/pages/wetter.js | 6 +++--- backend/static/sw.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/main.py b/backend/main.py index 8c5b390..cb3a5d4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "760" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "761" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/static/index.html b/backend/static/index.html index c3abcea..7224f77 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 0fe4e0e..87d6ada 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '760'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '761'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; diff --git a/backend/static/js/pages/wetter.js b/backend/static/js/pages/wetter.js index 31d6a4f..8eae462 100644 --- a/backend/static/js/pages/wetter.js +++ b/backend/static/js/pages/wetter.js @@ -450,7 +450,7 @@ window.Page_wetter = (() => {
- ${_esc(compass)} · ${Math.round(d.windspeed_max ?? 0)} km/h + ${_esc(compass)} · ${Math.round(d.wind_kmh ?? 0)} km/h
${_esc(bft)}
@@ -803,7 +803,7 @@ window.Page_wetter = (() => { let score = 10; const temp = d.temp_max ?? 20; const precip = d.precip_prob ?? 0; - const wind = d.windspeed_max ?? 0; + const wind = d.wind_kmh ?? 0; const asphalt = d.asphalt_temp ?? 0; // Temperatur (ideal: 10–20°C) @@ -998,7 +998,7 @@ window.Page_wetter = (() => { const temp = d.temp_max ?? 20; const tempMin = d.temp_min ?? temp; const precip = d.precip_prob ?? 0; - const wind = d.windspeed_max ?? 0; + const wind = d.wind_kmh ?? 0; const asphalt = d.asphalt_temp ?? 0; const wcode = d.weathercode ?? 0; const isSnow = wcode >= 71 && wcode <= 77; diff --git a/backend/static/sw.js b/backend/static/sw.js index ba39608..d3b9934 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v760'; +const CACHE_VERSION = 'by-v761'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From a3c8d77a140e93ac9f7079293c97ce7caf8e834d Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 7 May 2026 19:22:22 +0200 Subject: [PATCH 02/16] =?UTF-8?q?Fix:=20iOS=20SW-Update=20=E2=80=94=20SKIP?= =?UTF-8?q?=5FWAITING=20Handler,=20location.replace()=20statt=20reload(),?= =?UTF-8?q?=20no-store=20Header=20(SW=20by-v762)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 18 +++++++++--------- backend/static/index.html | 5 +++-- backend/static/js/app.js | 5 +++-- backend/static/sw.js | 6 +++++- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/main.py b/backend/main.py index cb3a5d4..a8b3f89 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "761" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "762" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): @@ -848,7 +848,7 @@ async def share_target(request: Request): # Weiterleitung zur App mit den Daten return FileResponse( f"{STATIC_DIR}/index.html", - headers={"Cache-Control": "no-cache"} + headers={"Cache-Control": "no-store, no-cache"} ) # Öffentliche Hunde-Profilseite (für NFC-Tags, kein Login nötig) @@ -1182,17 +1182,17 @@ async def public_dog_page(dog_id: int): # ------------------------------------------------------------------ @app.get("/teilen/{token}") async def invite_page(token: str): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) @app.get("/breeder/{zwingername}") async def breeder_profile_page(zwingername: str): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) @app.get("/litters") async def litters_page(): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) # ------------------------------------------------------------------ @@ -1200,7 +1200,7 @@ async def litters_page(): # ------------------------------------------------------------------ @app.get("/widget") async def widget_page(): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) # ------------------------------------------------------------------ @@ -1726,7 +1726,7 @@ async def partner_landing(): """ - return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) + return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"}) # ------------------------------------------------------------------ @@ -1924,8 +1924,8 @@ async def spa_fallback(full_path: str): '', '', ) - return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) + return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"}) return FileResponse( f"{STATIC_DIR}/index.html", - headers={"Cache-Control": "no-cache"} + headers={"Cache-Control": "no-store, no-cache"} ) diff --git a/backend/static/index.html b/backend/static/index.html index 7224f77..7368cc9 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + @@ -640,7 +640,8 @@ // Wenn neuer SW die Kontrolle übernimmt → Seite neu laden navigator.serviceWorker.addEventListener('controllerchange', () => { - window.location.reload(); + // location.replace statt reload() — bypassed iOS bfcache + window.location.replace(window.location.href); }); navigator.serviceWorker.addEventListener('message', e => { diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 87d6ada..29bb7fb 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '761'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '762'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; @@ -1045,7 +1045,8 @@ const App = (() => { const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch { /* ignorieren */ } - setTimeout(() => location.reload(), 600); + // location.replace bypassed iOS bfcache (reload() stellt alte Seite wieder her) + setTimeout(() => location.replace(location.href), 600); }); } diff --git a/backend/static/sw.js b/backend/static/sw.js index d3b9934..acf1e4f 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v761'; +const CACHE_VERSION = 'by-v762'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache @@ -344,6 +344,10 @@ self.addEventListener('sync', event => { // MESSAGE — Tile-Vorausladung (Offline-Speicherung) + Queue-Steuerung // ---------------------------------------------------------- self.addEventListener('message', event => { + if (event.data?.type === 'SKIP_WAITING') { + self.skipWaiting(); + return; + } if (event.data?.type === 'PROCESS_QUEUE') { event.waitUntil(_processQueue()); return; From 77795345b8c36dd1371dbc99939fc076dd5aaed8 Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 7 May 2026 19:25:27 +0200 Subject: [PATCH 03/16] UX: Abmelden-Button als prominenter Outline-Button mit Trennlinie (SW by-v763) --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 2 +- backend/static/js/pages/settings.js | 16 +++++++++++----- backend/static/sw.js | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/main.py b/backend/main.py index a8b3f89..1934bc3 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "762" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "763" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/static/index.html b/backend/static/index.html index 7368cc9..97e866f 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 29bb7fb..8162f75 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '762'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '763'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js index 0197f58..1a3ca99 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -285,11 +285,17 @@ window.Page_settings = (() => { ⭐ Ban Yaro Pro kommt bald — mehr Features, mehrere Hunde. ` : ''} - diff --git a/backend/static/sw.js b/backend/static/sw.js index acf1e4f..c5f525d 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v762'; +const CACHE_VERSION = 'by-v763'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From 7d89ed8bd2078222503f586a68cdf47ef3b79ce4 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 10:15:31 +0200 Subject: [PATCH 04/16] Fix: Schnell-Gassi zeigt korrekte Meldung wenn offline gequeuet statt falsches Erfolgs-Toast (SW by-v764) --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 2 +- backend/static/js/worlds.js | 8 ++++++-- backend/static/sw.js | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/main.py b/backend/main.py index 1934bc3..32cd7e6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "763" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "764" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/static/index.html b/backend/static/index.html index 97e866f..cfe32d2 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 8162f75..5666b13 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '763'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '764'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js index 438dd1f..3ad273f 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -513,9 +513,13 @@ window.Worlds = (() => { if (weatherData) { payload.weather_json = JSON.stringify(weatherData); } - await API.post(`/dogs/${dog.id}/diary`, payload); + const result = await API.post(`/dogs/${dog.id}/diary`, payload); _close(); - UI.toast?.success(`Gassi eingetragen! ${selectedMin} min 🐾`); + if (result?._queued) { + UI.toast?.info(`Gassi gespeichert — wird synchronisiert wenn online 🐾`); + } else { + UI.toast?.success(`Gassi eingetragen! ${selectedMin} min 🐾`); + } // Streak-Cache invalidieren try { localStorage.removeItem('w3_streak_' + dog.id); } catch {} // JETZT-Welt neu rendern für aktuellen Streak diff --git a/backend/static/sw.js b/backend/static/sw.js index c5f525d..74e9882 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v763'; +const CACHE_VERSION = 'by-v764'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From 2854c60ba804235bb2601b9fb9bcc50b25a0dedd Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 10:56:36 +0200 Subject: [PATCH 05/16] =?UTF-8?q?Fix:=20Update-Button=20h=C3=A4ngt=20nicht?= =?UTF-8?q?=20mehr=20(kein=20await=20update()),=20Wrapped-Modal=20=C3=97?= =?UTF-8?q?=20hinter=20Notch=20(safe-area)=20(SW=20by-v765)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 9 ++++----- backend/static/js/pages/dog-profile.js | 4 ++-- backend/static/sw.js | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/backend/main.py b/backend/main.py index 32cd7e6..9806d71 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "764" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "765" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/static/index.html b/backend/static/index.html index cfe32d2..50b42f8 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 5666b13..50f2399 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '764'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '765'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; @@ -1037,16 +1037,15 @@ const App = (() => { btn.textContent = 'Lädt…'; btn.disabled = true; sessionStorage.setItem('by_update_reload', APP_VER); + // Reload immer nach 800ms — NICHT auf SW-Operationen warten (können auf iOS hängen) + setTimeout(() => location.replace(location.href), 800); try { - // SW aktivieren + alle Caches leeren für sauberen Reload const reg = await navigator.serviceWorker?.getRegistration(); if (reg?.waiting) reg.waiting.postMessage({ type: 'SKIP_WAITING' }); - await reg?.update(); + reg?.update().catch(() => {}); // kein await — kann hängen const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch { /* ignorieren */ } - // location.replace bypassed iOS bfcache (reload() stellt alte Seite wieder her) - setTimeout(() => location.replace(location.href), 600); }); } diff --git a/backend/static/js/pages/dog-profile.js b/backend/static/js/pages/dog-profile.js index 58d5aa7..bdbf999 100644 --- a/backend/static/js/pages/dog-profile.js +++ b/backend/static/js/pages/dog-profile.js @@ -2073,8 +2073,8 @@ window.Page_dog_profile = (() => { const modalEl = document.createElement('div'); modalEl.style.cssText = 'position:fixed;inset:0;z-index:9999;background:#0d0d1a;display:flex;flex-direction:column;overflow:hidden;'; modalEl.innerHTML = ` -
- +
+
${cards[0]}
diff --git a/backend/static/sw.js b/backend/static/sw.js index 74e9882..d5b713f 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v764'; +const CACHE_VERSION = 'by-v765'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From b0ea8054344d9ba43381a2c1e71b940f591332e0 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 10:59:26 +0200 Subject: [PATCH 06/16] =?UTF-8?q?Fix:=20Hundebuch=20+=20Ausweis=20?= =?UTF-8?q?=E2=80=94=20Zur=C3=BCck=20zur=20App=20Button=20erg=C3=A4nzt=20(?= =?UTF-8?q?standalone=20Seiten=20ohne=20Navigation)=20(SW=20by-v765)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 9 ++++++++- backend/routes/dogs.py | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/backend/main.py b/backend/main.py index 9806d71..d8e616a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1355,7 +1355,14 @@ async def ausweis_page(dog_id: int, request: Request):
- +
+ + +

Impfungen

diff --git a/backend/routes/dogs.py b/backend/routes/dogs.py index 42b9b32..6c35334 100644 --- a/backend/routes/dogs.py +++ b/backend/routes/dogs.py @@ -742,9 +742,18 @@ async def get_hunde_buch( - +
{cover_img} From b6eaaad47aea96f4a19f13c364b1abb985b46d1a Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 11:06:23 +0200 Subject: [PATCH 07/16] =?UTF-8?q?Fix:=20'+=20Weiteren=20Hund=20anlegen'=20?= =?UTF-8?q?nur=20f=C3=BCr=20Pro=20sichtbar,=20App.hasPro()=20public=20(SW?= =?UTF-8?q?=20by-v766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 3 ++- backend/static/js/pages/dog-profile.js | 2 +- backend/static/sw.js | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/main.py b/backend/main.py index d8e616a..de9c1e6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "765" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "766" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): diff --git a/backend/static/index.html b/backend/static/index.html index 50b42f8..830d2ca 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 50f2399..9d78fe4 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '765'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '766'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; @@ -1090,6 +1090,7 @@ const App = (() => { } return { init, navigate, callModule, state, setActiveDog, + hasPro: (user) => _hasPro(user ?? state.user), renderDogSwitcher: _renderDogSwitcher, getInstallPrompt: () => _installPrompt, requireAuth, showOnboarding: _showOnboardingModal, diff --git a/backend/static/js/pages/dog-profile.js b/backend/static/js/pages/dog-profile.js index bdbf999..4405edf 100644 --- a/backend/static/js/pages/dog-profile.js +++ b/backend/static/js/pages/dog-profile.js @@ -202,7 +202,7 @@ window.Page_dog_profile = (() => { Visitenkarte teilen ` : ''} - ${!dog.is_guest ? `` : ''} ${!dog.is_guest ? `
diff --git a/backend/static/sw.js b/backend/static/sw.js index ccb77c3..45d2c78 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v771'; +const CACHE_VERSION = 'by-v772'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From ce14bb1d2c7eaaf73bc764ac95c870ea54ea9186 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 12:22:04 +0200 Subject: [PATCH 15/16] =?UTF-8?q?Refactoring:=20Inline-Styles=20=E2=86=92?= =?UTF-8?q?=20CSS-Klassen=20(worlds.js,=20dog-profile.js,=20settings.js)?= =?UTF-8?q?=20=E2=80=94=2020=20neue=20w3-/by-Klassen=20(SW=20by-v773)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 2 +- backend/static/css/components.css | 333 +++++++++++++++++++++++++ backend/static/index.html | 2 +- backend/static/js/app.js | 2 +- backend/static/js/pages/dog-profile.js | 27 +- backend/static/js/pages/settings.js | 49 ++-- backend/static/js/worlds.js | 87 +++---- backend/static/sw.js | 2 +- 8 files changed, 389 insertions(+), 115 deletions(-) diff --git a/backend/main.py b/backend/main.py index 07df6fc..0b2db2d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "772" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "773" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 2f95c24..71b87ca 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -8370,3 +8370,336 @@ svg.empty-state-icon { .breed-community-chip:hover, .breed-community-chip:active { background: #fff3e0; } + +/* ============================================================ + REFACTORING: Extrahierte Inline-Styles aus worlds.js, + dog-profile.js und settings.js + ============================================================ */ + +/* ---------------------------------------------------------- + Bottom-Sheet Overlay (position:fixed, flex-column, flex-end) + Verwendet in: _openFab, _openAllChips, _openQuickGassi, + _openConfigModal (worlds.js) + ---------------------------------------------------------- */ +.w3-sheet-overlay { + position: fixed; + inset: 0; + z-index: 460; + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +/* Backdrop (halbtransparent + blur) */ +.w3-backdrop { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(2px); +} + +/* Sheet-Panel (weißer Boden, abgerundete Oberkante) */ +.w3-sheet-panel { + position: relative; + z-index: 1; + background: var(--c-bg); + border-radius: 24px 24px 0 0; + padding: 20px 16px calc(env(safe-area-inset-bottom, 16px) + 16px); + box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.2); +} + +/* Sheet-Panel mit Scroll (für lange Inhalte) */ +.w3-sheet-panel--scroll { + max-height: 82vh; + overflow-y: auto; +} + +/* Sheet-Header-Zeile (Titel links, Button rechts) */ +.w3-sheet-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.w3-sheet-header--mb20 { + margin-bottom: 20px; +} + +/* Sheet-Titel */ +.w3-sheet-title { + font-size: var(--text-base); + font-weight: 700; +} + +/* Runder Schließen-Button (28px) */ +.w3-close-btn { + background: var(--c-border); + border: none; + border-radius: 50%; + width: 28px; + height: 28px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* Runder Schließen-Button (32px — größere Variante) */ +.w3-close-btn--lg { + background: var(--c-border); + border: none; + border-radius: 50%; + width: 32px; + height: 32px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* FAB-Options-Button (Zeilen-Stil, flex, card-bg) */ +.w3-fab-option { + display: flex; + align-items: center; + gap: 14px; + width: 100%; + background: var(--c-bg-card, var(--c-surface)); + border: 1px solid var(--c-border); + border-radius: 14px; + padding: 14px 16px; + cursor: pointer; + text-align: left; + transition: background 0.12s; +} + +/* Icon-Dot (runder farbiger Container für ph-icons) */ +.w3-icon-dot { + width: 40px; + height: 40px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* Icon-Dot groß (44px) */ +.w3-icon-dot--lg { + width: 44px; + height: 44px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +/* Chip-Button (Grid-Spalten, vertikal gestapelt) */ +.w3-chip-btn { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + background: var(--c-bg-card, var(--c-surface)); + border: 1px solid var(--c-border); + border-radius: 14px; + padding: 12px 6px; + cursor: pointer; + transition: background 0.12s; +} + +/* Chip-Button Label */ +.w3-chip-label { + font-size: 10px; + font-weight: 600; + color: var(--c-text); + text-align: center; + line-height: 1.2; + word-break: break-word; +} + +/* Vertikal gestapelte Button-Gruppe (Modal-Footer) */ +.w3-btn-stack { + display: flex; + flex-direction: column; + gap: var(--space-2); + width: 100%; +} + +/* Sektion-Label (uppercase, klein, gedämpft) */ +.w3-section-label { + font-size: var(--text-xs); + font-weight: 700; + color: var(--c-text-secondary); + letter-spacing: 0.08em; + text-transform: uppercase; + margin-bottom: 10px; +} + +/* Schnell-Gassi Dauer-Button */ +.w3-dur-btn { + padding: 12px 6px; + border-radius: 12px; + border: 2px solid var(--c-border); + background: var(--c-bg-card, var(--c-surface)); + cursor: pointer; + font-weight: 700; + font-size: var(--text-sm); + color: var(--c-text); +} +.w3-dur-btn.active { + border-color: var(--c-primary); + background: var(--c-primary-subtle); + color: var(--c-primary); +} + +/* Submit-Button (volle Breite, primary, flex-center) */ +.w3-submit-btn { + width: 100%; + padding: 16px; + border-radius: 14px; + background: var(--c-primary); + color: white; + border: none; + cursor: pointer; + font-size: var(--text-base); + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +/* "Weitere Funktionen"-Link-Button */ +.w3-all-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 100%; + padding: 12px; + border: none; + background: none; + cursor: pointer; + color: var(--c-primary); + font-size: var(--text-sm); + font-weight: 600; +} + +/* ---------------------------------------------------------- + Settings / Dog-Profile: Card-Sektion-Header + (uppercase Label mit Border-Bottom) + ---------------------------------------------------------- */ +.by-card-section-header { + padding: var(--space-3) var(--space-4); + font-size: var(--text-xs); + font-weight: 600; + color: var(--c-text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid var(--c-border); +} + +/* ---------------------------------------------------------- + Dog-Profile: Info-Card-Zeile (xs-Label + Wert) + ---------------------------------------------------------- */ +.dp-info-label { + font-size: var(--text-xs); + color: var(--c-text-secondary); + margin-bottom: 2px; +} + +/* Passport: Datensatz-Zeile */ +.pp-data-row { + display: flex; + align-items: flex-start; + gap: var(--space-3); + padding: var(--space-3) 0; + border-bottom: 1px solid var(--c-border); +} + +/* Settings: Sidebar-Item mit Padding */ +.settings-sidebar-item { + padding: var(--space-4); + border-radius: 0; + border-bottom: 1px solid var(--c-border); +} + +/* Settings: Toggle-Zeile (flex, icon + label + toggle) */ +.settings-toggle-row { + display: flex; + align-items: center; + gap: var(--space-3); + padding: var(--space-4); + border-bottom: 1px solid var(--c-border); +} + +/* Settings: Toggle-Label-Block */ +.settings-toggle-label { + flex: 1; +} + +/* Settings: Inline-Toggle (44×24px) */ +.by-toggle-wrap { + position: relative; + display: inline-block; + width: 44px; + height: 24px; + flex-shrink: 0; +} +.by-toggle-wrap input { + opacity: 0; + width: 0; + height: 0; + position: absolute; +} +.by-toggle-track { + position: absolute; + cursor: pointer; + inset: 0; + border-radius: 12px; + background: var(--c-border); + transition: background 0.2s; +} +.by-toggle-thumb { + position: absolute; + top: 2px; + width: 20px; + height: 20px; + border-radius: 50%; + background: #fff; + transition: left 0.2s; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); +} + +/* Settings: Version-Badge */ +.by-version-badge { + background: var(--c-surface-2); + border: 1px solid var(--c-border); + border-radius: 100px; + padding: 2px 10px; + font-family: monospace; + font-size: 10px; + color: var(--c-text-muted); +} + +/* Avatar-Kreis (56px, primary bg) */ +.by-avatar-circle { + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--c-primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + font-weight: 700; + flex-shrink: 0; + cursor: pointer; + overflow: hidden; + position: relative; +} diff --git a/backend/static/index.html b/backend/static/index.html index e8b3004..9795931 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 265cc6c..c140371 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '772'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '773'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen diff --git a/backend/static/js/pages/dog-profile.js b/backend/static/js/pages/dog-profile.js index 4405edf..85ce02d 100644 --- a/backend/static/js/pages/dog-profile.js +++ b/backend/static/js/pages/dog-profile.js @@ -108,8 +108,7 @@ window.Page_dog_profile = (() => { margin-bottom:var(--space-5);text-align:left"> ${geburtstag ? `
-
Geburtstag
+
Geburtstag
${geburtstag}
${_calcAlter(dog.geburtstag)} @@ -118,8 +117,7 @@ window.Page_dog_profile = (() => { ` : ''} ${dog.geschlecht ? `
-
${dog.geschlecht === 'm' ? '' : ''} Geschlecht
+
${dog.geschlecht === 'm' ? '' : ''} Geschlecht
${dog.geschlecht === 'm' ? 'Rüde' : 'Hündin'}
@@ -127,8 +125,7 @@ window.Page_dog_profile = (() => { ` : ''} ${dog.gewicht_kg ? `
-
Gewicht
+
Gewicht
${dog.gewicht_kg} kg
` : ''} @@ -521,9 +518,7 @@ window.Page_dog_profile = (() => { let activeHtml = ''; if (active.length) { activeHtml = active.map(s => ` -
+
${_esc(s.sitter_name)} @@ -619,7 +614,7 @@ window.Page_dog_profile = (() => { value="${_esc(dog.chip_nr || '')}" placeholder="z.B. 276009200123456" maxlength="20">
`, footer: ` -
+
`, @@ -676,7 +671,7 @@ window.Page_dog_profile = (() => { `; const footer = ` -
+
${hasPhoto ? `` : ''}
${hasPhoto ? `` : ''} @@ -1044,7 +1039,7 @@ window.Page_dog_profile = (() => { title: 'Weiteren Hund anlegen', body: _formHTML(null, true), footer: ` -
+
@@ -1061,7 +1056,7 @@ window.Page_dog_profile = (() => { title: `${dog.name} bearbeiten`, body: _formHTML(dog, true), footer: ` -
+
@@ -1657,8 +1652,7 @@ window.Page_dog_profile = (() => {
` : vaccs.map(v => `
+ class="pp-data-row">
${_esc(v.krankheit)}
@@ -1695,8 +1689,7 @@ window.Page_dog_profile = (() => {
` : meds.map(m => `
+ class="pp-data-row">
${_esc(m.name)}
diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js index bc2fd40..07cfab3 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -120,12 +120,7 @@ window.Page_settings = (() => {
-
+
${avatarInner}
-
Aktivität
+
Aktivität
Lädt…
@@ -238,9 +231,7 @@ window.Page_settings = (() => {
-
Trophäen
+
Trophäen
Lädt…
@@ -301,19 +292,15 @@ window.Page_settings = (() => {
-
+
App-Einstellungen
-
+
-
+
Dark Mode
Erscheinungsbild der App @@ -335,9 +322,9 @@ window.Page_settings = (() => {
-
+
-
+
KI-Notiz-Assistent
Erkennt Muster in deinen Notizen und macht Vorschläge @@ -359,9 +346,9 @@ window.Page_settings = (() => {
-
+
-
+
Goldene Gassi-Stunde täglich
Täglich um 07:00 Uhr: bestes Wetterfenster für den Gassi-Gang @@ -398,10 +385,7 @@ window.Page_settings = (() => {
-
+
App installieren
@@ -717,7 +701,7 @@ window.Page_settings = (() => { `, footer: ` -
+
@@ -1042,12 +1026,7 @@ window.Page_settings = (() => { slot.innerHTML = `
-
- Züchter-Profil -
+
Züchter-Profil
${statusBadge} ${actionBlock} @@ -1269,7 +1248,7 @@ window.Page_settings = (() => { `, footer: ` -
+
diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js index e286106..4fcd9de 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -274,28 +274,20 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'fab-overlay'; - ov.style.cssText = 'position:fixed;inset:0;z-index:460;display:flex;flex-direction:column;justify-content:flex-end'; + ov.className = 'w3-sheet-overlay'; ov.innerHTML = ` -
-
-
-
${options.length ? title : 'Schnellzugriff'}
-
${options.map(o => ` - `).join('')}
- @@ -353,20 +343,14 @@ window.Worlds = (() => { if (!chips.length) return ''; return `
-
${worldLabels[w]}
+
${chips.map(c => ` - `).join('')}
@@ -376,16 +360,13 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'fab-overlay'; - ov.style.cssText = 'position:fixed;inset:0;z-index:460;display:flex;flex-direction:column;justify-content:flex-end'; + ov.className = 'w3-sheet-overlay'; ov.innerHTML = ` -
-
-
-
Ausgeblendete Funktionen
-
@@ -430,7 +411,8 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'quick-gassi-overlay'; - ov.style.cssText = 'position:fixed;inset:0;z-index:400;display:flex;flex-direction:column;justify-content:flex-end'; + ov.className = 'w3-sheet-overlay'; + ov.style.zIndex = '400'; const weatherLine = weatherData ? `
@@ -438,20 +420,17 @@ window.Worlds = (() => {
` : ''; ov.innerHTML = ` -
-
-
+
+
+
-
🐾 Schnell-Gassi
+
🐾 Schnell-Gassi
${_esc(dog.name)} · ohne GPS
${weatherLine}
-
@@ -459,20 +438,13 @@ window.Worlds = (() => {
Dauer
${durations.map(d => ` - `).join('')}
- @@ -490,10 +462,7 @@ window.Worlds = (() => { btn.addEventListener('click', () => { selectedMin = parseInt(btn.dataset.min); ov.querySelectorAll('.qg-dur').forEach(b => { - const active = parseInt(b.dataset.min) === selectedMin; - b.style.borderColor = active ? 'var(--c-primary)' : 'var(--c-border)'; - b.style.background = active ? 'var(--c-primary-subtle)' : 'var(--c-bg-card)'; - b.style.color = active ? 'var(--c-primary)' : 'var(--c-text)'; + b.classList.toggle('active', parseInt(b.dataset.min) === selectedMin); }); }); }); diff --git a/backend/static/sw.js b/backend/static/sw.js index 45d2c78..52ca2cf 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v772'; +const CACHE_VERSION = 'by-v773'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache From 8467f01b5bc295a4840e59617569ebc22184e046 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 8 May 2026 12:27:50 +0200 Subject: [PATCH 16/16] =?UTF-8?q?Hotfix:=20SW=20null-Response=20Bug=20?= =?UTF-8?q?=E2=80=94=20catch=20gibt=20503=20statt=20undefined=20zur=C3=BCc?= =?UTF-8?q?k=20(Safari=20FetchEvent-Fehler)=20(SW=20by-v774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 2 +- backend/static/index.html | 2 +- backend/static/js/app.js | 2 +- backend/static/sw.js | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/main.py b/backend/main.py index 0b2db2d..4fdbfae 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "773" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "774" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/static/index.html b/backend/static/index.html index 9795931..856efe8 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index c140371..9887ed7 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '773'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '774'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen diff --git a/backend/static/sw.js b/backend/static/sw.js index 52ca2cf..171d7f4 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v773'; +const CACHE_VERSION = 'by-v774'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache @@ -292,7 +292,8 @@ self.addEventListener('fetch', event => { } return response; }) - .catch(() => caches.match(event.request)) + .catch(() => caches.match(event.request) + .then(cached => cached || new Response('', { status: 503 }))) ); return; } @@ -324,9 +325,8 @@ self.addEventListener('fetch', event => { }) ) .catch(() => { - if (event.request.mode === 'navigate') { - return caches.match('/'); - } + if (event.request.mode === 'navigate') return caches.match('/'); + return new Response('', { status: 503 }); }) ); });