/** * JobProgressCard component - Display job status and progress */ import React from 'react'; import { Box, Typography, Paper, LinearProgress, Chip, } from '@mui/material'; import { AccessTime as TimeIcon, Speed as SpeedIcon, CheckCircle as CompleteIcon, Error as ErrorIcon, HourglassEmpty as PendingIcon, } from '@mui/icons-material'; interface JobProgressCardProps { jobId: string; status: 'pending' | 'processing' | 'completed' | 'failed' | 'running' | 'queued'; rowsProcessed: number; totalRows: number; startedAt?: string; completedAt?: string; accuracy?: number; } const JobProgressCard: React.FC = ({ jobId, status, rowsProcessed, totalRows, startedAt, completedAt, accuracy, }) => { const progress = totalRows > 0 ? (rowsProcessed / totalRows) * 100 : 0; const getStatusColor = (): 'default' | 'primary' | 'success' | 'error' | 'warning' => { switch (status) { case 'completed': return 'success'; case 'failed': return 'error'; case 'processing': case 'running': return 'primary'; case 'queued': return 'warning'; default: return 'default'; } }; const getStatusIcon = () => { switch (status) { case 'completed': return ; case 'failed': return ; case 'processing': case 'running': return ; default: return ; } }; const getStatusLabel = (): string => { switch (status) { case 'completed': return 'Completed'; case 'failed': return 'Failed'; case 'processing': case 'running': return 'Processing'; case 'queued': return 'Queued'; default: return 'Pending'; } }; const formatElapsedTime = (): string => { if (!startedAt) return 'N/A'; const start = new Date(startedAt); const end = completedAt ? new Date(completedAt) : new Date(); const diffMs = end.getTime() - start.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); if (diffHours > 0) { return `${diffHours}h ${diffMins % 60}m`; } return `${diffMins}m`; }; const estimateRemaining = (): string => { if ((status !== 'processing' && status !== 'running') || !startedAt || rowsProcessed === 0) return 'Calculating...'; const start = new Date(startedAt); const now = new Date(); const elapsedMs = now.getTime() - start.getTime(); const rate = rowsProcessed / elapsedMs; const remaining = totalRows - rowsProcessed; const remainingMs = remaining / rate; const remainingMins = Math.ceil(remainingMs / 60000); if (remainingMins > 60) { const hours = Math.floor(remainingMins / 60); return `~${hours}h ${remainingMins % 60}m`; } return `~${remainingMins}m`; }; return ( {/* Header */} Job ID {jobId} {/* Progress */} Progress {rowsProcessed.toLocaleString()} / {totalRows.toLocaleString()} rows ({progress.toFixed(1)}%) {/* Stats */} Elapsed Time {formatElapsedTime()} {status === 'processing' && ( Est. Remaining {estimateRemaining()} )} {accuracy !== undefined && ( Accuracy {(accuracy * 100).toFixed(1)}% )} ); }; export default JobProgressCard;