'use client' import { useState, useEffect, useRef, useLayoutEffect } from 'react' import { useRouter } from 'next/navigation' import { Navigation } from '@/components/Navigation' import AudioPlayer from '@/components/AudioPlayer' import VoiceRecorder from '@/components/VoiceRecorder' import LearningComplete from '@/components/LearningComplete' import ReviewTypeIndicator from '@/components/review/ReviewTypeIndicator' import MasteryIndicator from '@/components/review/MasteryIndicator' import { flashcardsService, type Flashcard } from '@/lib/services/flashcards' import { calculateCurrentMastery, getReviewTypesByDifficulty } from '@/lib/utils/masteryCalculator' // 擴展的Flashcard接口,包含智能複習需要的欄位 interface ExtendedFlashcard extends Omit { userLevel?: number; // 學習者程度 (1-100) wordLevel?: number; // 詞彙難度 (1-100) nextReviewDate?: string; // 下次復習日期 (可選) currentInterval?: number; // 當前間隔天數 isOverdue?: boolean; // 是否逾期 overdueDays?: number; // 逾期天數 baseMasteryLevel?: number; // 基礎熟悉度 lastReviewDate?: string; // 最後復習日期 synonyms?: string[]; // 同義詞 (暫時保留mock格式) difficulty?: string; // CEFR等級 (暫時保留mock格式) exampleImage?: string; // 例句圖片 (暫時保留mock格式) } export default function LearnPage() { const router = useRouter() const [mounted, setMounted] = useState(false) // 智能複習狀態 const [currentCard, setCurrentCard] = useState(null) const [dueCards, setDueCards] = useState([]) const [currentCardIndex, setCurrentCardIndex] = useState(0) const [isLoadingCard, setIsLoadingCard] = useState(false) // 複習模式狀態 (系統自動選擇) const [mode, setMode] = useState<'flip-memory' | 'vocab-choice' | 'vocab-listening' | 'sentence-listening' | 'sentence-fill' | 'sentence-reorder' | 'sentence-speaking'>('flip-memory') const [isAutoSelecting, setIsAutoSelecting] = useState(true) // 答題狀態 const [score, setScore] = useState({ correct: 0, total: 0 }) const [selectedAnswer, setSelectedAnswer] = useState(null) const [showResult, setShowResult] = useState(false) const [fillAnswer, setFillAnswer] = useState('') const [showHint, setShowHint] = useState(false) const [isFlipped, setIsFlipped] = useState(false) // UI狀態 const [modalImage, setModalImage] = useState(null) const [showReportModal, setShowReportModal] = useState(false) const [reportReason, setReportReason] = useState('') const [reportingCard, setReportingCard] = useState(null) const [showComplete, setShowComplete] = useState(false) const [showNoDueCards, setShowNoDueCards] = useState(false) const [cardHeight, setCardHeight] = useState(400) // 題型特定狀態 const [quizOptions, setQuizOptions] = useState([]) const [sentenceOptions, setSentenceOptions] = useState([]) // 例句重組狀態 const [shuffledWords, setShuffledWords] = useState([]) const [arrangedWords, setArrangedWords] = useState([]) const [reorderResult, setReorderResult] = useState(null) // Refs for measuring card content heights const cardFrontRef = useRef(null) const cardBackRef = useRef(null) const cardContainerRef = useRef(null) // Calculate optimal card height based on content (only when card changes) const calculateCardHeight = () => { if (!cardFrontRef.current || !cardBackRef.current) return 400; // Get the scroll heights to measure actual content const frontHeight = cardFrontRef.current.scrollHeight; const backHeight = cardBackRef.current.scrollHeight; console.log('Heights calculated:', { frontHeight, backHeight }); // Debug log // Use the maximum height with padding const maxHeight = Math.max(frontHeight, backHeight); const paddedHeight = maxHeight + 40; // Add padding for visual spacing // Ensure minimum height for visual consistency return Math.max(paddedHeight, 450); }; // Update card height only when card content changes (not on flip) useLayoutEffect(() => { if (mounted && cardFrontRef.current && cardBackRef.current) { // Wait for DOM to be fully rendered const timer = setTimeout(() => { const newHeight = calculateCardHeight(); setCardHeight(newHeight); }, 50); return () => clearTimeout(timer); } }, [currentCardIndex, mounted]); // Client-side mounting useEffect(() => { setMounted(true) loadDueCards() // 載入到期詞卡 }, []) // 載入到期詞卡列表 const loadDueCards = async () => { try { setIsLoadingCard(true) // 完全使用後端API數據 const apiResult = await flashcardsService.getDueFlashcards(50); if (apiResult.success && apiResult.data && apiResult.data.length > 0) { const cardsToUse = apiResult.data; console.log('載入後端API數據:', cardsToUse.length, '張詞卡'); setDueCards(cardsToUse); // 設置第一張卡片 const firstCard = cardsToUse[0]; setCurrentCard(firstCard); setCurrentCardIndex(0); // 系統自動選擇模式 const selectedMode = await selectOptimalReviewMode(firstCard); setMode(selectedMode); setIsAutoSelecting(false); console.log(`初始載入: ${firstCard.word}, 選擇模式: ${selectedMode}`); } else { // 沒有到期詞卡 console.log('沒有到期的詞卡'); setDueCards([]); setCurrentCard(null); setShowNoDueCards(true); } } catch (error) { console.error('載入到期詞卡失敗:', error); setDueCards([]); setCurrentCard(null); } finally { setIsLoadingCard(false); } } // 智能載入下一張卡片並自動選擇模式 const loadNextCardWithAutoMode = async (cardIndex: number) => { try { setIsAutoSelecting(true); // 等待dueCards載入完成 if (dueCards.length === 0) { console.log('等待詞卡載入...'); return; } const card = dueCards[cardIndex]; if (!card) { setShowComplete(true); setIsAutoSelecting(false); return; } setCurrentCard(card); setCurrentCardIndex(cardIndex); // 系統自動選擇最適合的複習模式 const selectedMode = await selectOptimalReviewMode(card); setMode(selectedMode); // 重置所有答題狀態 resetAllStates(); console.log(`載入卡片: ${card.word}, 選擇模式: ${selectedMode}`); } catch (error) { console.error('載入卡片失敗:', error); } finally { setIsAutoSelecting(false); } } // 系統自動選擇最適合的複習模式 const selectOptimalReviewMode = async (card: ExtendedFlashcard): Promise => { try { // 使用CEFR字符串進行智能選擇 const userCEFRLevel = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFRLevel = card.difficultyLevel || 'A2'; console.log(`CEFR智能選擇: 用戶${userCEFRLevel} vs 詞彙${wordCEFRLevel}`); const apiResult = await flashcardsService.getOptimalReviewMode(card.id, userCEFRLevel, wordCEFRLevel); if (apiResult.success && apiResult.data?.selectedMode) { const selectedMode = apiResult.data.selectedMode; console.log(`後端智能選擇: ${selectedMode}`); // 映射到前端模式名稱 const modeMapping: { [key: string]: typeof mode } = { 'flip-memory': 'flip-memory', 'vocab-choice': 'vocab-choice', 'vocab-listening': 'vocab-listening', 'sentence-fill': 'sentence-fill', 'sentence-reorder': 'sentence-reorder', 'sentence-speaking': 'sentence-speaking', 'sentence-listening': 'sentence-listening' }; return modeMapping[selectedMode] || 'flip-memory'; } else { console.log('後端API失敗,使用前端邏輯'); } } catch (error) { console.error('智能選擇API錯誤:', error); } // 備用: 使用前端CEFR邏輯 const userCEFRLevel = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFRLevel = card.difficultyLevel || 'A2'; const availableModes = getReviewTypesByCEFR(userCEFRLevel, wordCEFRLevel); const modeMapping: { [key: string]: typeof mode } = { 'flip-memory': 'flip-memory', 'vocab-choice': 'vocab-choice', 'vocab-listening': 'vocab-listening', 'sentence-fill': 'sentence-fill', 'sentence-reorder': 'sentence-reorder', 'sentence-speaking': 'sentence-speaking', 'sentence-listening': 'sentence-listening' }; const selectedType = availableModes[0] || 'flip-memory'; console.log(`前端CEFR邏輯選擇: ${selectedType}`); return modeMapping[selectedType] || 'flip-memory'; } // 前端CEFR備用選擇邏輯 const getReviewTypesByCEFR = (userCEFR: string, wordCEFR: string): string[] => { const userLevel = getCEFRToLevel(userCEFR); const wordLevel = getCEFRToLevel(wordCEFR); const difficulty = wordLevel - userLevel; if (userCEFR === 'A1') { return ['flip-memory', 'vocab-choice', 'vocab-listening']; } else if (difficulty < -10) { return ['sentence-reorder', 'sentence-fill']; } else if (difficulty >= -10 && difficulty <= 10) { return ['sentence-fill', 'sentence-reorder', 'sentence-speaking']; } else { return ['flip-memory', 'vocab-choice']; } } // CEFR轉換為數值 (前端計算用) const getCEFRToLevel = (cefr: string): number => { const mapping: { [key: string]: number } = { 'A1': 20, 'A2': 35, 'B1': 50, 'B2': 65, 'C1': 80, 'C2': 95 }; return mapping[cefr] || 50; } // 取得當前學習情境 const getCurrentContext = (userCEFR: string, wordCEFR: string): string => { const userLevel = getCEFRToLevel(userCEFR); const wordLevel = getCEFRToLevel(wordCEFR); const difficulty = wordLevel - userLevel; if (userCEFR === 'A1') return 'A1學習者'; if (difficulty < -10) return '簡單詞彙'; if (difficulty >= -10 && difficulty <= 10) return '適中詞彙'; return '困難詞彙'; } // 生成完整四情境對照表數據 const generateContextTable = (currentUserCEFR: string, currentWordCEFR: string) => { const currentContext = getCurrentContext(currentUserCEFR, currentWordCEFR); const contexts = [ { type: 'A1學習者', icon: '🛡️', reviewTypes: ['🔄翻卡記憶', '✅詞彙選擇', '🎧詞彙聽力'], purpose: '建立基礎信心', condition: '用戶等級 = A1', description: '初學者保護機制,使用最基礎的3種題型' }, { type: '簡單詞彙', icon: '🎯', reviewTypes: ['✏️例句填空', '🔀例句重組'], purpose: '應用練習', condition: '用戶等級 > 詞彙等級', description: '詞彙對您較簡單,重點練習拼寫和語法應用' }, { type: '適中詞彙', icon: '⚖️', reviewTypes: ['✏️例句填空', '🔀例句重組', '🗣️例句口說'], purpose: '全方位練習', condition: '用戶等級 ≈ 詞彙等級', description: '詞彙難度適中,進行聽說讀寫全方位練習' }, { type: '困難詞彙', icon: '📚', reviewTypes: ['🔄翻卡記憶', '✅詞彙選擇'], purpose: '基礎重建', condition: '用戶等級 < 詞彙等級', description: '詞彙對您較困難,回歸基礎重建記憶' } ]; return contexts.map(context => ({ ...context, isCurrent: context.type === currentContext })); } // 取得題型圖標 const getModeIcon = (mode: string): string => { const icons: { [key: string]: string } = { 'flip-memory': '🔄', 'vocab-choice': '✅', 'vocab-listening': '🎧', 'sentence-listening': '👂', 'sentence-fill': '✏️', 'sentence-reorder': '🔀', 'sentence-speaking': '🗣️' }; return icons[mode] || '📝'; } // 取得題型中文名稱 const getModeLabel = (mode: string): string => { const labels: { [key: string]: string } = { 'flip-memory': '翻卡記憶', 'vocab-choice': '詞彙選擇', 'vocab-listening': '詞彙聽力', 'sentence-listening': '例句聽力', 'sentence-fill': '例句填空', 'sentence-reorder': '例句重組', 'sentence-speaking': '例句口說' }; return labels[mode] || mode; } // 重置所有答題狀態 const resetAllStates = () => { setIsFlipped(false); setSelectedAnswer(null); setShowResult(false); setFillAnswer(''); setShowHint(false); setShuffledWords([]); setArrangedWords([]); setReorderResult(null); setQuizOptions([]); } // Quiz options generation useEffect(() => { if (!currentCard) return; const currentWord = currentCard.word; // Generate quiz options with current word and other words const otherWords = dueCards .filter(card => card.id !== currentCard.id) .map(card => card.word); // If we don't have enough words in the deck, add some default options const additionalOptions = ['determine', 'achieve', 'consider', 'negotiate', 'establish', 'maintain']; const allOtherWords = [...otherWords, ...additionalOptions]; // Take 3 other words (avoiding duplicates) const selectedOtherWords: string[] = []; for (const word of allOtherWords) { if (selectedOtherWords.length >= 3) break; if (word !== currentWord && !selectedOtherWords.includes(word)) { selectedOtherWords.push(word); } } // Ensure we have exactly 4 options: current word + 3 others const options = [currentWord, ...selectedOtherWords].sort(() => Math.random() - 0.5); setQuizOptions(options); // Reset quiz state when card changes setSelectedAnswer(null); setShowResult(false); }, [currentCard, dueCards]) // Sentence options generation for sentence listening useEffect(() => { if (!currentCard || mode !== 'sentence-listening') return; const currentSentence = currentCard.example; // Generate sentence options with current sentence and other sentences const otherSentences = dueCards .filter(card => card.id !== currentCard.id) .map(card => card.example); // Add some default sentence options if not enough const additionalSentences = [ 'I think this is a good opportunity for us.', 'She decided to take a different approach.', 'They managed to solve the problem quickly.', 'We need to consider all possible solutions.' ]; const allOtherSentences = [...otherSentences, ...additionalSentences]; // Take 3 other sentences (avoiding duplicates) const selectedOtherSentences: string[] = []; for (const sentence of allOtherSentences) { if (selectedOtherSentences.length >= 3) break; if (sentence !== currentSentence && !selectedOtherSentences.includes(sentence)) { selectedOtherSentences.push(sentence); } } // Ensure we have exactly 4 options: current sentence + 3 others const options = [currentSentence, ...selectedOtherSentences].sort(() => Math.random() - 0.5); setSentenceOptions(options); }, [currentCard, dueCards, mode]) // Initialize sentence reorder when card changes or mode switches to sentence-reorder useEffect(() => { if (mode === 'sentence-reorder' && currentCard) { const words = currentCard.example.split(/\s+/).filter(word => word.length > 0) const shuffled = [...words].sort(() => Math.random() - 0.5) setShuffledWords(shuffled) setArrangedWords([]) // 只在卡片或模式切換時重置結果,不在其他狀態變化時重置 if (reorderResult !== null) { setReorderResult(null) } } }, [currentCard, mode]) // 移除reorderResult依賴,避免循環重置 // Sentence reorder handlers const handleWordClick = (word: string) => { // Move word from shuffled to arranged setShuffledWords(prev => prev.filter(w => w !== word)) setArrangedWords(prev => [...prev, word]) setReorderResult(null) } const handleRemoveFromArranged = (word: string) => { setArrangedWords(prev => prev.filter(w => w !== word)) setShuffledWords(prev => [...prev, word]) setReorderResult(null) } const handleCheckReorderAnswer = async () => { if (!currentCard) return; const userSentence = arrangedWords.join(' ') const correctSentence = currentCard.example const isCorrect = userSentence.toLowerCase().trim() === correctSentence.toLowerCase().trim() setReorderResult(isCorrect) // 更新分數 setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果 await submitReviewResult(isCorrect, userSentence); } const handleResetReorder = () => { if (!currentCard) return; const words = currentCard.example.split(/\s+/).filter(word => word.length > 0) const shuffled = [...words].sort(() => Math.random() - 0.5) setShuffledWords(shuffled) setArrangedWords([]) setReorderResult(null) } const handleFlip = () => { setIsFlipped(!isFlipped) } const handleNext = async () => { if (currentCardIndex < dueCards.length - 1) { await loadNextCardWithAutoMode(currentCardIndex + 1); } else { setShowComplete(true); } } const handlePrevious = async () => { if (currentCardIndex > 0) { await loadNextCardWithAutoMode(currentCardIndex - 1); } } const handleQuizAnswer = async (answer: string) => { if (showResult || !currentCard) return setSelectedAnswer(answer) setShowResult(true) const isCorrect = answer === currentCard.word setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果到後端 await submitReviewResult(isCorrect, answer); } // 提交復習結果 const submitReviewResult = async (isCorrect: boolean, userAnswer?: string, confidenceLevel?: number) => { if (!currentCard) return; try { const result = await flashcardsService.submitReview(currentCard.id, { isCorrect, confidenceLevel, questionType: mode, userAnswer, timeTaken: Date.now() - Date.now() // 簡化時間計算 }); if (result.success && result.data) { console.log('復習結果提交成功:', result.data); // 更新卡片的熟悉度等資訊,但不觸發卡片重新載入 setCurrentCard(prev => prev ? { ...prev, masteryLevel: result.data!.masteryLevel, nextReviewDate: result.data!.nextReviewDate } : null); } else { console.log('復習結果提交失敗,繼續運行'); } } catch (error) { console.error('提交復習結果失敗:', error); // 不中斷流程,允許用戶繼續學習 } } const handleFillAnswer = async () => { if (showResult || !currentCard) return setShowResult(true) const isCorrect = fillAnswer.toLowerCase().trim() === currentCard.word.toLowerCase() setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果 await submitReviewResult(isCorrect, fillAnswer); } const handleListeningAnswer = async (answer: string) => { if (showResult || !currentCard) return setSelectedAnswer(answer) setShowResult(true) const isCorrect = answer === currentCard.word setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果 await submitReviewResult(isCorrect, answer); } const handleSpeakingAnswer = async (transcript: string) => { if (!currentCard) return setShowResult(true) const isCorrect = transcript.toLowerCase().includes(currentCard.word.toLowerCase()) setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果 await submitReviewResult(isCorrect, transcript); } const handleSentenceListeningAnswer = async (answer: string) => { if (showResult || !currentCard) return setSelectedAnswer(answer) setShowResult(true) const isCorrect = answer === currentCard.example setScore(prev => ({ correct: isCorrect ? prev.correct + 1 : prev.correct, total: prev.total + 1 })) // 提交復習結果 await submitReviewResult(isCorrect, answer); } const handleReportSubmit = () => { console.log('Report submitted:', { card: reportingCard, reason: reportReason }) setShowReportModal(false) setReportReason('') setReportingCard(null) } const handleRestart = async () => { setScore({ correct: 0, total: 0 }) setShowComplete(false) setShowNoDueCards(false) await loadDueCards(); // 重新載入到期詞卡 } // Show loading screen until mounted or while loading cards if (!mounted || isLoadingCard) { return (
{isAutoSelecting ? '系統正在選擇最適合的複習方式...' : '載入中...'}
) } // Show no due cards screen if (showNoDueCards) { return (
router.push('/dashboard')} />
📚

