import React, { useState } from 'react'; import { FileText, AlertCircle } from 'lucide-react'; import FileUpload from './components/FileUpload'; import ProgressIndicator from './components/ProgressIndicator'; import ResultCard from './components/ResultCard'; import ImagePreview from './components/ImagePreview'; import { convertFileToImages, dataUrlToBlob } from './utils/fileConverter'; import { processSingleInvoice } from './utils/api'; function App() { const [processing, setProcessing] = useState(false); const [results, setResults] = useState([]); const [progress, setProgress] = useState({ total: 0, completed: 0, current: '' }); const [error, setError] = useState(null); const [imageDataMap, setImageDataMap] = useState({}); const [previewImages, setPreviewImages] = useState([]); const [processingIndex, setProcessingIndex] = useState(null); const [resolutionMap, setResolutionMap] = useState({}); const [resultResolutionMap, setResultResolutionMap] = useState({}); const [enhancedMap, setEnhancedMap] = useState({}); // Track which images are enhanced const [reasoningMap, setReasoningMap] = useState({}); // Track which images use reasoning mode const handleFilesSelected = async (files) => { setProcessing(false); setResults([]); setError(null); setImageDataMap({}); setPreviewImages([]); setResolutionMap({}); setEnhancedMap({}); // Reset enhanced state setReasoningMap({}); // Reset reasoning state try { // Step 1: Convert all files to images and show previews const allImages = []; const imageData = {}; const previews = []; for (const file of files) { try { const images = await convertFileToImages(file); images.forEach((img, idx) => { const key = `${file.name}_${idx}`; allImages.push({ key: key, dataUrl: img.dataUrl, filename: img.filename, originalFile: img.originalFile, pageNumber: img.pageNumber, }); imageData[key] = img.dataUrl; previews.push({ key: key, dataUrl: img.dataUrl, filename: img.filename, }); }); } catch (err) { console.error(`Error converting ${file.name}:`, err); setError(`Failed to convert ${file.name}: ${err.message}`); } } setImageDataMap(imageData); setPreviewImages(previews); // Initialize resolution map with 100% for all images const initialResolutions = {}; previews.forEach(p => { initialResolutions[p.key] = { dataUrl: p.dataUrl, resolution: 100 }; }); setResolutionMap(initialResolutions); } catch (err) { console.error('Processing error:', err); setError(`Processing failed: ${err.message}`); } }; const handleProcessImages = async () => { setProcessing(true); setResults([]); setError(null); setProgress({ total: previewImages.length, completed: 0, current: '' }); try { for (let i = 0; i < previewImages.length; i++) { const preview = previewImages[i]; setProcessingIndex(i); setProgress(prev => ({ ...prev, current: preview.filename })); try { // Use resolution-adjusted image if available const processData = resolutionMap[preview.key] || { dataUrl: preview.dataUrl, resolution: 100 }; const blob = dataUrlToBlob(processData.dataUrl); const isEnhanced = enhancedMap[preview.key] || false; const reasoningMode = reasoningMap[preview.key] ? "reason" : "simple"; const result = await processSingleInvoice(blob, preview.filename, isEnhanced, reasoningMode); const resultWithMetadata = { ...result, filename: preview.filename, originalFile: preview.filename, index: i, success: true, key: preview.key, processedImageData: processData.dataUrl, processedResolution: processData.resolution }; setResults(prev => [...prev, resultWithMetadata]); } catch (error) { const errorResult = { filename: preview.filename, index: i, success: false, error: error.response?.data?.detail || error.message, key: preview.key }; setResults(prev => [...prev, errorResult]); } setProgress(prev => ({ ...prev, completed: i + 1 })); } } catch (err) { console.error('Processing error:', err); setError(`Processing failed: ${err.message}`); } finally { setProcessing(false); setProcessingIndex(null); setProgress(prev => ({ ...prev, current: '' })); } }; const handleReprocess = async (result, resolution, adjustedDataUrl) => { const index = results.findIndex(r => r.key === result.key); if (index === -1) return; setProcessingIndex(index); try { // Use resolution-adjusted image from ResultCard const blob = dataUrlToBlob(adjustedDataUrl || imageDataMap[result.key]); const isEnhanced = enhancedMap[result.key] || false; const reasoningMode = reasoningMap[result.key] ? "reason" : "simple"; const newResult = await processSingleInvoice(blob, result.filename, isEnhanced, reasoningMode); const resultWithMetadata = { ...newResult, filename: result.filename, originalFile: result.originalFile, index: index, success: true, key: result.key, processedImageData: adjustedDataUrl || imageDataMap[result.key], processedResolution: resolution }; setResults(prev => { const newResults = [...prev]; newResults[index] = resultWithMetadata; return newResults; }); } catch (error) { setError(`Reprocessing failed: ${error.message}`); } finally { setProcessingIndex(null); } }; const handleResolutionChange = (key, dataUrl, resolution) => { setResolutionMap(prev => ({ ...prev, [key]: { dataUrl, resolution } })); }; const handleEnhanceToggle = (key) => { setEnhancedMap(prev => ({ ...prev, [key]: !prev[key] })); }; const handleReasoningModeToggle = (key) => { setReasoningMap(prev => ({ ...prev, [key]: !prev[key] })); }; return (
Extract text, detect signatures and stamps from invoices using AI
{error}
Upload images or PDFs. PDFs are automatically converted to images.
Process multiple documents at once and see results one by one.
Automatically detect and highlight signatures and stamps on documents.
By Team DevBytes ⚡