"use client"; import Link from "next/link"; import { useEffect, useState } from "react"; import { displayJobItemError, isYouTubeUploadLimitError } from "@/lib/youtube/errors"; import type { JobResponse } from "@/lib/types"; import { YouTubeLimitBanner } from "./YouTubeLimitBanner"; type Props = { jobId: string; }; const STATUS_LABELS: Record = { PENDING: "Queued", ENCODING: "Encoding video…", UPLOADING: "Uploading to YouTube…", COMPLETED: "Completed", FAILED: "Failed", }; export function JobProgress({ jobId }: Props) { const [job, setJob] = useState(null); const [quota, setQuota] = useState<{ remaining: number; limit: number; plan: string; resetsIn: string; } | null>(null); const [error, setError] = useState(null); useEffect(() => { let active = true; async function poll() { try { const [jobRes, quotaRes] = await Promise.all([ fetch(`/api/jobs/${jobId}`), fetch("/api/quota"), ]); if (!jobRes.ok) throw new Error("Failed to load job"); const jobData = await jobRes.json(); if (active) setJob(jobData); if (quotaRes.ok) { const quotaData = await quotaRes.json(); if (active) setQuota({ remaining: quotaData.remaining, limit: quotaData.limit, plan: quotaData.plan, resetsIn: quotaData.resetsIn }); } } catch (err) { if (active) setError(err instanceof Error ? err.message : "Error loading job"); } } poll(); const interval = setInterval(poll, 3000); return () => { active = false; clearInterval(interval); }; }, [jobId]); if (error) { return
{error}
; } if (!job) { return
Loading job status…
; } const allDone = job.items.every((i) => i.status === "COMPLETED" || i.status === "FAILED"); const youtubeLimitHit = job.items.some( (i) => i.status === "FAILED" && i.error && isYouTubeUploadLimitError(i.error), ); return (

Job Status

Overall: {job.status}

{quota && (
{quota.remaining} / {quota.limit} videos remaining ·{" "} {quota.plan === "FREE" ? `resets in ${quota.resetsIn}` : `monthly quota resets on ${quota.resetsIn}`}
)}
{youtubeLimitHit && }
{job.items.map((item) => (

{item.title}

{item.audioFilename}

{STATUS_LABELS[item.status] || item.status}
{item.youtubeVideoId && ( View on YouTube )} {item.status === "FAILED" && displayJobItemError(item.error) && (

{displayJobItemError(item.error)}

)}
))}
{allDone && ( Create another video )}
); }