[Frontend] Add a persistent nav bar with real destinations #378
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Impact: HIGH
Found in the August 2026 session lifecycle review (#319).
What the user experiences
The only persistent navigation in the entire product is a corner strip with a display name, Profile, Admin (for admins), a theme toggle, and Sign out. There is no global link to campaigns, no "my next session", no search, and no command palette outside a hidden
Ctrl-Kshortcut buried inside the Session Shelf. Every cross-page move depends on a page-specific "← Campaigns" / "← Campaign" / "← Session" breadcrumb, so navigation is a strict tree walk: getting from a wiki article to a different campaign's session requires walking back up through the wiki, which itself opens in a new browser tab and destroys the back button for that entire subtree.Evidence
webapp/frontend/src/components/NavBar.jsx:16-40— contains only display name, Profile, Admin, theme toggle, Sign out; no dashboard link, no campaign switcher, no search.webapp/frontend/src/pages/CampaignDetail.jsx:2587-2605— the "Wiki" tab (TabLink) is styled identically to the adjacent no-op "Campaign Journal" tab but is actually atarget="_blank"link (:2594, also:2785), so clicking it opens a new tab instead of swapping the panel — the user's expectation of an in-place tab switch is violated, and it breaks back-button navigation for the whole wiki subtree.Why it matters for a hosted product
Without a persistent nav spine, the Workbench, the Quote Board, and the wiki each depend on a single buried link surviving every redesign, and every cross-campaign move is a manual multi-click tree walk. This compounds every other buried-entry-point finding in this milestone.
Proposed fix
Add a campaign switcher, a "Quest Board" home link, and — when inside a campaign — direct links to Sessions / Prep / Wiki / Quotes to
NavBar.jsx. Droptarget="_blank"fromCampaignDetail.jsx:2594and:2785at the same time so the wiki opens in-place. This is the audit's P2.Acceptance criteria
NavBarshows a persistent link back to the dashboard from every page.NavBarprovides a campaign switcher when the user is a member of more than one campaign.NavBar(or an equivalent persistent element) links directly to Sessions, Prep, Wiki, and Quotes for that campaign.CampaignDetail.jsx:2594and:2785no longer open the wiki in a new tab.Picking this up as v4.3.0 phase 2 (#514), the spine, on one lane with #379 so the nav links carry the final names from the start. Decisions: one nav component rendered from the app layout; "inside a campaign" comes from the route match with a small campaign context as fallback for session pages; the switcher reads the campaign list the dashboard already fetches; labels are Sessions / Prep / Wiki / Highlights; route paths do not change; per-page breadcrumbs stay until #374 and #386 reduce them.
Done in PR #519 (merged); on dev now, prod with v4.3.0.
NavBaris now the whole bar rather than the right-hand cluster of one, andApp.jsxrenders it once from a layout route (AppLayout) instead of each page pasting a copy into its own header. Left to right: the Quest Board wordmark (always home), a campaign switcher, and, inside a campaign, Sessions / Prep / Wiki / Highlights for it. The existing right-hand cluster (name, Profile, Admin, theme, Sign out) is unchanged. On a phone the bar is one wrapping flex row, so the campaign links drop to a second line rather than overflow; no new dependency. Folding the guard into the layout also removed 16 copies of<AuthGuard>."Inside a campaign" is read from the route on
/campaigns/:id/*, and on the session surfaces (whose URL has no campaign id) from a declaration the page makes once it has loaded its session (SessionDetail,RecordingDashboard,SessionShelf, via the newCampaignContext.jsx). Declarations are tagged with the pathname they were made on, so a stale campaign cannot linger after you navigate away, with no clear-on-unmount to flicker the links between two sessions of the same campaign.Switcher data is the campaign list the dashboard already fetches, requested once in the bar's mount effect; the layout outlives every page, so that is one request per app load, and the shared client de-duplicates it against the dashboard's call. It appears only when you are in more than one campaign; with exactly one, the bar shows that campaign's name instead. Prep is GM-only:
/planningalready bounces everyone else, and a link that exists to reject you is worse than no link.Labels use #379's vocabulary from the start (Sessions / Prep / Wiki / Highlights, not "Quotes").
Also: the wiki tab and "Open wiki" button on the campaign page dropped
target="_blank"(the tab was styled identically to the no-op journal tab and broke the back button for the whole wiki subtree), and the shelf's "Draft created — Review" link is now an in-app<Link>. Every remaining_blankin the frontend points at an external URL.One layout knock-on worth knowing: pages each carried
min-h-screen, which under an in-flow bar put a permanent scrollbar on every page. The shell owns viewport height now and the page roots dropped it; the two wiki layouts that hard-codedmin-h-[calc(100vh-61px)]useflex-1. Per-page breadcrumbs are untouched; #374 and #386 reduce them.SessionTable(the player kiosk) declares no campaign and so shows no campaign links; it fetches only reveals and has no campaign id, which #386 can revisit.8 new tests in
NavBar.test.jsx; suite 523 passed (48 files), eslint clean apart from the pre-existingCampaignDetailwarning,vite buildgreen.