'use client' import { useState, useMemo, useCallback, useEffect } from 'react' import { ProtectedRoute } from '@/components/shared/ProtectedRoute' import { Navigation } from '@/components/shared/Navigation' import { WordPopup } from '@/components/word/WordPopup' import { BluePlayButton } from '@/components/shared/BluePlayButton' import { useToast } from '@/components/shared/Toast' import { flashcardsService } from '@/lib/services/flashcards' import { getLevelIndex } from '@/lib/utils/cefrUtils' import { useWordAnalysis } from '@/hooks/word/useWordAnalysis' import { API_CONFIG } from '@/lib/config/api' import Link from 'next/link' const MAX_MANUAL_INPUT_LENGTH = 300 interface GrammarCorrection { hasErrors: boolean; originalText: string; correctedText: string | null; corrections: Array<{ position: { start: number; end: number }; error: string; correction: string; type: string; explanation: string; severity: 'high' | 'medium' | 'low'; }>; confidenceScore: number; } function GenerateContent() { const toast = useToast() const { findWordAnalysis, getWordClass } = useWordAnalysis() const [textInput, setTextInput] = useState('') const userLevel = typeof window !== 'undefined' ? localStorage.getItem('userEnglishLevel') || 'A2' : 'A2' const [isAnalyzing, setIsAnalyzing] = useState(false) const [sentenceAnalysis, setSentenceAnalysis] = useState | null>(null) const [sentenceMeaning, setSentenceMeaning] = useState('') const [grammarCorrection, setGrammarCorrection] = useState(null) const [selectedIdiom, setSelectedIdiom] = useState(null) const [selectedWord, setSelectedWord] = useState(null) // UX 改善:追蹤分析狀態,避免輸入和結果不匹配 const [lastAnalyzedText, setLastAnalyzedText] = useState('') // 追蹤本次會話已保存的詞彙(防重複保存) const [savedWordsInSession, setSavedWordsInSession] = useState>(new Set()) // 獲取當前用戶 ID 用於快取隔離 const getCurrentUserId = useCallback(() => { const token = typeof window !== 'undefined' ? localStorage.getItem('auth_token') : null if (!token) return null try { // 解析 JWT token 獲取用戶 ID const payload = JSON.parse(atob(token.split('.')[1])) return payload.nameid || payload.sub || null } catch { return null } }, []) // localStorage 快取函數(用戶隔離) const saveAnalysisToCache = useCallback((cacheData: any) => { try { const userId = getCurrentUserId() if (!userId) return const cacheKey = `generate_analysis_cache_${userId}` localStorage.setItem(cacheKey, JSON.stringify({ ...cacheData, userId, timestamp: Date.now() })) } catch (error) { console.warn('無法保存分析快取:', error) } }, [getCurrentUserId]) const loadAnalysisFromCache = useCallback(() => { try { const userId = getCurrentUserId() if (!userId) return null const cacheKey = `generate_analysis_cache_${userId}` const cached = localStorage.getItem(cacheKey) if (!cached) return null const cacheData = JSON.parse(cached) // 驗證快取是否屬於當前用戶 if (cacheData.userId !== userId) { localStorage.removeItem(cacheKey) return null } // 檢查快取是否過期(24小時) const isExpired = Date.now() - cacheData.timestamp > 24 * 60 * 60 * 1000 if (isExpired) { localStorage.removeItem(cacheKey) return null } return cacheData } catch (error) { console.warn('無法載入分析快取:', error) return null } }, [getCurrentUserId]) const clearAnalysisCache = useCallback(() => { try { const userId = getCurrentUserId() if (!userId) return const cacheKey = `generate_analysis_cache_${userId}` localStorage.removeItem(cacheKey) } catch (error) { console.warn('無法清除分析快取:', error) } }, [getCurrentUserId]) // 組件載入時恢復快取的分析結果 useEffect(() => { const cached = loadAnalysisFromCache() if (cached) { setTextInput(cached.textInput || '') setLastAnalyzedText(cached.textInput || '') // 同步記錄上次分析的文本 setSentenceAnalysis(cached.sentenceAnalysis || null) setSentenceMeaning(cached.sentenceMeaning || '') setGrammarCorrection(cached.grammarCorrection || null) // 恢復已保存詞彙的狀態 if (cached.savedWordsInSession) { setSavedWordsInSession(new Set(cached.savedWordsInSession)) } console.log('✅ 已恢復快取的分析結果') } }, [loadAnalysisFromCache]) // 處理句子分析 const handleAnalyzeSentence = async () => { // 清除舊的分析快取 clearAnalysisCache() // 重置本次會話的已保存詞彙追蹤 setSavedWordsInSession(new Set()) setIsAnalyzing(true) try { // 獲取認證 token const token = typeof window !== 'undefined' ? localStorage.getItem('auth_token') : null const response = await fetch(`${API_CONFIG.BASE_URL}/api/ai/analyze-sentence`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token && { 'Authorization': `Bearer ${token}` }) }, body: JSON.stringify({ inputText: textInput, analysisMode: 'full', options: { includeGrammarCheck: true, includeVocabularyAnalysis: true, includeTranslation: true, includeIdiomDetection: true, includeExamples: true } }) }) if (!response.ok) { if (response.status === 401) { throw new Error('請先登入後再使用 AI 分析功能') } throw new Error(`API請求失敗: ${response.status}`) } const result = await response.json() if (!result.success || !result.data) { throw new Error('API回應格式錯誤') } const apiData = result.data.data const analysisData = { originalText: apiData.originalText, sentenceMeaning: apiData.sentenceMeaning, grammarCorrection: apiData.grammarCorrection, vocabularyAnalysis: apiData.vocabularyAnalysis, idioms: apiData.idioms || [], processingTime: result.processingTime } setSentenceAnalysis(analysisData) setSentenceMeaning(apiData.sentenceMeaning || '') if (apiData.grammarCorrection) { setGrammarCorrection({ hasErrors: apiData.grammarCorrection.hasErrors, originalText: textInput, correctedText: apiData.grammarCorrection.correctedText || textInput, corrections: apiData.grammarCorrection.corrections || [], confidenceScore: apiData.grammarCorrection.confidenceScore || 0.9 }) } // 保存分析結果到快取(包含已保存詞彙狀態) const cacheData = { textInput, sentenceAnalysis: analysisData, sentenceMeaning: apiData.sentenceMeaning || '', grammarCorrection: apiData.grammarCorrection ? { hasErrors: apiData.grammarCorrection.hasErrors, originalText: textInput, correctedText: apiData.grammarCorrection.correctedText || textInput, corrections: apiData.grammarCorrection.corrections || [], confidenceScore: apiData.grammarCorrection.confidenceScore || 1.0 } : null, savedWordsInSession: Array.from(savedWordsInSession) } saveAnalysisToCache(cacheData) // 記錄此次分析的文本 setLastAnalyzedText(textInput) } catch (error) { console.error('分析錯誤:', error) toast.error('分析過程中發生錯誤,請稍後再試。') } finally { setIsAnalyzing(false) } } // 語法修正處理 const handleAcceptCorrection = useCallback(() => { if (grammarCorrection?.correctedText) { setTextInput(grammarCorrection.correctedText) setGrammarCorrection(null) } }, [grammarCorrection?.correctedText]) const handleRejectCorrection = useCallback(() => { setGrammarCorrection(null) }, []) // 詞彙統計 const vocabularyStats = useMemo(() => { if (!sentenceAnalysis?.vocabularyAnalysis) { return { simpleCount: 0, moderateCount: 0, difficultCount: 0, idiomCount: 0 } } let simpleCount = 0, moderateCount = 0, difficultCount = 0 Object.values(sentenceAnalysis.vocabularyAnalysis).forEach((wordData: any) => { const cefr = wordData?.cefr || 'A1' const userIndex = getLevelIndex(userLevel) const wordIndex = getLevelIndex(cefr) if (userIndex > wordIndex) { simpleCount++ } else if (userIndex === wordIndex) { moderateCount++ } else { difficultCount++ } }) return { simpleCount, moderateCount, difficultCount, idiomCount: sentenceAnalysis.idioms?.length || 0 } }, [sentenceAnalysis, userLevel]) // 保存詞彙(帶重複檢查) const handleSaveWord = useCallback(async (word: string, analysis: any) => { try { // 檢查是否已在本次會話中保存過 if (savedWordsInSession.has(word.toLowerCase())) { const confirmSave = window.confirm( `詞彙「${word}」已在本次分析中保存過,確定要重複保存嗎?\n\n重複保存會創建完全相同的詞卡。` ) if (!confirmSave) { toast.info(`已取消保存「${word}」`) return { success: false, cancelled: true } } } const cardData = { word: word, translation: analysis.translation || '', definition: analysis.definition || '', pronunciation: analysis.pronunciation || `/${word}/`, partOfSpeech: analysis.partOfSpeech || 'noun', example: analysis.example || `Example sentence with ${word}.`, exampleTranslation: analysis.exampleTranslation, synonyms: analysis.synonyms ? JSON.stringify(analysis.synonyms) : undefined, cefr: analysis.cefr || 'A0' } const response = await flashcardsService.createFlashcard(cardData) if (response.success) { // 標記為已保存 setSavedWordsInSession(prev => new Set([...prev, word.toLowerCase()])) toast.success(`已成功將「${word}」保存到詞卡庫!`) return { success: true } } else { throw new Error(response.error || '保存失敗') } } catch (error) { const errorMessage = error instanceof Error ? error.message : '保存失敗' toast.error(`保存詞卡失敗: ${errorMessage}`) return { success: false, error: errorMessage } } }, [toast, savedWordsInSession]) return (
{/* 頁面標題和程度指示器 */}

AI 智能生成詞卡

你的程度 {userLevel}
{/* 輸入區域 */}

輸入英文文本