298 lines
13 KiB
TypeScript
298 lines
13 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
import Link from 'next/link'
|
||
|
||
export default function GeneratePage() {
|
||
const [mode, setMode] = useState<'text' | 'theme'>('text')
|
||
const [textInput, setTextInput] = useState('')
|
||
const [selectedTheme, setSelectedTheme] = useState('')
|
||
const [difficulty, setDifficulty] = useState('intermediate')
|
||
const [cardCount, setCardCount] = useState(10)
|
||
const [isGenerating, setIsGenerating] = useState(false)
|
||
const [generatedCards, setGeneratedCards] = useState<any[]>([])
|
||
const [showPreview, setShowPreview] = useState(false)
|
||
|
||
const themes = [
|
||
{ id: 'daily', name: '日常對話', icon: '🗣️' },
|
||
{ id: 'business', name: '商務英語', icon: '💼' },
|
||
{ id: 'tv', name: '美劇經典', icon: '📺' },
|
||
{ id: 'movie', name: '電影台詞', icon: '🎬' },
|
||
{ id: 'academic', name: '學術英語', icon: '🎓' },
|
||
{ id: 'travel', name: '旅遊英語', icon: '✈️' },
|
||
]
|
||
|
||
const mockGeneratedCards = [
|
||
{
|
||
id: 1,
|
||
word: 'negotiate',
|
||
partOfSpeech: 'verb',
|
||
pronunciation: '/nɪˈɡoʊʃieɪt/',
|
||
translation: '協商、談判',
|
||
definition: 'To discuss something with someone in order to reach an agreement',
|
||
example: 'We need to negotiate a better deal with our suppliers.',
|
||
exampleTranslation: '我們需要與供應商協商更好的交易。',
|
||
difficulty: 'intermediate'
|
||
},
|
||
{
|
||
id: 2,
|
||
word: 'perspective',
|
||
partOfSpeech: 'noun',
|
||
pronunciation: '/pərˈspektɪv/',
|
||
translation: '觀點、看法',
|
||
definition: 'A particular way of considering something',
|
||
example: 'From my perspective, this is the best solution.',
|
||
exampleTranslation: '從我的角度來看,這是最好的解決方案。',
|
||
difficulty: 'intermediate'
|
||
},
|
||
{
|
||
id: 3,
|
||
word: 'accomplish',
|
||
partOfSpeech: 'verb',
|
||
pronunciation: '/əˈkɒmplɪʃ/',
|
||
translation: '完成、達成',
|
||
definition: 'To finish something successfully or to achieve something',
|
||
example: 'She accomplished her goal of running a marathon.',
|
||
exampleTranslation: '她完成了跑馬拉松的目標。',
|
||
difficulty: 'intermediate'
|
||
}
|
||
]
|
||
|
||
const handleGenerate = () => {
|
||
setIsGenerating(true)
|
||
// Simulate AI generation
|
||
setTimeout(() => {
|
||
setGeneratedCards(mockGeneratedCards)
|
||
setShowPreview(true)
|
||
setIsGenerating(false)
|
||
}, 2000)
|
||
}
|
||
|
||
const handleSaveCards = () => {
|
||
// Mock save action
|
||
alert('詞卡已保存到您的卡組!')
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50">
|
||
{/* Navigation */}
|
||
<nav className="bg-white shadow-sm border-b">
|
||
<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 space-x-8">
|
||
<Link href="/dashboard" className="text-2xl font-bold text-primary">DramaLing</Link>
|
||
<div className="hidden md:flex space-x-6">
|
||
<Link href="/dashboard" className="text-gray-600 hover:text-gray-900">儀表板</Link>
|
||
<Link href="/flashcards" className="text-gray-600 hover:text-gray-900">詞卡</Link>
|
||
<Link href="/learn" className="text-gray-600 hover:text-gray-900">學習</Link>
|
||
<Link href="/generate" className="text-gray-900 font-medium">AI 生成</Link>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
{!showPreview ? (
|
||
<div className="max-w-4xl mx-auto">
|
||
<h1 className="text-3xl font-bold mb-8">AI 智能生成詞卡</h1>
|
||
|
||
{/* 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('text')}
|
||
className={`p-4 rounded-lg border-2 transition-all ${
|
||
mode === 'text'
|
||
? 'border-primary bg-primary-light'
|
||
: '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('theme')}
|
||
className={`p-4 rounded-lg border-2 transition-all ${
|
||
mode === 'theme'
|
||
? 'border-primary bg-primary-light'
|
||
: '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>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content Input */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
|
||
{mode === 'text' ? (
|
||
<div>
|
||
<h2 className="text-lg font-semibold mb-4">輸入文本內容</h2>
|
||
<textarea
|
||
value={textInput}
|
||
onChange={(e) => setTextInput(e.target.value)}
|
||
placeholder="貼上您想要學習的英文文本,例如影劇對話、文章段落..."
|
||
className="w-full h-40 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent outline-none resize-none"
|
||
/>
|
||
<div className="mt-2 text-sm text-gray-600">
|
||
最多 5000 字元 • 目前:{textInput.length} 字元
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div>
|
||
<h2 className="text-lg font-semibold mb-4">選擇學習主題</h2>
|
||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||
{themes.map((theme) => (
|
||
<button
|
||
key={theme.id}
|
||
onClick={() => setSelectedTheme(theme.id)}
|
||
className={`p-4 rounded-lg border-2 transition-all ${
|
||
selectedTheme === theme.id
|
||
? 'border-primary bg-primary-light'
|
||
: 'border-gray-200 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
<div className="text-2xl mb-1">{theme.icon}</div>
|
||
<div className="font-medium text-sm">{theme.name}</div>
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Generation Settings */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
|
||
<h2 className="text-lg font-semibold mb-4">生成設定</h2>
|
||
<div className="space-y-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
難度等級
|
||
</label>
|
||
<select
|
||
value={difficulty}
|
||
onChange={(e) => setDifficulty(e.target.value)}
|
||
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent outline-none"
|
||
>
|
||
<option value="beginner">初級</option>
|
||
<option value="intermediate">中級</option>
|
||
<option value="advanced">高級</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
生成數量:{cardCount} 個
|
||
</label>
|
||
<input
|
||
type="range"
|
||
min="5"
|
||
max="20"
|
||
value={cardCount}
|
||
onChange={(e) => setCardCount(Number(e.target.value))}
|
||
className="w-full"
|
||
/>
|
||
<div className="flex justify-between text-xs text-gray-500 mt-1">
|
||
<span>5</span>
|
||
<span>20</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Generate Button */}
|
||
<button
|
||
onClick={handleGenerate}
|
||
disabled={isGenerating || (mode === 'text' && !textInput) || (mode === 'theme' && !selectedTheme)}
|
||
className="w-full bg-primary text-white py-4 rounded-lg font-semibold hover:bg-primary-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
{isGenerating ? (
|
||
<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>
|
||
AI 正在生成中...
|
||
</span>
|
||
) : (
|
||
'🤖 開始生成詞卡'
|
||
)}
|
||
</button>
|
||
</div>
|
||
) : (
|
||
/* Preview Generated Cards */
|
||
<div className="max-w-6xl mx-auto">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h1 className="text-3xl font-bold">生成結果預覽</h1>
|
||
<button
|
||
onClick={() => setShowPreview(false)}
|
||
className="text-gray-600 hover:text-gray-900"
|
||
>
|
||
← 返回
|
||
</button>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-semibold">已生成 {generatedCards.length} 個詞卡</h2>
|
||
<div className="space-x-3">
|
||
<button className="text-primary hover:text-primary-hover font-medium">
|
||
重新生成
|
||
</button>
|
||
<button
|
||
onClick={handleSaveCards}
|
||
className="bg-primary text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-hover transition-colors"
|
||
>
|
||
保存到卡組
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{generatedCards.map((card) => (
|
||
<div key={card.id} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
||
<div className="flex items-start justify-between mb-3">
|
||
<div>
|
||
<h3 className="text-lg font-semibold">{card.word}</h3>
|
||
<div className="text-sm text-gray-600">
|
||
{card.partOfSpeech} • {card.pronunciation}
|
||
</div>
|
||
</div>
|
||
<button className="text-red-500 hover:text-red-700">
|
||
×
|
||
</button>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div>
|
||
<div className="text-sm font-medium text-gray-700">翻譯</div>
|
||
<div className="text-sm">{card.translation}</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-sm font-medium text-gray-700">定義</div>
|
||
<div className="text-sm text-gray-600">{card.definition}</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-sm font-medium text-gray-700">例句</div>
|
||
<div className="text-sm text-gray-600">{card.example}</div>
|
||
<div className="text-sm text-gray-500 mt-1">{card.exampleTranslation}</div>
|
||
</div>
|
||
</div>
|
||
<div className="mt-3 pt-3 border-t">
|
||
<button className="text-primary text-sm hover:text-primary-hover">
|
||
編輯詞卡
|
||
</button>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
} |