AIEXTRACT1 / frontend /src /pages /Dashboard.jsx
Seth0330's picture
Update frontend/src/pages/Dashboard.jsx
b90a9db verified
// frontend/src/pages/Dashboard.jsx
import React, { useState } from "react";
import { motion } from "framer-motion";
import { Sparkles, Zap, FileText, TrendingUp, Clock, AlertCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
import UploadZone from "@/components/ocr/UploadZone";
import DocumentPreview from "@/components/ocr/DocumentPreview";
import ExtractionOutput from "@/components/ocr/ExtractionOutput";
import ExportButtons from "@/components/ExportButtons";
import ProcessingStatus from "@/components/ocr/ProcessingStatus";
import { extractDocument } from "@/services/api";
export default function Dashboard() {
const [selectedFile, setSelectedFile] = useState(null);
const [isProcessing, setIsProcessing] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const [extractionResult, setExtractionResult] = useState(null);
const [error, setError] = useState(null);
const handleFileSelect = (file) => {
setSelectedFile(file);
setIsComplete(false);
setExtractionResult(null);
setError(null);
};
const handleClear = () => {
setSelectedFile(null);
setIsProcessing(false);
setIsComplete(false);
setExtractionResult(null);
setError(null);
};
const handleExtract = async () => {
if (!selectedFile) return;
setIsProcessing(true);
setIsComplete(false);
setError(null);
setExtractionResult(null);
try {
const result = await extractDocument(selectedFile);
setExtractionResult(result);
setIsComplete(true);
} catch (err) {
console.error("Extraction error:", err);
setError(err.message || "Failed to extract document. Please try again.");
setIsComplete(false);
} finally {
setIsProcessing(false);
}
};
return (
<div className="min-h-screen bg-[#FAFAFA]">
{/* Header */}
<header className="bg-white border-b border-slate-200/80 sticky top-0 z-40">
<div className="px-8 py-4 flex items-center justify-between">
<div>
<h1 className="text-xl font-bold text-slate-900 tracking-tight">
Document Extraction
</h1>
<p className="text-sm text-slate-500 mt-0.5">
Upload any document and extract structured data with AI
</p>
</div>
<div className="flex items-center gap-3">
{/* Stats Pills */}
<div className="hidden lg:flex items-center gap-2">
<div className="flex items-center gap-2 px-3 py-1.5 bg-slate-100 rounded-lg">
<FileText className="h-4 w-4 text-slate-500" />
<span className="text-sm font-medium text-slate-700">
247 Extracted
</span>
</div>
<div className="flex items-center gap-2 px-3 py-1.5 bg-emerald-50 rounded-lg">
<TrendingUp className="h-4 w-4 text-emerald-600" />
<span className="text-sm font-medium text-emerald-700">
98.5% Accuracy
</span>
</div>
</div>
<ExportButtons isComplete={isComplete} extractionResult={extractionResult} />
</div>
</div>
</header>
{/* Main Content */}
<div className="p-8">
{/* Upload Section */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="max-w-3xl mx-auto mb-4"
>
<UploadZone
onFileSelect={handleFileSelect}
selectedFile={selectedFile}
onClear={handleClear}
/>
{/* Extract Button */}
{selectedFile && !isProcessing && !isComplete && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mt-4 flex justify-center"
>
<Button
onClick={handleExtract}
size="lg"
className="h-14 px-8 rounded-2xl font-semibold text-base bg-gradient-to-r from-indigo-600 to-violet-600 hover:from-indigo-700 hover:to-violet-700 shadow-xl shadow-indigo-500/25 hover:shadow-2xl hover:shadow-indigo-500/30 transition-all duration-300 hover:-translate-y-0.5"
>
<Sparkles className="h-5 w-5 mr-2" />
Start Extraction
<Zap className="h-4 w-4 ml-2 opacity-70" />
</Button>
</motion.div>
)}
</motion.div>
{/* Error Message */}
{error && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
className="max-w-3xl mx-auto mb-6"
>
<div className="bg-red-50 border border-red-200 rounded-2xl p-4 flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-red-600 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<h3 className="font-semibold text-red-900 mb-1">Extraction Failed</h3>
<p className="text-sm text-red-700">{error}</p>
</div>
<button
onClick={() => setError(null)}
className="text-red-400 hover:text-red-600 transition-colors"
>
×
</button>
</div>
</motion.div>
)}
{/* Processing Status */}
{(isProcessing || isComplete) && (
<div className="max-w-3xl mx-auto mb-4">
<ProcessingStatus
isProcessing={isProcessing}
isComplete={isComplete}
/>
</div>
)}
{/* Split View */}
{selectedFile && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="grid grid-cols-1 lg:grid-cols-2 gap-4"
style={{ height: "calc(100vh - 320px)", minHeight: "450px" }}
>
<DocumentPreview file={selectedFile} isProcessing={isProcessing} />
<ExtractionOutput
hasFile={!!selectedFile}
isProcessing={isProcessing}
isComplete={isComplete}
extractionResult={extractionResult}
/>
</motion.div>
)}
{/* Empty State Features */}
{!selectedFile && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className="max-w-5xl mx-auto mt-12"
>
<div className="text-center mb-10">
<h2 className="text-2xl font-bold text-slate-900 mb-2">
Powered by Advanced AI
</h2>
<p className="text-slate-500">
Extract structured data from any document
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[
{
icon: Zap,
title: "Lightning Fast",
description:
"Process documents faster with our optimized AI pipeline",
color: "amber",
},
{
icon: Sparkles,
title: "98.5% Accuracy",
description:
"Industry-leading extraction accuracy",
color: "indigo",
},
{
icon: Clock,
title: "Any Format",
description:
"Support for PDF, images, spreadsheets, and scanned documents",
color: "emerald",
},
].map((feature, index) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 + index * 0.1 }}
className="group bg-white rounded-2xl border border-slate-200 p-6 hover:shadow-xl hover:shadow-slate-200/50 transition-all duration-300 hover:-translate-y-1"
>
<div
className={`h-12 w-12 rounded-xl bg-${feature.color}-50 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300`}
>
<feature.icon
className={`h-6 w-6 text-${feature.color}-600`}
/>
</div>
<h3 className="font-semibold text-slate-900 mb-2">
{feature.title}
</h3>
<p className="text-sm text-slate-500 leading-relaxed">
{feature.description}
</p>
</motion.div>
))}
</div>
{/* Supported Formats */}
<div className="mt-12 text-center">
<p className="text-xs text-slate-400 uppercase tracking-wider mb-4 font-medium">
Supported Formats
</p>
<div className="flex items-center justify-center gap-6 flex-wrap">
{["PDF", "PNG", "JPG", "TIFF", "DOCX", "XLSX"].map((format) => (
<div
key={format}
className="flex items-center gap-2 text-slate-400"
>
<FileText className="h-4 w-4" />
<span className="text-sm font-medium">{format}</span>
</div>
))}
</div>
</div>
</motion.div>
)}
</div>
</div>
);
}