Severity: High.From a frontend UX audit. This is the worst readability defect in the detail view — and the machinery to fix it already exists.
Problems
Complex flexible-field values dumped as raw JSON.web/src/objects/object-detail.tsx:83-89 falls back to JSON.stringify(value) for object-typed values, so a term shows as {"id":"abc-123"}, an authority as a UUID blob, and a localized_text as {"sv":"…"}. The explicit goal — "term/authority values shown as human labels not ids" — is unmet. The form already resolves these correctly via useTerms/useAuthorities + labelText (lib/labels.ts); detail just doesn't reuse it.
Flexible fields are a flat, unordered block.object-detail.tsx:74-92 ignores FieldDefinitionView.group and renders in Object.entries(object.fields) (insertion) order rather than definition order. The form (object-form.tsx:165) has the same omission.
Detail polish: "Edit" is a bare indigo text <Link> crammed onto the <h2> line next to a delete button + badge (mixed affordance types) — object-detail.tsx:62-65; no export/copy action. Empty fields silently vanish (:17 returns null) so layout shifts per object. recording_date is shown verbatim though default_timezone/default_language are plumbed in ConfigView and unused.
Suggested fixes
Resolve term/authority values to labels (labelText + useTerms/useAuthorities), render localized_text by active language; switch on definitions.find(d=>d.key).data_type, not typeof value.
Iterate definitions in stable order, group flexible fields by def.group with subheadings (detail and form).
Make Edit a primary Button in a right-aligned actions toolbar (+ export); render known core fields with a muted "—" placeholder; format dates via Intl.DateTimeFormat(i18n.language).
Source: frontend UX/design audit, 2026-06-06.
**Severity: High.** _From a frontend UX audit. This is the worst readability defect in the detail view — and the machinery to fix it already exists._
## Problems
- **Complex flexible-field values dumped as raw JSON.** `web/src/objects/object-detail.tsx:83-89` falls back to `JSON.stringify(value)` for object-typed values, so a **term** shows as `{"id":"abc-123"}`, an **authority** as a UUID blob, and a **localized_text** as `{"sv":"…"}`. The explicit goal — "term/authority values shown as human labels not ids" — is unmet. The form already resolves these correctly via `useTerms`/`useAuthorities` + `labelText` (`lib/labels.ts`); detail just doesn't reuse it.
- **Flexible fields are a flat, unordered block.** `object-detail.tsx:74-92` ignores `FieldDefinitionView.group` and renders in `Object.entries(object.fields)` (insertion) order rather than definition order. The form (`object-form.tsx:165`) has the same omission.
- **Detail polish:** "Edit" is a bare indigo text `<Link>` crammed onto the `<h2>` line next to a delete button + badge (mixed affordance types) — `object-detail.tsx:62-65`; no export/copy action. Empty fields silently vanish (`:17` returns null) so layout shifts per object. `recording_date` is shown verbatim though `default_timezone`/`default_language` are plumbed in `ConfigView` and unused.
## Suggested fixes
- Resolve term/authority values to labels (`labelText` + `useTerms`/`useAuthorities`), render `localized_text` by active language; switch on `definitions.find(d=>d.key).data_type`, not `typeof value`.
- Iterate `definitions` in stable order, group flexible fields by `def.group` with subheadings (detail **and** form).
- Make Edit a primary `Button` in a right-aligned actions toolbar (+ export); render known core fields with a muted "—" placeholder; format dates via `Intl.DateTimeFormat(i18n.language)`.
_Source: frontend UX/design audit, 2026-06-06._
Done in e2ae093 (merged to main). Object detail now resolves term/authority ids to their active-locale labels and localized_text to the active-language string (via a per-field FlexibleFieldValue reusing useTerms/useAuthorities + labelText), with a muted (unknown) fallback for deleted refs and raw-value muted rendering for orphaned keys. Flexible fields are grouped by def.group in definition order; core fields always render with a "—" placeholder; dates are locale-formatted; the header is an Edit/Delete actions toolbar. Out of scope (filed/known): export/PDF (#39), the object form's grouping, backend label resolution.
Done in `e2ae093` (merged to `main`). Object detail now resolves term/authority ids to their active-locale **labels** and `localized_text` to the active-language string (via a per-field `FlexibleFieldValue` reusing `useTerms`/`useAuthorities` + `labelText`), with a muted `(unknown)` fallback for deleted refs and raw-value muted rendering for orphaned keys. Flexible fields are **grouped by `def.group`** in definition order; core fields always render with a "—" placeholder; dates are locale-formatted; the header is an Edit/Delete actions toolbar. Out of scope (filed/known): export/PDF (#39), the object form's grouping, backend label resolution.
Follow-up shipped: the deferred object-form flexible-field grouping is now done (merge 6e72f24).
Extracted a shared groupDefinitions helper (web/src/lib/group-fields.ts) so the detail view and the form group identically (one source of truth, unit-tested).
The object form now renders flexible-field inputs grouped by def.group (definition order, "Other" last) with label-caption subheadings; the <fieldset> keeps an sr-only legend for a11y.
Object-detail refactored onto the same helper (output-preserving — its tests stayed green).
No new dependency, no new i18n keys. 229 frontend tests pass; typecheck/lint/build clean; check:size 215.5 KB gz; check:colors clean.
Follow-up shipped: the deferred object-**form** flexible-field grouping is now done (merge `6e72f24`).
- Extracted a shared `groupDefinitions` helper (`web/src/lib/group-fields.ts`) so the detail view and the form group identically (one source of truth, unit-tested).
- The object form now renders flexible-field inputs grouped by `def.group` (definition order, "Other" last) with `label-caption` subheadings; the `<fieldset>` keeps an `sr-only` legend for a11y.
- Object-detail refactored onto the same helper (output-preserving — its tests stayed green).
- No new dependency, no new i18n keys. 229 frontend tests pass; typecheck/lint/build clean; check:size 215.5 KB gz; check:colors clean.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: High. From a frontend UX audit. This is the worst readability defect in the detail view — and the machinery to fix it already exists.
Problems
web/src/objects/object-detail.tsx:83-89falls back toJSON.stringify(value)for object-typed values, so a term shows as{"id":"abc-123"}, an authority as a UUID blob, and a localized_text as{"sv":"…"}. The explicit goal — "term/authority values shown as human labels not ids" — is unmet. The form already resolves these correctly viauseTerms/useAuthorities+labelText(lib/labels.ts); detail just doesn't reuse it.object-detail.tsx:74-92ignoresFieldDefinitionView.groupand renders inObject.entries(object.fields)(insertion) order rather than definition order. The form (object-form.tsx:165) has the same omission.<Link>crammed onto the<h2>line next to a delete button + badge (mixed affordance types) —object-detail.tsx:62-65; no export/copy action. Empty fields silently vanish (:17returns null) so layout shifts per object.recording_dateis shown verbatim thoughdefault_timezone/default_languageare plumbed inConfigViewand unused.Suggested fixes
labelText+useTerms/useAuthorities), renderlocalized_textby active language; switch ondefinitions.find(d=>d.key).data_type, nottypeof value.definitionsin stable order, group flexible fields bydef.groupwith subheadings (detail and form).Buttonin a right-aligned actions toolbar (+ export); render known core fields with a muted "—" placeholder; format dates viaIntl.DateTimeFormat(i18n.language).Source: frontend UX/design audit, 2026-06-06.
Done in
e2ae093(merged tomain). Object detail now resolves term/authority ids to their active-locale labels andlocalized_textto the active-language string (via a per-fieldFlexibleFieldValuereusinguseTerms/useAuthorities+labelText), with a muted(unknown)fallback for deleted refs and raw-value muted rendering for orphaned keys. Flexible fields are grouped bydef.groupin definition order; core fields always render with a "—" placeholder; dates are locale-formatted; the header is an Edit/Delete actions toolbar. Out of scope (filed/known): export/PDF (#39), the object form's grouping, backend label resolution.Follow-up shipped: the deferred object-form flexible-field grouping is now done (merge
6e72f24).groupDefinitionshelper (web/src/lib/group-fields.ts) so the detail view and the form group identically (one source of truth, unit-tested).def.group(definition order, "Other" last) withlabel-captionsubheadings; the<fieldset>keeps ansr-onlylegend for a11y.