Spaces:
Build error
Build error
| 'use client' | |
| import { Grid3X3 } from 'lucide-react' | |
| interface Metrics { | |
| tp: number | |
| fp: number | |
| tn: number | |
| fn: number | |
| } | |
| interface ConfusionMatrixProps { | |
| metrics: Metrics | null | |
| } | |
| export default function ConfusionMatrix({ metrics }: ConfusionMatrixProps) { | |
| if (!metrics) { | |
| return ( | |
| <div className="glass rounded-xl p-6 h-full flex flex-col items-center justify-center"> | |
| <Grid3X3 className="w-12 h-12 text-dark-500 mb-4" /> | |
| <p className="text-dark-400 text-center"> | |
| Process transactions to see confusion matrix | |
| </p> | |
| </div> | |
| ) | |
| } | |
| const total = metrics.tp + metrics.fp + metrics.tn + metrics.fn | |
| const Cell = ({ | |
| value, | |
| label, | |
| color, | |
| description | |
| }: { | |
| value: number | |
| label: string | |
| color: string | |
| description: string | |
| }) => ( | |
| <div | |
| className={` | |
| ${color} rounded-lg p-4 text-center transition-transform hover:scale-105 | |
| `} | |
| title={description} | |
| > | |
| <p className="text-2xl font-bold">{value}</p> | |
| <p className="text-xs opacity-80">{label}</p> | |
| <p className="text-xs opacity-60 mt-1"> | |
| {total > 0 ? ((value / total) * 100).toFixed(1) : 0}% | |
| </p> | |
| </div> | |
| ) | |
| return ( | |
| <div className="glass rounded-xl p-6"> | |
| <h3 className="text-lg font-semibold mb-4 flex items-center gap-2"> | |
| <Grid3X3 className="w-5 h-5 text-primary-500" /> | |
| Detection Performance | |
| </h3> | |
| {/* Matrix labels */} | |
| <div className="mb-2 text-center"> | |
| <span className="text-xs text-dark-400">Predicted</span> | |
| </div> | |
| <div className="flex"> | |
| {/* Y-axis label */} | |
| <div className="flex items-center -rotate-90 mr-2"> | |
| <span className="text-xs text-dark-400 whitespace-nowrap">Actual</span> | |
| </div> | |
| {/* Matrix grid */} | |
| <div className="flex-1"> | |
| {/* Column headers */} | |
| <div className="grid grid-cols-2 gap-2 mb-2"> | |
| <div className="text-center text-xs text-dark-400">Safe</div> | |
| <div className="text-center text-xs text-dark-400">Fraud</div> | |
| </div> | |
| {/* Matrix cells */} | |
| <div className="space-y-2"> | |
| {/* Row: Actual Safe */} | |
| <div className="grid grid-cols-2 gap-2"> | |
| <Cell | |
| value={metrics.tn} | |
| label="True Negative" | |
| color="bg-gray-600/50" | |
| description="Correctly identified as safe" | |
| /> | |
| <Cell | |
| value={metrics.fp} | |
| label="False Positive" | |
| color="bg-yellow-500/30" | |
| description="Safe but flagged as fraud" | |
| /> | |
| </div> | |
| {/* Row: Actual Fraud */} | |
| <div className="grid grid-cols-2 gap-2"> | |
| <Cell | |
| value={metrics.fn} | |
| label="False Negative" | |
| color="bg-red-500/30" | |
| description="Fraud but missed" | |
| /> | |
| <Cell | |
| value={metrics.tp} | |
| label="True Positive" | |
| color="bg-green-500/30" | |
| description="Correctly caught fraud" | |
| /> | |
| </div> | |
| </div> | |
| {/* Row labels */} | |
| <div className="mt-2 grid grid-cols-2 gap-2"> | |
| <div className="text-xs text-dark-400 text-right pr-4">Safe</div> | |
| <div className="text-xs text-dark-400 text-right pr-4">Fraud</div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Summary stats */} | |
| <div className="mt-4 pt-4 border-t border-dark-700 grid grid-cols-2 gap-4 text-sm"> | |
| <div> | |
| <span className="text-dark-400">F1 Score</span> | |
| <p className="font-semibold text-primary-500"> | |
| {(() => { | |
| const precision = metrics.tp / (metrics.tp + metrics.fp) || 0 | |
| const recall = metrics.tp / (metrics.tp + metrics.fn) || 0 | |
| const f1 = 2 * (precision * recall) / (precision + recall) || 0 | |
| return f1.toFixed(3) | |
| })()} | |
| </p> | |
| </div> | |
| <div> | |
| <span className="text-dark-400">Total</span> | |
| <p className="font-semibold">{total.toLocaleString()}</p> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } | |