149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
'use client'
|
||
|
||
import { useState, useCallback } from 'react'
|
||
import { CardState } from '@/lib/data/reviewSimpleData'
|
||
import { SimpleTestHeader } from './SimpleTestHeader'
|
||
|
||
interface VocabChoiceTestProps {
|
||
card: CardState
|
||
options: string[]
|
||
onAnswer: (confidence: number) => void
|
||
onSkip: () => void
|
||
}
|
||
|
||
export function VocabChoiceTest({ card, options, onAnswer, onSkip }: VocabChoiceTestProps) {
|
||
const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null)
|
||
const [showResult, setShowResult] = useState(false)
|
||
|
||
const handleAnswerSelect = useCallback((answer: string) => {
|
||
if (showResult) return
|
||
|
||
setSelectedAnswer(answer)
|
||
setShowResult(true)
|
||
|
||
// 判斷答案是否正確,正確給3分,錯誤給1分
|
||
const isCorrect = answer === card.word
|
||
const confidence = isCorrect ? 2 : 0
|
||
|
||
// 延遲一點再調用回調,讓用戶看到選擇結果
|
||
setTimeout(() => {
|
||
onAnswer(confidence)
|
||
// 重置狀態為下一題準備
|
||
setSelectedAnswer(null)
|
||
setShowResult(false)
|
||
}, 1500)
|
||
}, [showResult, card.word, onAnswer])
|
||
|
||
const handleSkipClick = useCallback(() => {
|
||
onSkip()
|
||
}, [onSkip])
|
||
|
||
const isCorrect = selectedAnswer === card.word
|
||
|
||
return (
|
||
<div>
|
||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||
<SimpleTestHeader
|
||
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={showResult}
|
||
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}
|
||
</p>
|
||
<p className="text-gray-700">
|
||
<strong>例句:</strong> "{card.example}"
|
||
</p>
|
||
<p className="text-gray-600 text-sm">
|
||
{card.exampleTranslation}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* 跳過按鈕 - 移到卡片外面 */}
|
||
{!showResult && (
|
||
<div className="mt-6">
|
||
<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>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
} |