fraudshield/frontend/src/api/client.ts
Felipe Domingues 1175ae7ff0 feat: implement rule management (US3) + dashboard (US4)
US3 - Rule Management:
- Backend: CRUD API + activate/deactivate + test against 90d history
- Frontend: RuleList sidebar, RuleForm (create/edit), RuleTestResults
- Weight/threshold sliders, condition type selector, audit logging

US4 - Dashboard:
- Backend: GET /dashboard/metrics (24h/7d/30d/90d), CSV export
- Frontend: MetricCards, alerts-by-hour bar chart, status pie chart,
  top rules horizontal bar chart via Recharts

Stack: 20 API routes total. Frontend tsc clean, vite build passes.
2026-05-12 19:14:44 -03:00

131 lines
3.4 KiB
TypeScript

const API_BASE = '/api/v1';
function getAuthHeaders(): Record<string, string> {
const token = localStorage.getItem('fraudshield_token');
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (token) headers['Authorization'] = `Bearer ${token}`;
return headers;
}
async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
...options,
headers: { ...getAuthHeaders(), ...((options.headers as Record<string, string>) || {}) },
});
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(err.detail || `HTTP ${res.status}`);
}
return res.json();
}
export interface AlertFilters {
status?: string;
min_score?: number;
from_date?: string;
to_date?: string;
triggered_rule?: string;
limit?: number;
offset?: number;
}
export interface TriggeredRule {
rule_id: string;
rule_name: string;
score: number;
explanation: string;
}
export interface AlertSummary {
id: string;
score: number;
status: string;
amount: number;
merchant_name: string;
top_rule: string;
created_at: string;
}
export interface AlertDetail {
id: string;
transaction_id: string;
score: number;
triggered_rules: TriggeredRule[];
explanation: string;
status: string;
assigned_to?: string | null;
decision?: string | null;
decided_by?: string | null;
decided_at?: string | null;
justification?: string | null;
created_at: string;
amount: number;
merchant_name: string;
merchant_category: string;
channel: string;
}
export interface AlertListResponse {
items: AlertSummary[];
total: number;
limit: number;
offset: number;
}
export interface AlertDecisionRequest {
decision: string;
justification?: string;
notes?: string;
}
export interface TransactionAccepted {
transaction_id: string;
status: string;
trace_id: string;
}
export interface DashboardMetrics {
period: string;
total_transactions: number;
total_alerts: number;
fraud_rate_pct: number;
false_positive_rate_pct: number;
alerts_by_status: { pending: number; confirmed: number; false_positive: number; escalated: number };
alerts_by_hour: { hour: number; count: number }[];
top_triggering_rules: { rule_name: string; alert_count: number }[];
score_distribution: { low: number; medium: number; high: number; critical: number };
}
export interface DetectionRule {
id: string;
name: string;
description: string;
condition: string;
weight: number;
threshold: number;
is_active: boolean;
version: number;
created_by: string;
created_at: string;
updated_at: string;
}
export function listAlerts(params: AlertFilters = {}): Promise<AlertListResponse> {
const qs = new URLSearchParams();
Object.entries(params).forEach(([k, v]) => { if (v !== undefined) qs.set(k, String(v)); });
return apiFetch<AlertListResponse>(`/alerts?${qs.toString()}`);
}
export function getAlert(id: string): Promise<AlertDetail> {
return apiFetch<AlertDetail>(`/alerts/${id}`);
}
export function decideAlert(id: string, data: AlertDecisionRequest): Promise<AlertDetail> {
return apiFetch<AlertDetail>(`/alerts/${id}/decide`, { method: 'POST', body: JSON.stringify(data) });
}
export function getDashboardMetrics(period = '24h'): Promise<DashboardMetrics> {
return apiFetch<DashboardMetrics>(`/dashboard/metrics?period=${period}`);
}
export { getAuthHeaders };