File size: 2,313 Bytes
497bb49 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
import type { JobStatus } from '../types'
interface ProgressIndicatorProps {
progress: number
message: string
status: JobStatus
elapsedSeconds?: number
}
/**
* Visual progress indicator for long-running ML inference jobs.
*
* Shows:
* - Progress bar with percentage
* - Current operation message
* - Elapsed time
* - Status-appropriate coloring (blue for running, red for failed)
*/
export function ProgressIndicator({
progress,
message,
status,
elapsedSeconds,
}: ProgressIndicatorProps) {
const isError = status === 'failed'
const isComplete = status === 'completed'
// Determine bar color based on status
const barColorClass = isError
? 'bg-red-500'
: isComplete
? 'bg-green-500'
: 'bg-blue-500'
// Animate the bar while running
const animationClass =
status === 'running' || status === 'pending' ? 'animate-pulse' : ''
return (
<div className="bg-gray-800 rounded-lg p-4 space-y-3">
{/* Header with message and percentage */}
<div className="flex justify-between items-center text-sm">
<span className="text-gray-300 font-medium">{message}</span>
<span className="text-gray-400 tabular-nums">{progress}%</span>
</div>
{/* Progress bar */}
<div className="w-full bg-gray-700 rounded-full h-2.5 overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-500 ease-out ${barColorClass} ${animationClass}`}
style={{ width: `${progress}%` }}
role="progressbar"
aria-valuenow={progress}
aria-valuemin={0}
aria-valuemax={100}
aria-label={message}
/>
</div>
{/* Footer with elapsed time and status */}
<div className="flex justify-between items-center text-xs text-gray-500">
{elapsedSeconds !== undefined ? (
<span className="tabular-nums">
Elapsed: {elapsedSeconds.toFixed(1)}s
</span>
) : (
<span>Starting...</span>
)}
<span
className={`capitalize ${
isError
? 'text-red-400'
: isComplete
? 'text-green-400'
: 'text-blue-400'
}`}
>
{status}
</span>
</div>
</div>
)
}
|