diff --git a/backend/routes/dogs.py b/backend/routes/dogs.py index a4ff043..db0da2e 100644 --- a/backend/routes/dogs.py +++ b/backend/routes/dogs.py @@ -131,10 +131,12 @@ async def get_welcome_dashboard(dog_id: int, user=Depends(get_current_user)): ).fetchone() last_diary = dict(last_diary_row) if last_diary_row else None - # Nächster Termin (kein Gewicht) + # Nächster Termin (kein Gewicht, nur innerhalb 60 Tage) next_appt_row = conn.execute( """SELECT bezeichnung, naechstes, typ FROM health - WHERE dog_id=? AND naechstes IS NOT NULL AND naechstes >= date('now') + WHERE dog_id=? AND naechstes IS NOT NULL + AND naechstes >= date('now') + AND naechstes <= date('now', '+60 days') AND typ != 'gewicht' ORDER BY naechstes ASC LIMIT 1""", (dog_id,) @@ -155,12 +157,28 @@ async def get_welcome_dashboard(dog_id: int, user=Depends(get_current_user)): "SELECT COUNT(*) AS n FROM diary WHERE dog_id=?", (dog_id,) ).fetchone()["n"] + # Tagesübung — stabil pro Tag, rotiert täglich + exercise_count = conn.execute( + "SELECT COUNT(*) AS n FROM training_exercises" + ).fetchone()["n"] + daily_exercise = None + if exercise_count: + import datetime as _dt + day_num = (_dt.date.today() - _dt.date(2024, 1, 1)).days + offset = day_num % exercise_count + ex_row = conn.execute( + "SELECT exercise_id, name, kategorie, schwierigkeit FROM training_exercises ORDER BY id LIMIT 1 OFFSET ?", + (offset,) + ).fetchone() + daily_exercise = dict(ex_row) if ex_row else None + return { "random_photo": random_photo, "last_diary": last_diary, "next_appointment": next_appointment, "last_weight": last_weight, "diary_count": diary_count, + "daily_exercise": daily_exercise, } diff --git a/backend/static/js/api.js b/backend/static/js/api.js index 96a3f46..ea6ad6d 100644 --- a/backend/static/js/api.js +++ b/backend/static/js/api.js @@ -694,6 +694,11 @@ const API = (() => { jahresbericht() { return post('/zucht-ki/jahresbericht', {}); }, }; + const osm = { + pois: (type, south, west, north, east) => + get(`/osm/pois?type=${type}&south=${south}&west=${west}&north=${north}&east=${east}&fast=true`), + }; + // Öffentliche API return { get, post, put, patch, del, upload, @@ -701,6 +706,7 @@ const API = (() => { places, routes, walks, events, sitting, forum, lost, knigge, weather, push, friends, chat, webcal, importData, sharing, widget, notifications, services, ratings, sittingAccess, training, notes, breeder, litters, breederPhotos, zuchthunde, zuchtKi, + osm, subscribeToPush, getLocation, clientNow, APIError, }; diff --git a/backend/static/js/app.js b/backend/static/js/app.js index bc72295..a7591e3 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 = '452'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '453'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const App = (() => { diff --git a/backend/static/js/pages/welcome.js b/backend/static/js/pages/welcome.js index 76ab1cb..92c4399 100644 --- a/backend/static/js/pages/welcome.js +++ b/backend/static/js/pages/welcome.js @@ -327,15 +327,39 @@ window.Page_welcome = (() => { `; } + function _chip2HTML(dashData) { + const appt = dashData?.next_appointment; + if (appt) { + const apptLabel = UI.escape(appt.bezeichnung); + const apptDate = _relDate(appt.naechstes) || appt.naechstes || '—'; + const apptIcon = _appointmentIcon(appt.typ); + return ` +