/**
* BreakPilot Studio - Multiple Choice Module
*
* Multiple Choice Quiz-Funktionalität:
* - Generierung von MC-Fragen aus Arbeitsblättern
* - Interaktives Quiz mit Auswertung
* - Druckfunktion (mit/ohne Lösungen)
*
* Refactored: 2026-01-19
*/
import { t } from './i18n.js';
import { setStatus } from './api-helpers.js';
// State
let currentMcData = null;
let mcAnswers = {};
// DOM References
let mcPreview = null;
let mcBadge = null;
let mcModal = null;
let mcModalBody = null;
let mcModalClose = null;
let btnMcGenerate = null;
let btnMcShow = null;
let btnMcPrint = null;
// Callback für aktuelle Datei
let getCurrentFileCallback = null;
let getEingangFilesCallback = null;
let getCurrentIndexCallback = null;
/**
* Initialisiert das Multiple Choice Modul
* @param {Object} options - Konfiguration
*/
export function initMcModule(options = {}) {
getCurrentFileCallback = options.getCurrentFile || (() => null);
getEingangFilesCallback = options.getEingangFiles || (() => []);
getCurrentIndexCallback = options.getCurrentIndex || (() => 0);
// DOM References
mcPreview = document.getElementById('mc-preview') || options.previewEl;
mcBadge = document.getElementById('mc-badge') || options.badgeEl;
mcModal = document.getElementById('mc-modal') || options.modalEl;
mcModalBody = document.getElementById('mc-modal-body') || options.modalBodyEl;
mcModalClose = document.getElementById('mc-modal-close') || options.modalCloseEl;
btnMcGenerate = document.getElementById('btn-mc-generate') || options.generateBtn;
btnMcShow = document.getElementById('btn-mc-show') || options.showBtn;
btnMcPrint = document.getElementById('btn-mc-print') || options.printBtn;
// Event-Listener
if (btnMcGenerate) {
btnMcGenerate.addEventListener('click', generateMcQuestions);
}
if (btnMcShow) {
btnMcShow.addEventListener('click', openMcModal);
}
if (btnMcPrint) {
btnMcPrint.addEventListener('click', openMcPrintDialog);
}
if (mcModalClose) {
mcModalClose.addEventListener('click', closeMcModal);
}
if (mcModal) {
mcModal.addEventListener('click', (ev) => {
if (ev.target === mcModal) {
closeMcModal();
}
});
}
// Event für Datei-Wechsel
window.addEventListener('fileSelected', () => {
loadMcPreviewForCurrent();
});
}
/**
* Generiert MC-Fragen für alle Arbeitsblätter
*/
export async function generateMcQuestions() {
try {
setStatus(t('mc_generating') || 'Generiere MC-Fragen …', t('ai_working') || 'Bitte warten, KI arbeitet.', 'busy');
if (mcBadge) mcBadge.textContent = t('generating') || 'Generiert...';
const resp = await fetch('/api/generate-mc', { method: 'POST' });
if (!resp.ok) {
throw new Error('HTTP ' + resp.status);
}
const result = await resp.json();
if (result.status === 'OK' && result.generated.length > 0) {
setStatus(t('mc_generated') || 'MC-Fragen generiert', result.generated.length + ' ' + (t('files_created') || 'Dateien erstellt'));
if (mcBadge) mcBadge.textContent = t('ready') || 'Fertig';
if (btnMcShow) btnMcShow.style.display = 'inline-block';
if (btnMcPrint) btnMcPrint.style.display = 'inline-block';
// Lade die erste MC-Datei für Vorschau
await loadMcPreviewForCurrent();
} else if (result.errors && result.errors.length > 0) {
setStatus(t('mc_error') || 'Fehler bei MC-Generierung', result.errors[0].error, 'error');
if (mcBadge) mcBadge.textContent = t('error') || 'Fehler';
} else {
setStatus(t('no_mc_generated') || 'Keine MC-Fragen generiert', t('analysis_missing') || 'Möglicherweise fehlen Analyse-Daten.', 'error');
if (mcBadge) mcBadge.textContent = t('ready') || 'Bereit';
}
} catch (e) {
console.error('MC-Generierung fehlgeschlagen:', e);
setStatus(t('mc_error') || 'Fehler bei MC-Generierung', String(e), 'error');
if (mcBadge) mcBadge.textContent = t('error') || 'Fehler';
}
}
/**
* Lädt MC-Vorschau für die aktuelle Datei
*/
export async function loadMcPreviewForCurrent() {
const eingangFiles = getEingangFilesCallback();
const currentIndex = getCurrentIndexCallback();
if (!eingangFiles.length) {
if (mcPreview) mcPreview.innerHTML = '
' + (t('no_worksheets') || 'Keine Arbeitsblätter vorhanden.') + '
';
return;
}
const currentFile = eingangFiles[currentIndex];
if (!currentFile) return;
try {
const resp = await fetch('/api/mc-data/' + encodeURIComponent(currentFile));
const result = await resp.json();
if (result.status === 'OK' && result.data) {
currentMcData = result.data;
renderMcPreview(result.data);
if (btnMcShow) btnMcShow.style.display = 'inline-block';
if (btnMcPrint) btnMcPrint.style.display = 'inline-block';
} else {
if (mcPreview) mcPreview.innerHTML = '' + (t('no_mc_for_worksheet') || 'Noch keine MC-Fragen für dieses Arbeitsblatt generiert.') + '
';
currentMcData = null;
if (btnMcPrint) btnMcPrint.style.display = 'none';
}
} catch (e) {
console.error('Fehler beim Laden der MC-Daten:', e);
if (mcPreview) mcPreview.innerHTML = '';
}
}
/**
* Rendert die MC-Vorschau
* @param {Object} mcData - MC-Daten
*/
function renderMcPreview(mcData) {
if (!mcPreview) return;
if (!mcData || !mcData.questions || mcData.questions.length === 0) {
mcPreview.innerHTML = '' + (t('no_questions') || 'Keine Fragen vorhanden.') + '
';
return;
}
const questions = mcData.questions;
const metadata = mcData.metadata || {};
let html = '';
// Zeige Metadaten
if (metadata.grade_level || metadata.subject) {
html += '';
if (metadata.subject) {
html += '
' + (t('subject') || 'Fach') + ': ' + escapeHtml(metadata.subject) + '
';
}
if (metadata.grade_level) {
html += '
' + (t('grade') || 'Stufe') + ': ' + escapeHtml(metadata.grade_level) + '
';
}
html += '
' + (t('questions') || 'Fragen') + ': ' + questions.length + '
';
html += '
';
}
// Zeige erste 2 Fragen als Vorschau
const previewQuestions = questions.slice(0, 2);
previewQuestions.forEach((q, idx) => {
html += '';
html += '
' + (idx + 1) + '. ' + escapeHtml(q.question) + '
';
html += '
';
q.options.forEach(opt => {
html += '
';
html += '' + opt.id + ') ' + escapeHtml(opt.text);
html += '
';
});
html += '
';
html += '
';
});
if (questions.length > 2) {
html += '+ ' + (questions.length - 2) + ' ' + (t('more_questions') || 'weitere Fragen') + '
';
}
mcPreview.innerHTML = html;
// Event-Listener für Antwort-Auswahl
mcPreview.querySelectorAll('.mc-option').forEach(optEl => {
optEl.addEventListener('click', () => handleMcOptionClick(optEl));
});
}
/**
* Behandelt Klick auf eine MC-Option
* @param {Element} optEl - Angeklicktes Option-Element
*/
function handleMcOptionClick(optEl) {
const qid = optEl.getAttribute('data-qid');
const optId = optEl.getAttribute('data-opt');
if (!currentMcData) return;
// Finde die Frage
const question = currentMcData.questions.find(q => q.id === qid);
if (!question) return;
// Markiere alle Optionen dieser Frage
const questionEl = optEl.closest('.mc-question');
const allOptions = questionEl.querySelectorAll('.mc-option');
allOptions.forEach(opt => {
opt.classList.remove('selected', 'correct', 'incorrect');
const thisOptId = opt.getAttribute('data-opt');
if (thisOptId === question.correct_answer) {
opt.classList.add('correct');
} else if (thisOptId === optId) {
opt.classList.add('incorrect');
}
});
// Speichere Antwort
mcAnswers[qid] = optId;
// Zeige Feedback
const isCorrect = optId === question.correct_answer;
let feedbackEl = questionEl.querySelector('.mc-feedback');
if (!feedbackEl) {
feedbackEl = document.createElement('div');
feedbackEl.className = 'mc-feedback';
questionEl.appendChild(feedbackEl);
}
if (isCorrect) {
feedbackEl.style.background = 'rgba(34,197,94,0.1)';
feedbackEl.style.borderColor = 'rgba(34,197,94,0.3)';
feedbackEl.style.color = 'var(--bp-accent)';
feedbackEl.textContent = (t('correct') || 'Richtig!') + ' ' + (question.explanation || '');
} else {
feedbackEl.style.background = 'rgba(239,68,68,0.1)';
feedbackEl.style.borderColor = 'rgba(239,68,68,0.3)';
feedbackEl.style.color = '#ef4444';
feedbackEl.textContent = (t('incorrect') || 'Leider falsch.') + ' ' + (question.explanation || '');
}
}
/**
* Öffnet das MC-Quiz-Modal
*/
export function openMcModal() {
if (!currentMcData || !currentMcData.questions) {
alert(t('no_mc_questions') || 'Keine MC-Fragen vorhanden. Bitte zuerst generieren.');
return;
}
mcAnswers = {}; // Reset Antworten
renderMcModal(currentMcData);
if (mcModal) mcModal.classList.remove('hidden');
}
/**
* Schließt das MC-Modal
*/
export function closeMcModal() {
if (mcModal) mcModal.classList.add('hidden');
}
/**
* Rendert den Modal-Inhalt
* @param {Object} mcData - MC-Daten
*/
function renderMcModal(mcData) {
if (!mcModalBody) return;
const questions = mcData.questions;
const metadata = mcData.metadata || {};
let html = '';
// Header mit Metadaten
html += '';
if (metadata.source_title) {
html += '
' + (t('worksheet') || 'Arbeitsblatt') + ': ' + escapeHtml(metadata.source_title) + '
';
}
if (metadata.subject) {
html += '
' + (t('subject') || 'Fach') + ': ' + escapeHtml(metadata.subject) + '
';
}
if (metadata.grade_level) {
html += '
' + (t('grade') || 'Stufe') + ': ' + escapeHtml(metadata.grade_level) + '
';
}
html += '
';
// Alle Fragen
questions.forEach((q, idx) => {
html += '';
html += '
' + (idx + 1) + '. ' + escapeHtml(q.question) + '
';
html += '
';
q.options.forEach(opt => {
html += '
';
html += '' + opt.id + ') ' + escapeHtml(opt.text);
html += '
';
});
html += '
';
html += '
';
});
// Auswertungs-Button
html += '';
html += '';
html += '
';
mcModalBody.innerHTML = html;
// Event-Listener
mcModalBody.querySelectorAll('.mc-option').forEach(optEl => {
optEl.addEventListener('click', () => {
const qid = optEl.getAttribute('data-qid');
const optId = optEl.getAttribute('data-opt');
// Deselektiere andere Optionen der gleichen Frage
const questionEl = optEl.closest('.mc-question');
questionEl.querySelectorAll('.mc-option').forEach(o => o.classList.remove('selected'));
optEl.classList.add('selected');
mcAnswers[qid] = optId;
});
});
const btnEvaluate = document.getElementById('btn-mc-evaluate');
if (btnEvaluate) {
btnEvaluate.addEventListener('click', evaluateMcQuiz);
}
}
/**
* Wertet das Quiz aus
*/
function evaluateMcQuiz() {
if (!currentMcData || !mcModalBody) return;
let correct = 0;
let total = currentMcData.questions.length;
currentMcData.questions.forEach(q => {
const questionEl = mcModalBody.querySelector('.mc-question[data-qid="' + q.id + '"]');
if (!questionEl) return;
const userAnswer = mcAnswers[q.id];
const allOptions = questionEl.querySelectorAll('.mc-option');
allOptions.forEach(opt => {
opt.classList.remove('correct', 'incorrect');
const optId = opt.getAttribute('data-opt');
if (optId === q.correct_answer) {
opt.classList.add('correct');
} else if (optId === userAnswer && userAnswer !== q.correct_answer) {
opt.classList.add('incorrect');
}
});
// Zeige Erklärung
let feedbackEl = questionEl.querySelector('.mc-feedback');
if (!feedbackEl) {
feedbackEl = document.createElement('div');
feedbackEl.className = 'mc-feedback';
questionEl.appendChild(feedbackEl);
}
if (userAnswer === q.correct_answer) {
correct++;
feedbackEl.style.background = 'rgba(34,197,94,0.1)';
feedbackEl.style.borderColor = 'rgba(34,197,94,0.3)';
feedbackEl.style.color = 'var(--bp-accent)';
feedbackEl.textContent = (t('correct') || 'Richtig!') + ' ' + (q.explanation || '');
} else if (userAnswer) {
feedbackEl.style.background = 'rgba(239,68,68,0.1)';
feedbackEl.style.borderColor = 'rgba(239,68,68,0.3)';
feedbackEl.style.color = '#ef4444';
feedbackEl.textContent = (t('incorrect') || 'Falsch.') + ' ' + (q.explanation || '');
} else {
feedbackEl.style.background = 'rgba(148,163,184,0.1)';
feedbackEl.style.borderColor = 'rgba(148,163,184,0.3)';
feedbackEl.style.color = 'var(--bp-text-muted)';
feedbackEl.textContent = (t('not_answered') || 'Nicht beantwortet.') + ' ' + (t('correct_was') || 'Richtig wäre:') + ' ' + q.correct_answer.toUpperCase();
}
});
// Zeige Gesamtergebnis
const percentage = Math.round(correct / total * 100);
const resultHtml = '' +
'
' + correct + ' ' + (t('of') || 'von') + ' ' + total + ' ' + (t('correct_answers') || 'richtig') + '
' +
'
' + percentage + '% ' + (t('percent_correct') || 'korrekt') + '
' +
'
';
const existingResult = mcModalBody.querySelector('.mc-result');
if (existingResult) {
existingResult.remove();
}
const resultDiv = document.createElement('div');
resultDiv.className = 'mc-result';
resultDiv.innerHTML = resultHtml;
mcModalBody.appendChild(resultDiv);
}
/**
* Öffnet den Druck-Dialog
*/
export function openMcPrintDialog() {
if (!currentMcData) {
alert(t('no_mc_questions') || 'Keine MC-Fragen vorhanden.');
return;
}
const eingangFiles = getEingangFilesCallback();
const currentIndex = getCurrentIndexCallback();
const currentFile = eingangFiles[currentIndex];
const confirmMsg = (t('mc_print_with_answers') || 'Mit Lösungen drucken?') +
'\n\nOK = ' + (t('solution_sheet') || 'Lösungsblatt mit markierten Antworten') +
'\n' + (t('cancel') || 'Abbrechen') + ' = ' + (t('exercise_sheet') || 'Übungsblatt ohne Lösungen');
const choice = confirm(confirmMsg);
const url = '/api/print-mc/' + encodeURIComponent(currentFile) + '?show_answers=' + choice;
window.open(url, '_blank');
}
// === Getter und Setter ===
/**
* Gibt die aktuellen MC-Daten zurück
* @returns {Object|null}
*/
export function getMcData() {
return currentMcData;
}
/**
* Setzt die MC-Daten
* @param {Object} data
*/
export function setMcData(data) {
currentMcData = data;
if (data) {
renderMcPreview(data);
}
}
/**
* Helper: HTML-Escape
*/
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}