diff --git a/apps/backoffice/app/components/JobDetailLive.tsx b/apps/backoffice/app/components/JobDetailLive.tsx
new file mode 100644
index 0000000..67731ef
--- /dev/null
+++ b/apps/backoffice/app/components/JobDetailLive.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { useRouter } from "next/navigation";
+
+interface JobDetailLiveProps {
+ jobId: string;
+ isTerminal: boolean;
+}
+
+export function JobDetailLive({ jobId, isTerminal }: JobDetailLiveProps) {
+ const router = useRouter();
+ const isTerminalRef = useRef(isTerminal);
+ isTerminalRef.current = isTerminal;
+
+ useEffect(() => {
+ if (isTerminalRef.current) return;
+
+ const eventSource = new EventSource(`/api/jobs/${jobId}/stream`);
+
+ eventSource.onmessage = (event) => {
+ try {
+ const data = JSON.parse(event.data);
+ router.refresh();
+
+ if (data.status === "success" || data.status === "failed" || data.status === "cancelled") {
+ eventSource.close();
+ }
+ } catch {
+ // ignore parse errors
+ }
+ };
+
+ eventSource.onerror = () => {
+ eventSource.close();
+ };
+
+ return () => {
+ eventSource.close();
+ };
+ }, [jobId, router]);
+
+ return null;
+}
diff --git a/apps/backoffice/app/jobs/[id]/page.tsx b/apps/backoffice/app/jobs/[id]/page.tsx
index 4bbf325..95b371d 100644
--- a/apps/backoffice/app/jobs/[id]/page.tsx
+++ b/apps/backoffice/app/jobs/[id]/page.tsx
@@ -1,3 +1,5 @@
+export const dynamic = "force-dynamic";
+
import { notFound } from "next/navigation";
import Link from "next/link";
import { apiFetch, getMetadataBatchReport, getMetadataBatchResults, getMetadataRefreshReport, MetadataBatchReportDto, MetadataBatchResultDto, MetadataRefreshReportDto } from "../../../lib/api";
@@ -5,6 +7,7 @@ import {
Card, CardHeader, CardTitle, CardDescription, CardContent,
StatusBadge, JobTypeBadge, StatBox, ProgressBar
} from "../../components/ui";
+import { JobDetailLive } from "../../components/JobDetailLive";
import { getServerTranslations } from "../../../lib/i18n/server";
interface JobDetailPageProps {
@@ -158,6 +161,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) {
const isCompleted = job.status === "success";
const isFailed = job.status === "failed";
const isCancelled = job.status === "cancelled";
+ const isTerminal = isCompleted || isFailed || isCancelled;
const isExtractingPages = job.status === "extracting_pages";
const isThumbnailPhase = job.status === "generating_thumbnails";
const isPhase2 = isExtractingPages || isThumbnailPhase;
@@ -199,6 +203,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) {
return (
<>
+