import { useEffect, ReactNode, useState } from "react"; import { Navigate, useLocation } from "react-router-dom"; import { useAuthStore } from "../../store/authStore"; import { useErrorHandler } from "../../hooks/useErrorHandler"; import { trackLoading } from "../common/LoadingStateMonitor"; interface ProtectedRouteProps { children: ReactNode; } /** * ProtectedRoute component - guards routes requiring authentication * Redirects to /signin if user is not authenticated */ export default function ProtectedRoute({ children }: ProtectedRouteProps) { const { isAuthenticated, loading, user, logout } = useAuthStore(); const location = useLocation(); const { addError } = useErrorHandler('ProtectedRoute'); const [showError, setShowError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const PLAN_ALLOWED_PATHS = [ '/account/plans', '/account/purchase-credits', '/account/settings', '/account/team', '/account/usage', '/billing', '/payment', ]; const isPlanAllowedPath = PLAN_ALLOWED_PATHS.some((prefix) => location.pathname.startsWith(prefix) ); // Track loading state useEffect(() => { trackLoading('auth-loading', loading); }, [loading]); // Validate account + plan whenever auth/user changes useEffect(() => { console.log('[ProtectedRoute] Auth state changed:', { isAuthenticated, hasUser: !!user, hasAccount: !!user?.account, pathname: location.pathname }); if (!isAuthenticated) { console.log('[ProtectedRoute] Not authenticated, will redirect to signin'); return; } if (!user?.account) { console.error('[ProtectedRoute] User has no account, logging out'); setErrorMessage('This user is not linked to an account. Please contact support.'); logout(); return; } console.log('[ProtectedRoute] Auth validation passed'); }, [isAuthenticated, user, logout]); // Immediate check on mount: if loading is true, reset it immediately useEffect(() => { if (loading) { console.warn('ProtectedRoute: Loading state is true on mount, resetting immediately'); useAuthStore.setState({ loading: false }); } }, []); // Safety timeout: if loading becomes true and stays stuck, show error useEffect(() => { if (loading) { const timeout1 = setTimeout(() => { setErrorMessage('Authentication check is taking longer than expected. This may indicate a network or server issue.'); setShowError(true); addError(new Error('Auth loading stuck for 3 seconds'), 'ProtectedRoute'); }, 3000); const timeout2 = setTimeout(() => { console.error('ProtectedRoute: Loading state stuck for 5 seconds, forcing reset'); useAuthStore.setState({ loading: false }); setShowError(false); }, 5000); return () => { clearTimeout(timeout1); clearTimeout(timeout2); }; } else { setShowError(false); } }, [loading, addError]); // Show loading state while checking authentication if (loading) { return (

Loading...

{showError && (

{errorMessage}

)}
); } // Redirect to signin if not authenticated if (!isAuthenticated) { console.log('[ProtectedRoute] Redirecting to /signin - not authenticated'); return ; } // If authenticated but missing an active plan, keep user inside billing/onboarding const accountStatus = user?.account?.status; const accountInactive = accountStatus && ['suspended', 'cancelled'].includes(accountStatus); const pendingPayment = accountStatus === 'pending_payment'; const isPrivileged = user?.role === 'developer' || user?.is_superuser; if (!isPrivileged) { if (pendingPayment && !isPlanAllowedPath) { return ; } if (accountInactive && !isPlanAllowedPath) { return ; } } return <>{children}; }