/** * useJobStatus — tracks a BullMQ job via WebSocket `job:update` events. * * Usage: * const { status, result, error } = useJobStatus(jobId); * * The hook listens for `job:update` events emitted by the backend workers. * When the jobId changes, the previous listener is removed and a fresh one * is registered for the new job. */ import { useEffect, useState } from "react"; import { socket } from "@/lib/socket"; export type JobStatus = "queued" | "active" | "completed" | "failed" | null; export interface JobUpdatePayload { jobId: string; jobType: string; status: JobStatus; message?: string; result?: any; error?: string; } export interface UseJobStatusReturn { status: JobStatus; message: string; result: any; error: string | null; socketId: string | null; } export function useJobStatus(jobId: string | null): UseJobStatusReturn { const [status, setStatus] = useState(jobId ? "queued" : null); const [message, setMessage] = useState(""); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [socketId, setSocketId] = useState( socket.id ?? null ); // Keep socketId in sync with the socket connection useEffect(() => { const onConnect = () => setSocketId(socket.id ?? null); socket.on("connect", onConnect); if (socket.connected) setSocketId(socket.id ?? null); return () => { socket.off("connect", onConnect); }; }, []); // Reset state when the jobId changes useEffect(() => { if (!jobId) { setStatus(null); setMessage(""); setResult(null); setError(null); return; } setStatus("queued"); setMessage(""); setResult(null); setError(null); const handler = (payload: JobUpdatePayload) => { if (payload.jobId !== jobId) return; setStatus(payload.status); if (payload.message) setMessage(payload.message); if (payload.result !== undefined) setResult(payload.result); if (payload.error) setError(payload.error); }; socket.on("job:update", handler); return () => { socket.off("job:update", handler); }; }, [jobId]); return { status, message, result, error, socketId }; }