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:
@@ -8,6 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { useToast } from '@/components/ui/toast'
|
||||
import { useUploadStore } from '@/store/uploadStore'
|
||||
import { apiClient } from '@/services/api'
|
||||
import { FileText, X, Upload, Trash2, CheckCircle2, ArrowRight } from 'lucide-react'
|
||||
|
||||
export default function UploadPage() {
|
||||
const { t } = useTranslation()
|
||||
@@ -71,69 +72,192 @@ export default function UploadPage() {
|
||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
const getFileIcon = (filename: string) => {
|
||||
const ext = filename.split('.').pop()?.toLowerCase()
|
||||
const colors = {
|
||||
pdf: 'text-red-500',
|
||||
doc: 'text-blue-500',
|
||||
docx: 'text-blue-500',
|
||||
ppt: 'text-orange-500',
|
||||
pptx: 'text-orange-500',
|
||||
jpg: 'text-green-500',
|
||||
jpeg: 'text-green-500',
|
||||
png: 'text-green-500',
|
||||
}
|
||||
return colors[ext as keyof typeof colors] || 'text-gray-500'
|
||||
}
|
||||
|
||||
const totalSize = selectedFiles.reduce((acc, file) => acc + file.size, 0)
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">{t('upload.title')}</h1>
|
||||
<div className="space-y-6">
|
||||
{/* Page Header */}
|
||||
<div className="page-header">
|
||||
<h1 className="page-title">{t('upload.title')}</h1>
|
||||
<p className="text-muted-foreground">
|
||||
選擇要進行 OCR 處理的檔案,支援圖片、PDF 和 Office 文件
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<FileUpload
|
||||
onFilesSelected={handleFilesSelected}
|
||||
disabled={uploadMutation.isPending}
|
||||
/>
|
||||
{/* Step Indicator */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex items-center justify-center w-10 h-10 rounded-full ${
|
||||
selectedFiles.length === 0 ? 'bg-primary text-white' : 'bg-success text-white'
|
||||
}`}>
|
||||
{selectedFiles.length === 0 ? '1' : <CheckCircle2 className="w-5 h-5" />}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-foreground">選擇檔案</div>
|
||||
<div className="text-xs text-muted-foreground">上傳要處理的文件</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArrowRight className="w-5 h-5 text-muted-foreground" />
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex items-center justify-center w-10 h-10 rounded-full ${
|
||||
selectedFiles.length > 0 ? 'bg-primary text-white' : 'bg-muted text-muted-foreground'
|
||||
}`}>
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<div className={`text-sm font-medium ${
|
||||
selectedFiles.length > 0 ? 'text-foreground' : 'text-muted-foreground'
|
||||
}`}>確認並上傳</div>
|
||||
<div className="text-xs text-muted-foreground">檢查並開始處理</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArrowRight className="w-5 h-5 text-muted-foreground" />
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-muted text-muted-foreground">
|
||||
3
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-muted-foreground">處理完成</div>
|
||||
<div className="text-xs text-muted-foreground">查看結果並導出</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Upload Area */}
|
||||
<div className="section">
|
||||
<FileUpload
|
||||
onFilesSelected={handleFilesSelected}
|
||||
disabled={uploadMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Selected Files Section */}
|
||||
{selectedFiles.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-lg">
|
||||
{t('upload.selectedFiles')} ({selectedFiles.length})
|
||||
</CardTitle>
|
||||
<Button variant="outline" size="sm" onClick={handleClearAll}>
|
||||
{t('upload.clearAll')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{selectedFiles.map((file, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-3 bg-muted rounded-md"
|
||||
>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">{file.name}</p>
|
||||
<p className="text-xs text-muted-foreground">{formatFileSize(file.size)}</p>
|
||||
<div className="space-y-4">
|
||||
{/* Summary Card */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-primary/10 rounded-lg">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-lg">
|
||||
{t('upload.selectedFiles')}
|
||||
</CardTitle>
|
||||
<p className="text-sm text-muted-foreground mt-0.5">
|
||||
已選擇 {selectedFiles.length} 個檔案,總大小 {formatFileSize(totalSize)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleRemoveFile(index)}
|
||||
disabled={uploadMutation.isPending}
|
||||
>
|
||||
{t('upload.removeFile')}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleClearAll}
|
||||
disabled={uploadMutation.isPending}
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
清空全部
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* Files Table */}
|
||||
<div className="space-y-2">
|
||||
{selectedFiles.map((file, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="group flex items-center gap-4 p-3 rounded-lg border border-border hover:bg-muted/50 transition-colors"
|
||||
>
|
||||
{/* File icon */}
|
||||
<div className={`p-2 rounded-lg bg-muted/50 ${getFileIcon(file.name)}`}>
|
||||
<FileText className="w-5 h-5" />
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-end gap-3">
|
||||
{/* File info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
{file.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatFileSize(file.size)} · {file.type || '未知類型'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Status badge */}
|
||||
<div className="status-badge-success">
|
||||
準備就緒
|
||||
</div>
|
||||
|
||||
{/* Remove button */}
|
||||
<button
|
||||
onClick={() => handleRemoveFile(index)}
|
||||
disabled={uploadMutation.isPending}
|
||||
className="p-2 rounded-lg text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title="移除檔案"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Action Bar */}
|
||||
<div className="flex items-center justify-between p-4 bg-card rounded-xl border border-border">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
請確認檔案無誤後點擊上傳按鈕開始處理
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleClearAll}
|
||||
disabled={uploadMutation.isPending}
|
||||
>
|
||||
{t('common.cancel')}
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleUpload} disabled={uploadMutation.isPending}>
|
||||
{uploadMutation.isPending ? t('upload.uploading') : t('upload.uploadButton')}
|
||||
<Button
|
||||
onClick={handleUpload}
|
||||
disabled={uploadMutation.isPending}
|
||||
className="gap-2"
|
||||
>
|
||||
{uploadMutation.isPending ? (
|
||||
<>
|
||||
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin"></div>
|
||||
{t('upload.uploading')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Upload className="w-4 h-4" />
|
||||
開始上傳並處理
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user