import { coursesAPI } from '../api/courses.js';
export class TestViewer {
constructor(container, stepId) {
this.container = container;
this.stepId = stepId;
this.questions = [];
this.settings = {};
this.attempts = [];
this.currentQuestionIndex = 0;
this.answers = {};
this.timeRemaining = 0;
this.timerInterval = null;
this.isSubmitted = false;
}
async load() {
try {
const response = await coursesAPI.getTestForViewing(this.stepId);
if (response) {
this.questions = response.questions || [];
this.settings = response.settings || {};
this.attempts = response.attempts || [];
// Initialize answers object
this.questions.forEach(q => {
if (q.question_type === 'single_choice') {
this.answers[q.id] = null;
} else if (q.question_type === 'multiple_choice') {
this.answers[q.id] = [];
} else if (q.question_type === 'text_answer') {
this.answers[q.id] = '';
} else if (q.question_type === 'match') {
this.answers[q.id] = {};
}
});
// Start timer if time limit is set
if (this.settings.time_limit > 0) {
this.timeRemaining = this.settings.time_limit * 60;
this.startTimer();
}
this.render();
}
} catch (error) {
console.error('Failed to load test:', error);
this.showError('Ошибка загрузки теста: ' + error.message);
}
}
startTimer() {
this.updateTimerDisplay();
this.timerInterval = setInterval(() => {
this.timeRemaining--;
this.updateTimerDisplay();
if (this.timeRemaining <= 0) {
clearInterval(this.timerInterval);
this.submitTest();
}
}, 1000);
}
updateTimerDisplay() {
const timerEl = document.getElementById('test-timer');
if (timerEl) {
const minutes = Math.floor(this.timeRemaining / 60);
const seconds = this.timeRemaining % 60;
timerEl.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
if (this.timeRemaining < 300) { // Less than 5 minutes
timerEl.classList.add('warning');
}
}
}
render() {
if (this.isSubmitted) {
return this.renderResults();
}
const question = this.questions[this.currentQuestionIndex];
this.container.innerHTML = `
${this.renderQuestion(question)}
${this.questions.map((q, idx) => `
`).join('')}
${this.currentQuestionIndex < this.questions.length - 1 ? `
` : `
`}
`;
}
renderQuestion(question) {
if (!question) return 'Вопрос не найден
';
return `
${question.question_text}
${this.renderAnswers(question)}
`;
}
renderAnswers(question) {
switch (question.question_type) {
case 'single_choice':
return this.renderSingleChoice(question);
case 'multiple_choice':
return this.renderMultipleChoice(question);
case 'text_answer':
return this.renderTextAnswer(question);
case 'match':
return this.renderMatch(question);
default:
return 'Неизвестный тип вопроса
';
}
}
renderSingleChoice(question) {
return `
${question.answers.map(answer => `
`).join('')}
`;
}
renderMultipleChoice(question) {
return `
${question.answers.map(answer => `
`).join('')}
`;
}
renderTextAnswer(question) {
return `
`;
}
renderMatch(question) {
// Shuffle right items for display
const rightItems = [...question.match_items].sort(() => Math.random() - 0.5);
return `
${question.match_items.map(item => `
${item.left_text}
`).join('')}
`;
}
renderResults() {
const lastAttempt = this.attempts[0];
if (!lastAttempt) return 'Результаты не найдены
';
const percentage = Math.round((lastAttempt.score / lastAttempt.max_score) * 100);
return `
Набрано баллов: ${lastAttempt.score} из ${lastAttempt.max_score}
Проходной балл: ${this.settings.passing_score}%
${this.settings.max_attempts > 0 ? `
Попыток использовано: ${this.attempts.length} из ${this.settings.max_attempts}
` : ''}
${!lastAttempt.passed && (this.settings.max_attempts === 0 || this.attempts.length < this.settings.max_attempts) ? `
` : ''}
`;
}
showError(message) {
this.container.innerHTML = `
`;
}
async submitTest() {
if (this.timerInterval) {
clearInterval(this.timerInterval);
}
// Validate all questions are answered
const unanswered = this.questions.filter(q => {
const answer = this.answers[q.id];
if (q.question_type === 'single_choice') return answer === null;
if (q.question_type === 'multiple_choice') return answer.length === 0;
if (q.question_type === 'text_answer') return !answer.trim();
if (q.question_type === 'match') return Object.keys(answer).length === 0;
return false;
});
if (unanswered.length > 0 && !confirm(`Осталось ${unanswered.length} без ответа. Продолжить?`)) {
return;
}
try {
const response = await coursesAPI.submitTest(this.stepId, this.answers);
if (response && response.attempt) {
this.attempts.unshift(response.attempt);
this.isSubmitted = true;
this.container.innerHTML = this.renderResults();
}
} catch (error) {
alert('Ошибка отправки теста: ' + error.message);
}
}
destroy() {
if (this.timerInterval) {
clearInterval(this.timerInterval);
}
}
}
// Global functions
window.prevQuestion = function() {
if (!window.currentTestViewer) return;
if (window.currentTestViewer.currentQuestionIndex > 0) {
window.currentTestViewer.currentQuestionIndex--;
window.currentTestViewer.render();
}
};
window.nextQuestion = function() {
if (!window.currentTestViewer) return;
if (window.currentTestViewer.currentQuestionIndex < window.currentTestViewer.questions.length - 1) {
window.currentTestViewer.currentQuestionIndex++;
window.currentTestViewer.render();
}
};
window.goToQuestion = function(index) {
if (!window.currentTestViewer) return;
window.currentTestViewer.currentQuestionIndex = index;
window.currentTestViewer.render();
};
window.selectAnswer = function(questionId, answerId) {
if (!window.currentTestViewer) return;
window.currentTestViewer.answers[questionId] = answerId;
};
window.toggleAnswer = function(questionId, answerId) {
if (!window.currentTestViewer) return;
const answers = window.currentTestViewer.answers[questionId];
const index = answers.indexOf(answerId);
if (index > -1) {
answers.splice(index, 1);
} else {
answers.push(answerId);
}
};
window.setTextAnswer = function(questionId, text) {
if (!window.currentTestViewer) return;
window.currentTestViewer.answers[questionId] = text;
};
window.selectMatch = function(questionId, leftId, rightValue) {
if (!window.currentTestViewer) return;
if (!window.currentTestViewer.answers[questionId]) {
window.currentTestViewer.answers[questionId] = {};
}
window.currentTestViewer.answers[questionId][leftId] = rightValue;
};
window.submitTest = function() {
if (!window.currentTestViewer) return;
window.currentTestViewer.submitTest();
};