({
children,
fallback,
requireAuth = true,
requireOrg = false,
roles = [],
redirectTo,
})
| 18 | } |
| 19 | |
| 20 | export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ |
| 21 | children, |
| 22 | fallback, |
| 23 | requireAuth = true, |
| 24 | requireOrg = false, |
| 25 | roles = [], |
| 26 | redirectTo, |
| 27 | }) => { |
| 28 | // All hooks must be called unconditionally at the top |
| 29 | const { isAuthenticated, isLoading, user } = useAuth(); |
| 30 | const authProvider = useAuthProvider(); |
| 31 | const { openSignIn, isOpen, close } = useAuthModal(); |
| 32 | const adminUsername = useAuthStore((state) => state.adminUsername); |
| 33 | const adminPassword = useAuthStore((state) => state.adminPassword); |
| 34 | |
| 35 | // Track whether we've attempted to open the sign-in dialog |
| 36 | const [signInAttempted, setSignInAttempted] = useState(false); |
| 37 | |
| 38 | // Function to trigger sign-in (can be called manually) |
| 39 | const triggerSignIn = useCallback(() => { |
| 40 | if (authProvider.name === 'clerk') { |
| 41 | setSignInAttempted(true); |
| 42 | authProvider.signIn(); |
| 43 | } |
| 44 | }, [authProvider]); |
| 45 | |
| 46 | // Automatically trigger sign-in for Clerk when not authenticated (only once on mount) |
| 47 | useEffect(() => { |
| 48 | if ( |
| 49 | !isLoading && |
| 50 | !isAuthenticated && |
| 51 | requireAuth && |
| 52 | authProvider.name === 'clerk' && |
| 53 | !signInAttempted |
| 54 | ) { |
| 55 | triggerSignIn(); |
| 56 | } |
| 57 | }, [isLoading, isAuthenticated, requireAuth, authProvider, signInAttempted, triggerSignIn]); |
| 58 | |
| 59 | // Reset signInAttempted when user becomes authenticated (for future sign-outs) |
| 60 | useEffect(() => { |
| 61 | if (isAuthenticated) { |
| 62 | setSignInAttempted(false); |
| 63 | } |
| 64 | }, [isAuthenticated]); |
| 65 | |
| 66 | // Compute derived values after hooks |
| 67 | const isLocalAuth = authProvider.name === 'local'; |
| 68 | const hasLocalCredentials = !!(adminUsername && adminPassword); |
| 69 | |
| 70 | // Show loading state |
| 71 | if (isLoading) { |
| 72 | return ( |
| 73 | <div className="flex items-center justify-center min-h-96"> |
| 74 | <div className="flex items-center space-x-2 text-muted-foreground"> |
| 75 | <Shield className="w-6 h-6 animate-pulse" /> |
| 76 | <span>Checking authentication...</span> |
| 77 | </div> |
nothing calls this directly
no test coverage detected