Feature: Alle 104 Übungen aus DB in Übungsseite — 9 Tabs, DB-basiert, abwärtskompatibel — SW by-v493, APP_VER 470

This commit is contained in:
rene 2026-04-29 11:44:47 +02:00
parent 175984e80f
commit 6d9f4a097e
5 changed files with 109 additions and 24 deletions

View file

@ -36,17 +36,23 @@ window.Page_uebungen = (() => {
// ----------------------------------------------------------
let _statsData = null; // cached stats from /api/training/stats
let _badgesData = null; // cached badges from /api/achievements
let _exercisesByTab = {}; // aus API geladen
let _exercisesLoaded = false;
// ----------------------------------------------------------
// DATEN
// ----------------------------------------------------------
const TABS = [
{ id: 'grundkommandos', label: 'Grundkommandos' },
{ id: 'tricks', label: 'Tricks & Beschäftigung' },
{ id: 'problemverhalten', label: 'Problemverhalten' },
{ id: 'grundlagen', label: 'Trainingsgrundlagen' },
{ id: 'ki-trainer', label: 'KI-Trainer' },
{ id: 'grundkommandos', label: 'Grundkommandos' },
{ id: 'tricks', label: 'Tricks' },
{ id: 'problemverhalten', label: 'Problemverhalten' },
{ id: 'mentale-auslastung', label: 'Mentale Auslastung' },
{ id: 'hundesport', label: 'Hundesport' },
{ id: 'koerperpflege', label: 'Körperpflege' },
{ id: 'welpe-basics', label: 'Welpe Basics' },
{ id: 'grundlagen', label: 'Trainingsgrundlagen' },
{ id: 'ki-trainer', label: 'KI-Trainer' },
];
// ----------------------------------------------------------
@ -435,13 +441,20 @@ window.Page_uebungen = (() => {
// ----------------------------------------------------------
// DB-Kategorien → Tab-IDs
const _KAT_TO_TAB = {
'grundkommando': 'grundkommandos',
'grundkommandos': 'grundkommandos',
'trick': 'tricks',
'tricks': 'tricks',
'problemverhalten':'problemverhalten',
'grundlagen': 'grundlagen',
'ki-trainer': 'ki-trainer',
'grundkommando': 'grundkommandos',
'grundkommandos': 'grundkommandos',
'trick': 'tricks',
'tricks': 'tricks',
'problemverhalten': 'problemverhalten',
'mentale auslastung': 'mentale-auslastung',
'mentale-auslastung': 'mentale-auslastung',
'hundesport': 'hundesport',
'körperpflege': 'koerperpflege',
'koerperpflege': 'koerperpflege',
'welpe basics': 'welpe-basics',
'welpe-basics': 'welpe-basics',
'grundlagen': 'grundlagen',
'ki-trainer': 'ki-trainer',
};
const _VALID_TABS = new Set(TABS.map(t => t.id));
@ -461,6 +474,16 @@ window.Page_uebungen = (() => {
if (_VALID_TABS.has(mapped)) _activeTab = mapped;
}
_render();
// Übungen aus DB laden (parallel mit Progress)
if (!_exercisesLoaded) {
API.get('/training/exercises').then(data => {
_exercisesByTab = data || {};
_exercisesLoaded = true;
_renderContent(); // neu rendern sobald Daten da
}).catch(() => {});
}
if (params.exercise_id || params.name) {
setTimeout(() => {
// Erst per exercise_id suchen (zuverlässig), dann per Name als Fallback
@ -911,7 +934,8 @@ window.Page_uebungen = (() => {
const el = _container.querySelector('#ueb-content');
if (!el) return;
const isExerciseTab = ['grundkommandos', 'tricks', 'problemverhalten'].includes(_activeTab);
const isExerciseTab = ['grundkommandos','tricks','problemverhalten',
'mentale-auslastung','hundesport','koerperpflege','welpe-basics'].includes(_activeTab);
const showIf = v => v ? '' : 'none';
const quickWrap = _container.querySelector('#ueb-quicksetup-btn')?.parentElement;
@ -924,11 +948,23 @@ window.Page_uebungen = (() => {
if (bannerEl) bannerEl.style.display = showIf(isExerciseTab);
switch (_activeTab) {
case 'grundkommandos': el.innerHTML = _renderUebungsList(GRUNDKOMMANDOS); break;
case 'tricks': el.innerHTML = _renderUebungsList(TRICKS); break;
case 'problemverhalten': el.innerHTML = _renderUebungsList(PROBLEMVERHALTEN); break;
case 'grundlagen': el.innerHTML = _renderGrundlagen(); break;
case 'ki-trainer': el.innerHTML = _renderKiTrainer(); break;
case 'grundkommandos':
case 'tricks':
case 'problemverhalten':
case 'mentale-auslastung':
case 'hundesport':
case 'koerperpflege':
case 'welpe-basics': {
const list = _exercisesByTab[_activeTab] || [];
el.innerHTML = list.length
? _renderUebungsList(list)
: `<div style="padding:var(--space-6);text-align:center;color:var(--c-text-muted)">
${UI.icon('spinner')} Übungen werden geladen
</div>`;
break;
}
case 'grundlagen': el.innerHTML = _renderGrundlagen(); break;
case 'ki-trainer': el.innerHTML = _renderKiTrainer(); break;
}
_bindAccordions();
_bindStatusButtons();
@ -971,7 +1007,7 @@ window.Page_uebungen = (() => {
const currentId = _getStatus(_activeTab, u.name);
const sm = _statusMeta(currentId);
const hasBody = u.schritte.length > 0 || u.fehler.length > 0 || u.steigerung;
const hasBody = (u.schritte?.length > 0) || (u.fehler?.length > 0) || !!u.steigerung || !!u.tipp;
return `
<div class="card" style="padding:0;overflow:hidden" data-exercise-name="${_esc(u.name)}" data-exercise-id="${_esc(_progressKey(_activeTab, u.name))}">
@ -1078,7 +1114,7 @@ window.Page_uebungen = (() => {
</svg>
</button>
<div id="${uid}" hidden style="padding:var(--space-4);border-top:1px solid var(--c-border)">
${u.schritte.length ? `
${u.schritte?.length ? `
<p style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
color:var(--c-text-secondary);margin-bottom:var(--space-2);text-transform:uppercase;letter-spacing:0.05em">
Schritt für Schritt
@ -1089,7 +1125,7 @@ window.Page_uebungen = (() => {
`).join('')}
</ol>
` : ''}
${u.fehler.length ? `
${u.fehler?.length ? `
<p style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
color:var(--c-text-secondary);margin-bottom:var(--space-2);text-transform:uppercase;letter-spacing:0.05em">
<svg class="ph-icon" style="width:12px;height:12px;color:var(--c-warning)" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
@ -1110,6 +1146,12 @@ window.Page_uebungen = (() => {
<strong>Steigerung:</strong> ${_esc(u.steigerung)}
</div>
` : ''}
${u.tipp ? `
<div style="margin-top:var(--space-3);padding:var(--space-2) var(--space-3);
background:var(--c-primary-subtle);border-radius:var(--radius-sm);
font-size:var(--text-xs);color:var(--c-text-secondary)">
💡 ${_esc(u.tipp)}
</div>` : ''}
</div>
` : ''}
</div>