Files
CurrenciCombo/src/pages/AccountsPage.tsx

232 lines
10 KiB
TypeScript
Raw Normal View History

feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
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';
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
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<AccountType, string> = {
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 })}`;
};
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
interface AccountRowProps {
account: Account;
level?: number;
onChainBalances: Record<string, OnChainBalance>;
balancesLoading: boolean;
}
function AccountRow({ account, level = 0, onChainBalances, balancesLoading }: AccountRowProps) {
const [expanded, setExpanded] = useState(false);
const hasChildren = account.subaccounts && account.subaccounts.length > 0;
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
const onChain = account.walletAddress ? onChainBalances[account.walletAddress] : undefined;
return (
<>
<div className={`account-table-row level-${level}`} style={{ paddingLeft: `${16 + level * 24}px` }}>
<div className="account-table-name">
{hasChildren ? (
<button className="expand-btn" onClick={() => setExpanded(!expanded)}>
{expanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
</button>
) : (
<span className="expand-placeholder" />
)}
<span className="account-type-dot" style={{ background: typeColors[account.type] }} />
<div>
<span className="account-name-text">{account.name}</span>
<span className="account-type-label">{account.type.replace('_', ' ')}</span>
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
{account.walletAddress && (
<span style={{ display: 'block', marginTop: 2 }}>
<OnChainBalanceTag
address={account.walletAddress}
balance={onChain}
loading={balancesLoading}
compact
/>
</span>
)}
</div>
</div>
<div className="account-table-cell currency">{account.currency}</div>
<div className="account-table-cell mono balance">{formatBalance(account.balance, account.currency)}</div>
<div className="account-table-cell mono available">{formatBalance(account.availableBalance, account.currency)}</div>
<div className="account-table-cell">
<span className={`account-status-badge ${account.status}`}>{account.status}</span>
</div>
<div className="account-table-cell identifier">
{account.iban && <span className="mono small">{account.iban}</span>}
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
{account.walletAddress && (
<a
href={explorerAddressUrl(account.walletAddress)}
target="_blank"
rel="noreferrer"
className="mono small"
title="View on SolaceScan"
style={{ color: 'inherit', textDecoration: 'underline dotted' }}
>
{account.walletAddress.slice(0, 10)}
</a>
)}
{account.swift && <span className="swift-badge">{account.swift}</span>}
</div>
<div className="account-table-cell">
<span className="mono small">{account.lastActivity.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
</div>
<div className="account-table-cell actions">
<button className="row-action-btn" title="View Details"><ExternalLink size={12} /></button>
<button className="row-action-btn" title="Copy ID"><Copy size={12} /></button>
<button className="row-action-btn" title="More"><MoreHorizontal size={12} /></button>
</div>
</div>
{expanded && hasChildren && account.subaccounts!.map(sub => (
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
<AccountRow key={sub.id} account={sub} level={level + 1} onChainBalances={onChainBalances} balancesLoading={balancesLoading} />
))}
</>
);
}
export default function AccountsPage() {
const [search, setSearch] = useState('');
const [typeFilter, setTypeFilter] = useState<string>('all');
const [view, setView] = useState<'tree' | 'flat'>('tree');
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
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 (
<div className="accounts-page">
<div className="page-header">
<div>
<h1><Building2 size={24} /> Account Management</h1>
<p className="page-subtitle">Multi-account and subaccount structures with consolidated views</p>
</div>
<div className="page-header-actions">
<button className="btn-secondary"><Download size={14} /> Export</button>
<button className="btn-primary"><Plus size={14} /> New Account</button>
</div>
</div>
{/* Summary Cards */}
<div className="accounts-summary">
<div className="summary-card">
<span className="summary-label">Total Accounts</span>
<span className="summary-value">{sampleAccounts.length + sampleAccounts.reduce((c, a) => c + (a.subaccounts?.length || 0), 0)}</span>
</div>
<div className="summary-card">
<span className="summary-label">Consolidated Balance (USD eq.)</span>
<span className="summary-value">${(totalBalance / 1_000_000).toFixed(2)}M</span>
</div>
<div className="summary-card">
<span className="summary-label">Active</span>
<span className="summary-value green">{sampleAccounts.filter(a => a.status === 'active').length}</span>
</div>
<div className="summary-card">
<span className="summary-label">Frozen</span>
<span className="summary-value orange">{sampleAccounts.filter(a => a.status === 'frozen').length}</span>
</div>
</div>
{/* Toolbar */}
<div className="table-toolbar">
<div className="table-toolbar-left">
<div className="search-input-wrapper">
<Search size={14} />
<input
type="text"
placeholder="Search accounts..."
value={search}
onChange={e => setSearch(e.target.value)}
/>
</div>
<div className="filter-group">
<Filter size={14} />
<select value={typeFilter} onChange={e => setTypeFilter(e.target.value)}>
<option value="all">All Types</option>
<option value="operating">Operating</option>
<option value="treasury">Treasury</option>
<option value="custody">Custody</option>
<option value="settlement">Settlement</option>
<option value="nostro">Nostro</option>
<option value="escrow">Escrow</option>
<option value="collateral">Collateral</option>
<option value="stablecoin">Stablecoin</option>
</select>
</div>
</div>
<div className="table-toolbar-right">
<div className="view-toggle">
<button className={view === 'tree' ? 'active' : ''} onClick={() => setView('tree')}>Tree</button>
<button className={view === 'flat' ? 'active' : ''} onClick={() => setView('flat')}>Flat</button>
</div>
</div>
</div>
{/* Account Table */}
<div className="account-table">
<div className="account-table-header">
<div className="account-table-name">Account</div>
<div className="account-table-cell currency">Currency</div>
<div className="account-table-cell balance">Balance</div>
<div className="account-table-cell available">Available</div>
<div className="account-table-cell">Status</div>
<div className="account-table-cell identifier">Identifier</div>
<div className="account-table-cell">Last Activity</div>
<div className="account-table-cell actions" />
</div>
<div className="account-table-body">
{filtered.map(acc => (
feat(portal): wire Accounts/Treasury/Reporting/Compliance/Settlements/TransactionBuilder to live Chain-138 + SolaceScan Extends the POC from #2 beyond the Dashboard so every portal page that can benefit from on-chain signal now pulls from live backends while preserving its existing UX. Pages without an on-chain analogue (the IFRS/GAAP/IPSAS report rows, the dbis_core compliance alerts) stay on sample data with an explicit 'mocked' note. New shared primitives --------------------- src/hooks/useLatestTransactions.ts — polls SolaceScan /transactions every 15s src/hooks/useAddressTransactions.ts — per-address tx feed, 60s polling src/components/portal/LiveTransactionsPanel.tsx — reusable live-tx card src/components/portal/LiveChainBanner.tsx — slim status banner src/components/portal/OnChainBalanceTag.tsx — shared live/off-chain pill Per-page wiring --------------- AccountsPage — on-chain pill + META balance + SolaceScan link on each account row that carries a walletAddress; overlay renders only on wallet rows (negative check). SettlementsPage — replaces the static 'Settlement Rate' tile with a live Chain-138 block + tx-today tile; adds a LiveTransactionsPanel above the CSD queue so the page no longer renders identical output when RPC is dead. ReportingPage — new On-Chain Reporting Snapshot row (Blockscout /stats: block depth, total tx, total addrs, utilisation, avg block time). Clear note that the IFRS/GAAP/IPSAS rows come from dbis_core and are still mocked. TreasuryPage — two new summary tiles: live Chain-138 gas + aggregated on-chain custody (META) from sample wallet addresses. Uses the same useOnChainBalances hook as Accounts. CompliancePage — AML monitor strip with wallet selector; dedicated 'On-Chain Tx Feed' card shows IN/OUT per tracked wallet via SolaceScan. dbis_core alerts still mocked (no public deploy). TransactionBuilder — LiveChainBanner inserted above the composer so users know chain health + gas + latency before composing; transaction-builder-module made a flex column so the banner doesn't cover the canvas. Assertions baked into every live widget --------------------------------------- - RPC failure flips colour + text to 'degraded'/'—' (no silent freeze). - Loading state is distinct from both live and degraded. - Each overlay is only rendered where real data differs from sample data (walletAddress rows for balances, tracked custody for AML, etc.) so a page without live overlays is proof-of-scope, not proof-of-brokenness. Verified locally ---------------- - tsc --noEmit: clean - npm run build: clean (2066 modules, 565 ms) Still intentionally mocked -------------------------- - proxmox.ts — CF-Access protected; a BFF route is now open in orchestrator PR (see companion PR for /api/proxmox/*). - dbisCore.ts — no public deployment exists yet.
2026-04-19 08:31:04 +00:00
<AccountRow
key={acc.id}
account={acc}
onChainBalances={onChainBalances}
balancesLoading={balancesLoading}
/>
))}
</div>
</div>
</div>
);
}