| import type { Job } from "../types"; | |
| // Presentational progress bar for a background job (load / encode). Shows status + processed/total. | |
| export default function JobProgressBar({ job, label }: { job: Job; label?: string }) { | |
| const pct = | |
| job.progress.total && job.progress.processed != null | |
| ? Math.round((job.progress.processed / job.progress.total) * 100) | |
| : job.status === "done" | |
| ? 100 | |
| : 0; | |
| return ( | |
| <div className="card"> | |
| <div className="flex items-center justify-between text-sm mb-1"> | |
| <span> | |
| {label ?? "Progress"}: <span className="font-medium">{job.status}</span> | |
| </span> | |
| {job.progress.total != null && ( | |
| <span className="text-gray-500"> | |
| {job.progress.processed ?? 0}/{job.progress.total} ({pct}%) | |
| </span> | |
| )} | |
| </div> | |
| <div className="h-2 w-full bg-gray-100 rounded-full overflow-hidden"> | |
| <div | |
| className={`h-2 rounded-full transition-all ${ | |
| job.status === "error" ? "bg-red-500" : "bg-brand-500" | |
| }`} | |
| style={{ width: `${pct}%` }} | |
| /> | |
| </div> | |
| {job.status === "error" && <p className="text-sm text-red-600 mt-2">{job.error}</p>} | |
| </div> | |
| ); | |
| } | |