import { useEffect, useRef } from 'react' import { useNavigate } from '@tanstack/react-router' import { cn } from '@/lib/utils' import { useTestStore } from '@/store/test-store' import { useRequireAuth } from '@/hooks/use-require-auth' import { useAuthStore } from '@/store/auth-store' import { saveTestResult } from '@/lib/progress-service' import { useAwardActivity } from '@/hooks/use-gamification' import { XP_REWARDS } from '@/lib/gamification-service' const ANSWER_LABELS = ['A', 'B', 'C', 'D'] function formatTime(s: number) { const m = Math.floor(s / 60) const sec = s % 60 if (m === 0) return `${sec}s` return `${m}m ${sec}s` } export function TestResult() { const navigate = useNavigate() const { testId, testName, parts, answers, timeUsed, reset } = useTestStore() const { isAuthenticated, isLoading } = useRequireAuth() const user = useAuthStore((s) => s.user) const savedRef = useRef(false) const { mutate: awardActivity } = useAwardActivity() // Flatten all questions across parts const allQuestions = parts.flatMap(p => p.questions) useEffect(() => { if (isLoading) return if (!isAuthenticated) navigate({ to: '/toeic' }) }, [isLoading, isAuthenticated, navigate]) useEffect(() => { if (!user || savedRef.current || allQuestions.length === 0) return savedRef.current = true const correct = allQuestions.filter(q => answers[q.id] === q.correctAnswer).length awardActivity({ xp: XP_REWARDS.test }) saveTestResult(user.id, { testId, selectedParts: parts.map(p => p.partNumber), score: correct, total: allQuestions.length, timeUsed, answers: allQuestions.map(q => ({ questionId: q.id, selected: answers[q.id] ?? null, correct: answers[q.id] === q.correctAnswer, })), }) }, [user, allQuestions.length]) if (allQuestions.length === 0) { return (

Không có dữ liệu bài thi.

) } const correct = allQuestions.filter(q => answers[q.id] === q.correctAnswer).length const wrong = allQuestions.filter(q => answers[q.id] !== null && answers[q.id] !== undefined && answers[q.id] !== q.correctAnswer).length const skipped = allQuestions.filter(q => answers[q.id] === null || answers[q.id] === undefined).length const total = allQuestions.length const percent = total > 0 ? Math.round((correct / total) * 100) : 0 const circumference = 2 * Math.PI * 52 const offset = circumference - (percent / 100) * circumference return (
{/* Score header */}
= 70 ? '#16a34a' : percent >= 50 ? '#2563eb' : '#dc2626'} strokeWidth="8" strokeLinecap="round" strokeDasharray={circumference} strokeDashoffset={offset} className="transition-all duration-700" />
{correct}/{total} điểm
{percent >= 80 ? 'Xuất sắc!' : percent >= 60 ? 'Hoàn thành!' : 'Cố gắng hơn nhé!'}
{testName}
{[ { label: 'Đúng', value: correct, cls: 'bg-green-50 border-green-100 text-green-600' }, { label: 'Sai', value: wrong, cls: 'bg-red-50 border-red-100 text-red-600' }, { label: 'Bỏ qua', value: skipped, cls: 'bg-slate-50 border-slate-200 text-slate-500' }, { label: 'Thời gian', value: formatTime(timeUsed), cls: 'bg-blue-50 border-blue-100 text-blue-600' }, ].map(({ label, value, cls }) => (
{value}
{label}
))}
{/* Answer review grouped by part */} {parts.map(part => (

Part {part.partNumber} — {part.partName}

{part.questions.map((q, i) => { const userAnswer = answers[q.id] ?? null const isCorrect = userAnswer === q.correctAnswer const isSkipped = userAnswer === null || userAnswer === undefined return (
{i + 1}
{q.text &&

{q.text}

}
{q.options.map((opt, j) => ( {ANSWER_LABELS[j]}. {opt} ))}
{q.explanation && (

Giải thích: {q.explanation}

)}
{isCorrect ? check_circle : isSkipped ? remove_circle : cancel}
) })}
))}
) }