import React, { useRef, useEffect, useState } from 'react'; import { SlidersHorizontal, ChevronDown, ChevronUp, Brain } from 'lucide-react'; const ResultCard = ({ result, imageData, processedImageData, onReprocess, isProcessing }) => { const canvasRef = useRef(null); const previewCanvasRef = useRef(null); const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); const [signatureCrop, setSignatureCrop] = useState(null); const [stampCrop, setStampCrop] = useState(null); const [resolution, setResolution] = useState(result.processedResolution || 100); const [adjustedDataUrl, setAdjustedDataUrl] = useState(null); const [previewDimensions, setPreviewDimensions] = useState({ width: 0, height: 0 }); const [currentImageData, setCurrentImageData] = useState(processedImageData || imageData); const [showReasoning, setShowReasoning] = useState(false); // Function to crop image regions const cropRegion = (img, coords, scaleX, scaleY) => { if (!coords || coords.length === 0) return null; const [x1, y1, x2, y2] = coords[0]; const width = (x2 - x1) * scaleX; const height = (y2 - y1) * scaleY; const cropCanvas = document.createElement('canvas'); cropCanvas.width = width; cropCanvas.height = height; const cropCtx = cropCanvas.getContext('2d'); // Draw the cropped region cropCtx.drawImage( img, x1, y1, x2 - x1, y2 - y1, 0, 0, width, height ); return cropCanvas.toDataURL(); }; // Initialize currentImageData with processedImageData when available useEffect(() => { if (processedImageData) { setCurrentImageData(processedImageData); } }, [processedImageData]); // Main effect to draw image with bounding boxes using currentImageData useEffect(() => { if (!currentImageData || !canvasRef.current) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { // Set canvas size to match container while maintaining aspect ratio const maxWidth = 800; const maxHeight = 600; let width = img.width; let height = img.height; if (width > maxWidth) { height = (height * maxWidth) / width; width = maxWidth; } if (height > maxHeight) { width = (width * maxHeight) / height; height = maxHeight; } canvas.width = width; canvas.height = height; setDimensions({ width, height }); // Draw image ctx.drawImage(img, 0, 0, width, height); // Calculate scale factors const scaleX = width / img.width; const scaleY = height / img.height; // Create cropped images for signature and stamp from the CURRENT (processed) image // This ensures crops match the resolution that was sent to the API if (result.signature_coords && result.signature_coords.length > 0) { const sigCrop = cropRegion(img, result.signature_coords, 1, 1); setSignatureCrop(sigCrop); } if (result.stamp_coords && result.stamp_coords.length > 0) { const stCrop = cropRegion(img, result.stamp_coords, 1, 1); setStampCrop(stCrop); } // Draw bounding boxes for signature if (result.signature_coords && result.signature_coords.length > 0) { ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 3; ctx.setLineDash([5, 5]); result.signature_coords.forEach(coords => { const [x1, y1, x2, y2] = coords; ctx.strokeRect( x1 * scaleX, y1 * scaleY, (x2 - x1) * scaleX, (y2 - y1) * scaleY ); }); // Add label ctx.fillStyle = '#ef4444'; ctx.font = 'bold 14px Arial'; ctx.fillText('Signature', result.signature_coords[0][0] * scaleX, result.signature_coords[0][1] * scaleY - 5); } // Draw bounding boxes for stamp if (result.stamp_coords && result.stamp_coords.length > 0) { ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 3; ctx.setLineDash([5, 5]); result.stamp_coords.forEach(coords => { const [x1, y1, x2, y2] = coords; ctx.strokeRect( x1 * scaleX, y1 * scaleY, (x2 - x1) * scaleX, (y2 - y1) * scaleY ); }); // Add label ctx.fillStyle = '#3b82f6'; ctx.font = 'bold 14px Arial'; ctx.fillText('Stamp', result.stamp_coords[0][0] * scaleX, result.stamp_coords[0][1] * scaleY - 5); } }; img.src = currentImageData; }, [currentImageData, imageData, result]); // Handle resolution adjustment for preview and update currentImageData useEffect(() => { if (!imageData || !previewCanvasRef.current) return; const canvas = previewCanvasRef.current; const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { const scale = resolution / 100; const newWidth = Math.floor(img.width * scale); const newHeight = Math.floor(img.height * scale); canvas.width = newWidth; canvas.height = newHeight; setPreviewDimensions({ width: newWidth, height: newHeight }); ctx.drawImage(img, 0, 0, newWidth, newHeight); // Generate adjusted data URL const adjustedUrl = canvas.toDataURL('image/jpeg', 0.95); setAdjustedDataUrl(adjustedUrl); // Update the current image data to reflect resolution change setCurrentImageData(adjustedUrl); }; img.src = imageData; }, [imageData, resolution]); if (!result.success) { return (

Processing Error

File: {result.filename}

Error: {result.error}

); } return (
{/* Header */}

{result.filename}

{result.pageNumber && (

Page {result.pageNumber}

)}
{onReprocess && ( )}
{/* Image with bounding boxes */}

Document Preview

{/* Resolution Slider */}
{resolution}%
setResolution(parseInt(e.target.value))} className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb" disabled={isProcessing} />
10% (Fast) Dimensions: {previewDimensions.width} × {previewDimensions.height}px 100% (Best)
{isProcessing && (
)}
{result.signature_coords && result.signature_coords.length > 0 && (
Signature Detected
)} {result.stamp_coords && result.stamp_coords.length > 0 && (
Stamp Detected
)}
{/* Extracted Information */}

Extracted Information

{/* Performance Metrics */}
{result.processing_time !== undefined && (
Processing Time
{result.processing_time.toFixed(2)}s
)} {result.confidence !== undefined && (
Confidence
{(result.confidence * 100).toFixed(1)}%
)} {result.cost_estimate_usd !== undefined && (
Cost
${result.cost_estimate_usd.toFixed(4)}
)}
Extracted Text
{result.extracted_text || 'No text extracted'}
{/* Reasoning Output (Chain of Thought) */} {result.timing_breakdown?.reasoning_output && (
{showReasoning && (
This is the model's reasoning before extracting structured fields
{result.timing_breakdown.reasoning_output}
)}
)} {/* Detection Status */}
Signature {result.signature_coords && result.signature_coords.length > 0 ? ( Detected ) : ( Not Found )}
Stamp {result.stamp_coords && result.stamp_coords.length > 0 ? ( Detected ) : ( Not Found )}
{/* Cropped Signature and Stamp */} {(signatureCrop || stampCrop) && (
Extracted Elements
{signatureCrop && (
Signature
Signature
)} {stampCrop && (
Stamp
Stamp
)}
)} {/* Coordinates Info */} {(result.signature_coords?.length > 0 || result.stamp_coords?.length > 0) && (
Coordinates
{result.signature_coords?.length > 0 && (
Signature: {JSON.stringify(result.signature_coords)}
)} {result.stamp_coords?.length > 0 && (
Stamp: {JSON.stringify(result.stamp_coords)}
)}
)}
{/* Hidden canvas for resolution preview generation */}
); }; export default ResultCard;