Compare commits

...

9 commits

Author SHA1 Message Date
bf26e5faf4 Debug: Toast mit Sidebar computed styles 2026-04-14 17:50:15 +02:00
d399cb84cf Fix: Sidebar/Backdrop aus #app raus, direkt in <body>
#sidebar und #sidebar-backdrop waren Kinder von #app (display:flex),
was auf iOS Safari Stacking-Context-Probleme verursacht. Beide Elemente
sind jetzt direkte Kinder von <body>. _openSidebar() zurueck zur
sauberen .open-Klassen-Methode. will-change:transform entfernt.
SW by-v32 -> by-v33.
2026-04-14 17:44:46 +02:00
3b79efb82b Fix: Sidebar transform:translateX(0) in Inline-Styles vergessen 2026-04-14 17:36:45 +02:00
1c6ec6f17e Fix: /update-Endpunkt zum manuellen SW-Cache-Reset + STATIC_ASSETS bereinigt
/update loescht SW + alle Caches via JS und leitet zur App zurueck.
Alter SW hat /update nie gecacht -> immer frisch vom Server.
STATIC_ASSETS ohne ?v= (verhindert fehlerhafte cache.addAll()-Fehler).
2026-04-14 17:33:12 +02:00
619ff559e6 Fix: SW Network-First für Navigation + versionierte CSS/JS-URLs (v=32)
- sw.js: index.html nie aus Cache (navigation → network-first)
- index.html: ?v=32 an layout.css, components.css, api/ui/app.js
- sw.js STATIC_ASSETS: versionierte URLs gecacht
- app.js: Sidebar-Background per getComputedStyle statt CSS-Variable-String
- SW by-v31 → by-v32
2026-04-14 17:26:02 +02:00
b6fae96334 Fix: Sidebar via JS-Inline-Styles (SW-Cache-unabhängig) + SW force-navigate
_openSidebar() setzt nun direkt style.cssText statt CSS-Klassen,
damit stale CSS-Cache keinen Einfluss hat. SW by-v30 → by-v31 mit
clients.navigate() im activate-Handler für automatischen Reload.
2026-04-14 17:22:13 +02:00
40b802ae86 Fix: Mobile Sidebar z-index 1000 + display:flex !important
Backdrop lag bei z-index 499 über der Sidebar (z-index 300 Base).
Sidebar bekommt auf Mobile z-index:1000, display:flex !important
und will-change:transform. SW by-v29 → by-v30.
2026-04-14 17:16:54 +02:00
1c8ed88dac Fix: Karte position:fixed + Mobile-Sidebar CSS-Kaskade
- .map-full-layout: position:fixed statt absolute;inset:0 mit
  expliziten Offsets (header/nav/sidebar) — unabhängig von
  height-Kette, überdeckt Sidebar nicht mehr
- layout.css: Mobile Drawer-CSS nach Base-#sidebar-Regel verschoben
  (vorher: display:none hat gewonnen weil später im File)
- map.js: zweites invalidateSize() nach 600ms
- SW by-v28 → by-v29
2026-04-14 17:09:00 +02:00
e5bf841d45 Fix: Karte-Seite erhält definite Höhe für Leaflet-Tiles
#page-map bekommt height:100% (statt min-height), damit .page-body und
.map-full-layout eine auflösbare Höhe haben. SW by-v27 → by-v28.
2026-04-14 17:02:03 +02:00
7 changed files with 138 additions and 49 deletions

View file

