import { useMemo, useState } from 'react'; import { Building2, ChevronRight, ChevronDown, Search, Filter, Plus, Download, ExternalLink, Copy, MoreHorizontal } from 'lucide-react'; import { sampleAccounts } from '../data/portalData'; import type { Account, AccountType } from '../types/portal'; import { useOnChainBalances } from '../hooks/useOnChainBalances'; import type { OnChainBalance } from '../services/chain138'; import OnChainBalanceTag from '../components/portal/OnChainBalanceTag'; import { explorerAddressUrl } from '../services/explorer'; const typeColors: Record = { operating: '#3b82f6', reserve: '#22c55e', custody: '#a855f7', escrow: '#f97316', settlement: '#06b6d4', nostro: '#eab308', vostro: '#ec4899', collateral: '#6366f1', treasury: '#14b8a6', crypto_wallet: '#8b5cf6', stablecoin: '#10b981', omnibus: '#64748b', }; const formatBalance = (amount: number, currency: string) => { if (currency === 'BTC') return `${amount.toFixed(4)} BTC`; if (currency === 'USDC') return `$${amount.toLocaleString()}`; const sym = currency === 'USD' ? '$' : currency === 'EUR' ? '€' : currency === 'GBP' ? '£' : ''; if (Math.abs(amount) >= 1_000_000) return `${sym}${(amount / 1_000_000).toFixed(2)}M`; return `${sym}${amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; }; interface AccountRowProps { account: Account; level?: number; onChainBalances: Record; balancesLoading: boolean; } function AccountRow({ account, level = 0, onChainBalances, balancesLoading }: AccountRowProps) { const [expanded, setExpanded] = useState(false); const hasChildren = account.subaccounts && account.subaccounts.length > 0; const onChain = account.walletAddress ? onChainBalances[account.walletAddress] : undefined; return ( <>
{hasChildren ? ( ) : ( )}
{account.name} {account.type.replace('_', ' ')} {account.walletAddress && ( )}
{account.currency}
{formatBalance(account.balance, account.currency)}
{formatBalance(account.availableBalance, account.currency)}
{account.status}
{account.iban && {account.iban}} {account.walletAddress && ( {account.walletAddress.slice(0, 10)}… )} {account.swift && {account.swift}}
{account.lastActivity.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{expanded && hasChildren && account.subaccounts!.map(sub => ( ))} ); } export default function AccountsPage() { const [search, setSearch] = useState(''); const [typeFilter, setTypeFilter] = useState('all'); const [view, setView] = useState<'tree' | 'flat'>('tree'); const onChainAddresses = useMemo( () => sampleAccounts .flatMap(a => [a, ...(a.subaccounts || [])]) .filter(a => !!a.walletAddress) .map(a => a.walletAddress as string), [], ); const { balances: onChainBalances, loading: balancesLoading } = useOnChainBalances(onChainAddresses); const allAccounts = view === 'flat' ? sampleAccounts.flatMap(a => [a, ...(a.subaccounts || [])]) : sampleAccounts; const filtered = allAccounts.filter(a => { const matchSearch = a.name.toLowerCase().includes(search.toLowerCase()) || a.currency.toLowerCase().includes(search.toLowerCase()) || a.type.includes(search.toLowerCase()); const matchType = typeFilter === 'all' || a.type === typeFilter; return matchSearch && matchType && (view === 'flat' || !a.parentId); }); const totalBalance = sampleAccounts.reduce((sum, a) => { if (a.currency === 'USD' || a.currency === 'USDC') return sum + a.balance; if (a.currency === 'EUR') return sum + a.balance * 1.08; if (a.currency === 'GBP') return sum + a.balance * 1.27; if (a.currency === 'BTC') return sum + a.balance * 67_000; return sum; }, 0); return (

Account Management

Multi-account and subaccount structures with consolidated views

{/* Summary Cards */}
Total Accounts {sampleAccounts.length + sampleAccounts.reduce((c, a) => c + (a.subaccounts?.length || 0), 0)}
Consolidated Balance (USD eq.) ${(totalBalance / 1_000_000).toFixed(2)}M
Active {sampleAccounts.filter(a => a.status === 'active').length}
Frozen {sampleAccounts.filter(a => a.status === 'frozen').length}
{/* Toolbar */}
setSearch(e.target.value)} />
{/* Account Table */}
Account
Currency
Balance
Available
Status
Identifier
Last Activity
{filtered.map(acc => ( ))}
); }