'use client' import { useEffect, useState, useRef, Suspense } from 'react' import { useRouter, useSearchParams } from 'next/navigation' // Determine API_BASE: if the baked-in env var is defined, use it. const getApiBase = () => { if (process.env.NEXT_PUBLIC_API_URL) return process.env.NEXT_PUBLIC_API_URL; if (typeof window !== 'undefined') return ''; // Production same-origin return 'http://localhost:8000'; // SSR fallback }; const API_BASE = getApiBase(); const VOC_COLORS: Record = { aeroplane:'#87CEEB', bicycle:'#FFA500', bird:'#FFD700', boat:'#00BFFF', bottle:'#9400D3', bus:'#FF1493', car:'#DC143C', cat:'#FF8C00', chair:'#8B4513', cow:'#D4A017', diningtable:'#D2691E', dog:'#BA55D3', horse:'#FF69B4', motorbike:'#22c55e', person:'#FF4500', 'potted plant':'#228B22', sheep:'#B8A40A', sofa:'#00CED1', train:'#3b82f6', 'tv/monitor':'#0D9488', } const STEPS = ['Queued', 'Inferring Frames', 'Encoding H.264', 'Complete'] function ProcessingContent() { const router = useRouter() const searchParams = useSearchParams() const jobId = searchParams?.get('id') ?? '' const cardRef = useRef(null) const [pct, setPct] = useState(0) const [status, setStatus] = useState('queued') const [detected, setDetected] = useState([]) const [error, setError] = useState(null) const [elapsed, setElapsed] = useState(0) const timerRef = useRef | null>(null) useEffect(() => { setTimeout(() => cardRef.current?.classList.add('scroll-visible'), 50) }, []) useEffect(() => { if (!jobId) return const start = Date.now() timerRef.current = setInterval(() => setElapsed(Math.floor((Date.now()-start)/1000)), 1000) const apiOrigin = API_BASE || (typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.host}` : ''); const wsUrl = jobId ? `${apiOrigin.replace('http','ws')}/ws/${jobId}` : ''; const ws = wsUrl ? new WebSocket(wsUrl) : null; if (!ws) return; ws.onmessage = (evt) => { const data = JSON.parse(evt.data) setStatus(data.status) if (data.pct !== undefined) setPct(data.pct) if (data.detected) setDetected(data.detected) if (data.status === 'done') { setPct(100); clearInterval(timerRef.current!) setTimeout(() => router.push(`/result?id=${jobId}`), 1200) } if (data.status === 'error') { setError(data.error ?? 'Failed'); clearInterval(timerRef.current!) } } ws.onerror = () => pollFallback() return () => { ws.close(); clearInterval(timerRef.current!) } }, [jobId, router]) const pollFallback = () => { const iv = setInterval(async () => { try { const endpoint = API_BASE ? `${API_BASE}/api/status/${jobId}` : `api/status/${jobId}` const d = await fetch(endpoint).then(r=>r.json()) setStatus(d.status) if (d.pct !== undefined) setPct(d.pct) if (d.detected) setDetected(d.detected) if (d.status === 'done') { clearInterval(iv); clearInterval(timerRef.current!) setTimeout(() => router.push(`/result?id=${jobId}`), 1200) } if (d.status === 'error') { setError(d.error); clearInterval(iv) } } catch {} }, 1200) } const fmtTime = (s: number) => `${Math.floor(s/60)}:${String(s%60).padStart(2,'0')}` const currentStep = status==='queued' ? 0 : status==='processing' ? 1 : status==='done' ? 3 : 2 return (
{status==='done' ? ( ) : status==='error' ? ( ) : ( )}

{status==='queued' ? 'In Queue' : status==='processing' ? 'Segmenting…' : status==='done' ? 'Complete!' : status==='error' ? 'Failed' : status}

Job {jobId?.slice(0,8)}… {status==='processing' && · {fmtTime(elapsed)}}

{status !== 'error' && (
Progress =100 ? 'text-green-600' : 'text-orange-500'}>{pct.toFixed(1)}%
)} {status !== 'error' && (
{STEPS.map((s, i) => (
{i < currentStep ? : i+1}

{s}

{i < STEPS.length-1 && (
)}
))}
)} {error && (
Error: {error}
)} {status === 'processing' && (
{[ { label:'Progress', val:`${pct.toFixed(0)}%`, color:'text-orange-500' }, { label:'Objects', val:`${detected.length}`, color:'text-slate-800' }, { label:'Elapsed', val:fmtTime(elapsed), color:'text-slate-800' }, ].map(s => (

{s.label}

{s.val}

))}
)} {status === 'queued' && (

Waiting for a worker to pick up this job…

)} {detected.length > 0 && (

Detected Objects · {detected.length}

{detected.map(cls => ( {cls} ))}
)} Back to upload
) } export default function ProcessingPage() { return ( Loading process…
}> ) }