Feature: Welten-Onboarding, Wetter-Motivation, UX-Fixes (SW by-v715)
Welten (worlds.js): - Swipe-Hints beim ersten Öffnen (JETZT ← → WELT animiert, einmalig) - Kein-Hund-Onboarding: Feature-Preview-Grid statt leerer Karte - Hintergrund-Foto-Hint: Kamera-Karte wenn noch kein Tagebuchfoto - worlds-back: navigiert zu Welcome wenn kein User eingeloggt - Nach Logout: worlds-back Button sofort ausgeblendet Wetter (wetter.js): - Standort-Fehlerseite zu Motivations-Seite umgebaut - Feature-Preview: Gassi-Score, 7-Tage, Regenradar, Rekorde - CTA: Standort freigeben + Registrieren (nur für Gäste) Settings (settings.js): - Logo in Auth-Form: display:block + margin:0 auto zentriert - Header bleibt sichtbar (FAB/Zurück-Navigation funktioniert) Jobs (jobs.js): - 2-Spalten-Grid auf Mobile: auto-fit statt festes 1fr 1fr - Kein doppeltes Padding im Wrapper Backend: - weather.py, achievements.py: diary JOIN fix (d.user_id → dogs JOIN) - Neue Wetter-Badges: wetter_tapfer, jahreszeiten, schnee - Ernährungs-, Reise-, Ausgaben-Seite: diverse UX-Verbesserungen - Presse-Seite erweitert - Ban Yaro Foto-Assets (WebP + HIRES JPG)
This commit is contained in:
parent
aa4849d947
commit
55069d246b
28 changed files with 719 additions and 198 deletions
|
|
@ -203,11 +203,11 @@ def check_and_award(user_id: int, conn):
|
|||
"SELECT current_streak FROM users WHERE id=?", (user_id,)
|
||||
).fetchone()
|
||||
|
||||
# Wetter-Tapferkeit: Diary-Einträge bei schlechtem Wetter
|
||||
# Wetter-Tapferkeit: Diary-Einträge bei schlechtem Wetter (über Dog-Join)
|
||||
wetter_row = conn.execute("""
|
||||
SELECT COUNT(*) AS cnt FROM diary d
|
||||
LEFT JOIN diary_dogs dd ON dd.diary_id = d.id
|
||||
WHERE d.user_id = ?
|
||||
JOIN dogs dog ON dog.id = d.dog_id
|
||||
WHERE dog.user_id = ?
|
||||
AND d.weather_json IS NOT NULL
|
||||
AND (
|
||||
CAST(json_extract(d.weather_json, '$.precip_prob') AS INTEGER) > 60
|
||||
|
|
@ -216,23 +216,28 @@ def check_and_award(user_id: int, conn):
|
|||
)
|
||||
""", (user_id,)).fetchone()
|
||||
|
||||
# Jahreszeiten: Anzahl Jahreszeiten mit mind. 5 Diary-Einträgen
|
||||
# Jahreszeiten: Anzahl Jahreszeiten mit mind. 5 Diary-Einträgen (über Dog-Join)
|
||||
jahreszeiten_row = conn.execute("""
|
||||
SELECT
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary WHERE user_id=? AND CAST(strftime('%m', datum) AS INTEGER) IN (3,4,5)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary WHERE user_id=? AND CAST(strftime('%m', datum) AS INTEGER) IN (6,7,8)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary WHERE user_id=? AND CAST(strftime('%m', datum) AS INTEGER) IN (9,10,11)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary WHERE user_id=? AND CAST(strftime('%m', datum) AS INTEGER) IN (12,1,2)) >= 5 THEN 1 ELSE 0 END)
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary d2 JOIN dogs g2 ON g2.id=d2.dog_id
|
||||
WHERE g2.user_id=? AND CAST(strftime('%m', d2.datum) AS INTEGER) IN (3,4,5)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary d2 JOIN dogs g2 ON g2.id=d2.dog_id
|
||||
WHERE g2.user_id=? AND CAST(strftime('%m', d2.datum) AS INTEGER) IN (6,7,8)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary d2 JOIN dogs g2 ON g2.id=d2.dog_id
|
||||
WHERE g2.user_id=? AND CAST(strftime('%m', d2.datum) AS INTEGER) IN (9,10,11)) >= 5 THEN 1 ELSE 0 END) +
|
||||
(CASE WHEN (SELECT COUNT(*) FROM diary d2 JOIN dogs g2 ON g2.id=d2.dog_id
|
||||
WHERE g2.user_id=? AND CAST(strftime('%m', d2.datum) AS INTEGER) IN (12,1,2)) >= 5 THEN 1 ELSE 0 END)
|
||||
AS jahreszeiten_score
|
||||
FROM (SELECT 1)
|
||||
""", (user_id, user_id, user_id, user_id)).fetchone()
|
||||
|
||||
# Schnee: Diary-Einträge bei Schnee (weathercode 71-77)
|
||||
# Schnee: Diary-Einträge bei Schnee (weathercode 71-77, über Dog-Join)
|
||||
schnee_row = conn.execute("""
|
||||
SELECT COUNT(*) AS cnt FROM diary
|
||||
WHERE user_id = ?
|
||||
AND weather_json IS NOT NULL
|
||||
AND CAST(json_extract(weather_json, '$.weathercode') AS INTEGER) BETWEEN 71 AND 77
|
||||
SELECT COUNT(*) AS cnt FROM diary d
|
||||
JOIN dogs dog ON dog.id = d.dog_id
|
||||
WHERE dog.user_id = ?
|
||||
AND d.weather_json IS NOT NULL
|
||||
AND CAST(json_extract(d.weather_json, '$.weathercode') AS INTEGER) BETWEEN 71 AND 77
|
||||
""", (user_id,)).fetchone()
|
||||
|
||||
metrics = {
|
||||
|
|
|
|||
|
|
@ -280,9 +280,14 @@ class UserPoiIn(BaseModel):
|
|||
ALLOWED_TYPES = {
|
||||
'waste_basket', 'drinking_water', 'dog_park',
|
||||
'giftkoeder', # Giftköder (exklusiv, kein Kombi)
|
||||
'gefahr', # Allgemeine Gefahr / Hinweis
|
||||
'freilauf', # Freilauffläche
|
||||
'restaurant', # Hundefreundliches Restaurant / Café
|
||||
'shop', # Hundefreundlicher Shop
|
||||
'tierarzt', # Tierarzt / Tierklinik
|
||||
'hundeschule', # Hundeschule / Trainer
|
||||
'kotbeutel', # Kotbeutelspender
|
||||
'bank', # Sitzbank
|
||||
'gefahr', # Allgemeine Gefahr / Hinweis
|
||||
'parkplatz', # Hundefreundlicher Parkplatz
|
||||
'treffpunkt', # Treffpunkt für Hundehalter
|
||||
'sonstiges',
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ async def weather_records(user=Depends(get_current_user)):
|
|||
rows = conn.execute("""
|
||||
SELECT d.datum, d.weather_json, d.titel
|
||||
FROM diary d
|
||||
WHERE d.user_id = ? AND d.weather_json IS NOT NULL
|
||||
JOIN dogs dog ON dog.id = d.dog_id
|
||||
WHERE dog.user_id = ? AND d.weather_json IS NOT NULL
|
||||
ORDER BY d.datum ASC
|
||||
""", (uid,)).fetchall()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""BAN YARO — Widget-Snapshot + Tagesspruch Endpoints"""
|
||||
|
||||
import json, random
|
||||
import json
|
||||
from datetime import date
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from typing import Optional
|
||||
|
|
@ -59,7 +59,8 @@ async def widget_snapshot(user=Depends(get_current_user)):
|
|||
(dog_id,)
|
||||
).fetchall()
|
||||
|
||||
random_photo = dict(random.choice(photos)) if photos else None
|
||||
day_num = (date.today() - date(2024, 1, 1)).days
|
||||
random_photo = dict(photos[day_num % len(photos)]) if photos else None
|
||||
|
||||
# Anzahl überfälliger Erinnerungen
|
||||
overdue = conn.execute(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue