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

298 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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 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>
)
}