Severity: Medium.From a frontend UX audit. The header is the most persistent surface in a hours-a-day tool and currently carries almost nothing.
Problems
The <header> (web/src/shell/app-shell.tsx:66-72) contains only a flex spacer, the language switch, and Sign out — no section title/breadcrumb, no logged-in user identity, no global-search entry.
No "where am I" on nested/create routes: deep-linking to /objects/new shows the Objects list with nothing selected and no header cue that you're in create mode (no breadcrumb/page title anywhere).
The configured instance name is dead data.ConfigProvider fetches /api/configapp_name, but the sidebar brand (app-shell.tsx:21) and login heading (login-page.tsx:34) render the hardcoded t("app.name") = "Collection" instead of useConfig().app_name (default "Collection Management System"). Two competing names; the configured one appears nowhere.
Global search is a sidebar nav item requiring a full route change away from the record you're editing.
Suggested fixes
Put a route-driven section title/breadcrumb on the header's left (filling the empty spacer), e.g. "Objects / New" or "Objects / {object_number}".
Show the signed-in user (useMe() already returns it) and a quick-search affordance on the right.
Render useConfig().app_name for the brand + login heading; drop/repurpose the hardcoded app.name.
Source: frontend UX/design audit, 2026-06-06.
**Severity: Medium.** _From a frontend UX audit. The header is the most persistent surface in a hours-a-day tool and currently carries almost nothing._
## Problems
- The `<header>` (`web/src/shell/app-shell.tsx:66-72`) contains only a flex spacer, the language switch, and Sign out — no section title/breadcrumb, no logged-in user identity, no global-search entry.
- **No "where am I" on nested/create routes:** deep-linking to `/objects/new` shows the Objects list with nothing selected and no header cue that you're in create mode (no breadcrumb/page title anywhere).
- **The configured instance name is dead data.** `ConfigProvider` fetches `/api/config` `app_name`, but the sidebar brand (`app-shell.tsx:21`) and login heading (`login-page.tsx:34`) render the hardcoded `t("app.name")` = "Collection" instead of `useConfig().app_name` (default "Collection Management System"). Two competing names; the configured one appears nowhere.
- Global search is a sidebar nav item requiring a full route change away from the record you're editing.
## Suggested fixes
- Put a route-driven section title/breadcrumb on the header's left (filling the empty spacer), e.g. "Objects / New" or "Objects / {object_number}".
- Show the signed-in user (`useMe()` already returns it) and a quick-search affordance on the right.
- Render `useConfig().app_name` for the brand + login heading; drop/repurpose the hardcoded `app.name`.
_Source: frontend UX/design audit, 2026-06-06._
Done — merged to main (b7242ca). The header now carries real wayfinding.
Breadcrumb (left): a page-driven useBreadcrumb(trail) hook + BreadcrumbProvider (parallel to the #57 title hook) — every AppShell route sets its trail and the header renders it (non-leaf crumbs are links):
Dynamic labels come from already-loaded data (object_number; vocab .key via the cache-shared useVocabularies), so no extra requests and no UUIDs in the crumb.
User menu (right): a new reusable ui/menu.tsx Base UI dropdown wrapper (validated by running) powers a UserMenu showing the signed-in email + role and a Sign out item; the standalone Sign out button is gone (logout lives in the menu).
Header search (right): a compact HeaderSearch input → /search?q=… (encoded), so you can start a search from any screen; hidden below sm. (The full ⌘K palette / in-place modal remains #33.)
Brand + login: the sidebar brand and login heading + tab title now render useConfig().app_name (default "Collection Management System"); the hardcoded app.name i18n key is removed from both locales.
No new dependency (Base UI Menu is a namespace export of the existing @base-ui/react); i18n net +1/−1 with en/sv parity; the breadcrumb hook is eslint-disable-free (ref + serialized-key pattern). Gate green: typecheck, lint, 194 tests, build, check:size (196.3 KB gz, +~12 KB for Menu, under the 250 KB budget), check:colors; no codename.
Follow-ups (out of scope): full ⌘K command palette (#33); broader responsive header (#58); /search/:id showing a search-relative (vs canonical-object) breadcrumb; user avatar / account settings page.
Done — merged to `main` (`b7242ca`). The header now carries real wayfinding.
**Breadcrumb (left):** a page-driven `useBreadcrumb(trail)` hook + `BreadcrumbProvider` (parallel to the #57 title hook) — every AppShell route sets its trail and the header renders it (non-leaf crumbs are links):
- list pages → `Objects` / `Vocabularies` / …; `Objects / New`, `Objects / Edit`; `Objects / {object_number}`; `Vocabularies / {key}`.
- Dynamic labels come from already-loaded data (object_number; vocab `.key` via the cache-shared `useVocabularies`), so no extra requests and no UUIDs in the crumb.
**User menu (right):** a new reusable `ui/menu.tsx` Base UI dropdown wrapper (validated by running) powers a `UserMenu` showing the signed-in `email` + `role` and a **Sign out** item; the standalone Sign out button is gone (logout lives in the menu).
**Header search (right):** a compact `HeaderSearch` input → `/search?q=…` (encoded), so you can start a search from any screen; hidden below `sm`. (The full ⌘K palette / in-place modal remains #33.)
**Brand + login:** the sidebar brand and login heading + tab title now render `useConfig().app_name` (default "Collection Management System"); the hardcoded `app.name` i18n key is removed from both locales.
No new dependency (Base UI Menu is a namespace export of the existing `@base-ui/react`); i18n net +1/−1 with en/sv parity; the breadcrumb hook is `eslint-disable`-free (ref + serialized-key pattern). Gate green: typecheck, lint, **194 tests**, build, check:size (196.3 KB gz, +~12 KB for Menu, under the 250 KB budget), check:colors; no codename.
Follow-ups (out of scope): full ⌘K command palette (#33); broader responsive header (#58); `/search/:id` showing a search-relative (vs canonical-object) breadcrumb; user avatar / account settings page.
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: Medium. From a frontend UX audit. The header is the most persistent surface in a hours-a-day tool and currently carries almost nothing.
Problems
<header>(web/src/shell/app-shell.tsx:66-72) contains only a flex spacer, the language switch, and Sign out — no section title/breadcrumb, no logged-in user identity, no global-search entry./objects/newshows the Objects list with nothing selected and no header cue that you're in create mode (no breadcrumb/page title anywhere).ConfigProviderfetches/api/configapp_name, but the sidebar brand (app-shell.tsx:21) and login heading (login-page.tsx:34) render the hardcodedt("app.name")= "Collection" instead ofuseConfig().app_name(default "Collection Management System"). Two competing names; the configured one appears nowhere.Suggested fixes
useMe()already returns it) and a quick-search affordance on the right.useConfig().app_namefor the brand + login heading; drop/repurpose the hardcodedapp.name.Source: frontend UX/design audit, 2026-06-06.
Done — merged to
main(b7242ca). The header now carries real wayfinding.Breadcrumb (left): a page-driven
useBreadcrumb(trail)hook +BreadcrumbProvider(parallel to the #57 title hook) — every AppShell route sets its trail and the header renders it (non-leaf crumbs are links):Objects/Vocabularies/ …;Objects / New,Objects / Edit;Objects / {object_number};Vocabularies / {key}..keyvia the cache-shareduseVocabularies), so no extra requests and no UUIDs in the crumb.User menu (right): a new reusable
ui/menu.tsxBase UI dropdown wrapper (validated by running) powers aUserMenushowing the signed-inemail+roleand a Sign out item; the standalone Sign out button is gone (logout lives in the menu).Header search (right): a compact
HeaderSearchinput →/search?q=…(encoded), so you can start a search from any screen; hidden belowsm. (The full ⌘K palette / in-place modal remains #33.)Brand + login: the sidebar brand and login heading + tab title now render
useConfig().app_name(default "Collection Management System"); the hardcodedapp.namei18n key is removed from both locales.No new dependency (Base UI Menu is a namespace export of the existing
@base-ui/react); i18n net +1/−1 with en/sv parity; the breadcrumb hook iseslint-disable-free (ref + serialized-key pattern). Gate green: typecheck, lint, 194 tests, build, check:size (196.3 KB gz, +~12 KB for Menu, under the 250 KB budget), check:colors; no codename.Follow-ups (out of scope): full ⌘K command palette (#33); broader responsive header (#58);
/search/:idshowing a search-relative (vs canonical-object) breadcrumb; user avatar / account settings page.