Session 2026-04-22: Training, Fixes, KI-Cloud, Dark-Mode

Training-System:
- Einheit-Dialog Bugs behoben (UI.toast callable, _dogId via _appState, activeDog.id)
- Virtueller Trainer (rein statistisch): üben/festigen/entdecken/levelup
  Empfehlungen auf Basis exercise_progress + sessions, Prognose bis 80%
- Stand erfassen Modal: alle Übungen auf einmal setzen (onboarding)
- Erfolgsindikatoren auf Karten: Ø-Quote + Trend-Pfeil + Anzahl Sessions
- exercise_progress → synthetische Stats im Trainer (ohne Sessions nutzbar)
- Levelup: Tricks empfehlen wenn ≥4 Grundkommandos sitzen
- Kommandos & Fähigkeiten im Hundeprofil + öffentlichem Profil
- 2 neue Problemverhalten-Übungen: Bellen/Kläffen, Enttriggern

Mobile/UI-Fixes:
- Übungskarten: Name + Difficulty oben, Buttons eigene Zeile (kein Umbruch)
- Trainingsgrundlagen: Padding in allen Karten, Hinweis-Boxen Dark-Mode-sicher
- Tab-Sichtbarkeit: Trainer/Suggestions nur auf Übungs-Tabs
- Tagebuch FAB (Neu-Eintrag Button) + Quick-Add Eintrag
- FAB Abstand fix (nav-bottom-height + safe-bottom)
- Suggestion-Karten rgba (Dark-Mode)
- routes.js + uebungen.js: alle Hellfarben → rgba (Dark-Mode-sicher)
- ui.js: UI.toast als callable Function-Object (war nur plain Object)

KI & Backend:
- KI_MODE=cloud + ANTHROPIC_API_KEY gesetzt
- ki.py: Cloud-Fallback wenn local nicht erreichbar + KI_MODE=cloud
- KI-Trainer Tageslimit 10 Anfragen/User + ki_daily_calls Tabelle
- Admin-Panel: KI-Nutzung (heute/Monat/User)
- Status-Report Fix (lost-Tabelle) → 06:00 + 18:00 täglich
- Wiki-Anreicherung läuft jetzt (50 Rassen Startup, 20/Nacht)
- landing.html: Trainings-Features in JSON-LD + Feature-Karten
This commit is contained in:
rene 2026-04-22 19:41:22 +02:00
parent 2b442ebd98
commit 44081a6b9d
16 changed files with 938 additions and 117 deletions

View file

