dramaling-vocab-learning/frontend/components/learn/TestRunner.tsx

197 lines
5.1 KiB
TypeScript

import { useEffect } from 'react'
import { useLearnStore } from '@/store/useLearnStore'
import { useUIStore } from '@/store/useUIStore'
import {
FlipMemoryTest,
VocabChoiceTest,
SentenceFillTest,
SentenceReorderTest,
VocabListeningTest,
SentenceListeningTest,
SentenceSpeakingTest
} from './tests'
interface TestRunnerProps {
className?: string
}
export const TestRunner: React.FC<TestRunnerProps> = ({ className }) => {
const {
currentCard,
currentMode,
updateScore,
recordTestResult,
error
} = useLearnStore()
const {
openReportModal,
openImageModal
} = useUIStore()
// 處理答題
const handleAnswer = async (answer: string, confidenceLevel?: number) => {
if (!currentCard) return
// 檢查答案正確性
const isCorrect = checkAnswer(answer, currentCard, currentMode)
// 更新分數
updateScore(isCorrect)
// 記錄到後端
await recordTestResult(isCorrect, answer, confidenceLevel)
}
// 檢查答案正確性
const checkAnswer = (answer: string, card: any, mode: string): boolean => {
switch (mode) {
case 'flip-memory':
return true // 翻卡記憶沒有對錯,只有信心等級
case 'vocab-choice':
case 'vocab-listening':
return answer === card.word
case 'sentence-fill':
return answer.toLowerCase().trim() === card.word.toLowerCase()
case 'sentence-reorder':
case 'sentence-listening':
return answer.toLowerCase().trim() === card.example.toLowerCase().trim()
case 'sentence-speaking':
return true // 口說測驗通常算正確
default:
return false
}
}
// 生成測驗選項
const generateOptions = (card: any, mode: string): string[] => {
// 這裡應該根據測驗類型生成對應的選項
// 暫時返回簡單的佔位符
switch (mode) {
case 'vocab-choice':
case 'vocab-listening':
return [card.word, '其他選項1', '其他選項2', '其他選項3'].sort(() => Math.random() - 0.5)
case 'sentence-listening':
return [
card.example,
'其他例句選項1',
'其他例句選項2',
'其他例句選項3'
].sort(() => Math.random() - 0.5)
default:
return []
}
}
if (error) {
return (
<div className="text-center py-8">
<div className="bg-red-50 border border-red-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-red-700 mb-2"></h3>
<p className="text-red-600">{error}</p>
</div>
</div>
)
}
if (!currentCard) {
return (
<div className="text-center py-8">
<div className="text-gray-500">...</div>
</div>
)
}
// 共同的 props
const commonProps = {
word: currentCard.word,
definition: currentCard.definition,
example: currentCard.example,
exampleTranslation: currentCard.translation || '',
pronunciation: currentCard.pronunciation,
difficultyLevel: currentCard.difficultyLevel || 'A2',
onReportError: () => openReportModal(currentCard),
onImageClick: openImageModal,
exampleImage: currentCard.exampleImage
}
// 渲染對應的測驗組件
switch (currentMode) {
case 'flip-memory':
return (
<FlipMemoryTest
{...commonProps}
synonyms={currentCard.synonyms}
onConfidenceSubmit={(level) => handleAnswer('', level)}
/>
)
case 'vocab-choice':
return (
<VocabChoiceTest
{...commonProps}
options={generateOptions(currentCard, currentMode)}
onAnswer={handleAnswer}
/>
)
case 'sentence-fill':
return (
<SentenceFillTest
{...commonProps}
onAnswer={handleAnswer}
/>
)
case 'sentence-reorder':
return (
<SentenceReorderTest
{...commonProps}
onAnswer={handleAnswer}
/>
)
case 'vocab-listening':
return (
<VocabListeningTest
{...commonProps}
options={generateOptions(currentCard, currentMode)}
onAnswer={handleAnswer}
/>
)
case 'sentence-listening':
return (
<SentenceListeningTest
{...commonProps}
options={generateOptions(currentCard, currentMode)}
onAnswer={handleAnswer}
/>
)
case 'sentence-speaking':
return (
<SentenceSpeakingTest
{...commonProps}
onAnswer={handleAnswer}
/>
)
default:
return (
<div className="text-center py-8">
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-yellow-700 mb-2"></h3>
<p className="text-yellow-600"> "{currentMode}" </p>
</div>
</div>
)
}
}