/** * AuthGuard Component * Protects routes by ensuring user is authenticated * Redirects to login if not authenticated, preserving return URL */ 'use client'; import { useEffect } from 'react'; import { useRouter, usePathname } from 'next/navigation'; import { useAuthStore } from '@/stores/authStore'; import { useMe } from '@/lib/api/hooks/useAuth'; import config from '@/config/app.config'; interface AuthGuardProps { children: React.ReactNode; requireAdmin?: boolean; fallback?: React.ReactNode; } /** * Loading spinner component */ function LoadingSpinner() { return (

Loading...

); } /** * AuthGuard - Client component for route protection * * @param children - Protected content to render if authenticated * @param requireAdmin - If true, requires user to be admin (is_superuser) * @param fallback - Optional fallback component while loading * * @example * ```tsx * // In app/(authenticated)/layout.tsx * export default function AuthenticatedLayout({ children }) { * return ( * * {children} * * ); * } * * // For admin routes * export default function AdminLayout({ children }) { * return ( * * {children} * * ); * } * ``` */ export function AuthGuard({ children, requireAdmin = false, fallback }: AuthGuardProps) { const router = useRouter(); const pathname = usePathname(); const { isAuthenticated, isLoading: authLoading, user } = useAuthStore(); // Fetch user data if authenticated but user not loaded const { isLoading: userLoading } = useMe(); // Determine overall loading state const isLoading = authLoading || (isAuthenticated && !user && userLoading); useEffect(() => { // If not loading and not authenticated, redirect to login if (!isLoading && !isAuthenticated) { // Preserve intended destination const returnUrl = pathname !== config.routes.login ? `?returnUrl=${encodeURIComponent(pathname)}` : ''; router.push(`${config.routes.login}${returnUrl}`); } // Note: 401 errors are handled by API interceptor which clears auth and redirects }, [isLoading, isAuthenticated, pathname, router]); // Check admin requirement useEffect(() => { if (requireAdmin && isAuthenticated && user && !user.is_superuser) { // User is authenticated but not admin console.warn('Access denied: Admin privileges required'); // TODO: Create dedicated 403 Forbidden page in Phase 4 router.push(config.routes.home); } }, [requireAdmin, isAuthenticated, user, router]); // Show loading state if (isLoading) { return fallback ? <>{fallback} : ; } // Show nothing if redirecting if (!isAuthenticated) { return null; } // Check admin requirement if (requireAdmin && !user?.is_superuser) { return null; // Will redirect via useEffect } // Render protected content return <>{children}; }