257 lines
12 KiB
TypeScript
257 lines
12 KiB
TypeScript
'use client'
|
||
|
||
import Link from 'next/link'
|
||
import { useState } from 'react'
|
||
import { ProtectedRoute } from '@/components/ProtectedRoute'
|
||
import { Navigation } from '@/components/Navigation'
|
||
import { useAuth } from '@/contexts/AuthContext'
|
||
|
||
function DashboardContent() {
|
||
const [activeTab, setActiveTab] = useState('overview')
|
||
const { user, logout } = useAuth()
|
||
|
||
// Mock data
|
||
const stats = {
|
||
totalWords: 234,
|
||
wordsToday: 12,
|
||
streak: 7,
|
||
accuracy: 85,
|
||
todayReview: 23,
|
||
completedToday: 15
|
||
}
|
||
|
||
const recentWords = [
|
||
{ id: 1, word: 'negotiate', translation: '協商', status: 'learned' },
|
||
{ id: 2, word: 'accomplish', translation: '完成', status: 'learning' },
|
||
{ id: 3, word: 'perspective', translation: '觀點', status: 'new' },
|
||
{ id: 4, word: 'substantial', translation: '大量的', status: 'learned' },
|
||
]
|
||
|
||
const cardSets = [
|
||
{ id: 1, name: '美劇經典台詞', count: 45, progress: 60 },
|
||
{ id: 2, name: '商務英文必備', count: 30, progress: 30 },
|
||
{ id: 3, name: '日常對話', count: 25, progress: 80 },
|
||
]
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50">
|
||
{/* Navigation */}
|
||
<Navigation />
|
||
|
||
{/* Main Content */}
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
{/* Welcome Section */}
|
||
<div className="bg-white rounded-xl shadow-sm p-6 mb-6">
|
||
<h2 className="text-2xl font-bold text-gray-900 mb-2">歡迎回來,{user?.displayName || user?.username}! 🌟</h2>
|
||
<p className="text-gray-600">今天有 {stats.todayReview} 個單字等待複習,繼續加油!</p>
|
||
<div className="mt-4 flex gap-3">
|
||
<Link
|
||
href="/learn"
|
||
className="bg-primary text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-hover transition-colors"
|
||
>
|
||
開始今日學習
|
||
</Link>
|
||
<Link
|
||
href="/generate"
|
||
className="border border-primary text-primary px-6 py-2 rounded-lg font-medium hover:bg-primary-light transition-colors"
|
||
>
|
||
AI 生成新詞卡
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stats Grid */}
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<span className="text-gray-600 text-sm">總學習單字</span>
|
||
<svg className="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||
</svg>
|
||
</div>
|
||
<div className="text-3xl font-bold text-gray-900">{stats.totalWords}</div>
|
||
<div className="text-sm text-green-600 mt-1">+{stats.wordsToday} 今日</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<span className="text-gray-600 text-sm">連續學習</span>
|
||
<svg className="w-5 h-5 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z" />
|
||
</svg>
|
||
</div>
|
||
<div className="text-3xl font-bold text-gray-900">{stats.streak} 天</div>
|
||
<div className="text-sm text-orange-600 mt-1">🔥 保持良好!</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<span className="text-gray-600 text-sm">正確率</span>
|
||
<svg className="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
</svg>
|
||
</div>
|
||
<div className="text-3xl font-bold text-gray-900">{stats.accuracy}%</div>
|
||
<div className="text-sm text-gray-600 mt-1">上周 82%</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-xl shadow-sm p-6">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<span className="text-gray-600 text-sm">今日進度</span>
|
||
<svg className="w-5 h-5 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
||
</svg>
|
||
</div>
|
||
<div className="text-3xl font-bold text-gray-900">{stats.completedToday}/{stats.todayReview}</div>
|
||
<div className="w-full bg-gray-200 rounded-full h-2 mt-2">
|
||
<div
|
||
className="bg-purple-500 h-2 rounded-full"
|
||
style={{ width: `${(stats.completedToday / stats.todayReview) * 100}%` }}
|
||
></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content Tabs */}
|
||
<div className="bg-white rounded-xl shadow-sm">
|
||
<div className="border-b border-gray-200">
|
||
<nav className="-mb-px flex space-x-8 px-6" aria-label="Tabs">
|
||
<button
|
||
onClick={() => setActiveTab('overview')}
|
||
className={`py-4 px-1 border-b-2 font-medium text-sm ${
|
||
activeTab === 'overview'
|
||
? 'border-primary text-primary'
|
||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
最近學習
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('sets')}
|
||
className={`py-4 px-1 border-b-2 font-medium text-sm ${
|
||
activeTab === 'sets'
|
||
? 'border-primary text-primary'
|
||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
我的卡組
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('progress')}
|
||
className={`py-4 px-1 border-b-2 font-medium text-sm ${
|
||
activeTab === 'progress'
|
||
? 'border-primary text-primary'
|
||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
學習統計
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<div className="p-6">
|
||
{activeTab === 'overview' && (
|
||
<div>
|
||
<h3 className="text-lg font-semibold mb-4">最近學習的單字</h3>
|
||
<div className="space-y-3">
|
||
{recentWords.map(word => (
|
||
<div key={word.id} className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50">
|
||
<div className="flex items-center space-x-4">
|
||
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||
<div>
|
||
<div className="font-semibold">{word.word}</div>
|
||
<div className="text-sm text-gray-600">{word.translation}</div>
|
||
</div>
|
||
</div>
|
||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||
word.status === 'learned' ? 'bg-green-100 text-green-800' :
|
||
word.status === 'learning' ? 'bg-yellow-100 text-yellow-800' :
|
||
'bg-gray-100 text-gray-800'
|
||
}`}>
|
||
{word.status === 'learned' ? '已掌握' :
|
||
word.status === 'learning' ? '學習中' : '新詞'}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{activeTab === 'sets' && (
|
||
<div>
|
||
<h3 className="text-lg font-semibold mb-4">我的卡組</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{cardSets.map(set => (
|
||
<div key={set.id} className="border rounded-lg p-4 hover:shadow-md transition-shadow">
|
||
<h4 className="font-semibold mb-2">{set.name}</h4>
|
||
<p className="text-sm text-gray-600 mb-3">{set.count} 個單字</p>
|
||
<div className="w-full bg-gray-200 rounded-full h-2 mb-3">
|
||
<div
|
||
className="bg-primary h-2 rounded-full"
|
||
style={{ width: `${set.progress}%` }}
|
||
></div>
|
||
</div>
|
||
<Link
|
||
href={`/flashcards/${set.id}`}
|
||
className="text-primary text-sm font-medium hover:text-primary-hover"
|
||
>
|
||
繼續學習 →
|
||
</Link>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{activeTab === 'progress' && (
|
||
<div>
|
||
<h3 className="text-lg font-semibold mb-4">學習統計</h3>
|
||
<div className="bg-gray-50 rounded-lg p-6">
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||
<div>
|
||
<div className="text-sm text-gray-600">本周學習</div>
|
||
<div className="text-2xl font-bold mt-1">89 個</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-sm text-gray-600">本月學習</div>
|
||
<div className="text-2xl font-bold mt-1">312 個</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-sm text-gray-600">平均每日</div>
|
||
<div className="text-2xl font-bold mt-1">15 個</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-sm text-gray-600">最佳紀錄</div>
|
||
<div className="text-2xl font-bold mt-1">32 個</div>
|
||
</div>
|
||
</div>
|
||
<div className="mt-6 pt-6 border-t">
|
||
<div className="text-sm text-gray-600 mb-2">每日學習趨勢(過去7天)</div>
|
||
<div className="flex items-end space-x-2 h-20">
|
||
{[15, 20, 18, 25, 22, 30, 12].map((value, index) => (
|
||
<div key={index} className="flex-1">
|
||
<div
|
||
className="bg-primary rounded-t"
|
||
style={{ height: `${(value / 30) * 100}%` }}
|
||
></div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default function DashboardPage() {
|
||
return (
|
||
<ProtectedRoute>
|
||
<DashboardContent />
|
||
</ProtectedRoute>
|
||
)
|
||
} |