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.currentQuestionIndex + 1} из ${this.questions.length} ${this.settings.time_limit > 0 ? ` ${Math.floor(this.timeRemaining / 60)}:${(this.timeRemaining % 60).toString().padStart(2, '0')} ` : ''}
${this.renderQuestion(question)}
${this.questions.map((q, idx) => ` `).join('')}
${this.currentQuestionIndex < this.questions.length - 1 ? ` ` : ` `}
`; } renderQuestion(question) { if (!question) return '

Вопрос не найден

'; return `
${question.points} балл(ов)
${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.passed ? '✓ Тест пройден!' : '✗ Тест не пройден'}

${percentage}%

Набрано баллов: ${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 = `

${message}

`; } 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(); };