diff --git a/backend/main.py b/backend/main.py index 4fdbfae..8c5b390 100644 --- a/backend/main.py +++ b/backend/main.py @@ -327,16 +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 = "774" # muss mit APP_VER in app.js übereinstimmen - -@app.get("/.well-known/assetlinks.json") -async def assetlinks(): - """TWA-Verifikation für Google Play Store (app.banyaro.twa).""" - return Response( - content='[{"relation":["delegate_permission/common.handle_all_urls"],"target":{"namespace":"android_app","package_name":"app.banyaro.twa","sha256_cert_fingerprints":["49:02:DC:5B:63:C0:D7:42:7F:A4:DC:2F:EB:78:73:11:CC:B9:36:22:00:01:A0:03:1C:0A:F9:41:35:9F:D4:B7"]}}]', - media_type="application/json", - headers={"Cache-Control": "no-cache"}, - ) +APP_VER = "760" # muss mit APP_VER in app.js übereinstimmen @app.get("/api/version") async def app_version(): @@ -857,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-store, no-cache"} + headers={"Cache-Control": "no-cache"} ) # Öffentliche Hunde-Profilseite (für NFC-Tags, kein Login nötig) @@ -1191,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-store, no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) @app.get("/breeder/{zwingername}") async def breeder_profile_page(zwingername: str): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) @app.get("/litters") async def litters_page(): - return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) # ------------------------------------------------------------------ @@ -1209,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-store, no-cache"}) + return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) # ------------------------------------------------------------------ @@ -1364,14 +1355,7 @@ async def ausweis_page(dog_id: int, request: Request):
-
- - -
+

Impfungen

@@ -1742,7 +1726,7 @@ async def partner_landing():
""" - return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"}) + return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) # ------------------------------------------------------------------ @@ -1940,8 +1924,8 @@ async def spa_fallback(full_path: str): '', '', ) - return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"}) + return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) return FileResponse( f"{STATIC_DIR}/index.html", - headers={"Cache-Control": "no-store, no-cache"} + headers={"Cache-Control": "no-cache"} ) diff --git a/backend/routes/dogs.py b/backend/routes/dogs.py index 6c35334..42b9b32 100644 --- a/backend/routes/dogs.py +++ b/backend/routes/dogs.py @@ -742,18 +742,9 @@ async def get_hunde_buch( - +
{cover_img} diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 71b87ca..2f95c24 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -8370,336 +8370,3 @@ 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 856efe8..c3abcea 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -578,7 +578,7 @@ - + @@ -640,7 +640,7 @@ // Wenn neuer SW die Kontrolle übernimmt → Seite neu laden navigator.serviceWorker.addEventListener('controllerchange', () => { - window.location.replace('/?_t=' + Date.now()); + window.location.reload(); }); navigator.serviceWorker.addEventListener('message', e => { diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 9887ed7..0fe4e0e 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,11 +3,9 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '774'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '760'; // ← 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 -if (location.search.includes('_t=')) history.replaceState(null, '', '/'); const App = (() => { @@ -1039,15 +1037,15 @@ const App = (() => { btn.textContent = 'Lädt…'; btn.disabled = true; sessionStorage.setItem('by_update_reload', APP_VER); - // ?_t= Timestamp zwingt iOS bfcache zur Aufgabe — wird beim Start sofort entfernt - setTimeout(() => location.replace('/?_t=' + Date.now()), 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' }); - reg?.update().catch(() => {}); // kein await — kann hängen + await reg?.update(); const keys = await caches.keys(); await Promise.all(keys.map(k => caches.delete(k))); } catch { /* ignorieren */ } + setTimeout(() => location.reload(), 600); }); } @@ -1092,7 +1090,6 @@ 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 85ce02d..58d5aa7 100644 --- a/backend/static/js/pages/dog-profile.js +++ b/backend/static/js/pages/dog-profile.js @@ -108,7 +108,8 @@ window.Page_dog_profile = (() => { margin-bottom:var(--space-5);text-align:left"> ${geburtstag ? `
-
Geburtstag
+
Geburtstag
${geburtstag}
${_calcAlter(dog.geburtstag)} @@ -117,7 +118,8 @@ window.Page_dog_profile = (() => { ` : ''} ${dog.geschlecht ? `
-
${dog.geschlecht === 'm' ? '' : ''} Geschlecht
+
${dog.geschlecht === 'm' ? '' : ''} Geschlecht
${dog.geschlecht === 'm' ? 'Rüde' : 'Hündin'}
@@ -125,7 +127,8 @@ window.Page_dog_profile = (() => { ` : ''} ${dog.gewicht_kg ? `
-
Gewicht
+
Gewicht
${dog.gewicht_kg} kg
` : ''} @@ -199,7 +202,7 @@ window.Page_dog_profile = (() => { Visitenkarte teilen ` : ''} - ${!dog.is_guest && App.hasPro(_appState.user) ? `` : ''} ${!dog.is_guest ? `
`, @@ -671,7 +676,7 @@ window.Page_dog_profile = (() => { `; const footer = ` -
+
${hasPhoto ? `` : ''}
${hasPhoto ? `` : ''} @@ -1039,7 +1044,7 @@ window.Page_dog_profile = (() => { title: 'Weiteren Hund anlegen', body: _formHTML(null, true), footer: ` -
+
@@ -1056,7 +1061,7 @@ window.Page_dog_profile = (() => { title: `${dog.name} bearbeiten`, body: _formHTML(dog, true), footer: ` -
+
@@ -1652,7 +1657,8 @@ window.Page_dog_profile = (() => {
` : vaccs.map(v => `
+ style="display:flex;align-items:flex-start;gap:var(--space-3); + padding:var(--space-3) 0;border-bottom:1px solid var(--c-border)">
${_esc(v.krankheit)}
@@ -1689,7 +1695,8 @@ window.Page_dog_profile = (() => {
` : meds.map(m => `
+ style="display:flex;align-items:flex-start;gap:var(--space-3); + padding:var(--space-3) 0;border-bottom:1px solid var(--c-border)">
${_esc(m.name)}
@@ -2066,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/js/pages/settings.js b/backend/static/js/pages/settings.js index 07cfab3..0197f58 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -120,7 +120,12 @@ window.Page_settings = (() => {
-
+
${avatarInner}
-
Aktivität
+
Aktivität
Lädt…
@@ -231,7 +238,9 @@ window.Page_settings = (() => {
-
Trophäen
+
Trophäen
Lädt…
@@ -276,31 +285,29 @@ window.Page_settings = (() => { ⭐ Ban Yaro Pro kommt bald — mehr Features, mehrere Hunde.
` : ''} -
- +
-
+
App-Einstellungen
-
+
-
+
Dark Mode
Erscheinungsbild der App @@ -322,9 +329,9 @@ window.Page_settings = (() => {
-
+
-
+
KI-Notiz-Assistent
Erkennt Muster in deinen Notizen und macht Vorschläge @@ -346,9 +353,9 @@ window.Page_settings = (() => {
-
+
-
+
Goldene Gassi-Stunde täglich
Täglich um 07:00 Uhr: bestes Wetterfenster für den Gassi-Gang @@ -385,7 +392,10 @@ window.Page_settings = (() => {
-
+
App installieren
@@ -701,7 +711,7 @@ window.Page_settings = (() => { `, footer: ` -
+
@@ -737,24 +747,27 @@ window.Page_settings = (() => { } if (btn) btn.textContent = 'Prüfe…'; try { - // Versionsnummer direkt vom API-Endpunkt holen - const r = await fetch('/api/version', { cache: 'no-store' }); - const { version: serverVersion } = await r.json(); - const localVersion = typeof APP_VER !== 'undefined' ? APP_VER : '0'; + // Aktuelle Version vom Server holen (no-cache) + const serverResp = await fetch('/js/app.js', { cache: 'no-store' }); + const serverText = await serverResp.text(); + const match = serverText.match(/APP_VERSION\s*=\s*'([^']+)'/); + const serverVersion = match?.[1] || null; + const localVersion = typeof APP_VERSION !== 'undefined' ? APP_VERSION : '0'; + // SW update anstoßen const reg = await navigator.serviceWorker.getRegistration(); - reg?.update().catch(() => {}); // kein await — kann hängen + await reg?.update(); if (serverVersion && serverVersion !== localVersion) { + // Neuere Version verfügbar — Seite neu laden if (reg?.waiting) reg.waiting.postMessage({ type: 'SKIP_WAITING' }); - UI.toast.info(`Update auf v${serverVersion} — Seite wird neu geladen…`); - setTimeout(() => location.replace('/?_t=' + Date.now()), 1500); + UI.toast.info(`Update auf v${serverVersion} verfügbar — Seite wird neu geladen…`); + setTimeout(() => location.reload(), 1500); } else if (reg?.waiting) { reg.waiting.postMessage({ type: 'SKIP_WAITING' }); UI.toast.success('Update wird installiert…'); - setTimeout(() => location.replace('/?_t=' + Date.now()), 1500); } else { - UI.toast.success(`Ban Yaro ist aktuell — Build ${localVersion}`); + UI.toast.success(`Ban Yaro ist aktuell — v${localVersion}`); } } catch { UI.toast.error('Update-Prüfung fehlgeschlagen.'); @@ -1026,7 +1039,12 @@ window.Page_settings = (() => { slot.innerHTML = `
-
Züchter-Profil
+
+ Züchter-Profil +
${statusBadge} ${actionBlock} @@ -1248,7 +1266,7 @@ window.Page_settings = (() => { `, footer: ` -
+
diff --git a/backend/static/js/pages/wetter.js b/backend/static/js/pages/wetter.js index 8eae462..31d6a4f 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.wind_kmh ?? 0)} km/h + ${_esc(compass)} · ${Math.round(d.windspeed_max ?? 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.wind_kmh ?? 0; + const wind = d.windspeed_max ?? 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.wind_kmh ?? 0; + const wind = d.windspeed_max ?? 0; const asphalt = d.asphalt_temp ?? 0; const wcode = d.weathercode ?? 0; const isSnow = wcode >= 71 && wcode <= 77; diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js index 4fcd9de..438dd1f 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -274,20 +274,28 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'fab-overlay'; - ov.className = 'w3-sheet-overlay'; + ov.style.cssText = 'position:fixed;inset:0;z-index:460;display:flex;flex-direction:column;justify-content:flex-end'; ov.innerHTML = ` -
-
-
-
${options.length ? title : 'Schnellzugriff'}
-
${options.map(o => ` - `).join('')}
- @@ -343,14 +353,20 @@ window.Worlds = (() => { if (!chips.length) return ''; return `
- +
${worldLabels[w]}
${chips.map(c => ` - `).join('')}
@@ -360,13 +376,16 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'fab-overlay'; - ov.className = 'w3-sheet-overlay'; + ov.style.cssText = 'position:fixed;inset:0;z-index:460;display:flex;flex-direction:column;justify-content:flex-end'; ov.innerHTML = ` -
-
-
-
Ausgeblendete Funktionen
-
@@ -411,8 +430,7 @@ window.Worlds = (() => { const ov = document.createElement('div'); ov.id = 'quick-gassi-overlay'; - ov.className = 'w3-sheet-overlay'; - ov.style.zIndex = '400'; + ov.style.cssText = 'position:fixed;inset:0;z-index:400;display:flex;flex-direction:column;justify-content:flex-end'; const weatherLine = weatherData ? `
@@ -420,17 +438,20 @@ window.Worlds = (() => {
` : ''; ov.innerHTML = ` -
-
-
+
+
+
-
🐾 Schnell-Gassi
+
🐾 Schnell-Gassi
${_esc(dog.name)} · ohne GPS
${weatherLine}
-
@@ -438,13 +459,20 @@ window.Worlds = (() => {
Dauer
${durations.map(d => ` - `).join('')}
- @@ -462,7 +490,10 @@ window.Worlds = (() => { btn.addEventListener('click', () => { selectedMin = parseInt(btn.dataset.min); ov.querySelectorAll('.qg-dur').forEach(b => { - b.classList.toggle('active', parseInt(b.dataset.min) === selectedMin); + 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)'; }); }); }); @@ -474,8 +505,15 @@ window.Worlds = (() => { submitBtn.textContent = 'Wird eingetragen…'; try { - // Kein Tagebucheintrag — nur Streak pingen - await API.post(`/streak/${dog.id}/ping`); + const payload = { + typ: 'gassi', + titel: 'Schnell-Gassi 🐾', + text: `Kurze Runde, ${selectedMin} Minuten`, + }; + if (weatherData) { + payload.weather_json = JSON.stringify(weatherData); + } + await API.post(`/dogs/${dog.id}/diary`, payload); _close(); UI.toast?.success(`Gassi eingetragen! ${selectedMin} min 🐾`); // Streak-Cache invalidieren @@ -1065,7 +1103,7 @@ window.Worlds = (() => { ${alertHtml} ${user && dog ? `
-
+
${weatherEmoji}
@@ -1074,7 +1112,7 @@ window.Worlds = (() => { ${gassiScore ?? '—'} ${gassiScore ? `/10` : ''}
- ${w ? `${Math.round(w.temp_c ?? 0)}° · ${w.precip_prob ?? 0}% Regen` : ''} + ${w ? `
${Math.round(w.temp_c ?? 0)}° · ${w.precip_prob ?? 0}% Regen
` : ''}
diff --git a/backend/static/sw.js b/backend/static/sw.js index 171d7f4..ba39608 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-v774'; +const CACHE_VERSION = 'by-v760'; 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,8 +292,7 @@ self.addEventListener('fetch', event => { } return response; }) - .catch(() => caches.match(event.request) - .then(cached => cached || new Response('', { status: 503 }))) + .catch(() => caches.match(event.request)) ); return; } @@ -325,8 +324,9 @@ self.addEventListener('fetch', event => { }) ) .catch(() => { - if (event.request.mode === 'navigate') return caches.match('/'); - return new Response('', { status: 503 }); + if (event.request.mode === 'navigate') { + return caches.match('/'); + } }) ); }); @@ -344,10 +344,6 @@ 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;