import React, { useState, useEffect } from 'react'; import { ShieldAlert, UploadCloud, RefreshCw, Settings, CheckCircle, Activity, FileSpreadsheet, AlertTriangle, Zap } from 'lucide-react'; import DashboardCharts from './components/DashboardCharts'; import TransactionTable from './components/TransactionTable'; import ManualQueryForm from './components/ManualQueryForm'; const BACKEND_URL = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' ? 'http://127.0.0.1:8000' : window.location.origin; export default function App() { const [modelName, setModelName] = useState('voting'); const [threshold, setThreshold] = useState(0.30); const [file, setFile] = useState(null); const [dragActive, setDragActive] = useState(false); const [processing, setProcessing] = useState(false); const [stats, setStats] = useState(null); const [lastUsedConfig, setLastUsedConfig] = useState(null); const [backendHealth, setBackendHealth] = useState({ connected: false, loading: true }); const [activeTab, setActiveTab] = useState('csv'); // 'csv' or 'manual' // Check backend health const checkHealth = async () => { try { const res = await fetch(`${BACKEND_URL}/api/health`); const data = await res.json(); if (data.status === 'healthy') { setBackendHealth({ connected: true, loading: false }); } else { setBackendHealth({ connected: false, loading: false }); } } catch (err) { setBackendHealth({ connected: false, loading: false }); } }; useEffect(() => { checkHealth(); // Poll health every 10 seconds const interval = setInterval(checkHealth, 10000); return () => clearInterval(interval); }, []); const handleDrag = (e) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { const droppedFile = e.dataTransfer.files[0]; if (droppedFile.name.endsWith('.csv')) { setFile(droppedFile); } else { alert("Only CSV files are supported!"); } } }; const handleFileChange = (e) => { if (e.target.files && e.target.files[0]) { setFile(e.target.files[0]); } }; const handleUpload = async () => { if (!file) return; setProcessing(true); setStats(null); const formData = new FormData(); formData.append("file", file); formData.append("model_name", modelName); formData.append("threshold", threshold); try { const response = await fetch(`${BACKEND_URL}/api/predict-csv`, { method: "POST", body: formData, }); if (!response.ok) { let errorMsg = "Upload analysis failed"; try { const errorData = await response.json(); errorMsg = errorData.detail || errorMsg; } catch (_) { try { errorMsg = await response.text(); } catch (_) {} } throw new Error(errorMsg); } const data = await response.json(); setStats(data); setLastUsedConfig({ modelName, threshold }); } catch (err) { console.error(err); alert(`Error running analysis: ${err.message}`); } finally { setProcessing(false); } }; // Submit manual prediction const handleManualPredict = async (dataRow) => { const response = await fetch(`${BACKEND_URL}/api/predict-single`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ data: dataRow, model_name: modelName, threshold: threshold }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.detail || "Query failed"); } return await response.json(); }; const configChanged = stats && lastUsedConfig && (lastUsedConfig.modelName !== modelName || lastUsedConfig.threshold !== threshold); return (
{/* 1. HEADER BRANDING */}

FraudShield AI

Suspicious Transactions & Mule Account Detection

{/* Backend health status badge */}
Backend: {backendHealth.loading ? ( Connecting... ) : backendHealth.connected ? ( ONLINE ) : ( OFFLINE )}
{/* 2. PARAMETERS CONTROLLER BAR */}

Global ML Config

{/* Model Selector */}
Active Model:
{/* Threshold Slider */}
Alert Sensitivity:
setThreshold(parseFloat(e.target.value))} /> {threshold.toFixed(2)}
{/* 3. APP NAVIGATION TABS */}
{/* 4. WORKSPACES */} {activeTab === 'csv' ? (
{configChanged && (
Configuration modified: Model settings have changed. Please click Execute Analysis to apply these settings to the active dataset.
)} {/* Drag & Drop Upload Container */}
{file && (
)}
{/* LOADING SCREEN */} {processing && (

Processing Financial Transaction Logs

Applying Robust Imputation, Vectorized Min-Max Scaling, and ensemble predictions. Please wait...

)} {/* CSV METRICS & RESULTS PANELS */} {stats && (
{/* Metrics summary row */}
{/* Metric 1 */}

Transactions Analyzed

{stats.total_count.toLocaleString()}

{/* Metric 2 */}

Suspicious Flagged

{stats.suspicious_count.toLocaleString()}

{/* Metric 3 */}

Alert Rate

{Math.round(stats.suspicious_rate * 1000) / 10}%

{/* Metric 4 */}

Mule Account Leads

{stats.mule_accounts?.length || 0}

{/* Recharts widgets */} {/* Transactions logs table */}
)}
) : ( /* MANUAL CHECK WORKSPACE */

Suspicious Indicators Explanation

Our machine learning models evaluate suspicious proceeds based on several key operational rules derived from the training dataset:

  • Transaction Interval (F3894): High frequency or consecutive rapid transfers (e.g., intervals under 5 seconds) suggest automated routing scripts often used by mule systems.
  • Target Account Profiling (F3886 & F3891): Accounts owned by students, unemployed individuals, or newly opened profiles receiving high-volume deposits.
  • Channel Threat Mapping (F3893): Rapid internet/mobile-based channels showing sudden variance compared to retail/branch histories.
  • Limit-matching amounts (F3895): Transactions structured just below mandatory reporting limits (e.g. daily account limits) to avoid immediate audit.
💡 Tip: Toggle between the Safe Preset and the Suspicious Preset in the inspector to test how the threshold affects the risk assessments.
)}
); }