Frontend UX: reference-data screens are inconsistent and hard to scan (vocab / authorities / fields) #50

Closed
opened 2026-06-06 18:52:20 +00:00 by logaritmisk · 1 comment
Owner

Severity: High. From a frontend UX audit. Three sibling screens with three different layouts and edit models, and lists you can't scan.

Problems

  • Inconsistent layout & edit modality. Vocab (vocabularies-page.tsx:7) and Fields (fields-page.tsx:13) are two-pane grid-cols-[20rem_1fr]; Authorities (authorities-page.tsx) is a single scrolling column (tabs + list + create form at the bottom). Create affordance lives in 3 places (vocab: fixed top-of-list; authorities: bottom of column; fields: the whole right pane). Edit happens 3 ways (vocab rename inline-in-row; term/authority inline-expand; fields in the right pane). No transferable mental model.
  • Lists order by id, not label, and have no in-screen filter. list_terms/list_by_kind return creation order; rows render that order (vocabulary-terms.tsx:65, authorities-page.tsx:74) with no client sort. Finding "Bronze" in a 200-term vocabulary by scanning creation order is effectively impossible, and there's no search box on any reference screen.
  • Read-mode rows hide disambiguators. term-row.tsx:58 / authority-row.tsx:58 show only the label; external_uri (the whole point of authority control — distinguishing two "Mercury"s) is only visible in edit mode. (Fields gets this right: field-list.tsx:73-77 shows label + key + type.)
  • No counts anywhere. No term count per vocabulary, no per-kind authority count on the tabs, no per-group field count. The Badge component already exists.
  • Small parity/validation gaps: authority create has no external_uri field though edit does (authorities-page.tsx:84 vs authority-row.tsx:32); vocab rename has no empty-guard (create does); URI fields have no type="url"/placeholder/validation.

Suggested fixes

  • Unify on one layout (two-pane) + one edit modality (pane-edit, like Fields' key-reset pattern) + one create location.
  • Sort lists by labelText with a locale-aware Intl.Collator; add a filter Input atop each list.
  • Show external_uri (linkified) as a muted secondary line in rows; add count badges (vocab rows, authority tabs, field groups).
  • Add the URI field to authority-create; add the rename empty-guard; add URL placeholder/validation.

Source: frontend UX/design audit, 2026-06-06.

**Severity: High.** _From a frontend UX audit. Three sibling screens with three different layouts and edit models, and lists you can't scan._ ## Problems - **Inconsistent layout & edit modality.** Vocab (`vocabularies-page.tsx:7`) and Fields (`fields-page.tsx:13`) are two-pane `grid-cols-[20rem_1fr]`; **Authorities** (`authorities-page.tsx`) is a single scrolling column (tabs + list + create form at the bottom). Create affordance lives in 3 places (vocab: fixed top-of-list; authorities: bottom of column; fields: the whole right pane). Edit happens 3 ways (vocab rename inline-in-row; term/authority inline-expand; fields in the right pane). No transferable mental model. - **Lists order by id, not label, and have no in-screen filter.** `list_terms`/`list_by_kind` return creation order; rows render that order (`vocabulary-terms.tsx:65`, `authorities-page.tsx:74`) with no client sort. Finding "Bronze" in a 200-term vocabulary by scanning creation order is effectively impossible, and there's no search box on any reference screen. - **Read-mode rows hide disambiguators.** `term-row.tsx:58` / `authority-row.tsx:58` show only the label; `external_uri` (the whole point of authority control — distinguishing two "Mercury"s) is only visible in edit mode. (Fields gets this right: `field-list.tsx:73-77` shows label + key + type.) - **No counts anywhere.** No term count per vocabulary, no per-kind authority count on the tabs, no per-group field count. The `Badge` component already exists. - **Small parity/validation gaps:** authority **create** has no `external_uri` field though edit does (`authorities-page.tsx:84` vs `authority-row.tsx:32`); vocab rename has no empty-guard (create does); URI fields have no `type="url"`/placeholder/validation. ## Suggested fixes - Unify on one layout (two-pane) + one edit modality (pane-edit, like Fields' `key`-reset pattern) + one create location. - Sort lists by `labelText` with a locale-aware `Intl.Collator`; add a filter `Input` atop each list. - Show `external_uri` (linkified) as a muted secondary line in rows; add count badges (vocab rows, authority tabs, field groups). - Add the URI field to authority-create; add the rename empty-guard; add URL placeholder/validation. _Source: frontend UX/design audit, 2026-06-06._
Author
Owner

Done — merged to main (3575282). Scoped to scannability + parity (layout/edit-modality unification and API-backed counts filed as follow-ups below).

Scannability:

  • Sort — new lib/sort.ts with a memoized locale-aware Intl.Collator (byLabel/byKey/compareStrings). Terms, authorities, fields-within-group sort by label; vocabularies by key; field groups A–Z with "Other" last.
  • Filter — a client-side filter Input atop every reference list, with a common.noMatches empty state.
  • Disambiguatorsexternal_uri shows as a muted, truncated, linkified secondary line in term + authority read rows (shared ExternalUriLink, rel="noopener noreferrer").
  • Counts — field-group headers show a count Badge (client-side).

Parity / validation:

  • Authority create gained an external_uri field and sends it (was hardcoded null).
  • Vocabulary rename got the empty-guard the create form already had.
  • All external_uri inputs are now type="url" + placeholder.

No layout/edit-modality change, no backend, no new dependency; 3 new i18n keys (en/sv parity); 221 tests green; typecheck/lint/build/check:size (215.3 KB gz)/check:colors clean; no codename. All list sorts operate on copies (no query-cache mutation).

Follow-ups (out of scope): the layout/edit-modality unification (authorities → two-pane pane-edit; vocab-rename/term/authority → pane-edit; one create location); API-backed counts (per-vocab term, per-kind authority — need view/count fields or endpoints); strict URL validation beyond type="url".

Done — merged to `main` (`3575282`). Scoped to **scannability + parity** (layout/edit-modality unification and API-backed counts filed as follow-ups below). **Scannability:** - **Sort** — new `lib/sort.ts` with a memoized locale-aware `Intl.Collator` (`byLabel`/`byKey`/`compareStrings`). Terms, authorities, fields-within-group sort by **label**; vocabularies by **key**; field groups A–Z with "Other" last. - **Filter** — a client-side filter `Input` atop every reference list, with a `common.noMatches` empty state. - **Disambiguators** — `external_uri` shows as a muted, truncated, linkified secondary line in **term** + **authority** read rows (shared `ExternalUriLink`, `rel="noopener noreferrer"`). - **Counts** — field-group headers show a count `Badge` (client-side). **Parity / validation:** - Authority **create** gained an `external_uri` field and sends it (was hardcoded `null`). - Vocabulary **rename** got the empty-guard the create form already had. - All `external_uri` inputs are now `type="url"` + placeholder. No layout/edit-modality change, no backend, no new dependency; 3 new i18n keys (en/sv parity); 221 tests green; typecheck/lint/build/check:size (215.3 KB gz)/check:colors clean; no codename. All list sorts operate on copies (no query-cache mutation). **Follow-ups (out of scope):** the layout/edit-modality **unification** (authorities → two-pane pane-edit; vocab-rename/term/authority → pane-edit; one create location); **API-backed counts** (per-vocab term, per-kind authority — need view/count fields or endpoints); strict URL validation beyond `type="url"`.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#50