From c9120848f5803c7141a75a89aab2130b54a9c5cd Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Fri, 5 Jun 2026 20:35:26 +0200 Subject: [PATCH] feat(web): edit/delete authorities in place (#30) --- web/src/authorities/authorities-page.tsx | 6 +- web/src/authorities/authority-row.stories.tsx | 30 ++++++++ web/src/authorities/authority-row.tsx | 77 +++++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 web/src/authorities/authority-row.stories.tsx create mode 100644 web/src/authorities/authority-row.tsx 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 })} + /> +
  • + ); +}