/* Dev Lab Hub – lädt den Spiele-Katalog aus /api/devlab/catalog (DB-gestützt) und gruppiert die Spiele nach Trainingspfad. Spielkarten verlinken auf /devlab/games/{slug}. */ (function () { const PATHS = [ { id: 'syntax', marker: '{}', title: 'Syntax & Grundlagen', type: 'Grundlagenpfad', copy: 'Sprachsyntax, Strukturen und Muster verstehen, bevor der Code komplex wird.', mechanics: ['Lücken füllen', 'Zusammensetzen', 'Multiple Choice', 'Regex live'], badge: 'Syntax Core' }, { id: 'debugging', marker: '', title: 'Debugging & Problemlösung', type: 'Ermittlungspfad', copy: 'Fehler finden, Ursache erkennen und den passenden Reparaturschritt wählen.', mechanics: ['Bug finden', 'Lücken füllen', 'Befehlsfolgen', 'Storyfälle'], badge: 'Bug Hunter' }, { id: 'visual', marker: '[]', title: 'Praxis & Visualisierung', type: 'Werkstattpfad', copy: 'Direkt sehen, was Regeln, Layouts, APIs und Kaskaden im System bewirken.', mechanics: ['Zuordnen', 'Multiple Choice', 'Statuscodes', 'Kaskade'], badge: 'Visual Builder' } ]; const pathRoot = document.getElementById('dl-stations'); const gameRoot = document.getElementById('dl-games'); const mechanicsRoot = document.getElementById('dl-mechanics'); const ui = { title: document.getElementById('dl-world-title'), type: document.getElementById('dl-world-type'), icon: document.getElementById('dl-world-icon'), copy: document.getElementById('dl-world-copy'), levels: document.getElementById('dl-levels'), xp: document.getElementById('dl-xp'), badge: document.getElementById('dl-badge'), gameCount: document.getElementById('dl-game-count') }; const esc = (v) => String(v).replace(/&/g, '&').replace(//g, '>'); let byPath = {}; function renderStations(activeId) { pathRoot.innerHTML = PATHS.map((p) => { const games = byPath[p.id] || []; return ``; }).join(''); } function renderPath(p) { const games = byPath[p.id] || []; const totalTasks = games.reduce((sum, g) => sum + (g.taskCount || 0), 0); ui.title.textContent = p.title; ui.type.textContent = p.type; ui.icon.textContent = p.marker; ui.copy.textContent = p.copy; ui.levels.textContent = games.length; ui.xp.textContent = totalTasks; ui.badge.textContent = p.badge; ui.gameCount.textContent = `${games.length} Spiele`; mechanicsRoot.innerHTML = p.mechanics.map((m) => `${esc(m)}`).join(''); gameRoot.innerHTML = games.length ? games.map((g) => `

${esc(g.title)}

${esc(g.description || '')}

${g.taskCount} Aufgaben Starten
`).join('') : '

Für diesen Pfad sind noch keine Spiele freigeschaltet.

'; renderStations(p.id); } pathRoot.addEventListener('click', (e) => { const btn = e.target.closest('.dl-station'); if (!btn) return; const p = PATHS.find((x) => x.id === btn.dataset.path); if (p) renderPath(p); }); fetch('/api/devlab/catalog') .then((res) => res.json()) .then((data) => { byPath = {}; (data.games || []).forEach((g) => { const key = PATHS.some((p) => p.id === g.path) ? g.path : 'syntax'; (byPath[key] = byPath[key] || []).push(g); }); renderPath(PATHS[0]); }) .catch(() => renderPath(PATHS[0])); })();