import { useEffect } from 'react' import { useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { useQuery, useMutation } from '@tanstack/react-query' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Progress } from '@/components/ui/progress' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { useToast } from '@/components/ui/toast' import { useUploadStore } from '@/store/uploadStore' import { apiClient } from '@/services/api' import { Play, CheckCircle, FileText, AlertCircle, Clock, Activity, Loader2 } from 'lucide-react' export default function ProcessingPage() { const { t } = useTranslation() const navigate = useNavigate() const { toast } = useToast() const { batchId, files } = useUploadStore() // Start OCR processing const processOCRMutation = useMutation({ mutationFn: () => apiClient.processOCR({ batch_id: batchId! }), onSuccess: () => { toast({ title: '開始處理', description: 'OCR 處理已開始', variant: 'success', }) }, onError: (error: any) => { toast({ title: t('errors.processingFailed'), description: error.response?.data?.detail || t('errors.networkError'), variant: 'destructive', }) }, }) // Poll batch status const { data: batchStatus } = useQuery({ queryKey: ['batchStatus', batchId], queryFn: () => apiClient.getBatchStatus(batchId!), enabled: !!batchId, refetchInterval: (query) => { const data = query.state.data if (!data) return 2000 // Stop polling if completed or failed if (data.batch.status === 'completed' || data.batch.status === 'failed') { return false } return 2000 // Poll every 2 seconds }, }) // Auto-redirect when completed useEffect(() => { if (batchStatus?.batch.status === 'completed') { setTimeout(() => { navigate('/results') }, 1000) } }, [batchStatus?.batch.status, navigate]) const handleStartProcessing = () => { processOCRMutation.mutate() } const handleViewResults = () => { navigate('/results') } const getStatusBadge = (status: string) => { switch (status) { case 'completed': return {t('processing.completed')} case 'processing': return {t('processing.processing')} case 'failed': return {t('processing.failed')} default: return {t('processing.pending')} } } // Show helpful message when no batch is selected if (!batchId) { return (
{t('processing.title')}

{t('processing.noBatchMessage', { defaultValue: '尚未選擇任何批次。請先上傳檔案以建立批次。' })}

) } const isProcessing = batchStatus?.batch.status === 'processing' const isCompleted = batchStatus?.batch.status === 'completed' const isPending = !batchStatus || batchStatus.batch.status === 'pending' return (
{/* Page Header */}

{t('processing.title')}

批次 ID: {batchId} · 共 {files.length} 個檔案

{isCompleted && (
處理完成
)} {isProcessing && (
處理中
)}
{/* Overall Progress */}
{t('processing.progress')}
{batchStatus && getStatusBadge(batchStatus.batch.status)}
{/* Progress bar */}
{t('processing.status')} {batchStatus?.batch.progress_percentage || 0}%
{/* Stats */} {batchStatus && (

已完成

{batchStatus.files.filter((f) => f.status === 'completed').length}

處理中

{batchStatus.files.filter((f) => f.status === 'processing').length}

失敗

{batchStatus.files.filter((f) => f.status === 'failed').length}

總計

{batchStatus.files.length}

)} {/* Action buttons */} {(isPending || isCompleted) && (
{isPending && ( )} {isCompleted && ( )}
)}
{/* File List */} {batchStatus && (
檔案處理狀態
{batchStatus.files.map((file) => (
{file.status === 'completed' ? ( ) : file.status === 'processing' ? ( ) : file.status === 'failed' ? ( ) : ( )}

{file.filename}

{file.processing_time && (

處理時間: {file.processing_time.toFixed(2)}s

)} {file.error && (

{file.error}

)}
{getStatusBadge(file.status)}
))}
)}
) }