'use client' import { useState, useMemo } from 'react' import { Search, ChevronDown, ChevronUp, AlertTriangle, CheckCircle } from 'lucide-react' interface Transaction { id: number amount: number merchant: string category: string is_fraud: number prediction: string final_score: number classical_score: number quantum_score: number } interface TransactionTableProps { transactions: Transaction[] compact?: boolean } export default function TransactionTable({ transactions, compact = false }: TransactionTableProps) { const [search, setSearch] = useState('') const [sortField, setSortField] = useState('id') const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc') const [filterPrediction, setFilterPrediction] = useState<'all' | 'Fraud' | 'Safe'>('all') const [page, setPage] = useState(0) const pageSize = compact ? 10 : 20 const filteredTransactions = useMemo(() => { let result = [...transactions] // Filter by search if (search) { const searchLower = search.toLowerCase() result = result.filter(t => t.merchant?.toLowerCase().includes(searchLower) || t.category?.toLowerCase().includes(searchLower) || t.id.toString().includes(searchLower) ) } // Filter by prediction if (filterPrediction !== 'all') { result = result.filter(t => t.prediction === filterPrediction) } // Sort result.sort((a, b) => { const aVal = a[sortField] const bVal = b[sortField] if (typeof aVal === 'number' && typeof bVal === 'number') { return sortDirection === 'asc' ? aVal - bVal : bVal - aVal } const aStr = String(aVal).toLowerCase() const bStr = String(bVal).toLowerCase() return sortDirection === 'asc' ? aStr.localeCompare(bStr) : bStr.localeCompare(aStr) }) return result }, [transactions, search, sortField, sortDirection, filterPrediction]) const paginatedTransactions = useMemo(() => { const start = page * pageSize return filteredTransactions.slice(start, start + pageSize) }, [filteredTransactions, page, pageSize]) const totalPages = Math.ceil(filteredTransactions.length / pageSize) const handleSort = (field: keyof Transaction) => { if (sortField === field) { setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc') } else { setSortField(field) setSortDirection('desc') } } const SortIcon = ({ field }: { field: keyof Transaction }) => { if (sortField !== field) return null return sortDirection === 'asc' ? : } if (transactions.length === 0) { return (

No transactions to display

) } return (
{/* Filters - only show if not compact */} {!compact && (
{/* Search */}
setSearch(e.target.value)} className="w-full bg-dark-700 border border-dark-600 rounded-lg pl-10 pr-4 py-2 text-sm focus:border-primary-500 focus:outline-none" />
{/* Filter dropdown */}
)} {/* Table */}
{!compact && ( <> )} {paginatedTransactions.map((t) => ( {!compact && ( <> )} ))}
handleSort('id')} >
ID
handleSort('amount')} >
Amount
Merchant Category handleSort('final_score')} >
Risk Score
Status Actual
#{t.id} ${t.amount.toFixed(2)}{t.merchant} {t.category}
0.7 ? 'bg-red-500' : t.final_score > 0.4 ? 'bg-yellow-500' : 'bg-green-500' }`} style={{ width: `${t.final_score * 100}%` }} />
{(t.final_score * 100).toFixed(0)}%
{t.prediction === 'Fraud' ? ( Fraud ) : ( Safe )} {t.is_fraud ? '⚠️ Fraud' : '✓ Safe'}
{/* Pagination - only show if not compact and multiple pages */} {!compact && totalPages > 1 && (

Showing {page * pageSize + 1} - {Math.min((page + 1) * pageSize, filteredTransactions.length)} of {filteredTransactions.length}

Page {page + 1} of {totalPages}
)}
) }