@ -129,6 +129,49 @@ async def share_target(request: Request):
headers={"Cache-Control": "no-cache"}
)
# Cache-Reset-Seite — löscht SW + Caches, leitet zur App weiter
@app.get("/update")
async def force_update():
from fastapi.responses import HTMLResponse
html = """<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ban Yaro Aktualisieren</title>
<style>
body{font-family:-apple-system,sans-serif;display:flex;align-items:center;
justify-content:center;min-height:100vh;margin:0;background:#faf8f5}
.box{text-align:center;padding:2rem}
h1{color:#C4843A;margin-bottom:.5rem}
p{color:#666;margin-bottom:1.5rem}
.sp{width:44px;height:44px;border:4px solid #eee;border-top-color:#C4843A;
border-radius:50%;animation:s .8s linear infinite;margin:0 auto}
@keyframes s{to{transform:rotate(360deg)}}
</style>
</head>
<body>
<div class="box">
<h1>Ban Yaro</h1>
<p>App wird aktualisiert&#8230;</p>
<div class="sp"></div>
</div>
<script>
(async () => {
if ('serviceWorker' in navigator) {
const regs = await navigator.serviceWorker.getRegistrations();
await Promise.all(regs.map(r => r.unregister()));
}
const keys = await caches.keys();
await Promise.all(keys.map(k => caches.delete(k)));
window.location.replace('/');
})();
</script>
</body>
</html>"""
return HTMLResponse(html, headers={"Cache-Control": "no-store"})
# SPA Fallback — ALLE nicht-API-Routen gehen zur index.html
@app.get("/{full_path:path}")
async def spa_fallback(full_path: str):

View file

