feat(web): useCreateFieldDefinition mutation + MSW handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 14:16:55 +02:00
parent df8f31d14d
commit 6ad1304efd
3 changed files with 61 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
import { expect, test } from "vitest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { server } from "../test/server";
import { useCreateFieldDefinition } from "./queries";
function wrapper({ children }: { children: React.ReactNode }) {
const qc = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } });
return <QueryClientProvider client={qc}>{children}</QueryClientProvider>;
}
test("useCreateFieldDefinition POSTs the request body", async () => {
let body: unknown;
server.use(
http.post("/api/admin/field-definitions", async ({ request }) => {
body = await request.json();
return HttpResponse.json({ key: "technique" }, { status: 201 });
}),
);
const { result } = renderHook(() => useCreateFieldDefinition(), { wrapper });
result.current.mutate({
key: "technique",
data_type: "term",
vocabulary_id: "v-technique",
authority_kind: null,
required: false,
group: "Provenance",
labels: [{ lang: "en", label: "Technique" }],
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect((body as { key: string }).key).toBe("technique");
expect((body as { data_type: string }).data_type).toBe("term");
});
+17
View File
@@ -315,6 +315,23 @@ export function useSearch(q: string, visibility: string | null) {
}); });
} }
type NewFieldDefinitionRequest = components["schemas"]["NewFieldDefinitionRequest"];
export function useCreateFieldDefinition() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (body: NewFieldDefinitionRequest) => {
const { data, response } = await api.POST("/api/admin/field-definitions", { body });
if (response.status !== 201 || !data) throw new Error("failed to create field definition");
return data;
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["field-definitions"] }),
});
}
type Visibility = "draft" | "internal" | "public"; type Visibility = "draft" | "internal" | "public";
/** Error carrying the HTTP status so callers can branch 422-gate vs 409-illegal. */ /** Error carrying the HTTP status so callers can branch 422-gate vs 409-illegal. */
+4
View File
@@ -68,4 +68,8 @@ export const handlers = [
http.post("/api/admin/logout", () => new HttpResponse(null, { status: 204 })), http.post("/api/admin/logout", () => new HttpResponse(null, { status: 204 })),
http.post("/api/admin/objects/:id/visibility", () => new HttpResponse(null, { status: 204 })), http.post("/api/admin/objects/:id/visibility", () => new HttpResponse(null, { status: 204 })),
http.post("/api/admin/field-definitions", () =>
HttpResponse.json({ key: "new_field" }, { status: 201 }),
),
]; ];