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>
381 lines
10 KiB
HTML
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>
|