first
This commit is contained in:
157
frontend/src/pages/ResultsPage.tsx
Normal file
157
frontend/src/pages/ResultsPage.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import ResultsTable from '@/components/ResultsTable'
|
||||
import MarkdownPreview from '@/components/MarkdownPreview'
|
||||
import { useToast } from '@/components/ui/toast'
|
||||
import { useUploadStore } from '@/store/uploadStore'
|
||||
import { apiClient } from '@/services/api'
|
||||
|
||||
export default function ResultsPage() {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const { toast } = useToast()
|
||||
const { batchId } = useUploadStore()
|
||||
const [selectedFileId, setSelectedFileId] = useState<number | null>(null)
|
||||
|
||||
// Get batch status to show results
|
||||
const { data: batchStatus, isLoading } = useQuery({
|
||||
queryKey: ['batchStatus', batchId],
|
||||
queryFn: () => apiClient.getBatchStatus(batchId!),
|
||||
enabled: !!batchId,
|
||||
})
|
||||
|
||||
// Get OCR result for selected file
|
||||
const { data: ocrResult, isLoading: isLoadingResult } = useQuery({
|
||||
queryKey: ['ocrResult', selectedFileId],
|
||||
queryFn: () => apiClient.getOCRResult(selectedFileId!.toString()),
|
||||
enabled: !!selectedFileId,
|
||||
})
|
||||
|
||||
const handleViewResult = (fileId: number) => {
|
||||
setSelectedFileId(fileId)
|
||||
}
|
||||
|
||||
const handleDownloadPDF = async (fileId: number) => {
|
||||
try {
|
||||
const blob = await apiClient.exportPDF(fileId)
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `ocr-result-${fileId}.pdf`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
document.body.removeChild(a)
|
||||
|
||||
toast({
|
||||
title: t('export.exportSuccess'),
|
||||
description: 'PDF 已下載',
|
||||
variant: 'success',
|
||||
})
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: t('export.exportError'),
|
||||
description: error.response?.data?.detail || t('errors.networkError'),
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleExport = () => {
|
||||
navigate('/export')
|
||||
}
|
||||
|
||||
// Show helpful message when no batch is selected
|
||||
if (!batchId) {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto mt-12">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t('results.title')}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-center space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
{t('results.noBatchMessage', { defaultValue: '尚未選擇任何批次。請先上傳並處理檔案。' })}
|
||||
</p>
|
||||
<Button onClick={() => navigate('/upload')}>
|
||||
{t('results.goToUpload', { defaultValue: '前往上傳頁面' })}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const completedFiles = batchStatus?.files.filter((f) => f.status === 'completed') || []
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground mb-2">{t('results.title')}</h1>
|
||||
<p className="text-muted-foreground">
|
||||
批次 ID: {batchId} - 已完成 {completedFiles.length} 個檔案
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={handleExport}>{t('nav.export')}</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled
|
||||
title={t('translation.comingSoon')}
|
||||
className="relative"
|
||||
>
|
||||
{t('translation.title')}
|
||||
<span className="ml-2 text-xs bg-yellow-100 text-yellow-800 px-2 py-0.5 rounded">
|
||||
{t('translation.comingSoon')}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Results Table */}
|
||||
<div>
|
||||
<ResultsTable
|
||||
files={batchStatus?.files || []}
|
||||
onViewResult={handleViewResult}
|
||||
onDownloadPDF={handleDownloadPDF}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Preview Panel */}
|
||||
<div>
|
||||
{selectedFileId && ocrResult ? (
|
||||
<div className="space-y-4">
|
||||
<MarkdownPreview
|
||||
title={`${t('results.viewMarkdown')} - ${ocrResult.filename}`}
|
||||
content={ocrResult.markdown_content}
|
||||
/>
|
||||
<div className="text-sm text-muted-foreground space-y-1">
|
||||
<p>
|
||||
{t('results.confidence')}: {((ocrResult.confidence || 0) * 100).toFixed(2)}%
|
||||
</p>
|
||||
<p>
|
||||
{t('results.processingTime')}: {(ocrResult.processing_time || 0).toFixed(2)}s
|
||||
</p>
|
||||
<p>
|
||||
{t('results.textBlocks')}: {ocrResult.json_data?.total_text_regions || 0}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full flex items-center justify-center border rounded-lg bg-muted/50">
|
||||
<p className="text-muted-foreground">
|
||||
{isLoadingResult ? t('common.loading') : '選擇檔案以查看結果'}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user