feat: modernize frontend architecture with professional UI/UX design

Complete redesign of frontend interface with focus on usability, visual hierarchy, and professional appearance:

**Design System:**
- Implemented clean blue color theme (#3B82F6) with professional palette
- Created consistent spacing, shadows, and typography system
- Added reusable utility classes (page-header, section, status-badge-*)
- Removed excessive gradients and decorative effects

**Layout Architecture:**
- Redesigned main layout with 256px sidebar navigation
- Sidebar includes logo, navigation with descriptions, and user profile
- Main content area with search bar and scrollable content
- Replaced horizontal navigation with vertical sidebar pattern

**Page Redesigns:**
1. LoginPage: Split-screen design with branding (left) and clean form (right)
   - Feature highlights with icons and statistics
   - Mobile responsive design
   - Professional gradient background with subtle pattern

2. UploadPage: Added 3-step visual progress indicator
   - Better file organization with summary and status badges
   - Clear action bar with confirmation message
   - Improved file list presentation

3. ProcessingPage: Enhanced progress visualization
   - Large progress bar with percentage display
   - 4-column stats grid (Completed, Processing, Failed, Total)
   - Clean file status list with processing times

4. ResultsPage: Improved 5-column layout (2 for list, 3 for preview)
   - Added stats cards for accuracy, processing time, and text blocks
   - Better preview panel with detailed metrics
   - Export and translate action buttons

5. ExportPage: Better organization with 2-column layout
   - Visual format selection with icons (TXT, JSON, Excel, Markdown, PDF)
   - Improved form controls and option organization
   - Sticky preview sidebar showing current configuration

**Component Updates:**
- Updated Button component with proper variants
- Enhanced Card component with hover effects
- Maintained FileUpload component functionality
- Added lucide-react for modern iconography

**Technical Improvements:**
- Fixed Tailwind CSS v4 compatibility issues with @apply
- Removed decorative animations in favor of functional ones
- Improved accessibility with proper labels and ARIA attributes
- Better color contrast and readability

This redesign transforms the interface from a basic layout to a professional, enterprise-ready application with clear visual hierarchy and excellent usability.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
beabigegg
2025-11-12 23:54:44 +08:00
parent 69302144f5
commit 21bc2f92f1
13 changed files with 1361 additions and 490 deletions

View File

@@ -1,11 +1,25 @@
import { Outlet, NavLink } from 'react-router-dom'
import { Outlet, NavLink, useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useAuthStore } from '@/store/authStore'
import { apiClient } from '@/services/api'
import {
Upload,
Settings,
FileText,
Download,
Activity,
LogOut,
LayoutDashboard,
ChevronRight,
Bell,
Search
} from 'lucide-react'
export default function Layout() {
const { t } = useTranslation()
const navigate = useNavigate()
const logout = useAuthStore((state) => state.logout)
const user = useAuthStore((state) => state.user)
const handleLogout = () => {
apiClient.logout()
@@ -13,59 +27,113 @@ export default function Layout() {
}
const navLinks = [
{ to: '/upload', label: t('nav.upload') },
{ to: '/processing', label: t('nav.processing') },
{ to: '/results', label: t('nav.results') },
{ to: '/export', label: t('nav.export') },
{ to: '/settings', label: t('nav.settings') },
{ to: '/upload', label: t('nav.upload'), icon: Upload, description: '上傳檔案' },
{ to: '/processing', label: t('nav.processing'), icon: Activity, description: '處理進度' },
{ to: '/results', label: t('nav.results'), icon: FileText, description: '查看結果' },
{ to: '/export', label: t('nav.export'), icon: Download, description: '導出文件' },
{ to: '/settings', label: t('nav.settings'), icon: Settings, description: '系統設定' },
]
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b bg-card">
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-foreground">{t('app.title')}</h1>
<p className="text-sm text-muted-foreground">{t('app.subtitle')}</p>
<div className="flex h-screen bg-background overflow-hidden">
{/* Sidebar */}
<aside className="w-64 bg-sidebar text-sidebar-foreground flex flex-col border-r border-border/20">
{/* Logo */}
<div className="px-6 py-5 border-b border-border/20">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-primary/20 flex items-center justify-center">
<LayoutDashboard className="w-5 h-5 text-primary" />
</div>
<div>
<h1 className="font-semibold text-lg">{t('app.title')}</h1>
<p className="text-xs text-sidebar-foreground/60">{t('app.subtitle')}</p>
</div>
</div>
</div>
{/* Navigation */}
<nav className="flex-1 px-3 py-6 space-y-1 overflow-y-auto scrollbar-thin">
{navLinks.map((link) => (
<NavLink
key={link.to}
to={link.to}
className={({ isActive }) =>
`group flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors ${
isActive
? 'bg-primary text-white'
: 'text-sidebar-foreground/70 hover:text-sidebar-foreground hover:bg-white/5'
}`
}
>
{({ isActive }) => (
<>
<link.icon className={`w-5 h-5 flex-shrink-0 ${isActive ? 'text-white' : ''}`} />
<div className="flex-1 min-w-0">
<div className="text-sm font-medium">{link.label}</div>
<div className="text-xs opacity-60 truncate">{link.description}</div>
</div>
{isActive && <ChevronRight className="w-4 h-4 flex-shrink-0" />}
</>
)}
</NavLink>
))}
</nav>
{/* User section */}
<div className="px-3 py-4 border-t border-border/20">
{user && (
<div className="flex items-center gap-3 px-3 py-2 rounded-lg bg-white/5 mb-2">
<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>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium truncate">{user.username}</div>
<div className="text-xs text-sidebar-foreground/60"></div>
</div>
</div>
)}
<button
onClick={handleLogout}
className="px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors"
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"
>
{t('nav.logout')}
<LogOut className="w-5 h-5" />
<span>{t('nav.logout')}</span>
</button>
</div>
</header>
</aside>
{/* Navigation */}
<nav className="border-b bg-card">
<div className="container mx-auto px-4">
<ul className="flex space-x-1">
{navLinks.map((link) => (
<li key={link.to}>
<NavLink
to={link.to}
className={({ isActive }) =>
`block px-4 py-3 text-sm font-medium transition-colors ${
isActive
? 'text-primary border-b-2 border-primary'
: 'text-muted-foreground hover:text-foreground'
}`
}
>
{link.label}
</NavLink>
</li>
))}
</ul>
</div>
</nav>
{/* 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>
{/* Main Content */}
<main className="container mx-auto px-4 py-8">
<Outlet />
</main>
{/* 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>
</div>
)
}