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>
  )
}