import React, { useState, useEffect } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio, TextField, Chip, Box, Typography, Alert, LinearProgress, Accordion, AccordionSummary, AccordionDetails, Checkbox, Slider, Stack, Card, CardContent, Divider, } from '@mui/material'; import { ExpandMore as ExpandMoreIcon, Schedule as ScheduleIcon, Assessment as AssessmentIcon, Refresh as RefreshIcon, } from '@mui/icons-material'; import { documentService, BulkOcrRetryRequest, OcrRetryFilter, BulkOcrRetryResponse, ErrorHelper, ErrorCodes } from '../services/api'; interface BulkRetryModalProps { open: boolean; onClose: () => void; onSuccess: (result: BulkOcrRetryResponse) => void; selectedDocumentIds?: string[]; } const COMMON_MIME_TYPES = [ { value: 'application/pdf', label: 'PDF' }, { value: 'image/png', label: 'PNG' }, { value: 'image/jpeg', label: 'JPEG' }, { value: 'image/tiff', label: 'TIFF' }, { value: 'text/plain', label: 'Text' }, ]; const COMMON_FAILURE_REASONS = [ { value: 'pdf_font_encoding', label: 'Font Encoding Issues' }, { value: 'ocr_timeout', label: 'Processing Timeout' }, { value: 'pdf_corruption', label: 'File Corruption' }, { value: 'low_ocr_confidence', label: 'Low Confidence' }, { value: 'no_extractable_text', label: 'No Text Found' }, { value: 'ocr_memory_limit', label: 'Memory Limit' }, ]; const FILE_SIZE_PRESETS = [ { label: '< 1MB', value: 1024 * 1024 }, { label: '< 5MB', value: 5 * 1024 * 1024 }, { label: '< 10MB', value: 10 * 1024 * 1024 }, { label: '< 50MB', value: 50 * 1024 * 1024 }, ]; export const BulkRetryModal: React.FC = ({ open, onClose, onSuccess, selectedDocumentIds = [], }) => { const [mode, setMode] = useState<'all' | 'specific' | 'filter'>('all'); const [filter, setFilter] = useState({}); const [priorityOverride, setPriorityOverride] = useState(10); const [usePriorityOverride, setUsePriorityOverride] = useState(false); const [previewOnly, setPreviewOnly] = useState(true); const [loading, setLoading] = useState(false); const [previewResult, setPreviewResult] = useState(null); const [error, setError] = useState(null); // Initialize mode based on selected documents useEffect(() => { if (selectedDocumentIds.length > 0) { setMode('specific'); } }, [selectedDocumentIds]); const handleModeChange = (event: React.ChangeEvent) => { setMode(event.target.value as 'all' | 'specific' | 'filter'); setPreviewResult(null); setError(null); }; const handleFilterChange = (key: keyof OcrRetryFilter, value: any) => { setFilter(prev => ({ ...prev, [key]: value, })); setPreviewResult(null); }; const handleMimeTypeToggle = (mimeType: string) => { const current = filter.mime_types || []; if (current.includes(mimeType)) { handleFilterChange('mime_types', current.filter(t => t !== mimeType)); } else { handleFilterChange('mime_types', [...current, mimeType]); } }; const handleFailureReasonToggle = (reason: string) => { const current = filter.failure_reasons || []; if (current.includes(reason)) { handleFilterChange('failure_reasons', current.filter(r => r !== reason)); } else { handleFilterChange('failure_reasons', [...current, reason]); } }; const buildRequest = (preview: boolean): BulkOcrRetryRequest => { const request: BulkOcrRetryRequest = { mode, preview_only: preview, }; if (mode === 'specific') { request.document_ids = selectedDocumentIds; } else if (mode === 'filter') { request.filter = filter; } if (usePriorityOverride) { request.priority_override = priorityOverride; } return request; }; const handlePreview = async () => { setLoading(true); setError(null); try { const request = buildRequest(true); const response = await documentService.bulkRetryOcr(request); setPreviewResult(response.data); } catch (err: any) { const errorInfo = ErrorHelper.formatErrorForDisplay(err, true); let errorMessage = 'Failed to preview retry operation'; // Handle specific bulk retry preview errors if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_SESSION_EXPIRED) || ErrorHelper.isErrorCode(err, ErrorCodes.USER_TOKEN_EXPIRED)) { errorMessage = 'Your session has expired. Please refresh the page and log in again.'; } else if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_PERMISSION_DENIED)) { errorMessage = 'You do not have permission to preview retry operations.'; } else if (ErrorHelper.isErrorCode(err, ErrorCodes.DOCUMENT_NOT_FOUND)) { errorMessage = 'No documents found matching the specified criteria.'; } else if (errorInfo.category === 'server') { errorMessage = 'Server error. Please try again later.'; } else if (errorInfo.category === 'network') { errorMessage = 'Network error. Please check your connection and try again.'; } else { errorMessage = errorInfo.message || 'Failed to preview retry operation'; } setError(errorMessage); setPreviewResult(null); } finally { setLoading(false); } }; const handleExecute = async () => { setLoading(true); setError(null); try { const request = buildRequest(false); const response = await documentService.bulkRetryOcr(request); onSuccess(response.data); onClose(); } catch (err: any) { const errorInfo = ErrorHelper.formatErrorForDisplay(err, true); let errorMessage = 'Failed to execute retry operation'; // Handle specific bulk retry execution errors if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_SESSION_EXPIRED) || ErrorHelper.isErrorCode(err, ErrorCodes.USER_TOKEN_EXPIRED)) { errorMessage = 'Your session has expired. Please refresh the page and log in again.'; } else if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_PERMISSION_DENIED)) { errorMessage = 'You do not have permission to execute retry operations.'; } else if (ErrorHelper.isErrorCode(err, ErrorCodes.DOCUMENT_NOT_FOUND)) { errorMessage = 'No documents found matching the specified criteria.'; } else if (ErrorHelper.isErrorCode(err, ErrorCodes.DOCUMENT_OCR_FAILED)) { errorMessage = 'Some documents cannot be retried due to processing issues.'; } else if (errorInfo.category === 'server') { errorMessage = 'Server error. Please try again later or contact support.'; } else if (errorInfo.category === 'network') { errorMessage = 'Network error. Please check your connection and try again.'; } else { errorMessage = errorInfo.message || 'Failed to execute retry operation'; } setError(errorMessage); } finally { setLoading(false); } }; const formatFileSize = (bytes: number) => { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`; }; const formatDuration = (minutes: number) => { if (minutes < 1) return `${Math.round(minutes * 60)} seconds`; if (minutes < 60) return `${Math.round(minutes)} minutes`; return `${Math.round(minutes / 60)} hours`; }; return ( Bulk OCR Retry {error && ( {error} )} {/* Selection Mode */} Retry Mode } label="Retry all failed OCR documents" /> } label={`Retry selected documents (${selectedDocumentIds.length} selected)`} disabled={selectedDocumentIds.length === 0} /> } label="Retry documents matching criteria" /> {/* Filter Options */} {mode === 'filter' && ( }> Filter Criteria {/* MIME Types */} File Types {COMMON_MIME_TYPES.map(({ value, label }) => ( handleMimeTypeToggle(value)} clickable /> ))} {/* Failure Reasons */} Failure Reasons {COMMON_FAILURE_REASONS.map(({ value, label }) => ( handleFailureReasonToggle(value)} clickable color="secondary" /> ))} {/* File Size */} Maximum File Size {FILE_SIZE_PRESETS.map(({ label, value }) => ( handleFilterChange('max_file_size', filter.max_file_size === value ? undefined : value)} clickable color="primary" /> ))} {filter.max_file_size && ( Max file size: {formatFileSize(filter.max_file_size)} )} {/* Limit */} handleFilterChange('limit', e.target.value ? parseInt(e.target.value) : undefined)} InputProps={{ inputProps: { min: 1, max: 1000 } }} helperText="Leave empty for no limit" /> )} {/* Priority Override */} }> Advanced Options setUsePriorityOverride(e.target.checked)} /> } label="Override processing priority" /> {usePriorityOverride && ( Priority: {priorityOverride} (Higher = More Urgent) setPriorityOverride(value as number)} min={1} max={20} marks={[ { value: 1, label: 'Low' }, { value: 10, label: 'Normal' }, { value: 20, label: 'High' }, ]} valueLabelDisplay="auto" /> )} {/* Preview Results */} {previewResult && ( Preview Results Documents matched: {previewResult.matched_count} Estimated processing time: {formatDuration(previewResult.estimated_total_time_minutes)} {previewResult.documents && previewResult.documents.length > 0 && ( Sample Documents: {(previewResult.documents || []).slice(0, 10).map((doc) => ( {doc.filename} ({formatFileSize(doc.file_size)}) {doc.ocr_failure_reason && ( )} ))} {previewResult.documents && previewResult.documents.length > 10 && ( ... and {previewResult.documents.length - 10} more documents )} )} )} {loading && } ); };