feat(cms): recover drafts and failed imports with verified UI workflows

This commit is contained in:
Simo committed 2026-09-11 08:58:10 +02:00
1 parent 410a1e466c
commit 88790d1a0d
84 files changed
+3952 -288

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
import { useState } from "react";
import { ImportAttachmentRecovery } from "@/components/admin/studio/import-attachment-recovery";
export function AttachmentHarness() {
const [resumed, setResumed] = useState(0);
return (
<>
<ImportAttachmentRecovery
jobId="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
classname="fixture_chair"
disabled={false}
onResumed={async () => {
setResumed((value) => value + 1);
}}
/>
<output aria-label="Resumed count">{resumed}</output>
</>
);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,35 @@
import { useState } from "react";
import { useFurnitureJobs } from "@/components/admin/studio/use-furniture-jobs";
export function FurnitureJobsHarness() {
const [completions, setCompletions] = useState(0);
const history = useFurnitureJobs(
() => setCompletions((value) => value + 1),
true,
);
return (
<section aria-label="Import history hook">
<button
type="button"
onClick={() => {
void history.refresh();
void history.refresh();
}}
>
Refresh twice
</button>
<button type="button" onClick={() => void history.navigate("next")}>
Next page
</button>
<button type="button" onClick={() => void history.navigate("previous")}>
Previous page
</button>
<output aria-label="Page">{history.page}</output>
<output aria-label="Completions">{completions}</output>
<output aria-label="Loading">{String(history.loading)}</output>
<output aria-label="Jobs">
{history.jobs.map((job) => `${job.id}:${job.state}`).join(",")}
</output>
</section>
);
}
+13
View File
@@ -0,0 +1,13 @@
const router = {
push: (url: string) => {
window.location.href = url;
},
replace: (url: string) => {
window.location.replace(url);
},
refresh: () => {},
};
export const useRouter = () => router;
export const usePathname = () => window.location.pathname;
export const useSearchParams = () =>
new URLSearchParams(window.location.search);
+7
View File
@@ -0,0 +1,7 @@
import type { AnchorHTMLAttributes } from "react";
export default function FixtureLink({
prefetch: _prefetch,
...props
}: AnchorHTMLAttributes<HTMLAnchorElement> & { prefetch?: boolean }) {
return <a {...props} />;
}
+29
View File
@@ -0,0 +1,29 @@
export async function fixtureAction(name: string, input: unknown = {}) {
const response = await fetch(`/fixture/actions/${name}`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(input),
});
if (!response.ok) throw Error(`Fixture action failed: ${response.status}`);
return response.json();
}
export const saveManagedSettings = (input: unknown) =>
fixtureAction("settings", input);
export const listTableViews = (input: unknown) =>
fixtureAction("list-views", input);
export const saveTableView = (input: unknown) =>
fixtureAction("save-view", input);
export const deleteTableView = (input: unknown) =>
fixtureAction("delete-view", input);
export const loadArticleRecovery = (key: unknown) =>
fixtureAction("load-recovery", { key });
export const autosaveArticle = (...input: unknown[]) =>
fixtureAction("autosave", input);
export const clearArticleRecovery = (...input: unknown[]) =>
fixtureAction("clear-recovery", input);
export const uploadMediaAndReturn = () => fixtureAction("upload");
export const createEvent = (input: unknown) =>
fixtureAction("create-event", input);
export const updateEvent = (input: unknown) =>
fixtureAction("update-event", input);