adminFetch called logout() directly on 401/403 responses, which cleared roles and caused RequireAdmin to redirect to /exchanges while users were editing forms. Now adminFetch attempts a token refresh before failing, and RequireAdmin tolerates a transient empty-roles state during refresh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
25 lines
956 B
TypeScript
25 lines
956 B
TypeScript
import { Navigate, Outlet } from 'react-router';
|
|
import { useAuthStore, useIsAdmin } from './auth-store';
|
|
|
|
/**
|
|
* Route guard for admin pages.
|
|
*
|
|
* Redirects non-admin users to '/'. The guard is intentionally lenient when
|
|
* the user is authenticated but their roles array is transiently empty (e.g.
|
|
* during a token refresh). In that case we render nothing rather than
|
|
* navigating away — the refresh will restore the roles within milliseconds and
|
|
* avoid losing unsaved form state on admin pages.
|
|
*/
|
|
export function RequireAdmin() {
|
|
const isAdmin = useIsAdmin();
|
|
const roles = useAuthStore((s) => s.roles);
|
|
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
|
|
// Authenticated but roles not yet populated (token refresh in progress) —
|
|
// render nothing and wait rather than redirecting.
|
|
if (isAuthenticated && roles.length === 0) return null;
|
|
|
|
if (!isAdmin) return <Navigate to="/" replace />;
|
|
return <Outlet />;
|
|
}
|