Skip to content

Convert route guards to React Router v8 middleware#11780

Open
nbudin wants to merge 6 commits into
mainfrom
fix-single-event-partials
Open

Convert route guards to React Router v8 middleware#11780
nbudin wants to merge 6 commits into
mainfrom
fix-single-event-partials

Conversation

@nbudin

@nbudin nbudin commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds React Router v8 middleware support (v8_middleware: true future flag) with an appRootMiddleware that runs the AppRoot query once per navigation and stores the result in appRootDataContext, eliminating redundant fetches
  • Converts all four render-time route guard components (EventPageGuard, EditEventGuard, AppRootContextRouteGuard, AuthorizationRequiredRouteGuard, LoginRequiredRouteGuard) to middleware that runs before loaders
  • Adds RouteErrorBoundary to handle 401 (OAuth login spinner), 403 (authorization error), and 404 (FourOhFourPage) responses returned by middleware, preserving existing UX within the app layout
  • Fixes a bug where /admin_events/:eventId/edit was inaccessible on single-event sites
  • Adds wrappers to load the Liquid-wrapped versions of event components with correct data

Test plan

  • Navigate to a convention site — home page, events, schedule load correctly
  • Navigate to a single-event site — event page loads, schedule/catalog routes redirect to /
  • Visit an admin route while logged out — OAuth spinner appears, login redirects back correctly
  • Visit an admin route without the required ability — authorization error is shown within the layout
  • Visit a non-existent route — 404 page is shown within the layout
  • On a single-event site, /admin_events/:eventId/edit is accessible; on a multi-event site it redirects to /admin_events

🤖 Generated with Claude Code

nbudin and others added 6 commits June 28, 2026 11:00
Single-event conventions redirect /admin_events to /:eventId/edit, but
that path had no matching route — only the multi-event paths existed under
the siteMode !== SingleEvent guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract RouteErrorBoundary to its own module for reuse
- Convert AppRoot from a static element to a lazy-loaded route module
  (exports Component, loader, ErrorBoundary instead of default export)
- Enable v8_middleware in createBrowserRouter; add appRootDataContext to
  the per-navigation router context so middleware can share data with loaders
- Add appRootMiddleware on the AppRoot route: runs the AppRootQuery once
  per navigation and stores the result in appRootDataContext, so the query
  is never duplicated across middleware and loaders
- AppRoot loader now reads from appRootDataContext rather than issuing its
  own query; retains the profile-setup mutation and redirect logic
- Replace EditEventGuard (render-time side-effect guard) with
  editEventMiddleware: reads siteMode from appRootDataContext and redirects
  single-event sites to /admin_events before any rendering occurs; restore
  LoginRequiredRouteGuard as the route element
- Fix EventPageGuard: move navigate() call into useEffect to avoid
  calling a side effect during render

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Converts the render-time EventPageGuard component to a middleware function,
consistent with editEventMiddleware. Removes useEffect/useNavigate imports
that were only needed for the guard's render-side navigation call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the render-time AppRootContextRouteGuard component with a
makeAppRootContextMiddleware factory that reads appRootDataContext and
returns a 404 Response before loaders run. RouteErrorBoundary now
renders FourOhFourPage for 404 responses so the layout is preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the render-time AuthorizationRequiredRouteGuard component with
makeAuthorizationMiddleware, which returns 401 for unauthenticated users
and 403 for users lacking the required abilities. RouteErrorBoundary now
handles 401 by rendering a LoginRequired component (preserving the async
OAuth spinner behavior) and 403 by rendering AuthorizationError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the render-time LoginRequiredRouteGuard component with a
loginRequiredMiddleware constant that returns 401 when no current user
is present. RouteErrorBoundary already handles 401 with LoginRequired,
so the OAuth spinner behavior is preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nbudin nbudin added frontend minor Bumps the minor version number on release tech debt labels Jun 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/javascript/AppRoot.tsx 🔴 0% 🔴 7.32% 🟢 7.32%
Overall Coverage 🟢 54.31% 🟢 54.26% 🔴 -0.05%

Minimum allowed coverage is 0%, this run produced 54.26%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend minor Bumps the minor version number on release tech debt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant