diff --git a/backend/main.py b/backend/main.py
index abb633e..f3cb136 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -410,7 +410,7 @@ async def serve_media(path: str, request: _Request):
raise _HE(404, "Nicht gefunden.")
return _media_response(filepath)
-APP_VER = "1036" # muss mit APP_VER in app.js übereinstimmen
+APP_VER = "1068" # 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 9be7812..4941267 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -101,9 +101,9 @@
-
-
-
+
+
+
@@ -460,6 +460,10 @@
+
+
@@ -508,6 +512,14 @@
+
+
+
+
@@ -604,10 +616,10 @@
-
-
-
-
+
+
+
+
@@ -693,7 +705,7 @@
// Backup: controllerchange (falls updatefound nicht feuert)
// NICHT registrieren wenn diese Seite selbst durch einen SW-Reload entstand (_t= im URL)
// — verhindert Dauerschleife wenn clients.claim() erst nach Seitenstart feuert
- if (!location.search.includes('_t=')) {
+ if (!window._BY_SW_RELOAD) {
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (sessionStorage.getItem('by_skip_sw_reload')) {
sessionStorage.removeItem('by_skip_sw_reload');
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index 40e063f..d29467b 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,11 +3,13 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '1036'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '1068'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.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, '', '/');
+// Cache-Bust-Parameter nach Update-Reload sofort entfernen.
+// Flag MUSS vor replaceState gesetzt werden — index.html liest es danach.
+window._BY_SW_RELOAD = location.search.includes('_t=');
+if (window._BY_SW_RELOAD) history.replaceState(null, '', '/');
const App = (() => {
@@ -67,13 +69,16 @@ const App = (() => {
agb: { title: 'AGB', module: null },
widget: { title: 'Widget', module: null, requiresAuth: true },
notifications: { title: 'Aktuelles', module: null, requiresAuth: true },
- breeder: { title: 'Züchter-Profil', module: null },
- litters: { title: 'Wurfverwaltung', module: null, requiresAuth: true },
+ breeder: { title: 'Züchter-Profil', module: null },
+ 'breeder-editor': { title: 'Profil bearbeiten', module: null, requiresAuth: true },
+ litters: { title: 'Wurfverwaltung', module: null, requiresAuth: true },
wurfboerse: { title: 'Wurfbörse', module: null },
zuchthunde: { title: 'Zuchtkartei', module: null, requiresAuth: true },
laeufi: { title: 'Läufigkeit', module: null, requiresAuth: true },
'zucht-profil': { title: 'Hunde-Profil', module: null },
gruender: { title: '100 Gründer', module: null },
+ partner: { title: 'Unsere Partner', module: null },
+ 'partner-profil': { title: 'Partner-Profil', module: null, requiresAuth: true },
jobs: { title: 'Wir suchen dich', module: null },
expenses: { title: 'Ausgaben', module: null, requiresAuth: true },
recalls: { title: 'Rückrufe', module: null },
@@ -608,11 +613,16 @@ const App = (() => {
_checkNearbyAlerts();
setInterval(() => { _updateNotifBadge(); _updateChatBadge(); }, 30_000);
setInterval(_checkNearbyAlerts, 5 * 60_000);
+ // App-Heartbeat: last_seen aktualisieren (Nutzungsfrequenz für Admin)
+ const _sendHeartbeat = () => API.post('/auth/heartbeat', {}).catch(() => {});
+ _sendHeartbeat();
+ setInterval(_sendHeartbeat, 5 * 60_000);
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
_updateNotifBadge();
_updateChatBadge();
_checkNearbyAlerts();
+ _sendHeartbeat();
if (state.page === 'chat') {
pages['chat']?.module?.refresh?.();
}
diff --git a/backend/static/sw.js b/backend/static/sw.js
index df8e777..e0f0fc5 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -3,7 +3,9 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
-const CACHE_VERSION = 'by-v1036';
+// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
+const VER = '1068';
+const CACHE_VERSION = `by-v${VER}`;
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
@@ -22,13 +24,14 @@ const PRIORITY_PAGES = [
// index.html wird NICHT pre-gecacht (immer Network-First)
const STATIC_ASSETS = [
- '/css/design-system.css?v=982',
- '/css/layout.css?v=982',
- '/css/components.css?v=982',
+ `/css/design-system.css?v=${VER}`,
+ `/css/layout.css?v=${VER}`,
+ `/css/components.css?v=${VER}`,
'/icons/phosphor.svg',
- '/js/api.js',
- '/js/ui.js',
- '/js/app.js',
+ `/js/api.js?v=${VER}`,
+ `/js/ui.js?v=${VER}`,
+ `/js/app.js?v=${VER}`,
+ `/js/worlds.js?v=${VER}`,
'/js/leaflet.markercluster.js',
'/css/MarkerCluster.css',
'/css/MarkerCluster.Default.css',