This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/h5p-service/players/quiz-player.html
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

381 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quiz Player - BreakPilot H5P</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f5f5;
padding: 20px;
min-height: 100vh;
}
.container {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 30px;
max-width: 800px;
margin: 0 auto;
}
h1 {
color: #333;
margin-bottom: 12px;
font-size: 28px;
}
.description {
color: #6b7280;
margin-bottom: 32px;
line-height: 1.6;
}
.question-card {
background: #f9fafb;
border-radius: 8px;
padding: 24px;
margin-bottom: 24px;
}
.question-number {
color: #667eea;
font-weight: 600;
margin-bottom: 12px;
font-size: 14px;
}
.question-text {
font-size: 18px;
font-weight: 600;
color: #1f2937;
margin-bottom: 20px;
}
.answer {
background: white;
border: 2px solid #e5e7eb;
border-radius: 6px;
padding: 14px 16px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 12px;
}
.answer:hover {
border-color: #9ca3af;
}
.answer.selected {
border-color: #667eea;
background: #f0f4ff;
}
.answer.correct {
border-color: #10b981;
background: #d1fae5;
}
.answer.incorrect {
border-color: #ef4444;
background: #fee2e2;
}
.answer .checkbox {
width: 20px;
height: 20px;
border: 2px solid #9ca3af;
border-radius: 4px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.answer.selected .checkbox {
background: #667eea;
border-color: #667eea;
color: white;
}
.answer.correct .checkbox {
background: #10b981;
border-color: #10b981;
color: white;
}
.answer.incorrect .checkbox {
background: #ef4444;
border-color: #ef4444;
color: white;
}
.answer-text {
flex: 1;
}
.feedback {
margin-top: 12px;
padding: 12px;
border-radius: 6px;
font-size: 14px;
display: none;
}
.feedback.correct {
background: #d1fae5;
color: #065f46;
border: 1px solid #10b981;
display: block;
}
.feedback.incorrect {
background: #fee2e2;
color: #991b1b;
border: 1px solid #ef4444;
display: block;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5568d3;
}
.btn-primary:disabled {
background: #9ca3af;
cursor: not-allowed;
}
.btn-group {
display: flex;
gap: 12px;
margin-top: 24px;
}
.results {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 12px;
padding: 32px;
text-align: center;
margin-bottom: 24px;
display: none;
}
.results h2 {
font-size: 32px;
margin-bottom: 16px;
}
.results .score {
font-size: 64px;
font-weight: 700;
margin: 20px 0;
}
.results .percentage {
font-size: 24px;
opacity: 0.9;
}
.progress-bar {
background: #e5e7eb;
height: 8px;
border-radius: 4px;
margin-bottom: 24px;
overflow: hidden;
}
.progress-fill {
background: #667eea;
height: 100%;
transition: width 0.3s;
}
</style>
</head>
<body>
<div class="container">
<div class="progress-bar">
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
</div>
<h1 id="quizTitle"></h1>
<p class="description" id="quizDescription"></p>
<div class="results" id="results">
<h2>🎉 Quiz abgeschlossen!</h2>
<div class="score" id="scoreDisplay"></div>
<div class="percentage" id="percentageDisplay"></div>
</div>
<div id="questionsContainer"></div>
<div class="btn-group">
<button class="btn btn-primary" id="checkBtn" onclick="checkAnswers()" disabled>
Antworten überprüfen
</button>
<button class="btn btn-primary" id="nextBtn" onclick="nextQuestion()" style="display: none;">
Nächste Frage →
</button>
<button class="btn btn-primary" id="finishBtn" onclick="finishQuiz()" style="display: none;">
Quiz beenden
</button>
</div>
</div>
<script>
let quizData = null;
let currentQuestion = 0;
let score = 0;
let answers = [];
function loadQuiz() {
const urlParams = new URLSearchParams(window.location.search);
const dataParam = urlParams.get('data');
if (dataParam) {
try {
quizData = JSON.parse(decodeURIComponent(dataParam));
} catch (e) {
alert('Fehler beim Laden des Quiz!');
return;
}
}
if (!quizData) {
alert('Keine Quiz-Daten gefunden!');
return;
}
document.getElementById('quizTitle').textContent = quizData.title;
document.getElementById('quizDescription').textContent = quizData.description || '';
answers = new Array(quizData.questions.length).fill(null).map(() => []);
showQuestion(0);
}
function showQuestion(index) {
currentQuestion = index;
const question = quizData.questions[index];
const container = document.getElementById('questionsContainer');
// Update progress
const progress = ((index + 1) / quizData.questions.length) * 100;
document.getElementById('progressFill').style.width = progress + '%';
container.innerHTML = `
<div class="question-card">
<div class="question-number">Frage ${index + 1} von ${quizData.questions.length}</div>
<div class="question-text">${question.text}</div>
${question.answers.filter(a => a.text).map((answer, aIndex) => `
<div class="answer" id="answer_${aIndex}" onclick="selectAnswer(${aIndex})">
<div class="checkbox"></div>
<div class="answer-text">${answer.text}</div>
</div>
`).join('')}
<div class="feedback" id="feedback"></div>
</div>
`;
// Reset buttons
document.getElementById('checkBtn').style.display = 'inline-block';
document.getElementById('checkBtn').disabled = true;
document.getElementById('nextBtn').style.display = 'none';
document.getElementById('finishBtn').style.display = 'none';
}
function selectAnswer(answerIndex) {
const question = quizData.questions[currentQuestion];
// Check if multiple answers are allowed
const correctCount = question.answers.filter(a => a.correct).length;
const isMultipleChoice = correctCount > 1;
if (!isMultipleChoice) {
// Single choice - deselect all others
answers[currentQuestion] = [answerIndex];
document.querySelectorAll('.answer').forEach((el, idx) => {
el.classList.toggle('selected', idx === answerIndex);
});
} else {
// Multiple choice - toggle
const answerEl = document.getElementById('answer_' + answerIndex);
const isSelected = answerEl.classList.contains('selected');
if (isSelected) {
answerEl.classList.remove('selected');
answers[currentQuestion] = answers[currentQuestion].filter(a => a !== answerIndex);
} else {
answerEl.classList.add('selected');
answers[currentQuestion].push(answerIndex);
}
}
// Enable check button if at least one answer is selected
document.getElementById('checkBtn').disabled = answers[currentQuestion].length === 0;
}
function checkAnswers() {
const question = quizData.questions[currentQuestion];
const userAnswers = answers[currentQuestion];
const feedback = document.getElementById('feedback');
let isCorrect = true;
// Check each answer
question.answers.forEach((answer, index) => {
if (!answer.text) return; // Skip empty answers
const answerEl = document.getElementById('answer_' + index);
const isSelected = userAnswers.includes(index);
const shouldBeCorrect = answer.correct;
if (shouldBeCorrect) {
answerEl.classList.add('correct');
if (!isSelected) {
isCorrect = false;
}
} else if (isSelected) {
answerEl.classList.add('incorrect');
isCorrect = false;
}
// Disable clicking
answerEl.style.cursor = 'default';
answerEl.onclick = null;
});
// Show feedback
if (isCorrect) {
feedback.className = 'feedback correct';
feedback.textContent = '✅ Richtig! Sehr gut!';
score++;
} else {
feedback.className = 'feedback incorrect';
feedback.textContent = '❌ Leider falsch. Die richtigen Antworten sind grün markiert.';
}
// Show appropriate button
document.getElementById('checkBtn').style.display = 'none';
if (currentQuestion < quizData.questions.length - 1) {
document.getElementById('nextBtn').style.display = 'inline-block';
} else {
document.getElementById('finishBtn').style.display = 'inline-block';
}
}
function nextQuestion() {
if (currentQuestion < quizData.questions.length - 1) {
showQuestion(currentQuestion + 1);
}
}
function finishQuiz() {
const percentage = Math.round((score / quizData.questions.length) * 100);
document.getElementById('scoreDisplay').textContent = `${score} / ${quizData.questions.length}`;
document.getElementById('percentageDisplay').textContent = `${percentage}% richtig`;
document.getElementById('results').style.display = 'block';
document.getElementById('questionsContainer').style.display = 'none';
document.querySelector('.btn-group').style.display = 'none';
}
// Load quiz on page load
loadQuiz();
</script>
</body>
</html>