@ -1565,9 +1565,20 @@ textarea.form-control {
ZENTRALE KARTE (map.js)
============================================================ */
.map-full-layout {
position: absolute;
inset: 0;
position: fixed;
top: calc(var(--header-height) + var(--safe-top));
left: 0;
right: 0;
bottom: calc(var(--nav-bottom-height) + var(--safe-bottom));
overflow: hidden;
z-index: 1;
}
@media (min-width: 768px) {
.map-full-layout {
top: 0;
left: var(--nav-sidebar-width);
bottom: 0;
}
}
.map-full {
width: 100%;

View file

@ -118,17 +118,15 @@
.sidebar-backdrop { display: none !important; }
}
/* Mobile Sidebar als Drawer */
@media (max-width: 767px) {
#sidebar {
display: flex;
z-index: 500;
transform: translateX(-100%);
transition: transform 0.28s ease;
}
#sidebar.open {
transform: translateX(0);
}
/* Karte: volle Höhe, kein Scrollen, kein Padding */
#page-map {
height: 100%;
overflow: hidden;
}
#page-map > .page-body {
padding: 0 !important;
overflow: hidden;
height: 100%;
}
/* ------------------------------------------------------------
@ -267,6 +265,30 @@
#sidebar { display: flex; }
}
/* Mobile Sidebar als Drawer */
@media (max-width: 767px) {
#sidebar {
display: flex !important; /* überschreibt display:none aus Base */
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: var(--nav-sidebar-width);
z-index: 1000; /* klar über Backdrop (999) */
transform: translateX(-100%);
transition: transform 0.28s ease;
background: var(--c-surface);
box-shadow: none;
}
#sidebar.open {
transform: translateX(0);
box-shadow: 4px 0 24px rgba(0,0,0,0.18);
}
.sidebar-backdrop {
z-index: 999; /* unter Sidebar, über Seiteninhalt */
}
}
.sidebar-logo {
padding: var(--space-6) var(--space-5);
display: flex;

View file

@ -20,33 +20,17 @@
<title>Ban Yaro</title>
<!-- CSS: Reihenfolge ist wichtig -->
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css">
<link rel="stylesheet" href="/css/layout.css">
<link rel="stylesheet" href="/css/components.css">
<link rel="stylesheet" href="/css/layout.css?v=32">
<link rel="stylesheet" href="/css/components.css?v=32">
</head>
<body>
<!-- ============================================================
APP SHELL
============================================================ -->
<div id="app">
<!-- Backdrop + Sidebar direkt im body (kein Ancestor-Stacking-Context) -->
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
<!-- MOBILE HEADER (wird per JS mit Seitentitel befüllt) -->
<header id="app-header">
<button class="header-back hidden" id="header-back" aria-label="Zurück">&#8592;</button>
<div id="header-dog-switcher" class="dog-switcher">
<span class="header-title" id="header-title">Ban Yaro</span>
</div>
<div id="header-actions"></div>
<button class="header-menu-btn" id="header-menu-btn" aria-label="Menü"></button>
</header>
<!-- Backdrop für mobile Sidebar-Drawer -->
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
<!-- DESKTOP SIDEBAR -->
<nav id="sidebar" role="navigation" aria-label="Hauptnavigation">
<nav id="sidebar" role="navigation" aria-label="Hauptnavigation">
<div class="sidebar-logo" id="sidebar-dog-switcher">
<img class="sidebar-logo-img" src="/icons/icon-180.png" alt="Ban Yaro">
<span class="sidebar-logo-text">Ban Yaro</span>
@ -111,6 +95,21 @@
</div>
</nav>
<!-- ============================================================
APP SHELL
============================================================ -->
<div id="app">
<!-- MOBILE HEADER -->
<header id="app-header">
<button class="header-back hidden" id="header-back" aria-label="Zurück">&#8592;</button>
<div id="header-dog-switcher" class="dog-switcher">
<span class="header-title" id="header-title">Ban Yaro</span>
</div>
<div id="header-actions"></div>
<button class="header-menu-btn" id="header-menu-btn" aria-label="Menü"></button>
</header>
<!-- HAUPT-INHALTSBEREICH -->
<main id="page-content" role="main">
@ -215,9 +214,9 @@
<div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js"></script>
<script src="/js/ui.js"></script>
<script src="/js/app.js"></script>
<script src="/js/api.js?v=32"></script>
<script src="/js/ui.js?v=32"></script>
<script src="/js/app.js?v=32"></script>
<!-- Feature-Seiten werden lazy geladen -->

View file

@ -193,7 +193,11 @@ const App = (() => {
}
function _openSidebar() {
document.getElementById('sidebar')?.classList.add('open');
const s = document.getElementById('sidebar');
if (!s) { UI.toast.error('sidebar: NULL'); return; }
s.classList.add('open');
const cs = getComputedStyle(s);
UI.toast.info(`display:${cs.display} | transform:${cs.transform} | z:${cs.zIndex}`);
document.getElementById('sidebar-backdrop')?.classList.add('visible');
}

View file

@ -148,8 +148,9 @@ window.Page_map = (() => {
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 })
.addTo(_map);
// invalidateSize nach kurzer Verzögerung, damit der Browser das Layout abgeschlossen hat
setTimeout(() => _map.invalidateSize(), 150);
// invalidateSize zweimal: einmal früh, einmal nach möglichen Layout-Delays
setTimeout(() => _map.invalidateSize(), 100);
setTimeout(() => _map.invalidateSize(), 600);
window.addEventListener('resize', () => _map.invalidateSize());
}

View file

@ -3,12 +3,11 @@
Offline-Cache + Push Notifications
============================================================ */
const CACHE_VERSION = 'by-v27';
const CACHE_VERSION = 'by-v33';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
// Diese Dateien werden beim Install gecacht (App Shell)
// index.html wird NICHT pre-gecacht (immer Network-First)
const STATIC_ASSETS = [
'/',
'/css/design-system.css',
'/css/layout.css',
'/css/components.css',
@ -44,7 +43,7 @@ self.addEventListener('activate', event => {
});
// ----------------------------------------------------------
// FETCH — Cache-First für statische Assets, Network-First für API
// FETCH — Network-First für HTML, Cache-First für Assets, Network für API
// ----------------------------------------------------------
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
@ -60,12 +59,25 @@ self.addEventListener('fetch', event => {
return;
}
// Navigation (index.html): immer Network-First
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
return response;
})
.catch(() => caches.match('/'))
);
return;
}
// Statische Assets: Cache-First
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request)
.then(response => {
// Erfolgreiche Responses für statische Assets cachen
if (response.ok && event.request.method === 'GET') {
const clone = response.clone();
caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
@ -74,7 +86,6 @@ self.addEventListener('fetch', event => {
})
)
.catch(() => {
// Offline-Fallback: App Shell zurückgeben
if (event.request.mode === 'navigate') {
return caches.match('/');
}
@ -136,7 +147,6 @@ self.addEventListener('notificationclick', event => {
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then(windowClients => {
// Offenes Fenster fokussieren
for (const client of windowClients) {
if (client.url.includes(self.location.origin)) {
client.focus();
@ -144,7 +154,6 @@ self.addEventListener('notificationclick', event => {
return;
}
}
// Neues Fenster öffnen
return clients.openWindow(url);
})
);