/** * ProtectedRoute — renders children when authenticated; redirects to /login otherwise. * * Additional gate (M2-T07): * - If the authenticated user has force_password_change === true, redirect to * /change-password instead of rendering children. This prevents access to any * protected page until the password is changed. * - Shows a loading spinner while the session is still resolving to avoid flash-of-login. * - On unauthenticated access, preserves the intended destination in location.state.from * so LoginPage can redirect back after login. */ import type { ReactNode } from 'react' import { Navigate, useLocation } from 'react-router-dom' import { Center, Loader } from '@mantine/core' import { useSession } from './SessionProvider' interface ProtectedRouteProps { children: ReactNode } export function ProtectedRoute({ children }: ProtectedRouteProps) { const { status, user } = useSession() const location = useLocation() if (status === 'loading') { // Render a centred spinner while we check the session — avoids a flash to /login. return (
) } if (status === 'unauthenticated') { // Preserve the intended destination so LoginPage can redirect back after login. return } // Authenticated but forced to change password — gate all protected pages. if (user?.force_password_change && location.pathname !== '/change-password') { return } return <>{children} }