150 lines
4.9 KiB
TypeScript
150 lines
4.9 KiB
TypeScript
import { useState } from 'react'
|
||
import AudioPlayer from '@/components/AudioPlayer'
|
||
|
||
interface VocabChoiceTestProps {
|
||
word: string
|
||
definition: string
|
||
example: string
|
||
exampleTranslation: string
|
||
pronunciation?: string
|
||
synonyms?: string[]
|
||
difficultyLevel: string
|
||
options: string[]
|
||
onAnswer: (answer: string) => void
|
||
onReportError: () => void
|
||
disabled?: boolean
|
||
}
|
||
|
||
export const VocabChoiceTest: React.FC<VocabChoiceTestProps> = ({
|
||
word,
|
||
definition,
|
||
example,
|
||
exampleTranslation,
|
||
pronunciation,
|
||
synonyms = [],
|
||
difficultyLevel,
|
||
options,
|
||
onAnswer,
|
||
onReportError,
|
||
disabled = false
|
||
}) => {
|
||
const [selectedAnswer, setSelectedAnswer] = useState<string | null>(null)
|
||
const [showResult, setShowResult] = useState(false)
|
||
|
||
const handleAnswerSelect = (answer: string) => {
|
||
if (disabled || showResult) return
|
||
setSelectedAnswer(answer)
|
||
setShowResult(true)
|
||
onAnswer(answer)
|
||
}
|
||
|
||
const isCorrect = selectedAnswer === word
|
||
|
||
return (
|
||
<div className="relative">
|
||
{/* 錯誤回報按鈕 */}
|
||
<div className="flex justify-end mb-2">
|
||
<button
|
||
onClick={onReportError}
|
||
className="px-3 py-2 rounded-md transition-colors text-gray-600 hover:text-gray-900"
|
||
>
|
||
🚩 回報錯誤
|
||
</button>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-lg p-8">
|
||
{/* 標題區 */}
|
||
<div className="flex justify-between items-start mb-6">
|
||
<h2 className="text-2xl font-bold text-gray-900">詞彙選擇</h2>
|
||
<span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
|
||
{difficultyLevel}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 指示文字 */}
|
||
<p className="text-lg text-gray-700 mb-6 text-left">
|
||
請選擇符合上述定義的英文詞彙:
|
||
</p>
|
||
|
||
{/* 定義顯示區 */}
|
||
<div className="text-center mb-8">
|
||
<div className="bg-gray-50 rounded-lg p-4 mb-6">
|
||
<h3 className="font-semibold text-gray-900 mb-2 text-left">定義</h3>
|
||
<p className="text-gray-700 text-left">{definition}</p>
|
||
</div>
|
||
|
||
{/* 同義詞顯示 */}
|
||
{synonyms && synonyms.length > 0 && (
|
||
<div className="bg-blue-50 rounded-lg p-4 mb-6">
|
||
<h3 className="font-semibold text-gray-900 mb-2 text-left">同義詞提示</h3>
|
||
<div className="flex flex-wrap gap-2 justify-start">
|
||
{synonyms.map((synonym, index) => (
|
||
<span
|
||
key={index}
|
||
className="px-3 py-1 bg-blue-100 text-blue-700 text-sm rounded-full font-medium"
|
||
>
|
||
{synonym}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* 選項區域 - 響應式網格布局 */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-6">
|
||
{options.map((option, idx) => (
|
||
<button
|
||
key={idx}
|
||
onClick={() => handleAnswerSelect(option)}
|
||
disabled={disabled || showResult}
|
||
className={`p-4 text-center rounded-lg border-2 transition-all ${
|
||
showResult
|
||
? option === word
|
||
? 'border-green-500 bg-green-50 text-green-700'
|
||
: option === selectedAnswer
|
||
? 'border-red-500 bg-red-50 text-red-700'
|
||
: 'border-gray-200 bg-gray-50 text-gray-500'
|
||
: 'border-gray-200 hover:border-blue-300 hover:bg-blue-50'
|
||
}`}
|
||
>
|
||
<div className="text-lg font-medium">{option}</div>
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* 結果反饋區 */}
|
||
{showResult && (
|
||
<div className={`p-6 rounded-lg w-full mb-6 ${
|
||
isCorrect
|
||
? 'bg-green-50 border border-green-200'
|
||
: 'bg-red-50 border border-red-200'
|
||
}`}>
|
||
<p className={`font-semibold text-left text-xl mb-4 ${
|
||
isCorrect ? 'text-green-700' : 'text-red-700'
|
||
}`}>
|
||
{isCorrect ? '正確!' : '錯誤!'}
|
||
</p>
|
||
|
||
{!isCorrect && (
|
||
<div className="mb-4">
|
||
<p className="text-gray-700 text-left">
|
||
正確答案是:<strong className="text-lg">{word}</strong>
|
||
</p>
|
||
</div>
|
||
)}
|
||
|
||
<div className="space-y-3">
|
||
<div className="text-left">
|
||
<div className="flex items-center text-gray-600">
|
||
{pronunciation && <span className="mx-2">{pronunciation}</span>}
|
||
<AudioPlayer text={word} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
} |