Routen: Bestätigungs-Toast nach Ablaufen + "X× gelaufen · zuletzt" auf der Karte
- record_walk gibt total_km zurück → Toast "🐾 X km gezählt · Lebenswerk Y km". - Nachtrag-Toast (_flushPendingNavWalk) zeigt ebenfalls km + Lebenswerk. - list_routes liefert my_walk_count + my_last_walked → Routen-Karte zeigt "🐾 X× gelaufen · zuletzt heute/gestern/vor N Tagen". Macht für Angie sichtbar, dass das Ablaufen einer gespeicherten Route mitzählt.
This commit is contained in:
parent
91624dac25
commit
667ed91f33
7 changed files with 49 additions and 20 deletions
|
|
@ -95,6 +95,7 @@ async def list_routes(
|
|||
radius: int = 10000,
|
||||
user = Depends(get_current_user_optional),
|
||||
):
|
||||
_uid = user['id'] if user else -1
|
||||
with db() as conn:
|
||||
rows = conn.execute("""
|
||||
SELECT r.id, r.user_id, r.name, r.beschreibung,
|
||||
|
|
@ -106,11 +107,13 @@ async def list_routes(
|
|||
json_extract(r.gps_track, '$[0].lat') AS start_lat,
|
||||
json_extract(r.gps_track, '$[0].lon') AS start_lon,
|
||||
json_extract(r.gps_track, '$[#-1].lat') AS end_lat,
|
||||
json_extract(r.gps_track, '$[#-1].lon') AS end_lon
|
||||
json_extract(r.gps_track, '$[#-1].lon') AS end_lon,
|
||||
(SELECT COUNT(*) FROM route_walks w WHERE w.route_id=r.id AND w.user_id=?) AS my_walk_count,
|
||||
(SELECT MAX(walked_at) FROM route_walks w WHERE w.route_id=r.id AND w.user_id=?) AS my_last_walked
|
||||
FROM routes r
|
||||
LEFT JOIN users u ON u.id = r.user_id
|
||||
ORDER BY r.created_at DESC
|
||||
""").fetchall()
|
||||
""", (_uid, _uid)).fetchall()
|
||||
|
||||
result = []
|
||||
for row in rows:
|
||||
|
|
@ -492,7 +495,13 @@ async def record_walk(route_id: int, body: WalkRecord, user=Depends(get_current_
|
|||
)
|
||||
update_streak(uid, conn)
|
||||
new_badges = check_and_award(uid, conn)
|
||||
return {"ok": True, "new_badges": new_badges}
|
||||
total_km = conn.execute(
|
||||
"SELECT ROUND("
|
||||
" COALESCE((SELECT SUM(distanz_km) FROM routes WHERE user_id=? AND is_valid=1),0) +"
|
||||
" COALESCE((SELECT SUM(walked_km) FROM route_walks WHERE user_id=?),0), 1)",
|
||||
(uid, uid)
|
||||
).fetchone()[0]
|
||||
return {"ok": True, "new_badges": new_badges, "total_km": total_km}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -86,14 +86,14 @@
|
|||
<title>Ban Yaro</title>
|
||||
|
||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||
<script src="/js/boot-early.js?v=1160"></script>
|
||||
<script src="/js/boot-early.js?v=1161"></script>
|
||||
|
||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1160">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1160">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1160">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1160">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1160">
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1161">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1161">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1161">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1161">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1161">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -617,11 +617,11 @@
|
|||
<div id="modal-container"></div>
|
||||
|
||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||
<script src="/js/api.js?v=1160"></script>
|
||||
<script src="/js/ui.js?v=1160"></script>
|
||||
<script src="/js/app.js?v=1160"></script>
|
||||
<script src="/js/worlds.js?v=1160"></script>
|
||||
<script src="/js/offline-indicator.js?v=1160"></script>
|
||||
<script src="/js/api.js?v=1161"></script>
|
||||
<script src="/js/ui.js?v=1161"></script>
|
||||
<script src="/js/app.js?v=1161"></script>
|
||||
<script src="/js/worlds.js?v=1161"></script>
|
||||
<script src="/js/offline-indicator.js?v=1161"></script>
|
||||
|
||||
<!-- Feature-Seiten werden lazy geladen -->
|
||||
|
||||
|
|
@ -631,7 +631,7 @@
|
|||
|
||||
|
||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||
<script src="/js/boot.js?v=1160"></script>
|
||||
<script src="/js/boot.js?v=1161"></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1160'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1161'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||
window.APP_VERSION = APP_VERSION;
|
||||
|
|
|
|||
|
|
@ -1442,6 +1442,20 @@ window.Page_routes = (() => {
|
|||
? `<div class="rk-card-creator">${UI.icon('user')} ${UI.escape(r.user_name||'Anonym')}</div>`
|
||||
: '';
|
||||
|
||||
// „X× gelaufen · zuletzt …" — macht sichtbar, dass das Ablaufen mitzählt
|
||||
const _wc = r.my_walk_count || 0;
|
||||
let walkedLine = '';
|
||||
if (_wc > 0) {
|
||||
let last = '';
|
||||
if (r.my_last_walked) {
|
||||
const d = new Date(String(r.my_last_walked).replace(' ', 'T') + 'Z'); // walked_at ist UTC
|
||||
const days = Math.floor((Date.now() - d.getTime()) / 86400000);
|
||||
last = days <= 0 ? 'heute' : days === 1 ? 'gestern' : `vor ${days} Tagen`;
|
||||
}
|
||||
walkedLine = `<div style="font-size:11px;color:var(--c-text-secondary);margin:2px 0 0">
|
||||
🐾 ${_wc}× gelaufen${last ? ' · zuletzt ' + last : ''}</div>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="rk-card" data-id="${r.id}" ${r._isPending ? 'data-pending="1"' : ''}>
|
||||
<div class="rk-card-preview">${previewContent}</div>
|
||||
|
|
@ -1461,6 +1475,7 @@ window.Page_routes = (() => {
|
|||
${r.hunde_tauglichkeit ? _pill(HUNDE_TEXT[r.hunde_tauglichkeit]||'', 'rgba(234,179,8,0.10)','#facc15','rgba(234,179,8,0.30)') : ''}
|
||||
${!isDiscover && !r.is_public ? _pill('Privat','rgba(59,130,246,0.10)','#60a5fa','rgba(59,130,246,0.30)') : ''}
|
||||
</div>
|
||||
${walkedLine}
|
||||
<div class="rk-card-footer">
|
||||
<div class="rk-stars">${_starsHTML(r.id, r.bewertung||0, r.anz_bewertungen||0)}</div>
|
||||
<div class="rk-card-actions">
|
||||
|
|
@ -2014,6 +2029,9 @@ window.Page_routes = (() => {
|
|||
API.routes.walked(_navWalkMeta.routeId, walkedKm, pct)
|
||||
.then(res => {
|
||||
try { localStorage.removeItem(_PENDING_WALK_KEY); } catch {}
|
||||
const km = walkedKm.toFixed(1).replace('.', ',');
|
||||
const tot = res?.total_km != null ? ` · Lebenswerk ${String(res.total_km).replace('.', ',')} km` : '';
|
||||
UI.toast.success(`🐾 ${km} km gezählt${tot}`);
|
||||
if (res?.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`);
|
||||
})
|
||||
.catch(() => {}); // bleibt in localStorage → Nachtrag beim nächsten Start
|
||||
|
|
@ -2033,7 +2051,9 @@ window.Page_routes = (() => {
|
|||
API.routes.walked(p.routeId, p.walkedKm, p.pct)
|
||||
.then(res => {
|
||||
try { localStorage.removeItem(_PENDING_WALK_KEY); } catch {}
|
||||
UI.toast.success('Gelaufene Tour nachgetragen 🐾');
|
||||
const km = Number(p.walkedKm || 0).toFixed(1).replace('.', ',');
|
||||
const tot = res?.total_km != null ? ` · Lebenswerk ${String(res.total_km).replace('.', ',')} km` : '';
|
||||
UI.toast.success(`🐾 ${km} km nachgetragen${tot}`);
|
||||
if (res?.new_badges?.length) UI.toast.success(`🏅 Neues Badge: ${res.new_badges[0].name}!`);
|
||||
})
|
||||
.catch(() => {}); // bleibt für den nächsten Versuch
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<script src="/js/landing-init.js?v=1160"></script>
|
||||
<script src="/js/landing-init.js?v=1161"></script>
|
||||
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
||||
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
============================================================ */
|
||||
|
||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
||||
const VER = '1160';
|
||||
const VER = '1161';
|
||||
const CACHE_VERSION = `by-v${VER}`;
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue