Skip to content

fix: use React Router for frontend SPA navigation#3134

Open
adityathebe wants to merge 2 commits into
mainfrom
ui/spa
Open

fix: use React Router for frontend SPA navigation#3134
adityathebe wants to merge 2 commits into
mainfrom
ui/spa

Conversation

@adityathebe
Copy link
Copy Markdown
Member

@adityathebe adityathebe commented May 22, 2026

Sidebar navigation in ui/frontend was using a mix of manual history handling, raw anchors, and window.location.href, causing full document reloads.\n\nReplace the custom route state layer with react-router-dom, wire /ui routes through BrowserRouter, and convert internal navigation to Link/NavLink.\n\nThe sidebar now stays mounted during navigation; only active selection/content changes update.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added React Router v7 integration for improved application navigation and routing.
  • Refactor

    • Updated internal navigation system to use React Router primitives throughout the application.
    • Standardized link handling with a unified AppLink component for consistent routing behavior across all pages and features.

Review Change Stack

Sidebar and internal UI navigation used a mix of manual history updates, raw anchors, and window.location.href. That forced document reloads and remounted the frontend when users navigated between sidebar routes.

Wire the frontend through react-router-dom, define /ui routes declaratively, and convert internal navigation to Link/NavLink/AppLink so route changes update content and active state without rebuilding the shell.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Warning

Rate limit exceeded

@adityathebe has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 37 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a136fb1b-053c-4366-abc5-a0a795fe3c04

📥 Commits

Reviewing files that changed from the base of the PR and between 56643b5 and 24200c9.

📒 Files selected for processing (1)
  • ui/frontend/src/App.tsx

Walkthrough

This PR completes the migration from a custom history-based routing scheme to React Router v6/v7 across the frontend. It adds the react-router-dom dependency, introduces centralized navigation utilities, wraps the app with BrowserRouter, refactors App.tsx to use Routes/Route components, and updates all navigation patterns throughout the codebase to use Router primitives like useNavigate, useSearchParams, NavLink, and a shared AppLink component.

Changes

React Router Migration

Layer / File(s) Summary
Routing foundation and helpers
ui/frontend/package.json, ui/frontend/src/main.tsx, ui/frontend/src/navigation.tsx
Added react-router-dom dependency; created navigation.tsx with UI_BASE constant, routerPathFromHref, uiHref, isUiHref utilities, and AppLink component; wrapped app with BrowserRouter and basename="/ui" in main.tsx.
App component routing refactor
ui/frontend/src/App.tsx
Removed custom Route type and history-based routing; introduced Routes/Route with explicit route components (Home, TypeRoute, ItemRoute, etc.); updated sidebar to use NavLink instead of computed href + active state; added decodeParam helper and updated buildCommandHref to use UI_BASE; command runtime now uses useNavigate(routerPathFromHref(...)).
Catalog sidebar React Router integration
ui/frontend/src/CatalogSidebar.tsx
Exported as memo-wrapped component; removed selected prop and CatalogSidebarProps; leaf nodes now render as NavLink entries; navigation switched from window.location.href assignment to useNavigate with local typePath helper.
TypeView search params and table navigation
ui/frontend/src/TypeView.tsx
Switched from custom location-search hook to useNavigate + useSearchParams; config-type filter now calls navigateToConfigType(navigate, ...) to drive route transitions; table row navigation changed from getRowHref to onRowClick with navigate(...).
PlaybookBrowser navigation overhaul
ui/frontend/src/playbooks/PlaybookBrowser.tsx
Imports useNavigate, useSearchParams, Link from react-router-dom; replaced multiple <a> elements with AppLink (History action, run links, back navigation); tabs now render as AppLink entries; "Clear" filter control switched to Link; post-submit dialog uses useNavigate instead of window.history manipulation; usePlaybookRunFilters now derives params from useSearchParams.
AccessBrowser table and matrix navigation
ui/frontend/src/access/AccessBrowser.tsx
Added useNavigate() in users/groups/membership list components; replaced table getRowHref with onRowClick handlers; permissions matrix links changed from <a> to AppLink.
AppLink adoption across detail/config components
ui/frontend/src/components/ConfigItemSelector.tsx, ui/frontend/src/config-detail/ConfigItemDetail.tsx, ui/frontend/src/layout/DetailPageLayout.tsx, ui/frontend/src/settings/SettingsBrowser.tsx
Systematically replaced raw anchor tags with AppLink component for internal navigation: config type links, parent/location links, relationship node links, breadcrumb links, access matrix links, and scraper row links; preserved all existing href, title, and styling attributes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: migrating the frontend SPA navigation from manual history handling to React Router, which is the primary objective and focus across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ui/spa
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch ui/spa

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
ui/frontend/src/TypeView.tsx (1)

259-272: 💤 Low value

Consider preserving link semantics for row navigation.

Switching from getRowHref to onRowClick removes standard link behaviors—users can no longer middle-click or right-click → "Open in new tab" to open config items in separate tabs. If the DataTable component supports both getRowHref (for the anchor element) and onRowClick (for SPA interception), combining them would preserve both browser link affordances and client-side navigation.

If the component doesn't support that pattern or this UX trade-off is intentional, feel free to disregard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/frontend/src/TypeView.tsx` around lines 259 - 272, The row navigation
currently uses only onRowClick in ConfigTable, which loses native link
semantics; add a getRowHref prop to the DataTable that returns
`/item/${encodeURIComponent(String(row.id))}` (matching getRowId and using the
same encoding) so each row renders a real anchor, and keep the existing
onRowClick handler (navigate(...)) for SPA interception—this preserves
middle-click/right-click behavior while retaining client-side navigation via
navigate in onRowClick.
ui/frontend/src/main.tsx (1)

