684 lines
27 KiB
TypeScript
684 lines
27 KiB
TypeScript
'use client'
|
||
|
||
import { useState, useEffect } from 'react'
|
||
import { API_URLS } from '@/lib/config/api'
|
||
import { Navigation } from '@/components/shared/Navigation'
|
||
import { useAuth } from '@/contexts/AuthContext'
|
||
|
||
interface UserProfile {
|
||
id: string
|
||
email: string
|
||
displayName: string | null
|
||
avatarUrl: string | null
|
||
subscriptionType: string
|
||
createdAt: string
|
||
}
|
||
|
||
interface UserSettings {
|
||
dailyGoal: number
|
||
reminderTime: string
|
||
reminderEnabled: boolean
|
||
difficultyPreference: string
|
||
autoPlayAudio: boolean
|
||
showPronunciation: boolean
|
||
}
|
||
|
||
interface LanguageLevel {
|
||
value: string
|
||
label: string
|
||
description: string
|
||
examples: string[]
|
||
}
|
||
|
||
type TabType = 'profile' | 'settings' | 'level'
|
||
|
||
export default function ProfilePage() {
|
||
const { updateUser } = useAuth()
|
||
const [activeTab, setActiveTab] = useState<TabType>('profile')
|
||
const [profile, setProfile] = useState<UserProfile | null>(null)
|
||
const [settings, setSettings] = useState<UserSettings | null>(null)
|
||
const [userLevel, setUserLevel] = useState('A2')
|
||
const [isLoading, setIsLoading] = useState(true)
|
||
const [error, setError] = useState<string | null>(null)
|
||
const [isSaving, setIsSaving] = useState(false)
|
||
|
||
// 編輯表單狀態
|
||
const [editForm, setEditForm] = useState({
|
||
displayName: '',
|
||
dailyGoal: 20,
|
||
reminderEnabled: true,
|
||
difficultyPreference: 'balanced',
|
||
autoPlayAudio: true,
|
||
showPronunciation: true
|
||
})
|
||
|
||
// 英語程度定義
|
||
const levels: LanguageLevel[] = [
|
||
{
|
||
value: 'A1',
|
||
label: 'A1 - 初學者',
|
||
description: '能理解基本詞彙和簡單句子',
|
||
examples: ['hello', 'good', 'house', 'eat', 'happy']
|
||
},
|
||
{
|
||
value: 'A2',
|
||
label: 'A2 - 基礎',
|
||
description: '能處理日常對話和常見主題',
|
||
examples: ['important', 'difficult', 'interesting', 'beautiful', 'understand']
|
||
},
|
||
{
|
||
value: 'B1',
|
||
label: 'B1 - 中級',
|
||
description: '能理解清楚標準語言的要點',
|
||
examples: ['analyze', 'opportunity', 'environment', 'responsibility', 'development']
|
||
},
|
||
{
|
||
value: 'B2',
|
||
label: 'B2 - 中高級',
|
||
description: '能理解複雜文本的主要內容',
|
||
examples: ['sophisticated', 'implications', 'comprehensive', 'substantial', 'methodology']
|
||
},
|
||
{
|
||
value: 'C1',
|
||
label: 'C1 - 高級',
|
||
description: '能流利表達,理解含蓄意思',
|
||
examples: ['meticulous', 'predominantly', 'intricate', 'corroborate', 'paradigm']
|
||
},
|
||
{
|
||
value: 'C2',
|
||
label: 'C2 - 精通',
|
||
description: '接近母語水平',
|
||
examples: ['ubiquitous', 'ephemeral', 'perspicacious', 'multifarious', 'idiosyncratic']
|
||
}
|
||
]
|
||
|
||
const tabs = [
|
||
{ id: 'profile' as TabType, label: '個人資料', icon: '👤' },
|
||
// { id: 'settings' as TabType, label: '學習設定', icon: '⚙️' },
|
||
{ id: 'level' as TabType, label: '英語程度', icon: '🎯' }
|
||
]
|
||
|
||
// 載入資料
|
||
useEffect(() => {
|
||
loadData()
|
||
}, [])
|
||
|
||
const loadData = async () => {
|
||
setIsLoading(true)
|
||
setError(null)
|
||
|
||
try {
|
||
const token = localStorage.getItem('auth_token')
|
||
if (!token) {
|
||
setError('請先登入')
|
||
return
|
||
}
|
||
|
||
console.log('🚀 載入個人檔案資料...')
|
||
|
||
// 並行載入所有資料
|
||
const [profileResponse, settingsResponse] = await Promise.all([
|
||
fetch(API_URLS.auth('/profile'), {
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
}),
|
||
fetch(API_URLS.auth('/settings'), {
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
})
|
||
])
|
||
|
||
if (!profileResponse.ok || !settingsResponse.ok) {
|
||
if (profileResponse.status === 401 || settingsResponse.status === 401) {
|
||
localStorage.removeItem('auth_token')
|
||
setError('登入已過期,請重新登入')
|
||
} else {
|
||
setError('載入個人資料失敗')
|
||
}
|
||
return
|
||
}
|
||
|
||
const [profileData, settingsData] = await Promise.all([
|
||
profileResponse.json(),
|
||
settingsResponse.json()
|
||
])
|
||
|
||
if (profileData.success && settingsData.success) {
|
||
setProfile(profileData.data)
|
||
setSettings(settingsData.data)
|
||
|
||
// 初始化編輯表單
|
||
setEditForm({
|
||
displayName: profileData.data.displayName || '',
|
||
dailyGoal: settingsData.data.dailyGoal || 20,
|
||
reminderEnabled: settingsData.data.reminderEnabled ?? true,
|
||
difficultyPreference: settingsData.data.difficultyPreference || 'balanced',
|
||
autoPlayAudio: settingsData.data.autoPlayAudio ?? true,
|
||
showPronunciation: settingsData.data.showPronunciation ?? true
|
||
})
|
||
|
||
console.log('✅ 個人檔案資料載入成功')
|
||
} else {
|
||
setError('載入資料失敗')
|
||
}
|
||
|
||
// 載入英語程度
|
||
const savedLevel = localStorage.getItem('userEnglishLevel')
|
||
if (savedLevel) {
|
||
setUserLevel(savedLevel)
|
||
}
|
||
} catch (error) {
|
||
console.error('載入個人資料錯誤:', error)
|
||
setError(error instanceof Error ? error.message : '載入失敗')
|
||
} finally {
|
||
setIsLoading(false)
|
||
}
|
||
}
|
||
|
||
const handleSaveProfile = async () => {
|
||
if (!profile) return
|
||
|
||
setIsSaving(true)
|
||
try {
|
||
const token = localStorage.getItem('auth_token')
|
||
if (!token) {
|
||
setError('請先登入')
|
||
return
|
||
}
|
||
|
||
const response = await fetch(API_URLS.auth('/profile'), {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
body: JSON.stringify({ displayName: editForm.displayName })
|
||
})
|
||
|
||
if (response.ok) {
|
||
await loadData() // 重新載入資料
|
||
|
||
// 同步更新全域 AuthContext 中的用戶資料
|
||
updateUser({ displayName: editForm.displayName })
|
||
|
||
console.log('✅ 個人資料更新成功')
|
||
} else {
|
||
setError('儲存失敗')
|
||
}
|
||
} catch (error) {
|
||
setError('儲存失敗')
|
||
} finally {
|
||
setIsSaving(false)
|
||
}
|
||
}
|
||
|
||
const handleSaveSettings = async () => {
|
||
setIsSaving(true)
|
||
try {
|
||
const token = localStorage.getItem('auth_token')
|
||
if (!token) {
|
||
setError('請先登入')
|
||
return
|
||
}
|
||
|
||
const response = await fetch(API_URLS.auth('/settings'), {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
body: JSON.stringify({
|
||
dailyGoal: editForm.dailyGoal,
|
||
reminderEnabled: editForm.reminderEnabled,
|
||
difficultyPreference: editForm.difficultyPreference,
|
||
autoPlayAudio: editForm.autoPlayAudio,
|
||
showPronunciation: editForm.showPronunciation
|
||
})
|
||
})
|
||
|
||
if (response.ok) {
|
||
await loadData() // 重新載入資料
|
||
console.log('✅ 學習設定更新成功')
|
||
} else {
|
||
setError('儲存失敗')
|
||
}
|
||
} catch (error) {
|
||
setError('儲存失敗')
|
||
} finally {
|
||
setIsSaving(false)
|
||
}
|
||
}
|
||
|
||
const handleSaveLevel = () => {
|
||
localStorage.setItem('userEnglishLevel', userLevel)
|
||
console.log('✅ 英語程度已保存:', userLevel)
|
||
}
|
||
|
||
const handleLogout = () => {
|
||
localStorage.removeItem('auth_token')
|
||
localStorage.removeItem('userEnglishLevel')
|
||
window.location.href = '/login'
|
||
}
|
||
|
||
// 載入狀態
|
||
if (isLoading) {
|
||
return (
|
||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||
<Navigation />
|
||
<div className="py-8">
|
||
<div className="max-w-4xl mx-auto px-4">
|
||
<div className="bg-white rounded-xl shadow-lg p-8 text-center">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||
<h2 className="text-xl font-semibold text-gray-700">載入個人資料中...</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
// 錯誤狀態
|
||
if (error) {
|
||
const isAuthError = error.includes('登入') || error.includes('認證')
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||
<Navigation />
|
||
<div className="py-8">
|
||
<div className="max-w-4xl mx-auto px-4">
|
||
<div className="bg-white rounded-xl shadow-lg p-8 text-center">
|
||
<div className={`text-4xl mb-4 ${isAuthError ? 'text-yellow-500' : 'text-red-500'}`}>
|
||
{isAuthError ? '🔒' : '⚠️'}
|
||
</div>
|
||
<h2 className={`text-xl font-semibold mb-2 ${isAuthError ? 'text-yellow-700' : 'text-red-700'}`}>
|
||
{isAuthError ? '需要重新登入' : '載入失敗'}
|
||
</h2>
|
||
<p className="text-gray-600 mb-6">{error}</p>
|
||
|
||
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||
{isAuthError ? (
|
||
<button
|
||
onClick={() => window.location.href = '/login'}
|
||
className="px-8 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-semibold"
|
||
>
|
||
前往登入
|
||
</button>
|
||
) : (
|
||
<button
|
||
onClick={loadData}
|
||
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||
>
|
||
重新載入
|
||
</button>
|
||
)}
|
||
|
||
<button
|
||
onClick={() => window.location.href = '/'}
|
||
className="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
|
||
>
|
||
回到首頁
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!profile || !settings) {
|
||
return null
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||
<Navigation />
|
||
|
||
<div className="py-8">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
{/* 頁面標題 */}
|
||
<div className="mb-8">
|
||
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">個人檔案</h1>
|
||
<p className="text-gray-600">管理您的帳戶資訊和學習偏好設定</p>
|
||
</div>
|
||
|
||
{/* 分頁導航 */}
|
||
<div className="mb-8">
|
||
<div className="border-b border-gray-200 bg-white rounded-t-xl">
|
||
<nav className="flex space-x-8 px-6 py-4">
|
||
{tabs.map((tab) => (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => setActiveTab(tab.id)}
|
||
className={`flex items-center space-x-2 py-2 px-1 border-b-2 font-medium text-sm transition-colors ${
|
||
activeTab === tab.id
|
||
? 'border-blue-500 text-blue-600'
|
||
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
<span>{tab.icon}</span>
|
||
<span>{tab.label}</span>
|
||
</button>
|
||
))}
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 分頁內容 */}
|
||
<div className="bg-white rounded-xl rounded-t-none shadow-lg">
|
||
{/* 個人資料分頁 */}
|
||
{activeTab === 'profile' && (
|
||
<div className="p-8">
|
||
<div className="max-w-2xl mx-auto">
|
||
{/* 用戶資訊卡片 */}
|
||
<div className="text-center mb-8">
|
||
<div className="w-24 h-24 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center mx-auto mb-4">
|
||
{profile.avatarUrl ? (
|
||
<img
|
||
src={profile.avatarUrl}
|
||
alt="用戶頭像"
|
||
className="w-full h-full rounded-full object-cover"
|
||
/>
|
||
) : (
|
||
<span className="text-3xl text-white">
|
||
{(profile.displayName || profile.email)[0].toUpperCase()}
|
||
</span>
|
||
)}
|
||
</div>
|
||
|
||
<h2 className="text-2xl font-semibold text-gray-900 mb-1">
|
||
{profile.displayName || '用戶'}
|
||
</h2>
|
||
<p className="text-gray-500 mb-3">{profile.email}</p>
|
||
|
||
<span className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${
|
||
profile.subscriptionType === 'premium'
|
||
? 'bg-yellow-100 text-yellow-800'
|
||
: 'bg-gray-100 text-gray-800'
|
||
}`}>
|
||
{profile.subscriptionType === 'premium' ? '🌟 Premium' : '🆓 免費版'}
|
||
</span>
|
||
</div>
|
||
|
||
{/* 編輯資料 */}
|
||
<div className="space-y-6">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
顯示名稱
|
||
</label>
|
||
<div className="flex space-x-3">
|
||
<input
|
||
type="text"
|
||
value={editForm.displayName}
|
||
onChange={(e) => setEditForm(prev => ({ ...prev, displayName: e.target.value }))}
|
||
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||
placeholder="請輸入顯示名稱"
|
||
/>
|
||
<button
|
||
onClick={handleSaveProfile}
|
||
disabled={isSaving}
|
||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium disabled:opacity-50"
|
||
>
|
||
{isSaving ? '儲存中...' : '儲存'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 帳戶資訊 */}
|
||
<div className="bg-gray-50 rounded-lg p-4">
|
||
<h3 className="font-semibold text-gray-900 mb-3">帳戶資訊</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||
<div>
|
||
<span className="text-gray-600">用戶 ID</span>
|
||
<p className="font-mono text-gray-800 text-xs break-all">{profile.id}</p>
|
||
</div>
|
||
<div>
|
||
<span className="text-gray-600">加入時間</span>
|
||
<p className="font-medium text-gray-800">
|
||
{new Date(profile.createdAt).toLocaleDateString('zh-TW')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 登出 */}
|
||
<div className="border-t pt-6">
|
||
<button
|
||
onClick={handleLogout}
|
||
className="w-full px-4 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors font-medium"
|
||
>
|
||
登出帳戶
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 學習設定分頁 */}
|
||
{activeTab === 'settings' && (
|
||
<div className="p-8">
|
||
<div className="max-w-2xl mx-auto">
|
||
<div className="space-y-6">
|
||
{/* 每日目標 */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||
每日學習目標
|
||
</label>
|
||
<div className="bg-gray-50 rounded-lg p-4">
|
||
<div className="flex items-center justify-between mb-3">
|
||
<span className="text-2xl font-bold text-blue-600">{editForm.dailyGoal}</span>
|
||
<span className="text-sm text-gray-600">張詞卡/天</span>
|
||
</div>
|
||
<input
|
||
type="range"
|
||
min="1"
|
||
max="100"
|
||
value={editForm.dailyGoal}
|
||
onChange={(e) => setEditForm(prev => ({ ...prev, dailyGoal: Number(e.target.value) }))}
|
||
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider"
|
||
/>
|
||
<div className="flex justify-between text-xs text-gray-500 mt-1">
|
||
<span>1</span>
|
||
<span>50</span>
|
||
<span>100</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 學習偏好 */}
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||
學習難度偏好
|
||
</label>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||
{[
|
||
{ value: 'conservative', label: '保守', desc: '較簡單', color: 'green' },
|
||
{ value: 'balanced', label: '均衡', desc: '適中', color: 'blue' },
|
||
{ value: 'aggressive', label: '積極', desc: '較難', color: 'purple' }
|
||
].map(option => (
|
||
<label
|
||
key={option.value}
|
||
className={`p-3 border-2 rounded-lg cursor-pointer transition-all ${
|
||
editForm.difficultyPreference === option.value
|
||
? `border-${option.color}-500 bg-${option.color}-50`
|
||
: 'border-gray-200 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
<input
|
||
type="radio"
|
||
name="difficulty"
|
||
value={option.value}
|
||
checked={editForm.difficultyPreference === option.value}
|
||
onChange={(e) => setEditForm(prev => ({ ...prev, difficultyPreference: e.target.value }))}
|
||
className="sr-only"
|
||
/>
|
||
<div className="text-center">
|
||
<div className="font-semibold text-gray-900">{option.label}</div>
|
||
<div className="text-sm text-gray-600">{option.desc}</div>
|
||
</div>
|
||
</label>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 音頻和顯示設定 */}
|
||
<div className="space-y-4">
|
||
<h3 className="font-medium text-gray-900">音頻和顯示設定</h3>
|
||
|
||
{[
|
||
{
|
||
key: 'reminderEnabled' as keyof typeof editForm,
|
||
label: '複習提醒',
|
||
desc: '接收學習提醒通知'
|
||
},
|
||
{
|
||
key: 'autoPlayAudio' as keyof typeof editForm,
|
||
label: '自動播放音頻',
|
||
desc: '顯示詞卡時自動播放發音'
|
||
},
|
||
{
|
||
key: 'showPronunciation' as keyof typeof editForm,
|
||
label: '顯示發音標示',
|
||
desc: '在詞卡上顯示音標'
|
||
}
|
||
].map(setting => (
|
||
<div key={setting.key} className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
||
<div>
|
||
<span className="font-medium text-gray-900">{setting.label}</span>
|
||
<p className="text-sm text-gray-600">{setting.desc}</p>
|
||
</div>
|
||
<label className="relative inline-flex items-center cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
checked={editForm[setting.key] as boolean}
|
||
onChange={(e) => setEditForm(prev => ({ ...prev, [setting.key]: e.target.checked }))}
|
||
className="sr-only peer"
|
||
/>
|
||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||
</label>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 儲存按鈕 */}
|
||
<div className="pt-4">
|
||
<button
|
||
onClick={handleSaveSettings}
|
||
disabled={isSaving}
|
||
className="w-full px-4 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium disabled:opacity-50"
|
||
>
|
||
{isSaving ? '儲存中...' : '儲存學習設定'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 英語程度分頁 */}
|
||
{activeTab === 'level' && (
|
||
<div className="p-8">
|
||
<div className="max-w-3xl mx-auto">
|
||
<div className="text-center mb-8">
|
||
<h2 className="text-2xl font-semibold text-gray-900 mb-2">🎯 英語程度設定</h2>
|
||
<p className="text-gray-600">
|
||
選擇您的英語程度,系統將為您提供個人化的詞彙學習建議
|
||
</p>
|
||
</div>
|
||
|
||
{/* 程度選擇 */}
|
||
<div className="space-y-4 mb-8">
|
||
{levels.map(level => (
|
||
<label
|
||
key={level.value}
|
||
className={`block p-6 border-2 rounded-xl cursor-pointer transition-all hover:shadow-md ${
|
||
userLevel === level.value
|
||
? 'border-blue-500 bg-blue-50 shadow-md'
|
||
: 'border-gray-200 hover:border-gray-300'
|
||
}`}
|
||
>
|
||
<input
|
||
type="radio"
|
||
name="level"
|
||
value={level.value}
|
||
checked={userLevel === level.value}
|
||
onChange={(e) => setUserLevel(e.target.value)}
|
||
className="sr-only"
|
||
/>
|
||
|
||
<div className="flex items-start justify-between">
|
||
<div className="flex-1">
|
||
<div className="font-bold text-xl text-gray-800 mb-2">
|
||
{level.label}
|
||
</div>
|
||
<div className="text-gray-600 mb-3">
|
||
{level.description}
|
||
</div>
|
||
<div className="flex flex-wrap gap-2">
|
||
{level.examples.map(example => (
|
||
<span
|
||
key={example}
|
||
className="px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm"
|
||
>
|
||
{example}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{userLevel === level.value && (
|
||
<div className="ml-4">
|
||
<div className="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
|
||
<svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</label>
|
||
))}
|
||
</div>
|
||
|
||
{/* 學習效果預覽 */}
|
||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 p-6 rounded-xl mb-6">
|
||
<h3 className="font-bold text-lg text-blue-800 mb-3">
|
||
💡 您的個人化學習效果
|
||
</h3>
|
||
<div className="grid md:grid-cols-2 gap-4">
|
||
<div>
|
||
<h4 className="font-semibold text-blue-700 mb-2">重點學習範圍</h4>
|
||
<p className="text-blue-600">
|
||
系統將為您標記比目前程度({userLevel})高1-2級的重點詞彙
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<h4 className="font-semibold text-blue-700 mb-2">學習建議</h4>
|
||
<p className="text-blue-600 text-sm">
|
||
專注學習具有挑戰性但不會太困難的詞彙,提升學習效率
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 儲存按鈕 */}
|
||
<button
|
||
onClick={handleSaveLevel}
|
||
className="w-full py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium text-lg"
|
||
>
|
||
保存英語程度設定
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
} |