diff --git a/web/src/authorities/authorities-page.tsx b/web/src/authorities/authorities-page.tsx
index 6b97aa4..676f92c 100644
--- a/web/src/authorities/authorities-page.tsx
+++ b/web/src/authorities/authorities-page.tsx
@@ -6,7 +6,7 @@ import type { components } from "../api/schema";
import { useAuthorities, useCreateAuthority } from "../api/queries";
import { LabelEditor } from "../components/label-editor";
import { Button } from "@/components/ui/button";
-import { labelText } from "../lib/labels";
+import { AuthorityRow } from "./authority-row";
type LabelInput = components["schemas"]["LabelInput"];
@@ -72,9 +72,7 @@ export function AuthoritiesPage() {
{t("authorities.empty")}
)}
{authorities?.map((a) => (
-
- {labelText(a.labels, lang)}
-
+
))}
diff --git a/web/src/authorities/authority-row.stories.tsx b/web/src/authorities/authority-row.stories.tsx
new file mode 100644
index 0000000..ac5ec5b
--- /dev/null
+++ b/web/src/authorities/authority-row.stories.tsx
@@ -0,0 +1,30 @@
+import type { Meta, StoryObj } from '@storybook/react-vite'
+import { expect, userEvent } from 'storybook/test'
+
+import { AuthorityRow } from './authority-row'
+
+const meta = {
+ component: AuthorityRow,
+ tags: ['ai-generated'],
+ args: {
+ kind: 'person',
+ lang: 'en',
+ authority: { id: 'a1', kind: 'person', external_uri: null, labels: [{ lang: 'en', label: 'Astrid Lindgren' }] },
+ },
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Display: Story = {
+ play: async ({ canvas }) => {
+ await expect(canvas.getByText('Astrid Lindgren')).toBeVisible()
+ },
+}
+
+export const TogglesEdit: Story = {
+ play: async ({ canvas }) => {
+ await userEvent.click(canvas.getByRole('button', { name: 'Edit' }))
+ await expect(canvas.getByRole('button', { name: 'Save' })).toBeVisible()
+ },
+}
diff --git a/web/src/authorities/authority-row.tsx b/web/src/authorities/authority-row.tsx
new file mode 100644
index 0000000..fe06c1b
--- /dev/null
+++ b/web/src/authorities/authority-row.tsx
@@ -0,0 +1,77 @@
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import type { components } from "../api/schema";
+import { useUpdateAuthority, useDeleteAuthority } from "../api/queries";
+import { LabelEditor } from "../components/label-editor";
+import { DeleteConfirmDialog } from "../components/delete-confirm-dialog";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { labelText } from "../lib/labels";
+
+type AuthorityView = components["schemas"]["AuthorityView"];
+type LabelInput = components["schemas"]["LabelInput"];
+
+export function AuthorityRow({ authority, kind, lang }: { authority: AuthorityView; kind: string; lang: string }) {
+ const { t } = useTranslation();
+
+ const updateAuthority = useUpdateAuthority();
+ const deleteAuthority = useDeleteAuthority();
+
+ const [editing, setEditing] = useState(false);
+ const [labels, setLabels] = useState(authority.labels as LabelInput[]);
+ const [uri, setUri] = useState(authority.external_uri ?? "");
+
+ if (editing) {
+ return (
+
+
+
+
+ setUri(e.target.value)} />
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+ {labelText(authority.labels, lang)}
+
+ deleteAuthority.mutateAsync({ id: authority.id, kind })}
+ />
+
+ );
+}