import React, { useState, useEffect, useRef } from 'react'; const WS_BASE = (import.meta.env.VITE_API_URL || 'http://localhost:8000').replace('http', 'ws'); const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000'; const DECISION_COLOR = { APPROVED: 'var(--accent)', REVIEW: 'var(--warning)', BLOCKED: 'var(--danger)', }; const DECISION_BG = { APPROVED: 'rgba(0,228,117,0.1)', REVIEW: 'rgba(255,179,0,0.1)', BLOCKED: 'rgba(255,51,102,0.1)', }; export default function MLGuard() { const [events, setEvents] = useState([]); const [connected, setConnected] = useState(false); const [stats, setStats] = useState({ approved: 0, review: 0, blocked: 0, total: 0, avgScore: 0 }); const [refundResult, setRefundResult] = useState(null); const [refundOrderId, setRefundOrderId] = useState(''); const wsRef = useRef(null); const feedEndRef = useRef(null); useEffect(() => { connect(); return () => wsRef.current?.close(); }, []); useEffect(() => { feedEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [events]); const connect = () => { try { const ws = new WebSocket(`${WS_BASE}/ws/fraud-feed`); wsRef.current = ws; ws.onopen = () => setConnected(true); ws.onclose = () => { setConnected(false); setTimeout(connect, 3000); }; ws.onmessage = (e) => { const ev = JSON.parse(e.data); setEvents(prev => [...prev.slice(-79), ev]); setStats(prev => { const total = prev.total + 1; const approved = prev.approved + (ev.decision === 'APPROVED' ? 1 : 0); const review = prev.review + (ev.decision === 'REVIEW' ? 1 : 0); const blocked = prev.blocked + (ev.decision === 'BLOCKED' ? 1 : 0); const avgScore = (prev.avgScore * prev.total + ev.fraud_score) / total; return { total, approved, review, blocked, avgScore }; }); }; } catch { setTimeout(connect, 3000); } }; const runRefundTriage = async () => { const oid = refundOrderId.trim() || 'HF-00001'; try { const res = await fetch(`${API_BASE}/api/ml/refund-triage?order_id=${encodeURIComponent(oid)}`); setRefundResult(await res.json()); } catch { setRefundResult({ error: 'Backend not reachable. Make sure FastAPI is running.' }); } }; return (
Semantic plausibility checker + SLA penalty engine. Auto-approves, flags for manual review, or escalates.
setRefundOrderId(e.target.value)} onKeyDown={e => e.key === 'Enter' && runRefundTriage()} />