今日學習已完成!

目前沒有到期需要複習的詞卡。您可以:

💡 建議行動
  • • 前往詞卡管理頁面新增詞卡
  • • 查看學習統計和進度
  • • 調整學習目標和設定
) } // Show current card interface if (!currentCard) { return (
載入詞卡中...
) } return (
{/* Navigation */} router.push('/dashboard')} />
{/* Progress Bar */}
進度
{currentCardIndex + 1} / {dueCards.length}
{score.correct} / {score.total} {score.total > 0 && ( ({Math.round((score.correct / score.total) * 100)}%) )}
{/* Demo Information Panel */}

🎯 智能適配演示

當前情境
{currentCard && (() => { const userCEFRLevel = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFRLevel = currentCard.difficultyLevel || 'A2'; const userLevel = getCEFRToLevel(userCEFRLevel); const wordLevel = getCEFRToLevel(wordCEFRLevel); const difficulty = wordLevel - userLevel; if (userCEFRLevel === 'A1') return 'A1學習者'; if (difficulty < -10) return '簡單詞彙'; if (difficulty >= -10 && difficulty <= 10) return '適中詞彙'; return '困難詞彙'; })()}
學習者等級
{localStorage.getItem('userEnglishLevel') || 'A2'}
詞彙等級
{currentCard?.difficultyLevel || 'A2'}
等級差異
{currentCard ? (() => { const userCEFR = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFR = currentCard.difficultyLevel || 'A2'; const diff = getCEFRToLevel(wordCEFR) - getCEFRToLevel(userCEFR); return diff > 0 ? `+${diff}` : diff.toString(); })() : '--'}
{/* 當前選擇突出顯示 */}
{currentCard && (() => { const userCEFR = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFR = currentCard.difficultyLevel || 'A2'; const context = getCurrentContext(userCEFR, wordCEFR); const contextData = generateContextTable(userCEFR, wordCEFR).find(c => c.isCurrent); return ( <>
當前情境: {contextData?.icon} {context}
可用題型: {contextData?.reviewTypes.join(' | ')}
); })()}
系統已選擇
{getModeIcon(mode)} {getModeLabel(mode)}
{/* 完整四情境對照表 */}
📚 智能複習四情境對照表
{currentCard && (() => { const userCEFR = localStorage.getItem('userEnglishLevel') || 'A2'; const wordCEFR = currentCard.difficultyLevel || 'A2'; const tableData = generateContextTable(userCEFR, wordCEFR); return tableData.map((row, index) => ( )); })()}
情境類型 建議複習方式 學習目的 判斷條件 狀態
{row.icon} {row.type}
{row.reviewTypes.map((type, idx) => ( {type} ))}
{row.purpose} {row.condition} {row.isCurrent && ← 目前}
🧠 智能適配說明:
系統根據您的CEFR等級和詞彙CEFR等級自動判斷學習情境,並智能選擇最適合的複習題型。
{/* Current Card Mastery Level */} {currentCard?.baseMasteryLevel && currentCard?.lastReviewDate && (
)} {/* System Auto-Selected Review Type Indicator */} {mode === 'flip-memory' ? ( /* Flip Card Mode */
{/* Error Report Button for Flip Mode */}
{/* Front */}
{/* Title and Instructions */}

翻卡記憶

{currentCard.difficultyLevel}
{/* Instructions Test Action */}

點擊卡片翻面,根據你對單字的熟悉程度進行自我評估:

{/* Word Display */}

{currentCard.word}

{currentCard.pronunciation}
{/* Back */}
{/* Content Sections */}
{/* Definition */}

定義

{currentCard.definition}

{/* Example */}

例句

"{currentCard.example}"

"{currentCard.exampleTranslation}"

{/* Synonyms */}

同義詞

{(currentCard.synonyms || []).map((synonym, index) => ( {synonym} ))}
{/* Navigation */}
) : mode === 'vocab-choice' ? ( /* Vocab Choice Mode - 詞彙選擇 */
{/* Error Report Button for Quiz Mode */}
{/* Title in top-left */}

詞彙選擇

{currentCard.difficulty}
{/* Instructions Test Action */}

請選擇符合上述定義的英文詞彙:

定義

{currentCard.definition}

{quizOptions.map((option, idx) => ( ))}
{showResult && (

{selectedAnswer === currentCard.word ? '正確!' : '錯誤!'}

{selectedAnswer !== currentCard.word && (

正確答案是:{currentCard.word}

)}
發音: {currentCard.pronunciation}
)}
{/* Navigation */}
) : mode === 'sentence-fill' ? ( /* Fill in the Blank Mode - 填空題 */
{/* Error Report Button for Fill Mode */}
{/* Title in top-left */}

例句填空

{currentCard.difficulty}
{/* Example Image */} {currentCard.exampleImage && (
Example illustration setModalImage(currentCard.exampleImage || null)} />
)} {/* Instructions Test Action */}

請點擊例句中的空白處輸入正確的單字:

{/* Example Sentence with Blanks */}
{currentCard.example.split(new RegExp(`(${currentCard.word})`, 'gi')).map((part, index) => { const isTargetWord = part.toLowerCase() === currentCard.word.toLowerCase(); return isTargetWord ? ( setFillAnswer(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && !showResult && fillAnswer.trim()) { handleFillAnswer() } }} placeholder="" disabled={showResult} className={`inline-block px-2 py-1 text-center bg-transparent focus:outline-none disabled:bg-gray-100 ${ fillAnswer ? 'border-b-2 border-blue-500' : 'border-b-2 border-dashed border-gray-400 focus:border-blue-400 focus:border-solid' }`} style={{ width: `${Math.max(100, Math.max(currentCard.word.length * 12, fillAnswer.length * 12 + 20))}px` }} /> {!fillAnswer && ( ____ )} ) : ( {part} ); })}
{/* Action Buttons */}
{!showResult && fillAnswer.trim() && ( )}
{/* Hint Section */} {showHint && (

詞彙定義:

{currentCard.definition}

)} {showResult && (

{fillAnswer.toLowerCase().trim() === currentCard.word.toLowerCase() ? '正確!' : '錯誤!'}

{fillAnswer.toLowerCase().trim() !== currentCard.word.toLowerCase() && (

正確答案是:{currentCard.word}

)}

{currentCard.pronunciation}

)}
{/* Navigation */}
) : mode === 'vocab-listening' ? ( /* Listening Test Mode - 聽力測試 */
{/* Error Report Button for Listening Mode */}
{/* Title in top-left */}

詞彙聽力 (暫時不上線)

{currentCard.difficulty}
{/* Instructions Test Action */}

請聽發音並選擇正確的英文單字:

{/* Content Sections */}
{/* Audio */}

發音

{currentCard.pronunciation}
{/* Word Options */}
{[currentCard.word, 'determine', 'achieve', 'consider'].map((word) => ( ))}
{showResult && (

{selectedAnswer === currentCard.word ? '正確!' : '錯誤!'}

{selectedAnswer !== currentCard.word && (

正確答案是:{currentCard.word}

發音:{currentCard.pronunciation}
)}
)}
{/* Navigation */}
) : mode === 'sentence-speaking' ? ( /* Speaking Test Mode - 口說測試 */
{/* Error Report Button for Speaking Mode */}
{/* Title in top-left */}

例句口說

{currentCard.difficulty}
{ // 簡化處理:直接顯示結果 handleSpeakingAnswer(currentCard.example) }} />
{showResult && (

錄音完成!

系統正在評估你的發音...

)}
{/* Navigation */}
) : mode === 'sentence-listening' ? ( /* Sentence Listening Test Mode - 例句聽力題 */
{/* Error Report Button */}
{/* Title in top-left */}

例句聽力

{currentCard.difficulty}
{/* Instructions Test Action */}

請聽例句並選擇正確的選項:

點擊播放聽例句

{sentenceOptions.map((sentence, idx) => ( ))}
{showResult && (

{selectedAnswer === currentCard.example ? '正確!' : '錯誤!'}

{selectedAnswer !== currentCard.example && (

正確答案是:"{currentCard.example}"

中文翻譯:{currentCard.exampleTranslation}

)}
)}
{/* Navigation */}
) : mode === 'sentence-reorder' ? ( /* Sentence Reorder Mode - 例句重組題 */
{/* Error Report Button */}
{/* Title in top-left */}

例句重組

{currentCard.difficulty}
{/* Example Image */} {currentCard.exampleImage && (
Example illustration setModalImage(currentCard.exampleImage || null)} />
)} {/* Arranged Sentence Area */}

重組區域:

{arrangedWords.length === 0 ? (
答案區
) : (
{arrangedWords.map((word, index) => (
handleRemoveFromArranged(word)} > {word} ×
))}
)}
{/* Instructions Test Action */}

點擊下方單字,依序重組成正確的句子:

{/* Shuffled Words */}

可用單字:

{shuffledWords.length === 0 ? (
所有單字都已使用
) : (
{shuffledWords.map((word, index) => ( ))}
)}
{/* Control Buttons */}
{arrangedWords.length > 0 && ( )}
{/* Result Feedback */} {reorderResult !== null && (

{reorderResult ? '正確!' : '錯誤!'}

{!reorderResult && (

正確答案是:"{currentCard.example}"

)}

中文翻譯:{currentCard.exampleTranslation}

)}
{/* Navigation */}
) : null} {/* Report Modal */} {showReportModal && (

回報錯誤

單字:{reportingCard?.word}

)} {/* Image Modal */} {modalImage && (
setModalImage(null)} >
放大圖片
)} {/* Complete Modal */} {showComplete && ( router.push('/dashboard')} /> )} {/* No Due Cards Modal */} {showNoDueCards && (
📚

今日學習已完成!

目前沒有到期需要複習的詞卡。您可以:

💡 建議行動
  • • 前往詞卡管理頁面新增詞卡
  • • 查看學習統計和進度
  • • 調整學習目標和設定
)}
) }