Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 16x 16x 16x 16x 16x 16x 16x 15x 2x 2x 13x 1x 16x 5x 11x 3x 8x 1x 7x | import { useEffect } from "react";
import { useNavigate, useRouterState } from "@tanstack/react-router";
import { useAuth } from "../../lib/auth-context";
import { usePro } from "../../lib/pro-context";
import { LoadingSpinner } from "./LoadingSpinner";
type OnboardingGuardProps = {
children: React.ReactNode;
};
/**
* OnboardingGuard wraps protected pro routes.
* It redirects users to /onboarding if they haven't completed onboarding.
* This guard should be used AFTER AuthGuard in the component hierarchy.
*/
export function OnboardingGuard({ children }: OnboardingGuardProps) {
const { isAuthenticated, isLoading: authLoading, hasProAccess } = useAuth();
const { onboardingRequired, pendingInvitationToken, isLoading: proLoading } = usePro();
const navigate = useNavigate();
const routerState = useRouterState();
const currentPath = routerState.location.pathname;
const isLoading = authLoading || proLoading;
useEffect(() => {
// Redirect to accept invitation if user has a pending team invitation
if (!isLoading && isAuthenticated && pendingInvitationToken) {
navigate({ to: "/accept-invitation", search: { token: pendingInvitationToken } });
return;
}
// Only redirect if we have pro access and onboarding is required.
// Admin-only users don't need onboarding.
// The `!currentPath.startsWith("/onboarding")` check is the real loop guard:
// it prevents re-redirect on remount when the user is already on /onboarding.
if (
!isLoading &&
isAuthenticated &&
hasProAccess &&
onboardingRequired &&
!currentPath.startsWith("/onboarding")
) {
navigate({ to: "/onboarding" });
}
}, [
isLoading,
isAuthenticated,
hasProAccess,
onboardingRequired,
pendingInvitationToken,
currentPath,
navigate,
]);
if (isLoading) {
return <LoadingSpinner />;
}
// Show spinner while redirecting to accept invitation
if (pendingInvitationToken) {
return <LoadingSpinner />;
}
// Show spinner while redirecting to onboarding
if (hasProAccess && onboardingRequired && !currentPath.startsWith("/onboarding")) {
return <LoadingSpinner />;
}
return <>{children}</>;
}
|