dramaling-vocab-learning/frontend/app/generate-demo/page.tsx

389 lines
16 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState } from 'react'
import { ClickableText } from '@/components/ClickableText'
export default function GenerateDemoPage() {
const [mode, setMode] = useState<'manual' | 'screenshot'>('manual')
const [textInput, setTextInput] = useState('')
const [extractionType, setExtractionType] = useState<'vocabulary' | 'smart'>('vocabulary')
const [cardCount, setCardCount] = useState(10)
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [showAnalysisView, setShowAnalysisView] = useState(false)
const [sentenceAnalysis, setSentenceAnalysis] = useState<any>(null)
const [sentenceMeaning, setSentenceMeaning] = useState('')
const [usageCount, setUsageCount] = useState(0)
const [isPremium] = useState(false)
// 模擬分析後的句子資料
const mockSentenceAnalysis = {
meaning: "他在我們的會議中提出了這件事,但沒有人同意。這句話表達了在會議中有人提出某個議題或想法,但得不到其他與會者的認同。",
words: {
"he": {
word: "he",
translation: "他",
definition: "Used to refer to a male person or animal",
partOfSpeech: "pronoun",
pronunciation: "/hiː/",
synonyms: ["him", "that man"],
isPhrase: false
},
"brought": {
word: "brought",
translation: "帶來、提出",
definition: "Past tense of bring; to take or carry something to a place",
partOfSpeech: "verb",
pronunciation: "/brɔːt/",
synonyms: ["carried", "took", "delivered"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "在這個句子中,\"brought up\" 是一個片語,意思是\"提出話題\",而不是單純的\"帶來\""
}
},
"this": {
word: "this",
translation: "這個",
definition: "Used to indicate something near or just mentioned",
partOfSpeech: "pronoun",
pronunciation: "/ðɪs/",
synonyms: ["that", "it"],
isPhrase: false
},
"thing": {
word: "thing",
translation: "事情、東西",
definition: "An object, fact, or situation",
partOfSpeech: "noun",
pronunciation: "/θɪŋ/",
synonyms: ["object", "matter", "item"],
isPhrase: false
},
"up": {
word: "up",
translation: "向上",
definition: "Toward a higher place or position",
partOfSpeech: "adverb",
pronunciation: "/ʌp/",
synonyms: ["upward", "above"],
isPhrase: true,
phraseInfo: {
phrase: "bring up",
meaning: "提出(話題)、養育",
warning: "\"up\" 在這裡是片語 \"bring up\" 的一部分,不是單獨的\"向上\"的意思"
}
},
"during": {
word: "during",
translation: "在...期間",
definition: "Throughout the course or duration of",
partOfSpeech: "preposition",
pronunciation: "/ˈdjʊərɪŋ/",
synonyms: ["throughout", "while"],
isPhrase: false
},
"our": {
word: "our",
translation: "我們的",
definition: "Belonging to us",
partOfSpeech: "pronoun",
pronunciation: "/aʊər/",
synonyms: ["ours"],
isPhrase: false
},
"meeting": {
word: "meeting",
translation: "會議",
definition: "An organized gathering of people for discussion",
partOfSpeech: "noun",
pronunciation: "/ˈmiːtɪŋ/",
synonyms: ["conference", "assembly", "gathering"],
isPhrase: false
},
"and": {
word: "and",
translation: "和、而且",
definition: "Used to connect words or clauses",
partOfSpeech: "conjunction",
pronunciation: "/ænd/",
synonyms: ["plus", "also"],
isPhrase: false
},
"no": {
word: "no",
translation: "沒有",
definition: "Not any; not one",
partOfSpeech: "determiner",
pronunciation: "/nəʊ/",
synonyms: ["none", "zero"],
isPhrase: false
},
"one": {
word: "one",
translation: "一個人、任何人",
definition: "A single person or thing",
partOfSpeech: "pronoun",
pronunciation: "/wʌn/",
synonyms: ["someone", "anybody"],
isPhrase: false
},
"agreed": {
word: "agreed",
translation: "同意",
definition: "Past tense of agree; to have the same opinion",
partOfSpeech: "verb",
pronunciation: "/əˈɡriːd/",
synonyms: ["consented", "accepted", "approved"],
isPhrase: false
}
}
}
// 處理句子分析
const handleAnalyzeSentence = async () => {
if (!textInput.trim()) return
// 檢查使用次數限制
if (!isPremium && usageCount >= 5) {
alert('❌ 免費用戶 3 小時內只能分析 5 次句子,請稍後再試或升級到付費版本')
return
}
setIsAnalyzing(true)
try {
// 模擬 API 調用
await new Promise(resolve => setTimeout(resolve, 2000))
setSentenceAnalysis(mockSentenceAnalysis.words)
setSentenceMeaning(mockSentenceAnalysis.meaning)
setShowAnalysisView(true)
setUsageCount(prev => prev + 1)
} catch (error) {
console.error('Error analyzing sentence:', error)
alert('分析句子時發生錯誤,請稍後再試')
} finally {
setIsAnalyzing(false)
}
}
return (
<div className="min-h-screen bg-gray-50">
{/* Navigation */}
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex items-center">
<h1 className="text-2xl font-bold text-blue-600">DramaLing</h1>
</div>
<div className="flex items-center space-x-4">
<span className="text-gray-600">Demo </span>
</div>
</div>
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{!showAnalysisView ? (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold mb-8">AI - </h1>
{/* Input Mode Selection */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="grid grid-cols-2 gap-4">
<button
onClick={() => setMode('manual')}
className={`p-4 rounded-lg border-2 transition-all ${
mode === 'manual'
? 'border-blue-600 bg-blue-50'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="text-2xl mb-2"></div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"></div>
</button>
<button
onClick={() => setMode('screenshot')}
disabled={!isPremium}
className={`p-4 rounded-lg border-2 transition-all relative ${
mode === 'screenshot'
? 'border-blue-600 bg-blue-50'
: isPremium
? 'border-gray-200 hover:border-gray-300'
: 'border-gray-200 bg-gray-100 cursor-not-allowed opacity-60'
}`}
>
<div className="text-2xl mb-2">📷</div>
<div className="font-semibold"></div>
<div className="text-sm text-gray-600 mt-1"> (Phase 2)</div>
{!isPremium && (
<div className="absolute top-2 right-2 px-2 py-1 bg-yellow-100 text-yellow-700 text-xs rounded-full">
</div>
)}
</button>
</div>
</div>
{/* Content Input */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<div>
<h2 className="text-lg font-semibold mb-4"></h2>
<textarea
value={textInput}
onChange={(e) => {
const value = e.target.value
if (mode === 'manual' && value.length > 50) {
return // 阻止輸入超過50字
}
setTextInput(value)
}}
placeholder={mode === 'manual'
? "輸入英文句子最多50字..."
: "貼上您想要學習的英文文本,例如影劇對話、文章段落..."
}
className={`w-full h-40 px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent outline-none resize-none ${
mode === 'manual' && textInput.length >= 45 ? 'border-yellow-400' :
mode === 'manual' && textInput.length >= 50 ? 'border-red-400' : 'border-gray-300'
}`}
/>
<div className="mt-2 flex justify-between text-sm">
<span className={`${
mode === 'manual' && textInput.length >= 45 ? 'text-yellow-600' :
mode === 'manual' && textInput.length >= 50 ? 'text-red-600' : 'text-gray-600'
}`}>
{mode === 'manual' ? `最多 50 字元 • 目前:${textInput.length} 字元` : `最多 5000 字元 • 目前:${textInput.length} 字元`}
</span>
{mode === 'manual' && textInput.length > 40 && (
<span className={textInput.length >= 50 ? 'text-red-600' : 'text-yellow-600'}>
{textInput.length >= 50 ? '已達上限!' : `還可輸入 ${50 - textInput.length} 字元`}
</span>
)}
</div>
{/* 預設示例 */}
{!textInput && (
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
<div className="text-sm text-gray-700 mb-2">
<strong></strong>
</div>
<button
onClick={() => setTextInput("He brought this thing up during our meeting and no one agreed.")}
className="text-sm text-blue-600 hover:text-blue-800 bg-blue-50 px-3 py-1 rounded border border-blue-200"
>
使He brought this thing up during our meeting and no one agreed.
</button>
</div>
)}
</div>
</div>
{/* 新的按鈕區域 */}
<div className="space-y-4">
{/* 分析句子按鈕 */}
<button
onClick={handleAnalyzeSentence}
disabled={isAnalyzing || (mode === 'manual' && (!textInput || textInput.length > 50)) || (mode === 'screenshot')}
className="w-full bg-blue-600 text-white py-4 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isAnalyzing ? (
<span className="flex items-center justify-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
...
</span>
) : (
'🔍 分析句子(點擊查詢單字)'
)}
</button>
{/* 使用次數顯示 */}
<div className="text-center text-sm text-gray-600">
{isPremium ? (
<span className="text-green-600">🌟 使</span>
) : (
<span className={usageCount >= 4 ? 'text-red-600' : usageCount >= 3 ? 'text-yellow-600' : 'text-gray-600'}>
使 {usageCount}/5 (3)
{usageCount >= 5 && <span className="block text-red-500 mt-1"></span>}
</span>
)}
</div>
</div>
</div>
) : (
/* 句子分析視圖 */
<div className="max-w-4xl mx-auto">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold"></h1>
<button
onClick={() => setShowAnalysisView(false)}
className="text-gray-600 hover:text-gray-900"
>
</button>
</div>
{/* 原始句子顯示 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="bg-gray-50 p-4 rounded-lg mb-4">
<div className="text-lg leading-relaxed">
{textInput}
</div>
</div>
<h3 className="text-base font-semibold mb-2"></h3>
<div className="text-gray-700 leading-relaxed">
{sentenceMeaning}
</div>
</div>
{/* 互動式文字 */}
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
<h2 className="text-lg font-semibold mb-4"></h2>
<div className="p-4 bg-blue-50 rounded-lg border-l-4 border-blue-400 mb-4">
<p className="text-sm text-blue-800">
💡 <strong>使</strong>
</p>
</div>
<div className="p-6 bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
<ClickableText
text={textInput}
analysis={sentenceAnalysis}
onWordClick={(word, analysis) => {
console.log('Clicked word:', word, analysis)
}}
/>
</div>
</div>
{/* 操作按鈕 */}
<div className="bg-white rounded-xl shadow-sm p-6">
<div className="flex gap-4">
<button
onClick={() => setShowAnalysisView(false)}
className="flex-1 bg-gray-200 text-gray-700 py-3 rounded-lg font-medium hover:bg-gray-300 transition-colors"
>
🔄
</button>
<button
onClick={() => alert('詞卡生成功能開發中...')}
className="flex-1 bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
📖
</button>
</div>
</div>
</div>
)}
</div>
</div>
)
}