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