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:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user