import { useState } from 'react'; import { Terminal, ShieldCheck, Radio, History, Mail, Activity, Maximize2, Minimize2, Search, Download, Filter, AlertOctagon, GitCompare } from 'lucide-react'; import type { BottomTab, TerminalEntry, AuditEntry, ValidationIssue } from '../types'; import { sampleTerminal, sampleValidation, sampleAudit, sampleSettlement, sampleReconciliation, sampleExceptions, sampleMessageQueue, sampleEvents } from '../data/sampleData'; const tabs: { id: BottomTab; icon: typeof Terminal; label: string }[] = [ { id: 'terminal', icon: Terminal, label: 'Terminal' }, { id: 'validation', icon: ShieldCheck, label: 'Validation' }, { id: '800system', icon: Radio, label: '800 System' }, { id: 'settlement', icon: Activity, label: 'Settlement Queue' }, { id: 'audit', icon: History, label: 'Audit Trail' }, { id: 'messages', icon: Mail, label: 'Messages' }, { id: 'events', icon: Activity, label: 'Events' }, { id: 'reconciliation', icon: GitCompare, label: 'Reconciliation' }, { id: 'exceptions', icon: AlertOctagon, label: 'Exceptions' }, ]; interface BottomPanelProps { height: number; isExpanded: boolean; onToggleExpand: () => void; terminalEntries: TerminalEntry[]; auditEntries: AuditEntry[]; validationIssues: ValidationIssue[]; } const statusColors: Record = { settled: '#22c55e', pending: '#eab308', in_review: '#3b82f6', awaiting_approval: '#f97316', dispatched: '#3b82f6', partially_settled: '#a855f7', failed: '#ef4444', }; const levelColors: Record = { info: '#6b7280', warn: '#eab308', error: '#ef4444', success: '#22c55e', }; export default function BottomPanel({ height, isExpanded, onToggleExpand, terminalEntries, auditEntries, validationIssues }: BottomPanelProps) { const [activeTab, setActiveTab] = useState('terminal'); const [terminalFilter, setTerminalFilter] = useState(''); const [bottomSearch, setBottomSearch] = useState(''); const [showSearchBar, setShowSearchBar] = useState(false); const [showFilterBar, setShowFilterBar] = useState(false); const [levelFilter, setLevelFilter] = useState('all'); const allTerminal = [...sampleTerminal, ...terminalEntries]; const filteredTerminal = allTerminal.filter(e => { const matchesText = e.message.toLowerCase().includes(terminalFilter.toLowerCase()) || e.source.toLowerCase().includes(terminalFilter.toLowerCase()); const matchesLevel = levelFilter === 'all' || e.level === levelFilter; return matchesText && matchesLevel; }); const allAudit = [...sampleAudit, ...auditEntries]; const allValidation = validationIssues.length > 0 ? validationIssues : sampleValidation; const handleExport = () => { let content = ''; if (activeTab === 'terminal') { content = allTerminal.map(e => `[${e.timestamp.toISOString()}] [${e.level}] [${e.source}] ${e.message}`).join('\n'); } else if (activeTab === 'audit') { content = allAudit.map(e => `[${e.timestamp.toISOString()}] ${e.user} ${e.action}: ${e.detail}`).join('\n'); } else if (activeTab === 'validation') { content = allValidation.map(e => `[${e.severity}] ${e.node ? e.node + ': ' : ''}${e.message}`).join('\n'); } else { content = `Export of ${activeTab} tab data`; } const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `transactflow-${activeTab}-${new Date().toISOString().slice(0, 10)}.txt`; a.click(); URL.revokeObjectURL(url); }; return (
{tabs.map(tab => { const Icon = tab.icon; return ( ); })}
{showSearchBar && (
setBottomSearch(e.target.value)} autoFocus />
)} {showFilterBar && activeTab === 'terminal' && (
{['all', 'info', 'warn', 'error', 'success'].map(level => ( ))}
)}
{activeTab === 'terminal' && (
setTerminalFilter(e.target.value)} />
{filteredTerminal.map(entry => (
{entry.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} [{entry.level.toUpperCase()}] [{entry.source}] {entry.message}
))}
)} {activeTab === 'validation' && (
{allValidation.map(issue => (
{issue.severity.toUpperCase()} {issue.node && {issue.node}} {issue.message}
))}
)} {activeTab === '800system' && (
Message Queue
0 pending
Healthy
Core Banking
Connected
Online
Ledger Feed
0 postings/s
Idle
SWIFT Gateway
Ready
Online
ISO-20022 Engine
3 schemas
Loaded
Retry Queue
0 items
Clear
)} {activeTab === 'settlement' && (
{sampleSettlement.map(item => ( ))}
TX ID Status Amount Asset Counterparty Time
{item.txId} {item.status.replace(/_/g, ' ')} {item.amount} {item.asset} {item.counterparty} {item.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
)} {activeTab === 'audit' && (
{allAudit.map(entry => (
{entry.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} {entry.user} {entry.action} {entry.detail}
))}
)} {activeTab === 'messages' && (
{sampleMessageQueue.map(msg => ( ))}
Type Direction Counterparty Status Time
{msg.msgType} {msg.direction === 'inbound' ? '← IN' : '→ OUT'} {msg.counterparty} {msg.status} {msg.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
)} {activeTab === 'events' && (
{sampleEvents.map(evt => (
{evt.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} {evt.type} {evt.detail}
))}
)} {activeTab === 'reconciliation' && (
{sampleReconciliation.map(item => ( ))}
TX ID Internal Ref External Ref Status Amount Asset
{item.txId} {item.internalRef} {item.externalRef} {item.status} {item.amount} {item.asset}
)} {activeTab === 'exceptions' && (
{sampleExceptions.map(exc => (
{exc.severity.toUpperCase()} {exc.txId} {exc.type} {exc.message}
))}
)}
); }