feat: simplify login page UX and add i18n English support

- Redesign LoginPage with minimal professional style
  - Remove animated gradient backgrounds and floating orbs
  - Remove marketing claims (99% accuracy, enterprise-grade)
  - Center login form with clean card design
- Add multi-language support (zh-TW, en-US)
  - Create LanguageSwitcher component in sidebar
  - Add en-US.json translation file
  - Persist language preference in localStorage
- Remove unused top header bar with search
- Move language switcher to sidebar user section

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-12 12:49:48 +08:00
parent 1f18010040
commit d5bc311757
10 changed files with 666 additions and 217 deletions

View File

@@ -0,0 +1,45 @@
import { useTranslation } from 'react-i18next'
import { Globe } from 'lucide-react'
const languages = [
{ code: 'zh-TW', label: '繁體中文' },
{ code: 'en-US', label: 'English' },
]
export default function LanguageSwitcher() {
const { i18n } = useTranslation()
const handleLanguageChange = (langCode: string) => {
i18n.changeLanguage(langCode)
localStorage.setItem('tool-ocr-language', langCode)
}
const currentLang = languages.find((lang) => lang.code === i18n.language) || languages[0]
return (
<div className="relative group">
<button
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-sidebar-foreground/70
hover:text-sidebar-foreground hover:bg-white/5 rounded-lg transition-colors"
>
<Globe className="w-5 h-5" />
<span>{currentLang.label}</span>
</button>
{/* Dropdown */}
<div className="absolute left-0 bottom-full mb-1 py-1 bg-card border border-border rounded-lg shadow-lg
opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all z-50 min-w-[140px]">
{languages.map((lang) => (
<button
key={lang.code}
onClick={() => handleLanguageChange(lang.code)}
className={`w-full px-3 py-2 text-sm text-left hover:bg-muted transition-colors
${i18n.language === lang.code ? 'text-primary font-medium' : 'text-foreground'}`}
>
{lang.label}
</button>
))}
</div>
</div>
)
}

View File

@@ -2,6 +2,7 @@ import { Outlet, NavLink, useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useAuthStore } from '@/store/authStore'
import { apiClientV2 } from '@/services/apiV2'
import LanguageSwitcher from '@/components/LanguageSwitcher'
import {
Upload,
Settings,
@@ -11,8 +12,6 @@ import {
LogOut,
LayoutDashboard,
ChevronRight,
Bell,
Search,
History,
Shield
} from 'lucide-react'
@@ -96,9 +95,9 @@ export default function Layout() {
</nav>
{/* User section */}
<div className="px-3 py-4 border-t border-border/20">
<div className="px-3 py-4 border-t border-border/20 space-y-2">
{user && (
<div className="flex items-center gap-3 px-3 py-2 rounded-lg bg-white/5 mb-2">
<div className="flex items-center gap-3 px-3 py-2 rounded-lg bg-white/5">
<div className="w-8 h-8 rounded-full bg-primary flex items-center justify-center text-white font-semibold text-sm flex-shrink-0">
{user.username.charAt(0).toUpperCase()}
</div>
@@ -108,6 +107,9 @@ export default function Layout() {
</div>
</div>
)}
<div className="px-3">
<LanguageSwitcher />
</div>
<button
onClick={handleLogout}
className="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sidebar-foreground/70 hover:text-sidebar-foreground hover:bg-white/5 transition-colors text-sm"
@@ -119,37 +121,11 @@ export default function Layout() {
</aside>
{/* Main content area */}
<div className="flex-1 flex flex-col overflow-hidden">
{/* Top bar */}
<header className="bg-card border-b border-border px-6 py-4">
<div className="flex items-center justify-between">
{/* Search bar - placeholder for future use */}
<div className="flex-1 max-w-2xl">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<input
type="search"
placeholder="搜尋檔案或功能..."
className="w-full pl-10 pr-4 py-2 bg-background border border-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"
/>
</div>
</div>
{/* Notifications */}
<button className="ml-4 p-2 rounded-lg hover:bg-muted transition-colors relative">
<Bell className="w-5 h-5 text-muted-foreground" />
<span className="absolute top-1 right-1 w-2 h-2 bg-destructive rounded-full"></span>
</button>
</div>
</header>
{/* Page content */}
<main className="flex-1 overflow-y-auto bg-background p-6 scrollbar-thin">
<div className="max-w-7xl mx-auto">
<Outlet />
</div>
</main>
</div>
<main className="flex-1 overflow-y-auto bg-background p-6 scrollbar-thin">
<div className="max-w-7xl mx-auto">
<Outlet />
</div>
</main>
</div>
)
}