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
This commit is contained in:
2026-02-06 16:09:50 +01:00
parent 52d230628d
commit 591b5df250
26 changed files with 3489 additions and 4 deletions

41
frontend/src/App.tsx Normal file
View File

@@ -0,0 +1,41 @@
import { Routes, Route, Navigate } from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import DashboardPage from "./pages/DashboardPage";
import TechniquesPage from "./pages/TechniquesPage";
import TestsPage from "./pages/TestsPage";
import SystemPage from "./pages/SystemPage";
import Layout from "./components/Layout";
import ProtectedRoute from "./components/ProtectedRoute";
export default function App() {
return (
<Routes>
{/* Public */}
<Route path="/login" element={<LoginPage />} />
{/* Protected — wrapped in shared Layout */}
<Route
element={
<ProtectedRoute>
<Layout />
</ProtectedRoute>
}
>
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/techniques" element={<TechniquesPage />} />
<Route path="/tests" element={<TestsPage />} />
<Route
path="/system"
element={
<ProtectedRoute roles={["admin"]}>
<SystemPage />
</ProtectedRoute>
}
/>
</Route>
{/* Catch-all → dashboard */}
<Route path="*" element={<Navigate to="/dashboard" replace />} />
</Routes>
);
}