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

29
frontend/src/api/auth.ts Normal file
View File

@@ -0,0 +1,29 @@
import client from "./client";
import type { User } from "../types/models";
interface TokenResponse {
access_token: string;
token_type: string;
}
/** Authenticate and return the access token. */
export async function login(
username: string,
password: string,
): Promise<string> {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
const { data } = await client.post<TokenResponse>("/auth/login", params, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
return data.access_token;
}
/** Fetch the currently authenticated user profile. */
export async function getMe(): Promise<User> {
const { data } = await client.get<User>("/auth/me");
return data;
}