4-4: ⚡ Quick win

Avoid duplicating the UI base path constant.

basename="/ui" duplicates UI_BASE and can silently drift from link-generation helpers.

Diff suggestion
 import { BrowserRouter } from "react-router-dom";
+import { UI_BASE } from "./navigation";
@@
-          <BrowserRouter basename="/ui">
+          <BrowserRouter basename={UI_BASE}>
             <App />
           </BrowserRouter>

Also applies to: 49-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/frontend/src/main.tsx` at line 4, The Router is hardcoding basename="/ui"
which duplicates the existing UI_BASE constant and can drift from link helpers;
update main.tsx to import and use the shared UI_BASE constant instead of the
literal string wherever basename is set (replace the BrowserRouter
basename="/ui" usage and the other occurrences around the 49-51 area), and
ensure any link-generation helpers continue to reference the same UI_BASE to
keep routing consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/frontend/src/App.tsx`:
- Around line 141-147: The route components are double-decoding URL params by
calling the decodeParam helper on values already returned decoded by useParams;
update ItemRoute and the TypeRoute/TypeView usage to stop calling decodeParam on
params from useParams (pass id and configType directly from useParams into
ItemView/TypeView), and either remove/rename or change the decodeParam helper to
a no-op for values already from useParams so other call sites aren’t
double-decoded (refer to ItemRoute, TypeView usage, and the decodeParam helper).

In `@ui/frontend/src/navigation.tsx`:
- Around line 6-10: routerPathFromHref currently only treats exact UI_BASE and
UI_BASE + '/' as internal, so URLs like "/ui?x=..." or "/ui#hash" fall through;
update routerPathFromHref to treat any href that equals UI_BASE or starts with
UI_BASE followed by '/', '?' or '#' as internal: if href === UI_BASE return "/";
else if href.startsWith(UI_BASE) and the next character (href[UI_BASE.length])
is '/', '?' or '#' return href.slice(UI_BASE.length) (ensuring leading '/' when
slicing yields empty); adjust the same logic used around lines 16-18 if there is
a symmetric helper to keep behavior consistent.

In `@ui/frontend/src/playbooks/PlaybookBrowser.tsx`:
- Line 1420: The navigate call currently uses
navigate(`/playbooks/runs/${encodeURIComponent(response.run_id)}`) which misses
the required "/ui" route prefix; update this invocation in PlaybookBrowser.tsx
to use the same routing pattern as other occurrences by changing the path to
`/ui/playbooks/runs/${encodeURIComponent(response.run_id)}` so navigation
matches routes used elsewhere (search for the navigate(...) call referencing
response.run_id to locate it).
- Around line 1899-1902: Update the Link in PlaybookBrowser.tsx that currently
navigates to "/playbooks/runs" so it uses the same UI route prefix as the rest
of the component (change the route to "/ui/playbooks/runs"); locate the Link
element near the Clear button (the JSX containing <Link ...> with Icon
name="lucide:x") and modify its to prop to include the "/ui" prefix to match
other references in this file.

---

Nitpick comments:
In `@ui/frontend/src/main.tsx`:
- Line 4: The Router is hardcoding basename="/ui" which duplicates the existing
UI_BASE constant and can drift from link helpers; update main.tsx to import and
use the shared UI_BASE constant instead of the literal string wherever basename
is set (replace the BrowserRouter basename="/ui" usage and the other occurrences
around the 49-51 area), and ensure any link-generation helpers continue to
reference the same UI_BASE to keep routing consistent.

In `@ui/frontend/src/TypeView.tsx`:
- Around line 259-272: The row navigation currently uses only onRowClick in
ConfigTable, which loses native link semantics; add a getRowHref prop to the
DataTable that returns `/item/${encodeURIComponent(String(row.id))}` (matching
getRowId and using the same encoding) so each row renders a real anchor, and
keep the existing onRowClick handler (navigate(...)) for SPA interception—this
preserves middle-click/right-click behavior while retaining client-side
navigation via navigate in onRowClick.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b82a66d5-eece-4663-a866-3c66789e61e2

📥 Commits

Reviewing files that changed from the base of the PR and between 93a892f and 56643b5.

⛔ Files ignored due to path filters (1)
  • ui/frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • ui/frontend/package.json
  • ui/frontend/src/App.tsx
  • ui/frontend/src/CatalogSidebar.tsx
  • ui/frontend/src/TypeView.tsx
  • ui/frontend/src/access/AccessBrowser.tsx
  • ui/frontend/src/components/ConfigItemSelector.tsx
  • ui/frontend/src/config-detail/ConfigItemDetail.tsx
  • ui/frontend/src/layout/DetailPageLayout.tsx
  • ui/frontend/src/main.tsx
  • ui/frontend/src/navigation.tsx
  • ui/frontend/src/playbooks/PlaybookBrowser.tsx
  • ui/frontend/src/settings/SettingsBrowser.tsx

Comment thread ui/frontend/src/App.tsx Outdated
Comment thread ui/frontend/src/navigation.tsx
Comment thread ui/frontend/src/playbooks/PlaybookBrowser.tsx
Comment thread ui/frontend/src/playbooks/PlaybookBrowser.tsx
React Router v7 already decodes values returned by useParams. Remove the extra decodeURIComponent helper from route wrappers so IDs and config types containing literal percent-encoded sequences are passed through unchanged.
@adityathebe adityathebe requested a review from moshloop May 22, 2026 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant