Editing a term/authority/field that already had labels in other languages
silently replaced the whole multilingual set with one default-language entry.
onChange now keeps non-default-language entries; the editor shows only the
default-language label (no longer falling back to an other-language one, which
made the clear/edit path write the wrong language) and surfaces a hint when
other-language labels exist on the record.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Declare meta on all 18 mutation hooks: meta.successMessage (toast.* key)
on every discrete user action, meta.suppressErrorToast where the consuming
component already renders the error inline. Corrected useUpdateFieldDefinition
to suppress (FieldForm renders update.isError as form.rejected inline).
Add an RTL+MSW integration test wiring the real MutationCache via
makeQueryClient() + ToastRegion: success toast, catch-all error toast, and
suppressed-no-toast. Tidy the toast Close aria-label to t("common.close")
with en/sv parity.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a module-scope Base UI toast manager bridged to the QueryClient so
every mutation can give consistent feedback. A MutationCache (extracted
into a makeQueryClient() factory for test reuse) emits a catch-all,
type-aware error toast (unless meta.suppressErrorToast) and an opt-in
success toast (meta.successMessage), reading mutation.meta + i18n.t
outside React. meta is type-checked via a react-query Register
augmentation. ToastRegion is mounted app-wide in main.tsx. Adds a toast
i18n namespace (en/sv parity) and a validated story.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refactor object-detail.tsx to resolve term/authority ids to labels via
FlexibleFieldValue, group flexible fields by def.group in definition order
(ungrouped → trailing "Other"), always show core fields with "—" placeholders,
and move Edit (button-styled Link) + Delete into a right-aligned toolbar.
Move formatDate into lib/format-date.ts so the component module no longer
co-exports a non-component (clears the react-refresh/only-export-components
warning); both flexible-field-value.tsx and object-detail.tsx import it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wide (>=1024px): right-hand pane beside the table with a close control.
Narrow: Base UI Drawer sliding from the right (lazy-loaded so its code splits
out of the main chunk). Both preserve the table's query string on close.
Remove the index SelectPrompt route (the table is the landing view) and delete
the now-unused SelectPrompt. Make table rows keyboard-activatable
(role=link, tabIndex, Enter).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the narrow ObjectList with a full-width ObjectsTable whose state
(sort/order/q/visibility/limit/offset) lives entirely in the URL via
useSearchParams. Sortable headers toggle sort+dir with aria-sort, a
debounced quick-filter and visibility chips mirror the search-panel
pattern, and a pagination footer offers prev/next + page-size select.
Rows deep-link to /objects/:id preserving the query string.
useObjectsPage now takes an ObjectListParams object (sort/order/
visibility/q) with keepPreviousData. ObjectsPage renders the table as
the full-width landing view, surfacing the nested <Outlet/> detail as a
simple right-side panel only when a :id child route is active (Phase 3
makes this responsive). object-list.tsx and its test are removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per WAI-ARIA, aria-selected must be on the same element carrying role="tab".
The previous impl placed it on an inner <span> via the render-prop, making it
invisible to assistive technology. Move both role="tab" and aria-selected to
the NavLink, compute currentKind once from useParams, drop the render-prop.
Add a Vitest assertion that the selected tab has aria-selected="true" and an
unselected tab has aria-selected="false".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>