@ -55,9 +55,9 @@ window.Page_routes = (() => {
const HUNDE_LABEL = { eingeschränkt: '🐾', gut: '🐾🐾', sehr_gut: '🐾🐾🐾', premium: '🐾🐾🐾🐾' };
const HUNDE_TEXT = { eingeschränkt: 'Leine', gut: 'gut',
sehr_gut: 'sehr gut', premium: 'premium' };
const DIFF_COLOR = { leicht: 'background:#dcfce7;color:#15803d;border-color:#bbf7d0',
mittel: 'background:#fef9c3;color:#92400e;border-color:#fde68a',
anspruchsvoll: 'background:#fee2e2;color:#991b1b;border-color:#fecaca' };
const DIFF_COLOR = { leicht: 'background:rgba(22,163,74,0.10);color:#4ade80;border-color:rgba(22,163,74,0.30)',
mittel: 'background:rgba(234,179,8,0.10);color:#facc15;border-color:rgba(234,179,8,0.30)',
anspruchsvoll: 'background:rgba(220,38,38,0.10);color:#f87171;border-color:rgba(220,38,38,0.30)' };
// POI-Typen entlang der Route — nur relevante/interessante Orte
const NEARBY_TYPES = [
@ -981,14 +981,14 @@ window.Page_routes = (() => {
${authorLine}
<div class="rk-card-name">${UI.escape(r.name)}</div>
<div style="display:flex;flex-wrap:wrap;gap:5px;margin:var(--space-2) 0">
${dist ? _pill(dist, '#f1f5f9','#475569','#e2e8f0') : ''}
${dur ? _pill(dur, '#f1f5f9','#475569','#e2e8f0') : ''}
${dist ? _pill(dist, 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
${dur ? _pill(dur, 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
${diffLabel ? _pill(diffLabel,
({leicht:'#dcfce7',mittel:'#fef9c3',anspruchsvoll:'#fee2e2'})[r.schwierigkeit]||'#f1f5f9',
({leicht:'#15803d',mittel:'#92400e',anspruchsvoll:'#991b1b'})[r.schwierigkeit]||'#475569',
({leicht:'#bbf7d0',mittel:'#fde68a',anspruchsvoll:'#fecaca'})[r.schwierigkeit]||'#e2e8f0') : ''}
${r.hunde_tauglichkeit ? _pill(HUNDE_TEXT[r.hunde_tauglichkeit]||'', '#fef3c7','#92400e','#fde68a') : ''}
${!isDiscover && !r.is_public ? _pill('Privat','#dbeafe','#1d4ed8','#bfdbfe') : ''}
({leicht:'rgba(22,163,74,0.10)',mittel:'rgba(234,179,8,0.10)',anspruchsvoll:'rgba(220,38,38,0.10)'})[r.schwierigkeit]||'rgba(107,114,128,0.10)',
({leicht:'#4ade80',mittel:'#facc15',anspruchsvoll:'#f87171'})[r.schwierigkeit]||'#9ca3af',
({leicht:'rgba(22,163,74,0.30)',mittel:'rgba(234,179,8,0.30)',anspruchsvoll:'rgba(220,38,38,0.30)'})[r.schwierigkeit]||'rgba(107,114,128,0.30)') : ''}
${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>
<div class="rk-card-footer">
<div class="rk-stars">${_starsHTML(r.id, r.bewertung||0, r.anz_bewertungen||0)}</div>
@ -1148,7 +1148,7 @@ window.Page_routes = (() => {
</div>
</div>
<div id="rk-nav-offwarn" style="display:none;text-align:center;padding:4px 8px;border-radius:6px;
background:#fef2f2;color:#dc2626;font-size:12px;font-weight:600;margin-bottom:6px">
background:rgba(220,38,38,0.10);color:#f87171;font-size:12px;font-weight:600;margin-bottom:6px">
Du hast die Route verlassen
</div>
<div style="height:4px;background:var(--c-border);border-radius:2px;overflow:hidden">
@ -1753,19 +1753,19 @@ window.Page_routes = (() => {
margin-bottom:var(--space-3);background:var(--c-surface-2)"></div>
${photoGallery}
<div style="display:flex;flex-wrap:wrap;gap:6px;margin:var(--space-3) 0">
${route.distanz_km ? _pill(route.distanz_km.toFixed(1)+' km', '#f1f5f9','#475569','#e2e8f0') : ''}
${route.dauer_min ? _pill(_fmtDur(route.dauer_min), '#f1f5f9','#475569','#e2e8f0') : ''}
${route.distanz_km ? _pill(route.distanz_km.toFixed(1)+' km', 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
${route.dauer_min ? _pill(_fmtDur(route.dauer_min), 'rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)') : ''}
${route.schwierigkeit ? _pill(DIFFICULTY_LABEL[route.schwierigkeit]||route.schwierigkeit,
({leicht:'#dcfce7',mittel:'#fef9c3',anspruchsvoll:'#fee2e2'})[route.schwierigkeit]||'#f1f5f9',
({leicht:'#15803d',mittel:'#92400e',anspruchsvoll:'#991b1b'})[route.schwierigkeit]||'#475569',
({leicht:'#bbf7d0',mittel:'#fde68a',anspruchsvoll:'#fecaca'})[route.schwierigkeit]||'#e2e8f0') : ''}
${route.hunde_tauglichkeit ? _pill(HUNDE_TEXT[route.hunde_tauglichkeit]||route.hunde_tauglichkeit,'#fef3c7','#92400e','#fde68a') : ''}
({leicht:'rgba(22,163,74,0.10)',mittel:'rgba(234,179,8,0.10)',anspruchsvoll:'rgba(220,38,38,0.10)'})[route.schwierigkeit]||'rgba(107,114,128,0.10)',
({leicht:'#4ade80',mittel:'#facc15',anspruchsvoll:'#f87171'})[route.schwierigkeit]||'#9ca3af',
({leicht:'rgba(22,163,74,0.30)',mittel:'rgba(234,179,8,0.30)',anspruchsvoll:'rgba(220,38,38,0.30)'})[route.schwierigkeit]||'rgba(107,114,128,0.30)') : ''}
${route.hunde_tauglichkeit ? _pill(HUNDE_TEXT[route.hunde_tauglichkeit]||route.hunde_tauglichkeit,'rgba(234,179,8,0.10)','#facc15','rgba(234,179,8,0.30)') : ''}
${isOwn
? `<button type="button" id="rd-vis-pill" title="${route.is_public ? 'Auf Privat setzen' : 'Auf Öffentlich setzen'}"
style="${_pillStyle(route.is_public?'#dcfce7':'#dbeafe', route.is_public?'#15803d':'#1d4ed8', route.is_public?'#bbf7d0':'#bfdbfe')}cursor:pointer;">
style="${_pillStyle(route.is_public?'rgba(22,163,74,0.10)':'rgba(59,130,246,0.10)', route.is_public?'#4ade80':'#60a5fa', route.is_public?'rgba(22,163,74,0.30)':'rgba(59,130,246,0.30)')}cursor:pointer;">
${route.is_public ? 'Öffentlich' : 'Privat'}
</button>`
: _pill(route.is_public?'Öffentlich':'Privat', route.is_public?'#dcfce7':'#dbeafe', route.is_public?'#15803d':'#1d4ed8', route.is_public?'#bbf7d0':'#bfdbfe')
: _pill(route.is_public?'Öffentlich':'Privat', route.is_public?'rgba(22,163,74,0.10)':'rgba(59,130,246,0.10)', route.is_public?'#4ade80':'#60a5fa', route.is_public?'rgba(22,163,74,0.30)':'rgba(59,130,246,0.30)')
}
</div>
${route.beschreibung ? `<p style="color:var(--c-text-secondary);margin-bottom:var(--space-3)">${UI.escape(route.beschreibung)}</p>` : ''}
@ -1855,9 +1855,9 @@ window.Page_routes = (() => {
route.is_public = !route.is_public;
if (pill) {
pill.style.cssText = _pillStyle(
route.is_public ? '#dcfce7' : '#dbeafe',
route.is_public ? '#15803d' : '#1d4ed8',
route.is_public ? '#bbf7d0' : '#bfdbfe') + 'cursor:pointer;';
route.is_public ? 'rgba(22,163,74,0.10)' : 'rgba(59,130,246,0.10)',
route.is_public ? '#4ade80' : '#60a5fa',
route.is_public ? 'rgba(22,163,74,0.30)' : 'rgba(59,130,246,0.30)') + 'cursor:pointer;';
pill.innerHTML = route.is_public ? 'Öffentlich' : 'Privat';
pill.title = route.is_public ? 'Auf Privat setzen' : 'Auf Öffentlich setzen';
}