diff --git a/backend/main.py b/backend/main.py index b0d5756..7c32d41 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 = "1027" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "1028" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/routes/profile.py b/backend/routes/profile.py index 2dfe098..ad387bc 100644 --- a/backend/routes/profile.py +++ b/backend/routes/profile.py @@ -2,6 +2,7 @@ import io import os +import re import uuid from typing import Optional @@ -29,6 +30,7 @@ class ProfileUpdate(BaseModel): gassi_stunde_push: Optional[int] = None preferred_theme: Optional[str] = None billing_address: Optional[str] = None + geburtstag: Optional[str] = None def _load_user(user_id: int) -> dict: @@ -36,7 +38,8 @@ def _load_user(user_id: int) -> dict: row = conn.execute( """SELECT id, name, real_name, email, rolle, is_premium, email_verified, bio, wohnort, erfahrung, social_link, - profil_sichtbarkeit, avatar_url, created_at, billing_address + profil_sichtbarkeit, avatar_url, created_at, billing_address, + geburtstag FROM users WHERE id=?""", (user_id,) ).fetchone() @@ -64,6 +67,9 @@ async def update_profile(data: ProfileUpdate, user=Depends(get_current_user)): raise HTTPException(400, "wohnort darf maximal 60 Zeichen lang sein.") if "social_link" in fields and len(fields["social_link"]) > 120: raise HTTPException(400, "social_link darf maximal 120 Zeichen lang sein.") + if "geburtstag" in fields and fields["geburtstag"]: + if not re.fullmatch(r"\d{2}-\d{2}", fields["geburtstag"]): + raise HTTPException(400, "geburtstag muss im Format MM-DD sein (z.B. 03-15).") if not fields: return _load_user(user["id"]) diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 6c62646..489d3a8 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 = '1027'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1028'; // ← 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 diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js index d46459d..c2906d8 100644 --- a/backend/static/js/pages/settings.js +++ b/backend/static/js/pages/settings.js @@ -1184,6 +1184,15 @@ window.Page_settings = (() => { placeholder="Musterstraße 1 12345 Berlin" style="${inputStyle};resize:vertical;font-family:inherit">${_esc(u.billing_address || '')} +
+ + +
Wird nur für Geburtstagsgrüße in der App verwendet.
+
@@ -1211,6 +1220,7 @@ window.Page_settings = (() => { social_link: fd.social_link || '', profil_sichtbarkeit: fd.profil_sichtbarkeit || 'public', billing_address: fd.billing_address || '', + geburtstag: fd.geburtstag || '', }); Object.assign(_appState.user, updated); UI.modal.close?.(); diff --git a/backend/static/js/worlds.js b/backend/static/js/worlds.js index b5a5f76..903b90c 100644 --- a/backend/static/js/worlds.js +++ b/backend/static/js/worlds.js @@ -1092,9 +1092,15 @@ window.Worlds = (() => { } else if (!dog) { _applyBgImage(null); } const hour = new Date().getHours(); - const greet = hour < 5 ? 'Gute Nacht' : hour < 12 ? 'Guten Morgen' : hour < 18 ? 'Hallo' : 'Guten Abend'; const firstName = user?.name?.split(' ')[0] || ''; const dayStr = new Date().toLocaleDateString('de-DE', { weekday:'long', day:'numeric', month:'long' }); + + // User-Geburtstag heute? + const _todayMmDd = (() => { const d = new Date(); return String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0'); })(); + const userBdayToday = user?.geburtstag && user.geburtstag === _todayMmDd; + const greet = userBdayToday + ? `Herzlichen Glückwunsch` + : (hour < 5 ? 'Gute Nacht' : hour < 12 ? 'Guten Morgen' : hour < 18 ? 'Hallo' : 'Guten Abend'); const stale = isOffline && staleMin > 5 ? `· Offline` : ''; @@ -1119,6 +1125,26 @@ window.Worlds = (() => { : (w.temp_c ?? 20) < 2 ? '🌨️' : '☀️'; + // User-Geburtstag Reminder + const userBdayHtml = userBdayToday ? ` +
+
+ + + + + + +
+
+ Alles Gute zum Geburtstag, ${_esc(firstName)}! +
+
+ Wir wünschen dir und deinem Hund einen wunderschönen Tag 🐾 +
+
` : ''; + // Alert-Reminder const alertHtml = alertList.slice(0,1).map(a => `
@@ -1165,6 +1191,7 @@ window.Worlds = (() => { ${user ? userAvatarHtml : ''}
+ ${userBdayHtml} ${alertHtml} ${user && dog ? `
diff --git a/backend/static/sw.js b/backend/static/sw.js index 018084c..fdd58d5 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-v1027'; +const CACHE_VERSION = 'by-v1028'; 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