"use client"; import { useEffect, useState } from "react"; import { StatusBadge, Badge, ProgressBar } from "./ui"; interface ProgressEvent { job_id: string; status: string; current_file: string | null; progress_percent: number | null; processed_files: number | null; total_files: number | null; stats_json: { scanned_files: number; indexed_files: number; removed_files: number; errors: number; } | null; } interface JobProgressProps { jobId: string; onComplete?: () => void; } export function JobProgress({ jobId, onComplete }: JobProgressProps) { const [progress, setProgress] = useState(null); const [error, setError] = useState(null); const [isComplete, setIsComplete] = useState(false); useEffect(() => { const eventSource = new EventSource(`/api/jobs/${jobId}/stream`); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); const progressData: ProgressEvent = { job_id: data.id, status: data.status, current_file: data.current_file, progress_percent: data.progress_percent, processed_files: data.processed_files, total_files: data.total_files, stats_json: data.stats_json, }; setProgress(progressData); if (data.status === "success" || data.status === "failed" || data.status === "cancelled") { setIsComplete(true); eventSource.close(); onComplete?.(); } } catch (err) { setError("Failed to parse SSE data"); } }; eventSource.onerror = (err) => { console.error("SSE error:", err); eventSource.close(); setError("Connection lost"); }; return () => { eventSource.close(); }; }, [jobId, onComplete]); if (error) { return (
Error: {error}
); } if (!progress) { return (
Loading progress...
); } const percent = progress.progress_percent ?? 0; const processed = progress.processed_files ?? 0; const total = progress.total_files ?? 0; const isPhase2 = progress.status === "extracting_pages" || progress.status === "generating_thumbnails"; const unitLabel = progress.status === "extracting_pages" ? "pages" : progress.status === "generating_thumbnails" ? "thumbnails" : "files"; return (
{isComplete && ( Complete )}
{processed} / {total} {unitLabel} {progress.current_file && ( Current: {progress.current_file.length > 40 ? progress.current_file.substring(0, 40) + "..." : progress.current_file} )}
{progress.stats_json && !isPhase2 && (
Scanned: {progress.stats_json.scanned_files} Indexed: {progress.stats_json.indexed_files} Removed: {progress.stats_json.removed_files} {progress.stats_json.errors > 0 && ( Errors: {progress.stats_json.errors} )}
)}
); }