feat(web): inline status-aware errors on term/authority edit rows + delete dialog (#63)

This commit is contained in:
2026-06-08 17:27:02 +02:00
parent dd131ee740
commit 6e02ac874f
5 changed files with 62 additions and 4 deletions
+51
View File
@@ -0,0 +1,51 @@
import { expect, test } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { http, HttpResponse } from "msw";
import type { TermView } from "../test/fixtures";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { TermRow } from "./term-row";
const term: TermView = {
id: "t1",
external_uri: null,
labels: [{ lang: "en", label: "Bronze" }],
};
test("a failed term update shows an inline error and keeps the row editable", async () => {
server.use(
http.patch("/api/admin/vocabularies/:id/terms/:term_id", () => new HttpResponse(null, { status: 403 })),
);
renderApp(
<ul>
<TermRow vocabularyId="v1" term={term} lang="en" />
</ul>,
);
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
await userEvent.click(screen.getByRole("button", { name: /save/i }));
expect(await screen.findByRole("alert")).toHaveTextContent(/permission/i);
expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument();
});
test("re-entering edit after a failure clears the stale error", async () => {
server.use(
http.patch("/api/admin/vocabularies/:id/terms/:term_id", () => new HttpResponse(null, { status: 403 })),
);
renderApp(
<ul>
<TermRow vocabularyId="v1" term={term} lang="en" />
</ul>,
);
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
await userEvent.click(screen.getByRole("button", { name: /save/i }));
expect(await screen.findByRole("alert")).toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
await userEvent.click(screen.getByRole("button", { name: /edit/i }));
expect(screen.queryByRole("alert")).toBeNull();
});
+3
View File
@@ -5,6 +5,7 @@ import type { components } from "../api/schema";
import { useUpdateTerm, useDeleteTerm } from "../api/queries";
import { LabelEditor } from "../components/label-editor";
import { DeleteConfirmDialog } from "../components/delete-confirm-dialog";
import { MutationError } from "../components/mutation-error";
import { ExternalUriLink } from "../components/external-uri-link";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -50,6 +51,7 @@ export function TermRow({ vocabularyId, term, lang }: { vocabularyId: string; te
{t("form.cancel")}
</Button>
</div>
<MutationError error={updateTerm.error} />
</li>
);
}
@@ -65,6 +67,7 @@ export function TermRow({ vocabularyId, term, lang }: { vocabularyId: string; te
variant="ghost"
size="sm"
onClick={() => {
updateTerm.reset();
setLabels(term.labels as LabelInput[]);
setUri(term.external_uri ?? "");
setEditing(true);