"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; interface Job { id: string; status: string; current_file: string | null; progress_percent: number | null; } interface JobsIndicatorProps { apiBaseUrl: string; apiToken: string; } export function JobsIndicator({ apiBaseUrl, apiToken }: JobsIndicatorProps) { const [activeJobs, setActiveJobs] = useState([]); const [isOpen, setIsOpen] = useState(false); useEffect(() => { const fetchActiveJobs = async () => { try { const response = await fetch(`${apiBaseUrl}/index/jobs/active`, { headers: { "Authorization": `Bearer ${apiToken}`, }, }); if (response.ok) { const jobs = await response.json(); // Enrich with details for running jobs const jobsWithDetails = await Promise.all( jobs.map(async (job: Job) => { if (job.status === "running") { try { const detailRes = await fetch(`${apiBaseUrl}/index/jobs/${job.id}`, { headers: { "Authorization": `Bearer ${apiToken}` }, }); if (detailRes.ok) { const detail = await detailRes.json(); return { ...job, ...detail }; } } catch { // ignore detail fetch errors } } return job; }) ); setActiveJobs(jobsWithDetails); } } catch { // Silently fail } }; fetchActiveJobs(); const interval = setInterval(fetchActiveJobs, 5000); return () => clearInterval(interval); }, [apiBaseUrl, apiToken]); const pendingCount = activeJobs.filter(j => j.status === "pending").length; const runningCount = activeJobs.filter(j => j.status === "running").length; const totalCount = activeJobs.length; if (totalCount === 0) { return ( ); } return (
{isOpen && (
Active Jobs setIsOpen(false)}>View all
{activeJobs.length === 0 ? (

No active jobs

) : (
    {activeJobs.map(job => (
  • {job.status} {job.id.slice(0, 8)}
    {job.status === "running" && job.progress_percent !== null && (
    {job.progress_percent}%
    )} {job.current_file && (

    {job.current_file.length > 30 ? job.current_file.substring(0, 30) + "..." : job.current_file}

    )}
  • ))}
)}
)}
); }