'use client' import { useState, useEffect, use } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { Navigation } from '@/components/shared/Navigation' import { ProtectedRoute } from '@/components/shared/ProtectedRoute' import { useToast } from '@/components/shared/Toast' import { flashcardsService, type Flashcard } from '@/lib/services/flashcards' import { imageGenerationService } from '@/lib/services/imageGeneration' import { getPartOfSpeechDisplay, getCEFRColor, getFlashcardImageUrl } from '@/lib/utils/flashcardUtils' import { useTTSPlayer } from '@/hooks/shared/useTTSPlayer' import { useFlashcardDetailData } from '@/hooks/flashcards/useFlashcardDetailData' import { TTSButton } from '@/components/shared/TTSButton' interface FlashcardDetailPageProps { params: Promise<{ id: string }> } export default function FlashcardDetailPage({ params }: FlashcardDetailPageProps) { const { id } = use(params) return ( ) } function FlashcardDetailContent({ cardId }: { cardId: string }) { const router = useRouter() const toast = useToast() // 使用數據管理Hook const { flashcard, loading, error, isEditing, editedCard, setFlashcard, setIsEditing, setEditedCard } = useFlashcardDetailData(cardId) // 圖片生成狀態 const [isGeneratingImage, setIsGeneratingImage] = useState(false) const [generationProgress, setGenerationProgress] = useState('') // 使用TTS Hook const { isPlayingWord, isPlayingExample, toggleWordTTS, toggleExampleTTS } = useTTSPlayer() // 處理收藏切換 const handleToggleFavorite = async () => { if (!flashcard) return try { // 假資料處理 if (flashcard.id.startsWith('mock')) { const updated = { ...flashcard, isFavorite: !flashcard.isFavorite } setFlashcard(updated) setEditedCard(updated) toast.success(`${flashcard.isFavorite ? '已取消收藏' : '已加入收藏'}「${flashcard.word}」`) return } // 真實API調用 const result = await flashcardsService.toggleFavorite(flashcard.id) if (result.success) { setFlashcard(prev => prev ? { ...prev, isFavorite: !prev.isFavorite } : null) toast.success(`${flashcard.isFavorite ? '已取消收藏' : '已加入收藏'}「${flashcard.word}」`) } } catch (error) { toast.error('操作失敗,請重試') } } // 處理編輯保存 const handleSaveEdit = async () => { if (!flashcard || !editedCard) return try { // 假資料處理 if (flashcard.id.startsWith('mock')) { setFlashcard(editedCard) setIsEditing(false) toast.success('詞卡更新成功!') return } // 真實API調用 const result = await flashcardsService.updateFlashcard(flashcard.id, { word: editedCard.word, translation: editedCard.translation, definition: editedCard.definition, pronunciation: editedCard.pronunciation, partOfSpeech: editedCard.partOfSpeech, example: editedCard.example, exampleTranslation: editedCard.exampleTranslation, cefr: editedCard.cefr }) if (result.success) { setFlashcard(editedCard) setIsEditing(false) toast.success('詞卡更新成功!') } else { toast.error(result.error || '更新失敗') } } catch (error) { toast.error('更新失敗,請重試') } } // 處理刪除 const handleDelete = async () => { if (!flashcard) return if (!confirm(`確定要刪除詞卡「${flashcard.word}」嗎?`)) { return } try { // 假資料處理 if (flashcard.id.startsWith('mock')) { toast.success('詞卡已刪除(模擬)') router.push('/flashcards') return } // 真實API調用 const result = await flashcardsService.deleteFlashcard(flashcard.id) if (result.success) { toast.success('詞卡已刪除') router.push('/flashcards') } else { toast.error(result.error || '刪除失敗') } } catch (error) { toast.error('刪除失敗,請重試') } } // 處理圖片生成 const handleGenerateImage = async () => { if (!flashcard || isGeneratingImage) return try { setIsGeneratingImage(true) setGenerationProgress('啟動生成中...') toast.info(`開始為「${flashcard.word}」生成例句圖片...`) const generateResult = await imageGenerationService.generateImage(flashcard.id) if (!generateResult.success || !generateResult.data) { throw new Error(generateResult.error || '啟動生成失敗') } const requestId = generateResult.data.requestId setGenerationProgress('Gemini 生成描述中...') const finalStatus = await imageGenerationService.pollUntilComplete( requestId, (status: any) => { const stage = status.stages.gemini.status === 'completed' ? 'Replicate 生成圖片中...' : 'Gemini 生成描述中...' setGenerationProgress(stage) }, 5 ) if (finalStatus.overallStatus === 'completed') { setGenerationProgress('生成完成,載入中...') // 重新載入詞卡資料 const result = await flashcardsService.getFlashcard(cardId) if (result.success && result.data) { setFlashcard(result.data) setEditedCard(result.data) } toast.success(`「${flashcard.word}」的例句圖片生成完成!`) } else { throw new Error('圖片生成未完成') } } catch (error: any) { toast.error(`圖片生成失敗: ${error.message || '未知錯誤'}`) } finally { setIsGeneratingImage(false) setGenerationProgress('') } } if (loading) { return (
載入中...
) } if (error || !flashcard) { return (
{error || '詞卡不存在'}
) } return (
{/* 導航欄 */}
{/* 主要詞卡內容 - 學習功能風格 */}
{/* CEFR標籤 - 右上角 */}
{flashcard.cefr || 'A1'}
{/* 標題區 */}

{flashcard.word}

{getPartOfSpeechDisplay(flashcard.partOfSpeech)} {flashcard.pronunciation}
{/* 學習統計 */}
{flashcard.masteryLevel}%
掌握程度
{flashcard.timesReviewed}
複習次數
{Math.ceil((new Date(flashcard.nextReviewDate).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24))}
天後複習
{/* 內容區 - 學習卡片風格 */}
{/* 翻譯區塊 */}

中文翻譯

{isEditing ? ( setEditedCard((prev: any) => ({ ...prev, translation: e.target.value }))} className="w-full p-3 border border-green-300 rounded-lg focus:ring-2 focus:ring-green-500 bg-white" placeholder="輸入中文翻譯" /> ) : (

{flashcard.translation}

)}
{/* 定義區塊 */}

英文定義

{isEditing ? (