feat: 優化用戶體驗與界面設計
- 修復用戶名稱更新後導航欄不即時更新的問題 - 新增 AuthContext.updateUser 方法同步全域用戶狀態 - 隱藏導航欄通知鈴鐺按鈕 - 隱藏儀表板導航項目 - 隱藏個人資料頁面的學習設定分頁 - 調整登出按鈕顏色為較溫和的灰色 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6b66c56adc
commit
b7c695bb4e
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Navigation } from '@/components/shared/Navigation'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
|
||||
interface UserProfile {
|
||||
id: string
|
||||
|
|
@ -31,6 +32,7 @@ interface LanguageLevel {
|
|||
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)
|
||||
|
|
@ -91,7 +93,7 @@ export default function ProfilePage() {
|
|||
|
||||
const tabs = [
|
||||
{ id: 'profile' as TabType, label: '個人資料', icon: '👤' },
|
||||
{ id: 'settings' as TabType, label: '學習設定', icon: '⚙️' },
|
||||
// { id: 'settings' as TabType, label: '學習設定', icon: '⚙️' },
|
||||
{ id: 'level' as TabType, label: '英語程度', icon: '🎯' }
|
||||
]
|
||||
|
||||
|
|
@ -198,6 +200,10 @@ export default function ProfilePage() {
|
|||
|
||||
if (response.ok) {
|
||||
await loadData() // 重新載入資料
|
||||
|
||||
// 同步更新全域 AuthContext 中的用戶資料
|
||||
updateUser({ displayName: editForm.displayName })
|
||||
|
||||
console.log('✅ 個人資料更新成功')
|
||||
} else {
|
||||
setError('儲存失敗')
|
||||
|
|
@ -442,7 +448,7 @@ export default function ProfilePage() {
|
|||
<div className="border-t pt-6">
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full px-4 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors font-medium"
|
||||
className="w-full px-4 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors font-medium"
|
||||
>
|
||||
登出帳戶
|
||||
</button>
|
||||
|
|
@ -670,29 +676,6 @@ export default function ProfilePage() {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 快速操作區 */}
|
||||
<div className="mt-8 bg-white rounded-xl shadow-lg p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">快速操作</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{[
|
||||
{ href: '/review', icon: '📚', label: '開始複習', desc: '複習詞卡' },
|
||||
{ href: '/generate', icon: '➕', label: '新增詞卡', desc: '建立內容' },
|
||||
{ href: '/flashcards', icon: '📋', label: '管理詞卡', desc: '編輯詞卡' },
|
||||
{ href: '/stats', icon: '📊', label: '學習統計', desc: '查看進度' }
|
||||
].map(action => (
|
||||
<button
|
||||
key={action.href}
|
||||
onClick={() => window.location.href = action.href}
|
||||
className="p-4 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors text-center"
|
||||
>
|
||||
<div className="text-2xl mb-1">{action.icon}</div>
|
||||
<div className="font-medium text-gray-900 text-sm">{action.label}</div>
|
||||
<div className="text-xs text-gray-500">{action.desc}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ export function Navigation({ showExitLearning = false, onExitLearning }: Navigat
|
|||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
||||
|
||||
const navItems = [
|
||||
{ href: '/dashboard', label: '儀表板' },
|
||||
// { href: '/dashboard', label: '儀表板' },
|
||||
{ href: '/flashcards', label: '詞卡' },
|
||||
{ href: '/review', label: '複習' },
|
||||
{ href: '/generate', label: 'AI 生成' }
|
||||
{ href: '/generate', label: 'AI詞卡' }
|
||||
]
|
||||
|
||||
return (
|
||||
|
|
@ -74,11 +74,11 @@ export function Navigation({ showExitLearning = false, onExitLearning }: Navigat
|
|||
) : (
|
||||
<>
|
||||
{/* 通知按鈕 - 桌面和手機都顯示 */}
|
||||
<button className="p-2 text-gray-600 hover:text-gray-900">
|
||||
{/* <button className="p-2 text-gray-600 hover:text-gray-900">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</button>
|
||||
</button> */}
|
||||
|
||||
{/* 用戶資訊 - 只在桌面版顯示 */}
|
||||
<div className="hidden md:flex items-center space-x-3">
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ interface AuthContextType extends AuthState {
|
|||
register: (username: string, email: string, password: string) => Promise<{ success: boolean; error?: string }>
|
||||
logout: () => void
|
||||
checkAuth: () => Promise<boolean>
|
||||
updateUser: (updatedUser: Partial<User>) => void
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined)
|
||||
|
|
@ -139,12 +140,27 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
|||
return true
|
||||
}
|
||||
|
||||
const updateUser = (updatedUser: Partial<User>) => {
|
||||
if (!state.user) return
|
||||
|
||||
const newUser = { ...state.user, ...updatedUser }
|
||||
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
user: newUser
|
||||
}))
|
||||
|
||||
// 同時更新 localStorage 中的用戶資料
|
||||
localStorage.setItem('user_data', JSON.stringify(newUser))
|
||||
}
|
||||
|
||||
const contextValue: AuthContextType = {
|
||||
...state,
|
||||
login,
|
||||
register,
|
||||
logout,
|
||||
checkAuth,
|
||||
updateUser,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in New Issue