diff --git a/src/app/[locale]/studio/render/[projectId]/page.tsx b/src/app/[locale]/studio/render/[projectId]/page.tsx index bdac867..3dc380e 100644 --- a/src/app/[locale]/studio/render/[projectId]/page.tsx +++ b/src/app/[locale]/studio/render/[projectId]/page.tsx @@ -10,6 +10,7 @@ import { Link2, Loader2, RefreshCw, + XCircle, } from "lucide-react"; import { useLocale } from "next-intl"; @@ -205,6 +206,12 @@ export default function RenderPage() { } else if (data.status === "failed") { setPhase("failed"); setErrorMessage(data.errorMessage ?? "Render failed."); + } else if (data.status?.toLowerCase() === "cancelled") { + // Cancelled here or from another surface (pill / dashboard) — reflect it. + setPhase("config"); + setProgress(0); + setEtaSec(null); + setErrorMessage("رندر لغو شد."); } } catch { setPhase("failed"); @@ -279,6 +286,26 @@ export default function RenderPage() { } }, [projectId, resolution, fps, locale]); + /** Fully cancel the active render — works in any state (Queued or Running). The + * server marks it Cancelled (the dev worker abandons it, a real node kills its + * process), so it stops fully. Optimistically return to config right away. */ + const cancelRender = useCallback(async () => { + if (!jobId) return; + const id = jobId; + setPhase("config"); + setProgress(0); + setEtaSec(null); + setProgressMessage(""); + setPreviewB64(null); + setErrorMessage("رندر لغو شد."); + setJobId(null); + try { + await apiFetch(`/api/renders/${id}/cancel`, { method: "POST" }); + } catch { + /* server is the source of truth; the dashboard can also cancel */ + } + }, [jobId]); + const backToStudio = `/studio/video/${projectId}`; const isBusy = phase === "submitting" || phase === "polling"; @@ -393,9 +420,19 @@ export default function RenderPage() { ) : isBusy ? ( -
- میتوانید این صفحه را ببندید؛ رندر در پسزمینه ادامه مییابد و از هر صفحهای قابل پیگیری است. -
++ میتوانید این صفحه را ببندید؛ رندر در پسزمینه ادامه مییابد و از هر صفحهای قابل پیگیری است. +
+ +