From 030472c2dae35fc40bdab71c26d6c1f8f72b501f Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Mon, 8 Jun 2026 19:56:42 +0200 Subject: [PATCH] =?UTF-8?q?docs(plans):=20unify=20vocab=20+=20authority=20?= =?UTF-8?q?CRUD=20=E2=80=94=204-task=20plan=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-06-08-unify-record-crud.md | 726 ++++++++++++++++++ 1 file changed, 726 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-08-unify-record-crud.md diff --git a/docs/superpowers/plans/2026-06-08-unify-record-crud.md b/docs/superpowers/plans/2026-06-08-unify-record-crud.md new file mode 100644 index 0000000..110f3e7 --- /dev/null +++ b/docs/superpowers/plans/2026-06-08-unify-record-crud.md @@ -0,0 +1,726 @@ +# Unify Vocabulary + Authority CRUD — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Collapse the duplicated Vocabulary-terms + Authorities CRUD (~280 lines across 4 files) into three shared components, with the rows and pages reduced to thin adapters — behavior-preserving. + +**Architecture:** Build `LabelledRecordRow`, `LabelledRecordCreateForm`, and `FilteredRecordList` in `src/components/` (Tasks 1-3, additive — existing app untouched, all existing tests stay green). Then rewire `term-row`/`authority-row` and `authorities-page`/`vocabulary-terms` onto them (Task 4) and run the full gate. Variance (mutation hooks, arg shapes, i18n keys, page chrome) lives entirely in the adapters. + +**Tech Stack:** React 19 + TS + pnpm, TanStack Query v5, react-i18next, Base UI, Vitest 4 (jsdom) + RTL. + +**Conventions:** pnpm; **no `any`/`eslint-disable`/`@ts-ignore`**; no codename; double-quote+semicolon; token classes only; `components/ui/*` untouched. Run a single test pass per task. + +**Spec:** `docs/superpowers/specs/2026-06-08-unify-record-crud-design.md` + +**Key facts:** +- Types: `type LabelView = components["schemas"]["LabelView"]`, `type LabelInput = components["schemas"]["LabelInput"]`. `labelText(labels: LabelView[], lang)` (`lib/labels`), `byLabel(lang)` returns a comparator over `{ labels: LabelView[] }` (`lib/sort`). +- Shared building blocks (in `src/components/`): `label-editor` (`LabelEditor`, uses `useId` since #62, needs `useConfig` which defaults to `DEFAULTS` — works under `renderApp`), `delete-confirm-dialog` (`DeleteConfirmDialog` — props `description`, `onConfirm: () => Promise`), `mutation-error` (`MutationError` — prop `error: unknown`), `external-uri-link` (`ExternalUriLink` — prop `uri`). +- UI kit: `Button`, `Input`, `Label` from `@/components/ui/*`; `ListSkeleton` from `@/components/ui/skeletons` (props `className`, `rows`). +- Test harness: `renderApp(ui, { route })` from `../test/render` (wraps QueryClient + memory router + i18n; NO ConfigProvider, but `useConfig` falls back to defaults). `HttpError` is exported from `../api/queries`. +- Existing tests that MUST stay green unchanged: `vocab/term-row.test.tsx`, `authorities/authorities.test.tsx`, `vocab/vocabularies.test.tsx`. +- Current `term-row.tsx`/`authority-row.tsx` are twins; `authorities-page.tsx` has a kind `