'use client' import { useState, useCallback } from 'react' import { CardState } from '@/lib/data/reviewSimpleData' import { QuizHeader } from '../ui/QuizHeader' import { AudioRecorder } from '@/components/shared/AudioRecorder' import { speechAssessmentService, PronunciationResult } from '@/lib/services/speechAssessment' import { getFlashcardImageUrl } from '@/lib/utils/flashcardUtils' interface SentenceSpeakingQuizProps { card: CardState onAnswer: (confidence: number) => void onSkip: () => void } export function SentenceSpeakingQuiz({ card, onAnswer, onSkip }: SentenceSpeakingQuizProps) { const [assessmentResult, setAssessmentResult] = useState(null) const [isEvaluating, setIsEvaluating] = useState(false) const [hasAnswered, setHasAnswered] = useState(false) const [error, setError] = useState(null) // 獲取例句圖片 - 多重來源檢查 const exampleImageUrl = getFlashcardImageUrl(card) || (card as any).PrimaryImageUrl || card.primaryImageUrl || null // 除錯資訊 - 檢查圖片資料 console.log('🔍 SentenceSpeaking 圖片除錯:', { cardId: card.id, word: card.word, hasExampleImage: card.hasExampleImage, primaryImageUrl: card.primaryImageUrl, PrimaryImageUrl: (card as any).PrimaryImageUrl, exampleImages: (card as any).exampleImages, FlashcardExampleImages: (card as any).FlashcardExampleImages, originalCard: card, computedImageUrl: exampleImageUrl }) // 處理錄音完成 const handleRecordingComplete = useCallback(async (audioBlob: Blob) => { if (hasAnswered) return setIsEvaluating(true) setError(null) try { console.log('🎤 開始發音評估...', { flashcardId: card.id, referenceText: card.example, audioSize: `${(audioBlob.size / 1024).toFixed(1)} KB` }) const response = await speechAssessmentService.evaluatePronunciation( audioBlob, card.example, card.id, 'en-US' ) if (response.success && response.data) { setAssessmentResult(response.data) setHasAnswered(true) // 稍後自動提交結果(給用戶時間查看評分) setTimeout(() => { onAnswer(response.data!.confidenceLevel) }, 2000) } else { throw new Error(response.error || '發音評估失敗') } } catch (error) { const errorMessage = error instanceof Error ? error.message : '評估過程發生錯誤' setError(errorMessage) console.error('發音評估錯誤:', error) } finally { setIsEvaluating(false) } }, [hasAnswered, card.id, card.example, onAnswer]) // 處理跳過 const handleSkip = useCallback(() => { onSkip() }, [onSkip]) // 手動提交結果(如果自動提交被取消) const handleSubmitResult = useCallback(() => { if (assessmentResult && hasAnswered) { onAnswer(assessmentResult.confidenceLevel) } }, [assessmentResult, hasAnswered, onAnswer]) // 重新錄音 const handleRetry = useCallback(() => { setAssessmentResult(null) setHasAnswered(false) setError(null) }, []) return (
{/* 說明文字 */}

📸 看圖說出完整例句,AI 將評估你的發音表現

{/* 例句圖片 - 更顯著的顯示 */} {exampleImageUrl ? (
{`${card.word}

💡 根據上圖理解情境,然後大聲說出例句

) : (

⚠️ 此詞卡暫無例句圖片,請根據文字內容進行練習

)} {/* 目標例句 */}

目標例句

{card.example}

{card.exampleTranslation}

{/* 錄音區域 */} {!hasAnswered && !isEvaluating && (

💡 建議清晰地讀出完整句子,包含正確的語調和停頓

)} {/* 評估中狀態 */} {isEvaluating && (

AI 正在評估發音...

正在分析您的發音準確度、流暢度和語調 (約需 2-3 秒)

)} {/* 錯誤顯示 */} {error && (

❌ 評估失敗

{error}

)} {/* 評估結果顯示 */} {assessmentResult && hasAnswered && (

🎉 發音評估結果

{/* 總分顯示 */}
{Math.round(assessmentResult.scores.overall)}
總分
/ 100
{/* 詳細評分 */}
準確度
{Math.round(assessmentResult.scores.accuracy)}
流暢度
{Math.round(assessmentResult.scores.fluency)}
完整度
{Math.round(assessmentResult.scores.completeness)}
語調
{Math.round(assessmentResult.scores.prosody)}
{/* 語音識別結果 */} {assessmentResult.transcribedText && (
AI 識別的內容
{assessmentResult.transcribedText}
)} {/* 改善建議 */} {assessmentResult.feedback && assessmentResult.feedback.length > 0 && (
改善建議:
{assessmentResult.feedback.map((feedback, index) => (
{feedback}
))}
)} {/* 操作按鈕 */}
)} {/* 跳過按鈕 */} {!hasAnswered && !isEvaluating && (
)}
) }