Spaces:
Build error
Build error
| 'use client'; | |
| import { useState, useEffect, useCallback } from 'react'; | |
| import { | |
| RefreshCw, Shield, AlertTriangle, CheckCircle, TrendingUp, | |
| Activity, Zap | |
| } from 'lucide-react'; | |
| import Sidebar from '@/components/Sidebar'; | |
| import SimulationEngine from '@/components/SimulationEngine'; | |
| import ChatPanel from '@/components/ChatPanel'; | |
| // Use environment variable or relative URL for production | |
| const API_URL = process.env.NEXT_PUBLIC_API_URL || ''; | |
| interface Transaction { | |
| id: number; | |
| amount: number; | |
| merchant: string; | |
| category: string; | |
| is_fraud: number; | |
| prediction: string; | |
| final_score: number; | |
| classical_score: number; | |
| quantum_score: number; | |
| quantum_details: { | |
| vqc: number; | |
| qaoa: number; | |
| qnn: number; | |
| }; | |
| } | |
| interface Metrics { | |
| total: number; | |
| flagged: number; | |
| actual_fraud: number; | |
| accuracy: number; | |
| precision: number; | |
| recall: number; | |
| f1: number; | |
| tp: number; | |
| fp: number; | |
| tn: number; | |
| fn: number; | |
| } | |
| export default function Home() { | |
| const [mounted, setMounted] = useState(false); | |
| const [activeTab, setActiveTab] = useState('dashboard'); | |
| const [isRunning, setIsRunning] = useState(false); | |
| const [transactions, setTransactions] = useState<Transaction[]>([]); | |
| const [metrics, setMetrics] = useState<Metrics>({ | |
| total: 0, flagged: 0, actual_fraud: 0, accuracy: 0, precision: 0, recall: 0, f1: 0, tp: 0, fp: 0, tn: 0, fn: 0 | |
| }); | |
| const [lastUpdated, setLastUpdated] = useState<Date | null>(null); | |
| // Hydration fix | |
| useEffect(() => { | |
| setMounted(true); | |
| setLastUpdated(new Date()); | |
| }, []); | |
| // Stable callbacks for SimulationEngine | |
| const handleTransactionUpdate = useCallback((transaction: Transaction, newMetrics: Metrics) => { | |
| setTransactions(prev => [transaction, ...prev].slice(0, 50)); | |
| setMetrics(newMetrics); | |
| setLastUpdated(new Date()); | |
| }, []); | |
| const handleRunningChange = useCallback((running: boolean) => { | |
| setIsRunning(running); | |
| }, []); | |
| // Manual refresh | |
| const processOneTransaction = useCallback(async () => { | |
| try { | |
| const response = await fetch(`${API_URL}/api/process-random`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| handleTransactionUpdate(data.transaction, data.metrics); | |
| } | |
| } catch (error) { | |
| console.error('Failed to process transaction:', error); | |
| } | |
| }, [handleTransactionUpdate]); | |
| const getRiskColor = (score: number) => { | |
| if (score >= 0.7) return 'text-red-700 bg-red-100'; | |
| if (score >= 0.4) return 'text-amber-700 bg-amber-100'; | |
| return 'text-green-700 bg-green-100'; | |
| }; | |
| const getRiskLevel = (score: number) => { | |
| if (score >= 0.7) return { label: 'HIGH', color: 'text-red-600 bg-red-50 border-red-200' }; | |
| if (score >= 0.4) return { label: 'MEDIUM', color: 'text-amber-600 bg-amber-50 border-amber-200' }; | |
| return { label: 'LOW', color: 'text-green-600 bg-green-50 border-green-200' }; | |
| }; | |
| // Page titles and descriptions | |
| const pageInfo: Record<string, { title: string; subtitle: string }> = { | |
| dashboard: { title: 'Dashboard', subtitle: 'Live Transaction Monitoring' }, | |
| models: { title: 'Model Performance', subtitle: 'Quantum & Classical Model Analysis' }, | |
| settings: { title: 'Settings', subtitle: 'System Configuration' }, | |
| }; | |
| const currentPage = pageInfo[activeTab] || pageInfo.dashboard; | |
| return ( | |
| <div className="min-h-screen bg-gray-50 flex"> | |
| <Sidebar activeTab={activeTab} onTabChange={setActiveTab} /> | |
| <div className="flex-1 ml-16"> | |
| {/* Header */} | |
| <header className="sticky top-0 z-40 backdrop-blur-xl bg-white/90 border-b border-gray-200 px-4 py-3 shadow-sm"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <h1 className="text-xl font-bold text-gray-900">{currentPage.title}</h1> | |
| <p className="text-xs text-gray-500">{currentPage.subtitle}</p> | |
| </div> | |
| <div className="flex items-center gap-3"> | |
| {/* System Status */} | |
| <div className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm ${isRunning ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-500'}`}> | |
| <div className={`w-2 h-2 rounded-full ${isRunning ? 'bg-green-500 animate-pulse' : 'bg-gray-400'}`} /> | |
| {isRunning ? 'Processing' : 'Idle'} | |
| </div> | |
| {/* Model Status */} | |
| <div className="hidden md:flex items-center gap-1.5 bg-amber-100 text-amber-700 rounded-lg px-3 py-1.5 text-sm"> | |
| <Zap className="w-3.5 h-3.5 text-amber-500" /> | |
| <span>Quantum Ready</span> | |
| </div> | |
| {/* Transaction Count */} | |
| <div className="hidden lg:flex items-center gap-1.5 bg-gray-100 text-gray-600 rounded-lg px-3 py-1.5 text-sm"> | |
| <Activity className="w-3.5 h-3.5" /> | |
| <span>{metrics.total} txns</span> | |
| </div> | |
| {/* Refresh */} | |
| <button onClick={processOneTransaction} className="p-2 hover:bg-gray-100 rounded-lg transition-colors" title="Process one transaction"> | |
| <RefreshCw className="w-4 h-4 text-gray-500" /> | |
| </button> | |
| </div> | |
| </div> | |
| <p className="text-[10px] text-gray-400 mt-1" suppressHydrationWarning> | |
| Last updated: {mounted && lastUpdated ? lastUpdated.toLocaleTimeString() : '--:--:--'} | |
| </p> | |
| </header> | |
| <main className="p-4"> | |
| {/* Dashboard View */} | |
| {activeTab === 'dashboard' && ( | |
| <> | |
| {/* Simulation Engine - Isolated Component */} | |
| <SimulationEngine | |
| onTransactionUpdate={handleTransactionUpdate} | |
| onRunningChange={handleRunningChange} | |
| /> | |
| {/* Stats Row */} | |
| <div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-3 mb-4"> | |
| <StatCard icon={<Activity className="w-4 h-4" />} label="Transactions" value={metrics.total} color="white" /> | |
| <StatCard icon={<AlertTriangle className="w-4 h-4" />} label="Flagged" value={metrics.flagged} color="yellow" /> | |
| <StatCard icon={<Shield className="w-4 h-4" />} label="Actual Fraud" value={metrics.actual_fraud} color="red" /> | |
| <StatCard icon={<CheckCircle className="w-4 h-4" />} label="Accuracy" value={`${(metrics.accuracy * 100).toFixed(1)}%`} color="green" /> | |
| <StatCard icon={<TrendingUp className="w-4 h-4" />} label="Precision" value={`${(metrics.precision * 100).toFixed(1)}%`} color="green" /> | |
| <StatCard icon={<Zap className="w-4 h-4" />} label="F1 Score" value={`${(metrics.f1 * 100).toFixed(1)}%`} color="yellow" /> | |
| </div> | |
| {/* Main Grid */} | |
| <div className="grid grid-cols-1 xl:grid-cols-3 gap-4"> | |
| {/* Transaction Table */} | |
| <div className="xl:col-span-2 bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm"> | |
| <div className="px-4 py-3 border-b border-gray-200 flex items-center justify-between"> | |
| <h3 className="text-gray-900 font-bold text-sm uppercase tracking-wide">Live Transactions</h3> | |
| <span className="text-xs text-gray-500">{transactions.length} records</span> | |
| </div> | |
| <div className="overflow-x-auto max-h-[400px] overflow-y-auto"> | |
| <table className="w-full text-sm"> | |
| <thead className="bg-gray-50 sticky top-0"> | |
| <tr className="text-gray-600 text-xs"> | |
| <th className="px-3 py-2 text-left">ID</th> | |
| <th className="px-3 py-2 text-left">Amount</th> | |
| <th className="px-3 py-2 text-left">Category</th> | |
| <th className="px-3 py-2 text-left"> | |
| <div className="flex items-center gap-1"> | |
| <span className="text-blue-600">C</span>/<span className="text-purple-600">Q</span> | |
| </div> | |
| </th> | |
| <th className="px-3 py-2 text-left">Final</th> | |
| <th className="px-3 py-2 text-left">Risk</th> | |
| <th className="px-3 py-2 text-left">Prediction</th> | |
| <th className="px-3 py-2 text-left">Actual</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {transactions.length === 0 ? ( | |
| <tr><td colSpan={8} className="px-3 py-8 text-center text-gray-400 text-sm">Click "Start" to begin processing transactions</td></tr> | |
| ) : ( | |
| transactions.map((tx, idx) => { | |
| const risk = getRiskLevel(tx.final_score); | |
| return ( | |
| <tr key={tx.id} className={`border-b border-gray-100 hover:bg-gray-50 transition-colors ${idx === 0 ? 'bg-blue-50 animate-pulse' : ''}`}> | |
| <td className="px-3 py-2 text-gray-500 font-mono text-xs">#{tx.id}</td> | |
| <td className="px-3 py-2 text-gray-900 font-medium">${tx.amount.toFixed(2)}</td> | |
| <td className="px-3 py-2 text-gray-500 text-xs">{tx.category}</td> | |
| <td className="px-3 py-2"> | |
| <div className="flex items-center gap-1"> | |
| <span className="text-xs text-blue-600 font-medium">{((tx.classical_score || tx.final_score * 0.8) * 100).toFixed(0)}%</span> | |
| <span className="text-gray-400">/</span> | |
| <span className="text-xs text-purple-600 font-medium">{((tx.quantum_score || tx.final_score * 0.2) * 100).toFixed(0)}%</span> | |
| </div> | |
| </td> | |
| <td className="px-3 py-2"> | |
| <span className={`px-2 py-0.5 rounded text-xs font-medium ${getRiskColor(tx.final_score)}`}> | |
| {(tx.final_score * 100).toFixed(0)}% | |
| </span> | |
| </td> | |
| <td className="px-3 py-2"> | |
| <span className={`px-2 py-0.5 rounded text-xs font-semibold border ${risk.color}`}> | |
| {risk.label} | |
| </span> | |
| </td> | |
| <td className="px-3 py-2"> | |
| <span className={`px-2 py-0.5 rounded text-xs ${tx.prediction === 'Fraud' ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'}`}> | |
| {tx.prediction} | |
| </span> | |
| </td> | |
| <td className="px-3 py-2"> | |
| <span className={`px-2 py-0.5 rounded text-xs ${tx.is_fraud ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'}`}> | |
| {tx.is_fraud ? 'Fraud' : 'Legit'} | |
| </span> | |
| </td> | |
| </tr> | |
| ); | |
| }) | |
| )} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| {/* Chat Panel - Isolated Component */} | |
| <ChatPanel | |
| transactions={transactions} | |
| metrics={{ total: metrics.total, flagged: metrics.flagged, accuracy: metrics.accuracy }} | |
| /> | |
| </div> | |
| {/* Bottom Row - Model Performance & Confusion Matrix */} | |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 mt-4"> | |
| {/* Live Quantum Analysis */} | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <h3 className="text-gray-900 font-bold text-sm mb-3 flex items-center gap-2 uppercase tracking-wide"> | |
| <Zap className="w-4 h-4 text-amber-500" /> Live Quantum Analysis | |
| </h3> | |
| {transactions.length > 0 ? ( | |
| <> | |
| <div className="space-y-3"> | |
| {/* Latest Transaction Quantum Breakdown */} | |
| <div className="bg-gradient-to-r from-indigo-50 to-purple-50 rounded-lg p-3 border border-indigo-100"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="text-xs text-gray-500">Transaction #{transactions[0].id}</span> | |
| <span className={`text-xs px-2 py-0.5 rounded ${transactions[0].prediction === 'Fraud' ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'}`}> | |
| {transactions[0].prediction} | |
| </span> | |
| </div> | |
| <div className="grid grid-cols-2 gap-2 text-center"> | |
| <div className="bg-white/60 rounded p-2"> | |
| <p className="text-blue-600 font-bold font-data text-lg">{((transactions[0].classical_score || 0) * 100).toFixed(1)}%</p> | |
| <p className="text-[10px] text-gray-500">Classical</p> | |
| </div> | |
| <div className="bg-white/60 rounded p-2"> | |
| <p className="text-purple-600 font-bold font-data text-lg">{((transactions[0].quantum_score || 0) * 100).toFixed(1)}%</p> | |
| <p className="text-[10px] text-gray-500">Quantum</p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Quantum Model Scores */} | |
| <div className="space-y-2"> | |
| {[ | |
| { name: 'VQC', value: transactions[0].quantum_details?.vqc || 0, color: 'bg-indigo-500', desc: 'Variational Quantum Classifier' }, | |
| { name: 'QAOA', value: transactions[0].quantum_details?.qaoa || 0, color: 'bg-purple-500', desc: 'Quantum Optimization' }, | |
| { name: 'QNN', value: transactions[0].quantum_details?.qnn || 0, color: 'bg-violet-500', desc: 'Quantum Neural Network' } | |
| ].map((model) => ( | |
| <div key={model.name} className="flex items-center gap-2"> | |
| <span className="text-gray-600 text-xs w-10 font-medium">{model.name}</span> | |
| <div className="flex-1 h-2 bg-gray-200 rounded-full overflow-hidden"> | |
| <div className={`h-full ${model.color} rounded-full transition-all duration-500`} style={{ width: `${model.value * 100}%` }} /> | |
| </div> | |
| <span className="text-gray-700 text-xs font-medium w-10 text-right">{(model.value * 100).toFixed(0)}%</span> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="mt-3 pt-3 border-t border-gray-200 flex justify-between text-xs"> | |
| <span className="text-gray-500">Final Score</span> | |
| <span className={`font-bold ${transactions[0].final_score >= 0.5 ? 'text-red-600' : 'text-green-600'}`}> | |
| {(transactions[0].final_score * 100).toFixed(1)}% | |
| </span> | |
| </div> | |
| </> | |
| ) : ( | |
| <div className="text-center text-gray-400 py-6 text-sm"> | |
| Start simulation to see live quantum analysis | |
| </div> | |
| )} | |
| </div> | |
| {/* Confusion Matrix */} | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <h3 className="text-gray-900 font-bold text-sm mb-3 uppercase tracking-wide">Confusion Matrix</h3> | |
| <div className="grid grid-cols-2 gap-2"> | |
| <div className="bg-green-100 rounded-lg p-3 text-center"> | |
| <p className="text-green-700 text-xl font-bold">{metrics.tp}</p> | |
| <p className="text-[10px] text-gray-500">True Positive</p> | |
| </div> | |
| <div className="bg-red-100 rounded-lg p-3 text-center"> | |
| <p className="text-red-700 text-xl font-bold">{metrics.fp}</p> | |
| <p className="text-[10px] text-gray-500">False Positive</p> | |
| </div> | |
| <div className="bg-amber-100 rounded-lg p-3 text-center"> | |
| <p className="text-amber-700 text-xl font-bold">{metrics.fn}</p> | |
| <p className="text-[10px] text-gray-500">False Negative</p> | |
| </div> | |
| <div className="bg-gray-100 rounded-lg p-3 text-center"> | |
| <p className="text-gray-700 text-xl font-bold">{metrics.tn}</p> | |
| <p className="text-[10px] text-gray-500">True Negative</p> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Model Weights */} | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <h3 className="text-gray-900 font-bold text-sm mb-3 uppercase tracking-wide">Model Weights</h3> | |
| <div className="space-y-2"> | |
| <div className="flex items-center justify-between bg-gray-50 rounded-lg px-3 py-2"> | |
| <span className="text-gray-600 text-xs">Classical Model</span> | |
| <span className="text-gray-900 font-bold font-data">80%</span> | |
| </div> | |
| <div className="flex items-center justify-between bg-gray-50 rounded-lg px-3 py-2"> | |
| <span className="text-gray-600 text-xs">Quantum Ensemble</span> | |
| <span className="text-gray-900 font-bold font-data">20%</span> | |
| </div> | |
| <div className="flex items-center justify-between bg-green-50 rounded-lg px-3 py-2 mt-3"> | |
| <span className="text-gray-600 text-xs">Ensemble Accuracy</span> | |
| <span className="text-green-600 font-bold font-data">{(metrics.accuracy * 100).toFixed(1) || '0.0'}%</span> | |
| </div> | |
| <div className="flex items-center justify-between bg-green-50 rounded-lg px-3 py-2"> | |
| <span className="text-gray-600 text-xs">System Status</span> | |
| <span className="text-green-600 font-bold">Online</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </> | |
| )} | |
| {/* Models View */} | |
| {activeTab === 'models' && ( | |
| <div className="space-y-4"> | |
| {/* Model Architecture Overview */} | |
| <div className="bg-white rounded-xl p-6 border border-gray-200 shadow-sm"> | |
| <h2 className="text-xl font-bold text-gray-900 mb-4 flex items-center gap-2 uppercase tracking-wide"> | |
| <Zap className="w-5 h-5 text-amber-500" /> Hybrid Model Architecture | |
| </h2> | |
| <p className="text-gray-600 text-sm mb-6"> | |
| Our fraud detection system uses a hybrid ensemble combining classical machine learning with quantum computing for enhanced pattern recognition. | |
| </p> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| {/* Classical Model */} | |
| <div className="bg-gray-50 rounded-lg p-4 border border-gray-200"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h3 className="text-gray-900 font-bold uppercase tracking-wide">Classical Model</h3> | |
| <span className="text-2xl font-bold text-gray-900 font-data">80%</span> | |
| </div> | |
| <p className="text-gray-500 text-xs mb-3">XGBoost Gradient Boosting</p> | |
| <div className="w-full h-3 bg-gray-200 rounded-full overflow-hidden"> | |
| <div className="h-full bg-gray-700 rounded-full" style={{ width: '80%' }} /> | |
| </div> | |
| <ul className="mt-3 space-y-1 text-xs text-gray-600"> | |
| <li>• High accuracy on structured data</li> | |
| <li>• Fast inference time</li> | |
| <li>• Handles missing values</li> | |
| </ul> | |
| </div> | |
| {/* Quantum Ensemble */} | |
| <div className="bg-gradient-to-br from-indigo-50 to-purple-50 rounded-lg p-4 border border-indigo-200"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h3 className="text-gray-900 font-bold uppercase tracking-wide">Quantum Ensemble</h3> | |
| <span className="text-2xl font-bold text-indigo-600 font-data">20%</span> | |
| </div> | |
| <p className="text-gray-500 text-xs mb-3">VQC + QAOA + QNN</p> | |
| <div className="w-full h-3 bg-indigo-200 rounded-full overflow-hidden"> | |
| <div className="h-full bg-indigo-500 rounded-full" style={{ width: '20%' }} /> | |
| </div> | |
| <ul className="mt-3 space-y-1 text-xs text-gray-600"> | |
| <li>• Detects quantum patterns</li> | |
| <li>• Finds subtle correlations</li> | |
| <li>• Improves edge cases</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Quantum Models Detail */} | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <div className="flex items-center gap-3 mb-3"> | |
| <div className="w-10 h-10 rounded-lg bg-indigo-100 flex items-center justify-center"> | |
| <span className="text-indigo-700 font-bold text-sm">VQC</span> | |
| </div> | |
| <div> | |
| <h3 className="text-gray-900 font-semibold text-sm">Variational Quantum Classifier</h3> | |
| <p className="text-gray-400 text-xs">40% of quantum weight</p> | |
| </div> | |
| </div> | |
| <p className="text-gray-600 text-xs"> | |
| Uses parameterized quantum circuits for classification. Learns optimal rotations for feature encoding. | |
| </p> | |
| <div className="mt-3 pt-3 border-t border-gray-200"> | |
| <div className="flex justify-between text-xs"> | |
| <span className="text-gray-400">Qubits</span> | |
| <span className="text-gray-900">4</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <div className="flex items-center gap-3 mb-3"> | |
| <div className="w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center"> | |
| <span className="text-purple-700 font-bold text-sm">QAOA</span> | |
| </div> | |
| <div> | |
| <h3 className="text-gray-900 font-semibold text-sm">Quantum Approximate Optimization</h3> | |
| <p className="text-gray-400 text-xs">30% of quantum weight</p> | |
| </div> | |
| </div> | |
| <p className="text-gray-600 text-xs"> | |
| Finds optimal solutions for combinatorial problems. Used for feature subset selection. | |
| </p> | |
| <div className="mt-3 pt-3 border-t border-gray-200"> | |
| <div className="flex justify-between text-xs"> | |
| <span className="text-gray-400">Layers</span> | |
| <span className="text-gray-900">2</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <div className="flex items-center gap-3 mb-3"> | |
| <div className="w-10 h-10 rounded-lg bg-violet-100 flex items-center justify-center"> | |
| <span className="text-violet-700 font-bold text-sm">QNN</span> | |
| </div> | |
| <div> | |
| <h3 className="text-gray-900 font-semibold text-sm">Quantum Neural Network</h3> | |
| <p className="text-gray-400 text-xs">30% of quantum weight</p> | |
| </div> | |
| </div> | |
| <p className="text-gray-600 text-xs"> | |
| Deep quantum circuits with entanglement layers. Captures complex non-linear patterns. | |
| </p> | |
| <div className="mt-3 pt-3 border-t border-gray-200"> | |
| <div className="flex justify-between text-xs"> | |
| <span className="text-gray-400">Depth</span> | |
| <span className="text-gray-900">3</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Performance Metrics */} | |
| <div className="bg-white rounded-xl p-4 border border-gray-200 shadow-sm"> | |
| <h3 className="text-gray-900 font-semibold text-sm mb-4">Current Session Performance</h3> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> | |
| <div className="text-center"> | |
| <p className="text-3xl font-bold text-green-600">{(metrics.accuracy * 100).toFixed(1)}%</p> | |
| <p className="text-gray-500 text-xs">Accuracy</p> | |
| </div> | |
| <div className="text-center"> | |
| <p className="text-3xl font-bold text-green-600">{(metrics.precision * 100).toFixed(1)}%</p> | |
| <p className="text-gray-500 text-xs">Precision</p> | |
| </div> | |
| <div className="text-center"> | |
| <p className="text-3xl font-bold text-amber-500">{(metrics.recall * 100).toFixed(1)}%</p> | |
| <p className="text-gray-500 text-xs">Recall</p> | |
| </div> | |
| <div className="text-center"> | |
| <p className="text-3xl font-bold text-amber-500">{(metrics.f1 * 100).toFixed(1)}%</p> | |
| <p className="text-gray-500 text-xs">F1 Score</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Settings View */} | |
| {activeTab === 'settings' && ( | |
| <div className="space-y-4 max-w-2xl"> | |
| <div className="bg-white rounded-xl p-6 border border-gray-200 shadow-sm"> | |
| <h2 className="text-xl font-bold text-gray-900 mb-4">System Settings</h2> | |
| <div className="space-y-4"> | |
| <div className="flex items-center justify-between py-3 border-b border-gray-200"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">API Endpoint</h3> | |
| <p className="text-gray-500 text-xs">Backend server URL</p> | |
| </div> | |
| <code className="bg-gray-100 px-3 py-1.5 rounded text-gray-700 text-xs font-mono">{API_URL}</code> | |
| </div> | |
| <div className="flex items-center justify-between py-3 border-b border-gray-200"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">Classical Weight</h3> | |
| <p className="text-gray-500 text-xs">XGBoost contribution</p> | |
| </div> | |
| <span className="text-gray-900 font-bold">80%</span> | |
| </div> | |
| <div className="flex items-center justify-between py-3 border-b border-gray-200"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">Quantum Weight</h3> | |
| <p className="text-gray-500 text-xs">Ensemble contribution</p> | |
| </div> | |
| <span className="text-indigo-600 font-bold">20%</span> | |
| </div> | |
| <div className="flex items-center justify-between py-3 border-b border-gray-200"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">Fraud Threshold</h3> | |
| <p className="text-gray-500 text-xs">Score cutoff for flagging</p> | |
| </div> | |
| <span className="text-gray-900 font-bold">0.5</span> | |
| </div> | |
| <div className="flex items-center justify-between py-3"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">Transaction Buffer</h3> | |
| <p className="text-gray-500 text-xs">Max transactions in view</p> | |
| </div> | |
| <span className="text-gray-900 font-bold">50</span> | |
| </div> | |
| </div> | |
| </div> | |
| {/* HuggingFace Cloud Integration */} | |
| <div className="bg-gradient-to-br from-amber-50 to-orange-50 rounded-xl p-6 border border-amber-200 shadow-sm"> | |
| <div className="flex items-center gap-2 mb-4"> | |
| <span className="text-2xl">🤗</span> | |
| <h2 className="text-lg font-bold text-gray-900">HuggingFace Cloud Integration</h2> | |
| </div> | |
| <p className="text-gray-600 text-sm mb-4"> | |
| Offload heavy ML computations to HuggingFace Inference API to reduce local system burden. | |
| </p> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between bg-white/60 rounded-lg px-4 py-3"> | |
| <div> | |
| <h3 className="text-gray-900 font-medium text-sm">Status</h3> | |
| <p className="text-gray-500 text-xs">Cloud inference availability</p> | |
| </div> | |
| <span className="px-3 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-600"> | |
| Check Backend | |
| </span> | |
| </div> | |
| <div className="bg-white/60 rounded-lg px-4 py-3"> | |
| <h3 className="text-gray-900 font-medium text-sm mb-2">Setup Instructions</h3> | |
| <ol className="text-xs text-gray-600 space-y-1 list-decimal list-inside"> | |
| <li>Get free API key from <a href="https://huggingface.co/settings/tokens" target="_blank" rel="noopener noreferrer" className="text-amber-600 hover:underline">huggingface.co/settings/tokens</a></li> | |
| <li>Create a <code className="bg-gray-100 px-1 rounded">.env</code> file in project root</li> | |
| <li>Add: <code className="bg-gray-100 px-1 rounded">HUGGINGFACE_API_KEY=your_key_here</code></li> | |
| <li>Restart the backend server</li> | |
| </ol> | |
| </div> | |
| <div className="text-xs text-amber-700 bg-amber-100 rounded-lg px-4 py-2"> | |
| 💡 Free tier includes ~30K requests/month - perfect for development and testing! | |
| </div> | |
| </div> | |
| </div> | |
| <div className="bg-white rounded-xl p-6 border border-gray-200 shadow-sm"> | |
| <h2 className="text-lg font-bold text-gray-900 mb-3">About</h2> | |
| <p className="text-gray-600 text-sm"> | |
| QuantumShield is a hybrid quantum-classical fraud detection system. It combines the power of XGBoost for handling structured transaction data with quantum computing circuits (VQC, QAOA, QNN) for enhanced pattern recognition. | |
| </p> | |
| <div className="mt-4 pt-4 border-t border-gray-200 flex gap-4 text-xs text-gray-400"> | |
| <span>Built with PennyLane</span> | |
| <span>•</span> | |
| <span>Next.js Frontend</span> | |
| <span>•</span> | |
| <span>FastAPI Backend</span> | |
| <span>•</span> | |
| <span>🤗 HuggingFace</span> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function StatCard({ icon, label, value, color }: { icon: React.ReactNode; label: string; value: string | number; color: string }) { | |
| const colors: Record<string, string> = { | |
| white: 'bg-white border-gray-200', | |
| yellow: 'bg-amber-50 border-amber-200', | |
| red: 'bg-red-50 border-red-200', | |
| green: 'bg-green-50 border-green-200', | |
| }; | |
| const textColors: Record<string, string> = { | |
| white: 'text-gray-900', | |
| yellow: 'text-amber-600', | |
| red: 'text-red-600', | |
| green: 'text-green-600', | |
| }; | |
| const iconColors: Record<string, string> = { | |
| white: 'text-gray-500', | |
| yellow: 'text-amber-500', | |
| red: 'text-red-500', | |
| green: 'text-green-500', | |
| }; | |
| return ( | |
| <div className={`${colors[color]} rounded-xl p-3 border shadow-sm`}> | |
| <div className="flex items-center gap-2 mb-1"> | |
| <span className={iconColors[color]}>{icon}</span> | |
| <span className="text-gray-500 text-[10px] uppercase font-bold tracking-wider">{label}</span> | |
| </div> | |
| <p className={`text-xl font-bold font-data tracking-tight ${textColors[color]}`}>{value}</p> | |
| </div> | |
| ); | |
| } | |