// API Base URL const API_BASE = ''; // Globale Variablen let totalPoints = 0; let tasks = []; let chapters = []; let activeChapter = null; // Initialisiere die App async function init() { await loadTasks(); extractChapters(); calculateTotalPoints(); renderTabs(); renderTasks(); updateProgress(); } // Berechne Gesamtpunktzahl aus abgeschlossenen Aufgaben function calculateTotalPoints() { totalPoints = tasks .filter(task => task.isCorrect === true) .reduce((sum, task) => sum + (task.points || 0), 0); updatePointsDisplay(); } // Lade Aufgaben vom Server async function loadTasks() { try { const response = await fetch(`${API_BASE}/api/tasks`); const data = await response.json(); tasks = data.tasks || []; } catch (error) { console.error('Fehler beim Laden der Aufgaben:', error); tasks = []; } } // Extrahiere alle Chapters aus den Tasks function extractChapters() { const chapterSet = new Set(); tasks.forEach(task => { if (task.chapter) { chapterSet.add(task.chapter); } }); chapters = Array.from(chapterSet).sort(); // Setze ersten Chapter als aktiv, falls vorhanden if (chapters.length > 0 && !activeChapter) { activeChapter = chapters[0]; } else if (chapters.length === 0) { // Wenn keine Chapters vorhanden, zeige alle Tasks activeChapter = null; } } // Rendere Tabs für alle Chapters function renderTabs() { const tabsContainer = document.getElementById('tabsContainer'); tabsContainer.innerHTML = ''; // Zeige Tabs nur an, wenn es Chapters gibt if (chapters.length === 0) { tabsContainer.style.display = 'none'; return; } tabsContainer.style.display = 'flex'; chapters.forEach(chapter => { const tabButton = document.createElement('button'); tabButton.className = `tab-button ${activeChapter === chapter ? 'active' : ''}`; tabButton.textContent = chapter; tabButton.onclick = () => switchChapter(chapter); tabsContainer.appendChild(tabButton); }); } // Wechsle zu einem anderen Chapter function switchChapter(chapter) { activeChapter = chapter; renderTabs(); renderTasks(); } // Hole Tasks für das aktive Chapter function getTasksForActiveChapter() { if (!activeChapter) { return tasks; } return tasks.filter(task => task.chapter === activeChapter); } // Rendere Aufgaben function renderTasks() { const container = document.getElementById('taskContainer'); container.innerHTML = ''; const tasksToShow = getTasksForActiveChapter(); tasksToShow.forEach(task => { // Überspringe bereits richtig beantwortete Aufgaben (werden nicht mehr angezeigt) if (task.isCorrect === true) { return; } const taskCard = document.createElement('div'); taskCard.className = 'task-card'; taskCard.id = `task-${task.id}`; const hasAnswer = task.userAnswer !== undefined; taskCard.innerHTML = `
Aufgabe
⭐ ${task.points} Punkte
${task.question}
${hasAnswer && !task.isCorrect ? '❌ Falsch - versuche es nochmal!' : ''}
`; if (hasAnswer && !task.isCorrect) { const statusEl = taskCard.querySelector('.task-status'); statusEl.className = 'task-status error'; } container.appendChild(taskCard); }); } // Prüfe Antwort async function checkAnswer(taskId) { const task = tasks.find(t => t.id === taskId); if (!task) return; const input = document.getElementById(`input-${taskId}`); const status = document.getElementById(`status-${taskId}`); const button = input.nextElementSibling; const userAnswer = parseInt(input.value); if (isNaN(userAnswer)) { status.textContent = 'Bitte gib eine Zahl ein!'; status.className = 'task-status error'; return; } // Prüfe ob Aufgabe bereits korrekt beantwortet wurde if (task.isCorrect === true) { status.textContent = '✅ Diese Aufgabe wurde bereits richtig beantwortet!'; status.className = 'task-status completed'; return; } // Sende Antwort an Backend zur Prüfung try { const response = await fetch(`${API_BASE}/api/check-answer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ taskId, answer: userAnswer }) }); const data = await response.json(); // Lade Tasks neu, um die gespeicherte Antwort zu erhalten await loadTasks(); extractChapters(); calculateTotalPoints(); if (data.correct) { status.textContent = '🎉 Richtig! Super gemacht!'; status.className = 'task-status success'; // Zeige Erfolgsnachricht showMessage(`🎉 ${data.points} Quest-Punkte verdient!`, 'success'); // Konfetti-Animation und Aufgabe verschwinden lassen const taskCard = document.getElementById(`task-${taskId}`); if (taskCard) { createConfetti(taskCard); taskCard.classList.add('confetti-animation'); // Nach Animation die Aufgabe entfernen setTimeout(() => { renderTabs(); renderTasks(); updateProgress(); }, 1500); } } else { status.textContent = '❌ Nicht ganz richtig. Versuch es nochmal!'; status.className = 'task-status error'; input.focus(); } // Rendere Tabs und Tasks neu, um den aktuellen Status anzuzeigen (nur wenn nicht korrekt) if (!data.correct) { renderTabs(); renderTasks(); updateProgress(); } } catch (error) { console.error('Fehler beim Prüfen der Antwort:', error); status.textContent = 'Fehler beim Prüfen. Bitte versuche es erneut.'; status.className = 'task-status error'; } } // Aktualisiere Punkte-Anzeige function updatePointsDisplay() { document.getElementById('totalPoints').textContent = totalPoints; } // Berechne maximale mögliche Punkte (Summe aller Tasks) function calculateMaxPoints() { return tasks.reduce((sum, task) => sum + (task.points || 0), 0); } // Aktualisiere Progress-Balken function updateProgress() { const maxPoints = calculateMaxPoints(); const percentage = maxPoints > 0 ? Math.min((totalPoints / maxPoints) * 100, 100) : 0; const progressBar = document.getElementById('progressBar'); const progressText = document.getElementById('progressText'); progressBar.style.width = `${percentage}%`; progressText.textContent = `${totalPoints} / ${maxPoints}`; } // Erstelle Konfetti-Animation function createConfetti(element) { const colors = ['#ffd700', '#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#f0932b', '#eb4d4b', '#6c5ce7']; const confettiCount = 50; // Hole Position des Elements const rect = element.getBoundingClientRect(); const container = element.closest('.container') || document.body; for (let i = 0; i < confettiCount; i++) { const confetti = document.createElement('div'); confetti.className = 'confetti'; // Zufällige Position relativ zum Element const randomX = (Math.random() - 0.5) * 2; // -1 bis 1 confetti.style.left = (rect.left + rect.width / 2 + (Math.random() - 0.5) * rect.width) + 'px'; confetti.style.top = (rect.top - 10) + 'px'; confetti.style.background = colors[Math.floor(Math.random() * colors.length)]; confetti.style.width = (Math.random() * 8 + 6) + 'px'; confetti.style.height = (Math.random() * 8 + 6) + 'px'; confetti.style.animationDuration = (Math.random() * 0.8 + 0.7) + 's'; confetti.style.animationDelay = Math.random() * 0.3 + 's'; confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '0%'; confetti.style.setProperty('--random-x', randomX); confetti.style.position = 'fixed'; document.body.appendChild(confetti); // Entferne Konfetti nach Animation setTimeout(() => { if (confetti.parentNode) { confetti.remove(); } }, 2000); } } // Zeige Nachricht function showMessage(text, type) { const message = document.getElementById('message'); message.textContent = text; message.className = `message ${type} show`; setTimeout(() => { message.classList.remove('show'); }, 3000); } // Enter-Taste für Input-Felder document.addEventListener('DOMContentLoaded', () => { init(); // Event Listener für Enter-Taste document.addEventListener('keypress', (e) => { if (e.key === 'Enter') { const input = document.activeElement; if (input && input.classList.contains('task-input')) { const taskId = input.id.replace('input-', ''); checkAnswer(taskId); } } }); });