feat(cms): improve catalog, editorial recovery and operations
CI / check (push) Successful in 52s
CI / deploy (push) Successful in 2m10s
CI / publish-container (push) Failing after 1m18s

This commit is contained in:
Simo committed 2026-09-09 19:36:15 +02:00
1 parent 2fead134e6
commit c389c3893d
122 files changed
+8137 -703

No files matched your search

@@ -1,10 +1,14 @@
"use client";
import { useTranslations } from "next-intl";
import { useCallback, useRef, useState } from "react";
import { toast } from "sonner";
import { useVisiblePolling } from "@/hooks/use-visible-polling";
import { adminFetch } from "@/lib/admin-fetch";
import type { ImportJob, ImportJobItem } from "@/lib/furni/import-job";
export function useFurnitureJobs(onComplete: () => void) {
const t = useTranslations("pages.admin.importHistory");
const [cancelling, setCancelling] = useState<string | null>(null);
const cancelInFlight = useRef(false);
const [jobs, setJobs] = useState<ImportJob[]>([]),
[busy, setBusy] = useState(false),
[error, setError] = useState("");
@@ -29,7 +33,9 @@ export function useFurnitureJobs(onComplete: () => void) {
let changed = false;
for (const job of data.jobs as ImportJob[])
if (
(job.state === "completed" || job.state === "interrupted") &&
(job.state === "completed" ||
job.state === "interrupted" ||
job.state === "cancelled") &&
!completed.current.has(job.id)
) {
completed.current.add(job.id);
@@ -44,7 +50,33 @@ export function useFurnitureJobs(onComplete: () => void) {
refreshing.current = false;
}
}, []);
useVisiblePolling(refresh, 5000);
const active = jobs.some(
(job) => job.state === "queued" || job.state === "running",
);
useVisiblePolling(refresh, active ? 5000 : 30000);
const cancel = async (id: string) => {
if (cancelInFlight.current) return;
cancelInFlight.current = true;
setCancelling(id);
try {
const response = await adminFetch("/api/admin/studio/import-jobs", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id }),
});
const data = await response.json();
if (!response.ok) throw Error(data.error || t("cancelFailed"));
setJobs((current) =>
current.map((job) => (job.id === id ? data.job : job)),
);
await refresh();
} catch (error) {
toast.error(error instanceof Error ? error.message : t("cancelFailed"));
} finally {
cancelInFlight.current = false;
setCancelling(null);
}
};
const submit = async (
items: ImportJobItem[],
options: {
@@ -88,5 +120,5 @@ export function useFurnitureJobs(onComplete: () => void) {
setBusy(false);
}
};
return { jobs, busy, error, submit, refresh };
return { jobs, busy, error, submit, refresh, cancel, cancelling };
}