import { useState, useEffect, useRef, useCallback } from "react"; import { submitContentJob } from "../api/contentJobs"; type JobStatus = "idle" | "queued" | "running" | "done" | "failed"; interface ContentJobState { jobId: string | null; status: JobStatus; progress: number; resultAssetId: string | null; errorMessage: string | null; } const INITIAL_STATE: ContentJobState = { jobId: null, status: "idle", progress: 0, resultAssetId: null, errorMessage: null, }; function statusToProgress(status: string): number { switch (status) { case "queued": return 5; case "running": return 50; case "done": return 100; case "failed": return 0; default: return 0; } } export function useContentJob(companyId: string | null) { const [state, setState] = useState(INITIAL_STATE); const eventSourceRef = useRef(null); const closeEventSource = useCallback(() => { if (eventSourceRef.current) { eventSourceRef.current.close(); eventSourceRef.current = null; } }, []); useEffect(() => { return () => { closeEventSource(); }; }, [closeEventSource]); const submit = useCallback( async (jobType: string, input: Record, sourceTaskId?: string | null) => { if (!companyId) return; closeEventSource(); setState({ ...INITIAL_STATE, status: "queued", progress: 5 }); const result = await submitContentJob(companyId, jobType, input, sourceTaskId); const jobId = result.jobId; setState((prev) => ({ ...prev, jobId, status: "queued", progress: 5 })); const url = `/api/companies/${companyId}/content-jobs/${jobId}/events`; const es = new EventSource(url, { withCredentials: true }); eventSourceRef.current = es; es.addEventListener("status", (e: MessageEvent) => { const data = JSON.parse(e.data as string) as { status?: string; progress?: number; resultAssetId?: string | null; errorMessage?: string | null; }; const status = (data.status ?? "queued") as JobStatus; const progress = typeof data.progress === "number" ? data.progress : statusToProgress(status); setState((prev) => ({ ...prev, status, progress, resultAssetId: data.resultAssetId ?? prev.resultAssetId, errorMessage: data.errorMessage ?? prev.errorMessage, })); if (status === "done" || status === "failed") { closeEventSource(); } }); es.addEventListener("error", () => { closeEventSource(); setState((prev) => ({ ...prev, status: "failed", errorMessage: prev.errorMessage ?? "Connection error", })); }); }, [companyId, closeEventSource], ); const reset = useCallback(() => { closeEventSource(); setState(INITIAL_STATE); }, [closeEventSource]); return { ...state, submit, reset }; }