/**
* BreakPilot Studio - Q&A Leitner Module
*
* Frage-Antwort Lernkarten mit Leitner-System:
* - Generieren von Q&A aus analysierten Arbeitsblättern
* - Leitner-Box-System (Neu, Gelernt, Gefestigt)
* - Lern-Session mit Selbstbewertung
* - Fortschrittsspeicherung
*
* Refactored: 2026-01-19
*/
import { t } from './i18n.js';
import { setStatus, setStatusWorking, setStatusError, setStatusSuccess, fetchJSON } from './api-helpers.js';
// State
let currentQaData = null;
let currentQaIndex = 0;
let qaSessionStats = { correct: 0, incorrect: 0, total: 0 };
// DOM References
let qaPreview = null;
let qaBadge = null;
let qaModal = null;
let qaModalBody = null;
let qaModalClose = null;
let btnQaGenerate = null;
let btnQaLearn = null;
let btnQaPrint = null;
// Callback für aktuelle Datei
let getCurrentFileCallback = null;
let getFilesCallback = null;
let getCurrentIndexCallback = null;
/**
* Initialisiert das Q&A Leitner-Modul
* @param {Object} options - Konfiguration
*/
export function initQaModule(options = {}) {
getCurrentFileCallback = options.getCurrentFile || (() => null);
getFilesCallback = options.getFiles || (() => []);
getCurrentIndexCallback = options.getCurrentIndex || (() => 0);
qaPreview = document.getElementById('qa-preview') || options.previewEl;
qaBadge = document.getElementById('qa-badge') || options.badgeEl;
qaModal = document.getElementById('qa-modal') || options.modalEl;
qaModalBody = document.getElementById('qa-modal-body') || options.modalBodyEl;
qaModalClose = document.getElementById('qa-modal-close') || options.closeBtn;
btnQaGenerate = document.getElementById('btn-qa-generate') || options.generateBtn;
btnQaLearn = document.getElementById('btn-qa-learn') || options.learnBtn;
btnQaPrint = document.getElementById('btn-qa-print') || options.printBtn;
// Event-Listener
if (btnQaGenerate) {
btnQaGenerate.addEventListener('click', generateQaQuestions);
}
if (btnQaLearn) {
btnQaLearn.addEventListener('click', openQaModal);
}
if (btnQaPrint) {
btnQaPrint.addEventListener('click', openQaPrintDialog);
}
if (qaModalClose) {
qaModalClose.addEventListener('click', closeQaModal);
}
if (qaModal) {
qaModal.addEventListener('click', (ev) => {
if (ev.target === qaModal) {
closeQaModal();
}
});
}
// Event für Datei-Wechsel
window.addEventListener('fileSelected', () => {
loadQaPreviewForCurrent();
});
}
/**
* Generiert Q&A für alle Dateien
*/
export async function generateQaQuestions() {
try {
setStatusWorking(t('status_generating_qa') || 'Generiere Q&A ...');
if (qaBadge) qaBadge.textContent = t('mc_generating') || 'Generiert...';
const resp = await fetch('/api/generate-qa', { method: 'POST' });
if (!resp.ok) {
throw new Error('HTTP ' + resp.status);
}
const result = await resp.json();
if (result.status === 'OK' && result.generated.length > 0) {
setStatusSuccess(
t('status_qa_generated') || 'Q&A generiert',
result.generated.length + ' ' + (t('status_files_created') || 'Dateien erstellt')
);
if (qaBadge) qaBadge.textContent = t('mc_done') || 'Fertig';
if (btnQaLearn) btnQaLearn.style.display = 'inline-block';
if (btnQaPrint) btnQaPrint.style.display = 'inline-block';
await loadQaPreviewForCurrent();
} else if (result.errors && result.errors.length > 0) {
setStatusError(t('error') || 'Fehler', result.errors[0].error);
if (qaBadge) qaBadge.textContent = t('mc_error') || 'Fehler';
} else {
setStatusError(t('error') || 'Fehler', 'Keine Q&A generiert.');
if (qaBadge) qaBadge.textContent = t('mc_ready') || 'Bereit';
}
} catch (e) {
console.error('Q&A-Generierung fehlgeschlagen:', e);
setStatusError(t('error') || 'Fehler', String(e));
if (qaBadge) qaBadge.textContent = t('mc_error') || 'Fehler';
}
}
/**
* Lädt die Q&A-Vorschau für die aktuelle Datei
*/
export async function loadQaPreviewForCurrent() {
const files = getFilesCallback();
if (!files.length) {
if (qaPreview) {
qaPreview.innerHTML = `
${t('qa_no_questions') || 'Noch keine Q&A vorhanden.'}
`;
}
return;
}
const currentFile = getCurrentFileCallback();
if (!currentFile) return;
try {
const resp = await fetch('/api/qa-data/' + encodeURIComponent(currentFile));
const result = await resp.json();
if (result.status === 'OK' && result.data) {
currentQaData = result.data;
renderQaPreview(result.data);
if (btnQaLearn) btnQaLearn.style.display = 'inline-block';
if (btnQaPrint) btnQaPrint.style.display = 'inline-block';
} else {
if (qaPreview) {
qaPreview.innerHTML = `${t('qa_no_questions') || 'Noch keine Q&A für dieses Arbeitsblatt generiert.'}
`;
}
currentQaData = null;
if (btnQaLearn) btnQaLearn.style.display = 'none';
if (btnQaPrint) btnQaPrint.style.display = 'none';
}
} catch (e) {
console.error('Fehler beim Laden der Q&A-Daten:', e);
if (qaPreview) qaPreview.innerHTML = '';
}
}
/**
* Rendert die Q&A-Vorschau
*/
function renderQaPreview(qaData) {
if (!qaPreview) return;
if (!qaData || !qaData.qa_items || qaData.qa_items.length === 0) {
qaPreview.innerHTML = `${t('qa_no_questions') || 'Keine Fragen vorhanden.'}
`;
return;
}
const items = qaData.qa_items;
// Zähle Fragen nach Box
let box0 = 0, box1 = 0, box2 = 0;
items.forEach(item => {
const box = item.leitner ? item.leitner.box : 0;
if (box === 0) box0++;
else if (box === 1) box1++;
else box2++;
});
let html = `
${t('qa_box_new') || 'Neu'}: ${box0}
${t('qa_box_learning') || 'Lernt'}: ${box1}
${t('qa_box_mastered') || 'Gefestigt'}: ${box2}
`;
// Zeige erste 2 Fragen als Vorschau
const previewItems = items.slice(0, 2);
previewItems.forEach((item, idx) => {
html += `
${idx + 1}. ${escapeHtml(item.question)}
→ ${escapeHtml(item.answer.substring(0, 60))}${item.answer.length > 60 ? '...' : ''}
`;
});
if (items.length > 2) {
html += `+ ${items.length - 2} ${t('questions') || 'weitere Fragen'}
`;
}
qaPreview.innerHTML = html;
}
/**
* Öffnet das Lern-Modal
*/
export function openQaModal() {
if (!currentQaData || !currentQaData.qa_items) {
alert(t('qa_no_questions') || 'Keine Q&A vorhanden. Bitte zuerst generieren.');
return;
}
currentQaIndex = 0;
qaSessionStats = { correct: 0, incorrect: 0, total: 0 };
renderQaLearningCard();
if (qaModal) qaModal.classList.remove('hidden');
}
/**
* Schließt das Lern-Modal
*/
export function closeQaModal() {
if (qaModal) qaModal.classList.add('hidden');
}
/**
* Rendert die aktuelle Lernkarte
*/
function renderQaLearningCard() {
const items = currentQaData.qa_items;
if (currentQaIndex >= items.length) {
renderQaSessionSummary();
return;
}
const item = items[currentQaIndex];
const leitner = item.leitner || { box: 0 };
const boxNames = [t('qa_box_new') || 'Neu', t('qa_box_learning') || 'Gelernt', t('qa_box_mastered') || 'Gefestigt'];
const boxColors = ['#ef4444', '#f59e0b', '#22c55e'];
let html = `
${t('question') || 'Frage'} ${currentQaIndex + 1} / ${items.length}
${boxNames[leitner.box]}
${t('question') || 'Frage'}:
${escapeHtml(item.question)}
${t('qa_your_answer') || 'Deine Antwort'}:
${t('qa_correct_answer') || 'Richtige Antwort'}:
${escapeHtml(item.answer)}
${item.key_terms && item.key_terms.length > 0 ? `
${t('qa_key_terms') || 'Schlüsselbegriffe'}: ${item.key_terms.join(', ')}
` : ''}
${t('qa_self_evaluate') || 'War deine Antwort richtig?'}
${t('qa_session_correct') || 'Richtig'}: ${qaSessionStats.correct}
${t('qa_session_incorrect') || 'Falsch'}: ${qaSessionStats.incorrect}
`;
if (qaModalBody) {
qaModalBody.innerHTML = html;
// Event Listener
document.getElementById('btn-qa-check-answer')?.addEventListener('click', () => {
const userAnswer = document.getElementById('qa-user-answer')?.value.trim() || '';
document.getElementById('qa-user-answer-text').textContent = userAnswer || (t('qa_no_answer') || '(keine Antwort eingegeben)');
document.getElementById('qa-input-container').style.display = 'none';
document.getElementById('qa-check-btn-container').style.display = 'none';
document.getElementById('qa-comparison-container').style.display = 'block';
});
// Enter zum Prüfen
document.getElementById('qa-user-answer')?.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
document.getElementById('btn-qa-check-answer')?.click();
}
});
// Fokus
setTimeout(() => {
document.getElementById('qa-user-answer')?.focus();
}, 100);
document.getElementById('btn-qa-correct')?.addEventListener('click', () => handleQaAnswer(true));
document.getElementById('btn-qa-incorrect')?.addEventListener('click', () => handleQaAnswer(false));
}
}
/**
* Verarbeitet die Antwort
*/
async function handleQaAnswer(correct) {
const item = currentQaData.qa_items[currentQaIndex];
qaSessionStats.total++;
if (correct) qaSessionStats.correct++;
else qaSessionStats.incorrect++;
// Speichere Fortschritt
try {
const currentFile = getCurrentFileCallback();
if (currentFile) {
await fetch(`/api/qa-progress?filename=${encodeURIComponent(currentFile)}&item_id=${encodeURIComponent(item.id)}&correct=${correct}`, {
method: 'POST'
});
}
} catch (e) {
console.error('Fehler beim Speichern des Fortschritts:', e);
}
currentQaIndex++;
renderQaLearningCard();
}
/**
* Rendert die Session-Zusammenfassung
*/
function renderQaSessionSummary() {
const percent = qaSessionStats.total > 0 ? Math.round(qaSessionStats.correct / qaSessionStats.total * 100) : 0;
const emoji = percent >= 80 ? '🎉' : percent >= 50 ? '👍' : '💪';
let html = `
${emoji}
${t('qa_session_complete') || 'Lernrunde abgeschlossen!'}
${qaSessionStats.correct} / ${qaSessionStats.total} ${t('qa_result_correct') || 'richtig'} (${percent}%)
${qaSessionStats.correct}
${t('qa_correct') || 'Richtig'}
${qaSessionStats.incorrect}
${t('qa_incorrect') || 'Falsch'}
`;
if (qaModalBody) {
qaModalBody.innerHTML = html;
document.getElementById('btn-qa-restart')?.addEventListener('click', () => {
currentQaIndex = 0;
qaSessionStats = { correct: 0, incorrect: 0, total: 0 };
renderQaLearningCard();
});
document.getElementById('btn-qa-close-summary')?.addEventListener('click', closeQaModal);
// Aktualisiere Preview
loadQaPreviewForCurrent();
}
}
/**
* Öffnet den Druck-Dialog
*/
function openQaPrintDialog() {
if (!currentQaData) {
alert(t('qa_no_questions') || 'Keine Q&A vorhanden.');
return;
}
const currentFile = getCurrentFileCallback();
if (!currentFile) return;
const choice = confirm((t('qa_print_with_answers') || 'Mit Lösungen drucken?') + '\n\nOK = Mit Lösungen\nAbbrechen = Nur Fragen');
const url = '/api/print-qa/' + encodeURIComponent(currentFile) + '?show_answers=' + choice;
window.open(url, '_blank');
}
/**
* Gibt die aktuellen Q&A-Daten zurück
*/
export function getQaData() {
return currentQaData;
}
/**
* Helper: HTML-Escape
*/
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text || '';
return div.innerHTML;
}