feat(web): mutation hooks + InUseError + i18n for reference-data edit/delete

This commit is contained in:
2026-06-05 20:06:23 +02:00
parent 78c950d2ee
commit 282e6430d4
4 changed files with 177 additions and 2 deletions
+11
View File
@@ -0,0 +1,11 @@
import { describe, it, expect } from "vitest";
import { InUseError } from "./queries";
describe("InUseError", () => {
it("carries the count", () => {
const e = new InUseError(7);
expect(e.count).toBe(7);
expect(e).toBeInstanceOf(Error);
});
});
+164
View File
@@ -17,6 +17,13 @@ export class FieldRejection extends Error {
}
}
export class InUseError extends Error {
constructor(public readonly count: number) {
super(`in use: ${count}`);
this.name = "InUseError";
}
}
type UserView = components["schemas"]["UserView"];
type LoginRequest = components["schemas"]["LoginRequest"];
@@ -381,3 +388,160 @@ export function useSetVisibility() {
},
});
}
export function useUpdateTerm() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({
vocabularyId,
termId,
external_uri,
labels,
}: {
vocabularyId: string;
termId: string;
external_uri: string | null;
labels: LabelInput[];
}) => {
const { response } = await api.PATCH("/api/admin/vocabularies/{id}/terms/{term_id}", {
params: { path: { id: vocabularyId, term_id: termId } },
body: { external_uri, labels },
});
if (response.status !== 204) throw new Error("update term failed");
},
onSuccess: (_d, { vocabularyId }) => qc.invalidateQueries({ queryKey: ["terms", vocabularyId] }),
});
}
export function useDeleteTerm() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({ vocabularyId, termId }: { vocabularyId: string; termId: string }) => {
const { error, response } = await api.DELETE("/api/admin/vocabularies/{id}/terms/{term_id}", {
params: { path: { id: vocabularyId, term_id: termId } },
});
if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
if (response.status !== 204) throw new Error("delete term failed");
},
onSuccess: (_d, { vocabularyId }) => qc.invalidateQueries({ queryKey: ["terms", vocabularyId] }),
});
}
export function useRenameVocabulary() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({ id, key }: { id: string; key: string }) => {
const { response } = await api.PATCH("/api/admin/vocabularies/{id}", {
params: { path: { id } },
body: { key },
});
if (response.status !== 204) throw new Error("rename failed");
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["vocabularies"] }),
});
}
export function useDeleteVocabulary() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (id: string) => {
const { error, response } = await api.DELETE("/api/admin/vocabularies/{id}", {
params: { path: { id } },
});
if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
if (response.status !== 204) throw new Error("delete vocabulary failed");
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["vocabularies"] }),
});
}
export function useUpdateAuthority() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({
id,
external_uri,
labels,
}: {
id: string;
kind: string;
external_uri: string | null;
labels: LabelInput[];
}) => {
const { response } = await api.PATCH("/api/admin/authorities/{id}", {
params: { path: { id } },
body: { external_uri, labels },
});
if (response.status !== 204) throw new Error("update authority failed");
},
onSuccess: (_d, { kind }) => qc.invalidateQueries({ queryKey: ["authorities", kind] }),
});
}
export function useDeleteAuthority() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({ id }: { id: string; kind: string }) => {
const { error, response } = await api.DELETE("/api/admin/authorities/{id}", {
params: { path: { id } },
});
if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
if (response.status !== 204) throw new Error("delete authority failed");
},
onSuccess: (_d, { kind }) => qc.invalidateQueries({ queryKey: ["authorities", kind] }),
});
}
export function useUpdateFieldDefinition() {
const qc = useQueryClient();
return useMutation({
mutationFn: async ({
key,
required,
group,
labels,
}: {
key: string;
required: boolean;
group: string | null;
labels: LabelInput[];
}) => {
const { response } = await api.PATCH("/api/admin/field-definitions/{key}", {
params: { path: { key } },
body: { required, group, labels },
});
if (response.status !== 204) throw new Error("update field failed");
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["field-definitions"] }),
});
}
export function useDeleteFieldDefinition() {
const qc = useQueryClient();
return useMutation({
mutationFn: async (key: string) => {
const { error, response } = await api.DELETE("/api/admin/field-definitions/{key}", {
params: { path: { key } },
});
if (response.status === 409) throw new InUseError((error as { count?: number })?.count ?? 0);
if (response.status !== 204) throw new Error("delete field failed");
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["field-definitions"] }),
});
}