dramaling-vocab-learning/frontend/components/review/quiz/VocabChoiceQuiz.tsx

183 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState, useCallback } from 'react'
import { CardState } from '@/lib/data/reviewSimpleData'
import { QuizHeader } from '../ui/QuizHeader'
import { BluePlayButton } from '@/components/shared/BluePlayButton'
interface VocabChoiceTestProps {
card: CardState
options: string[]
onAnswer: (confidence: number) => void
onSkip: () => void
}
export function VocabChoiceQuiz({ card, options, onAnswer, onSkip }: VocabChoiceTestProps) {
const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null)
const [showResult, setShowResult] = useState(false)
const [hasAnswered, setHasAnswered] = useState(false)
const handleAnswerSelect = useCallback((answer: string) => {
if (showResult || hasAnswered) return
setSelectedAnswer(answer)
setShowResult(true)
setHasAnswered(true)
}, [showResult, hasAnswered])
const handleSkipClick = useCallback(() => {
onSkip()
}, [onSkip])
const handleNext = useCallback(() => {
if (!hasAnswered || !selectedAnswer) return
// 判斷答案是否正確正確給2分錯誤給0分
const isCorrect = selectedAnswer === card.word
const confidence = isCorrect ? 2 : 0
onAnswer(confidence)
// 重置狀態為下一題準備
setSelectedAnswer(null)
setShowResult(false)
setHasAnswered(false)
}, [hasAnswered, selectedAnswer, card.word, onAnswer])
const isCorrect = selectedAnswer === card.word
return (
<div>
<div className="bg-white rounded-xl shadow-lg p-8">
<QuizHeader
title="詞彙選擇"
cefr={card.cefr}
/>
{/* 問題區域 */}
<div className="mb-8">
<div className="bg-gray-50 rounded-lg p-6 mb-4">
<h3 className="font-semibold text-gray-900 mb-3 text-left"></h3>
<p className="text-gray-700 text-left text-lg leading-relaxed">{card.definition}</p>
</div>
<p className="text-lg text-gray-700 text-left">
</p>
</div>
{/* 選項區域 */}
<div className="mb-6">
<div className="grid grid-cols-2 gap-3">
{options.map((option, index) => {
const isSelected = selectedAnswer === option
const isCorrectOption = option === card.word
let buttonClass = 'p-4 rounded-lg border-2 text-center font-medium transition-all duration-200 cursor-pointer active:scale-95'
if (showResult) {
if (isSelected && isCorrectOption) {
// 選中且正確
buttonClass += ' bg-green-100 text-green-700 border-green-200 ring-2 ring-green-400'
} else if (isSelected && !isCorrectOption) {
// 選中但錯誤
buttonClass += ' bg-red-100 text-red-700 border-red-200 ring-2 ring-red-400'
} else if (!isSelected && isCorrectOption) {
// 未選中但是正確答案
buttonClass += ' bg-green-50 text-green-600 border-green-200'
} else {
// 未選中且非正確答案
buttonClass += ' bg-gray-50 text-gray-500 border-gray-200'
}
} else {
// 未答題狀態
buttonClass += ' bg-blue-50 text-blue-700 border-blue-200 hover:bg-blue-100'
}
return (
<button
key={index}
onClick={() => handleAnswerSelect(option)}
disabled={hasAnswered}
className={buttonClass}
>
{option}
</button>
)
})}
</div>
</div>
{/* 結果顯示區域 */}
{showResult && (
<div className="mb-6">
<div className={`p-4 rounded-lg ${isCorrect ? 'bg-green-50 border border-green-200' : 'bg-red-50 border border-red-200'}`}>
<div className="flex items-center mb-3">
<span className={`text-2xl mr-3 ${isCorrect ? 'text-green-600' : 'text-red-600'}`}>
{isCorrect ? '✅' : '❌'}
</span>
<h3 className={`text-lg font-semibold ${isCorrect ? 'text-green-800' : 'text-red-800'}`}>
{isCorrect ? '答對了!' : '答錯了'}
</h3>
</div>
<div className="space-y-2 text-left">
<p className="text-gray-700">
<strong></strong> {card.word}
</p>
<p className="text-gray-700">
<strong></strong> {card.pronunciation}
<span className='ml-2' onClick={(e) => e.stopPropagation()}>
<BluePlayButton
text={card.word}
size="sm"
title="播放單詞發音"
rate={0.8}
lang="en-US"
/>
</span>
</p>
<p className="text-gray-700">
<strong></strong> "{card.example}"
<span className='ml-2' onClick={(e) => e.stopPropagation()}>
<BluePlayButton
text={card.example}
size="sm"
title="播放單詞發音"
rate={0.8}
lang="en-US"
/>
</span>
</p>
<p className="text-gray-600 text-sm">
{card.exampleTranslation}
</p>
</div>
</div>
</div>
)}
</div>
{/* 按鈕區域 - 根據答題狀態顯示不同按鈕 */}
<div className="mt-6">
{!hasAnswered ? (
// 未答題時顯示跳過按鈕
<button
onClick={handleSkipClick}
className="w-full border border-gray-300 text-gray-700 py-3 px-4 rounded-lg font-medium hover:bg-gray-50 transition-colors"
>
</button>
) : (
// 已答題時顯示下一題按鈕
<button
onClick={handleNext}
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
</button>
)}
</div>
</div>
)
}