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

View File

@@ -0,0 +1,91 @@
/* ── Shared TypeScript types matching the backend Pydantic schemas ── */
export interface User {
id: string;
username: string;
role: string;
}
export interface Technique {
id: string;
mitre_id: string;
name: string;
description: string | null;
tactic: string | null;
platforms: string[];
mitre_version: string | null;
mitre_last_modified: string | null;
is_subtechnique: boolean;
parent_mitre_id: string | null;
status_global: TechniqueStatus;
review_required: boolean;
last_review_date: string | null;
}
export type TechniqueStatus =
| "not_evaluated"
| "in_progress"
| "validated"
| "partial"
| "not_covered"
| "review_required";
export interface Test {
id: string;
technique_id: string;
name: string;
description: string | null;
platform: string | null;
procedure_text: string | null;
tool_used: string | null;
execution_date: string | null;
created_by: string | null;
result: TestResult | null;
state: TestState;
validated_by: string | null;
validated_at: string | null;
created_at: string;
}
export type TestState = "draft" | "in_review" | "validated" | "rejected";
export type TestResult = "detected" | "not_detected" | "partially_detected";
export interface Evidence {
id: string;
test_id: string;
file_name: string;
file_path: string;
sha256_hash: string;
uploaded_by: string | null;
uploaded_at: string;
}
export interface IntelItem {
id: string;
technique_id: string | null;
url: string;
title: string | null;
source: string | null;
detected_at: string;
reviewed: boolean;
}
export interface CoverageSummary {
total_techniques: number;
validated: number;
partial: number;
not_covered: number;
in_progress: number;
not_evaluated: number;
coverage_percentage: number;
}
export interface TacticCoverage {
tactic: string;
total: number;
validated: number;
partial: number;
not_covered: number;
not_evaluated: number;
in_progress: number;
}