Files
Aegis/frontend/src/components/ProtectedRoute.tsx
T
kitos 591b5df250 feat: Phase 7 - Frontend scaffolding and auth (T-023, T-024, T-025)
T-023: Initialize React project
- Vite + React 19 + TypeScript scaffold
- Tailwind CSS v4 with @tailwindcss/vite plugin
- Dependencies: react-router-dom, axios, @tanstack/react-query, lucide-react
- Project structure: api/, components/, pages/, context/, types/, hooks/, lib/

T-024: API client and auth context
- Axios client with JWT interceptor (auto-attach token, clear on 401)
- login() and getMe() API functions
- AuthContext: user state, login, logout, isAuthenticated, isLoading
- Token persistence via localStorage with hydration on mount
- TypeScript types for all backend models

T-025: Login page and layout
- LoginPage with form, error handling, redirect on success
- Layout with sidebar + header + Outlet
- Sidebar with role-aware navigation (System only for admin)
- ProtectedRoute wrapper with role-based access control
- Routes: /login, /dashboard, /techniques, /tests, /system
2026-02-06 16:09:50 +01:00

34 lines
926 B
TypeScript

import { Navigate } from "react-router-dom";
import { useAuth } from "../context/AuthContext";
interface Props {
children: React.ReactNode;
roles?: string[];
}
/**
* Wrapper that redirects to `/login` when the user is not authenticated.
* Optionally restricts access to specific roles (admins always pass).
*/
export default function ProtectedRoute({ children, roles }: Props) {
const { isAuthenticated, isLoading, user } = useAuth();
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center bg-gray-950">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-cyan-500 border-t-transparent" />
</div>
);
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (roles && user && !roles.includes(user.role) && user.role !== "admin") {
return <Navigate to="/dashboard" replace />;
}
return <>{children}</>;
}