import React, { useState, useEffect } from 'react' import { ChartBarIcon, ClockIcon, DocumentTextIcon, ExclamationCircleIcon, } from '@heroicons/react/24/outline' import { Document } from '../services/api' interface OcrAnalyticsProps { documents: Document[] } interface OcrStats { totalDocuments: number documentsWithOcr: number averageConfidence: number highConfidenceCount: number lowConfidenceCount: number failedCount: number processingCount: number totalWords: number averageProcessingTime: number } function OcrAnalytics({ documents }: OcrAnalyticsProps) { const [stats, setStats] = useState(null) useEffect(() => { if (documents.length === 0) { setStats(null) return } const ocrDocuments = documents.filter(doc => doc.has_ocr_text) const completedOcr = ocrDocuments.filter(doc => doc.ocr_status === 'completed') const failedOcr = ocrDocuments.filter(doc => doc.ocr_status === 'failed') const processingOcr = ocrDocuments.filter(doc => doc.ocr_status === 'processing') const confidenceScores = completedOcr .map(doc => doc.ocr_confidence) .filter((confidence): confidence is number => confidence !== undefined) const wordCounts = completedOcr .map(doc => doc.ocr_word_count) .filter((count): count is number => count !== undefined) const processingTimes = completedOcr .map(doc => doc.ocr_processing_time_ms) .filter((time): time is number => time !== undefined) const averageConfidence = confidenceScores.length > 0 ? confidenceScores.reduce((sum, conf) => sum + conf, 0) / confidenceScores.length : 0 const totalWords = wordCounts.reduce((sum, count) => sum + count, 0) const averageProcessingTime = processingTimes.length > 0 ? processingTimes.reduce((sum, time) => sum + time, 0) / processingTimes.length : 0 const highConfidenceCount = confidenceScores.filter(conf => conf >= 80).length const lowConfidenceCount = confidenceScores.filter(conf => conf < 60).length setStats({ totalDocuments: documents.length, documentsWithOcr: ocrDocuments.length, averageConfidence, highConfidenceCount, lowConfidenceCount, failedCount: failedOcr.length, processingCount: processingOcr.length, totalWords, averageProcessingTime, }) }, [documents]) if (!stats || stats.documentsWithOcr === 0) { return null } const formatTime = (ms: number) => { if (ms < 1000) return `${Math.round(ms)}ms` return `${(ms / 1000).toFixed(1)}s` } const getConfidenceColor = (confidence: number) => { if (confidence >= 80) return 'text-green-600' if (confidence >= 60) return 'text-yellow-600' return 'text-orange-600' } const successRate = ((stats.documentsWithOcr - stats.failedCount) / stats.documentsWithOcr) * 100 return (
OCR Analytics
{stats.documentsWithOcr} of {stats.totalDocuments} documents processed
{/* Success Rate */}
{successRate.toFixed(0)}%
Success Rate
{/* Average Confidence */}
{stats.averageConfidence.toFixed(0)}%
Avg Confidence
{/* Total Words */}
{stats.totalWords.toLocaleString()}
Words Extracted
{/* Average Processing Time */}
{formatTime(stats.averageProcessingTime)}
Avg Time
{/* Quality Distribution */}
High Quality: {stats.highConfidenceCount}
{stats.lowConfidenceCount > 0 && (
Low Quality: {stats.lowConfidenceCount}
)} {stats.failedCount > 0 && (
Failed: {stats.failedCount}
)} {stats.processingCount > 0 && (
Processing: {stats.processingCount}
)}
) } export default OcrAnalytics