feat(portal): wire DashboardPage to live Chain-138 RPC + SolaceScan Explorer
- Add services/{http,chain138,explorer,proxmox,dbisCore} + hooks/{useLiveChain,useOnChainBalances}
- Add BackendStatusBar + LiveNetworkPanel components on DashboardPage
- Overlay on-chain META balance on account rows carrying a walletAddress
- Normalize EIP-55 checksum in chain138.getNativeBalance so hand-typed
sample custody addresses (e.g. 0x742d35Cc...bD38) don't silently drop
out of the balance map
- Default RPC: https://rpc.d-bis.org (user-preferred gateway)
- proxmox.ts stays mocked (CF-Access, needs BFF); dbisCore.ts stays
mocked (no public deployment yet)
Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
TrendingUp, TrendingDown, DollarSign, Activity, AlertTriangle, Clock,
|
||||
@@ -6,6 +6,10 @@ import {
|
||||
Landmark, FileText, Shield, CheckSquare, ChevronRight, RefreshCw
|
||||
} from 'lucide-react';
|
||||
import { financialSummary, sampleAccounts, treasuryPositions, complianceAlerts, recentActivity, portalModules } from '../data/portalData';
|
||||
import LiveNetworkPanel from '../components/portal/LiveNetworkPanel';
|
||||
import BackendStatusBar from '../components/portal/BackendStatusBar';
|
||||
import { useOnChainBalances } from '../hooks/useOnChainBalances';
|
||||
import { endpoints } from '../config/endpoints';
|
||||
|
||||
const formatCurrency = (amount: number, currency = 'USD') => {
|
||||
if (Math.abs(amount) >= 1_000_000_000) return `${currency === 'USD' ? '$' : ''}${(amount / 1_000_000_000).toFixed(2)}B`;
|
||||
@@ -56,12 +60,19 @@ export default function DashboardPage() {
|
||||
|
||||
const openAlerts = complianceAlerts.filter(a => a.status !== 'resolved');
|
||||
|
||||
const onChainAddresses = useMemo(
|
||||
() => sampleAccounts.filter(a => !!a.walletAddress).map(a => a.walletAddress as string),
|
||||
[],
|
||||
);
|
||||
const { balances: onChainBalances, loading: balancesLoading } = useOnChainBalances(onChainAddresses);
|
||||
|
||||
return (
|
||||
<div className="dashboard-page">
|
||||
<div className="dashboard-header">
|
||||
<div className="dashboard-header-left">
|
||||
<h1>Portfolio Overview</h1>
|
||||
<p className="dashboard-subtitle">Solace Bank Group PLC — Consolidated View</p>
|
||||
<div style={{ marginTop: 10 }}><BackendStatusBar /></div>
|
||||
</div>
|
||||
<div className="dashboard-header-right">
|
||||
<div className="time-range-selector">
|
||||
@@ -154,6 +165,11 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
|
||||
<div className="dashboard-grid">
|
||||
{/* Chain 138 live network health — wired to rpc-core.d-bis.org + explorer */}
|
||||
<div style={{ gridColumn: '1 / -1' }}>
|
||||
<LiveNetworkPanel />
|
||||
</div>
|
||||
|
||||
{/* Asset Allocation */}
|
||||
<div className="dashboard-card asset-allocation">
|
||||
<div className="card-header">
|
||||
@@ -217,20 +233,33 @@ export default function DashboardPage() {
|
||||
<button className="card-action" onClick={() => navigate('/accounts')}>Manage <ChevronRight size={12} /></button>
|
||||
</div>
|
||||
<div className="accounts-list">
|
||||
{sampleAccounts.filter(a => !a.parentId).slice(0, 5).map(acc => (
|
||||
<div key={acc.id} className="account-row">
|
||||
<div className="account-info">
|
||||
<span className={`account-type-badge ${acc.type}`}>{acc.type}</span>
|
||||
<span className="account-name">{acc.name}</span>
|
||||
{sampleAccounts.filter(a => !a.parentId).slice(0, 5).map(acc => {
|
||||
const onChain = acc.walletAddress ? onChainBalances[acc.walletAddress] : undefined;
|
||||
return (
|
||||
<div key={acc.id} className="account-row">
|
||||
<div className="account-info">
|
||||
<span className={`account-type-badge ${acc.type}`}>{acc.type}</span>
|
||||
<span className="account-name">{acc.name}</span>
|
||||
{acc.walletAddress && (
|
||||
<span style={{ fontSize: 10, color: onChain ? '#22c55e' : balancesLoading ? '#eab308' : '#6b7280' }}>
|
||||
{onChain ? `● live · chain ${endpoints.chain138.chainId}` : balancesLoading ? '○ fetching…' : '○ off-chain'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="account-balance">
|
||||
<span className="mono">
|
||||
{acc.currency === 'BTC' ? `${acc.balance.toFixed(2)} BTC` : formatCurrency(acc.balance, acc.currency)}
|
||||
</span>
|
||||
<span className="account-currency">{acc.currency}</span>
|
||||
{onChain && (
|
||||
<span className="mono" style={{ fontSize: 10, color: '#60a5fa', marginTop: 2 }}>
|
||||
on-chain: {Number(onChain.balanceEth).toFixed(4)} META
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-balance">
|
||||
<span className="mono">
|
||||
{acc.currency === 'BTC' ? `${acc.balance.toFixed(2)} BTC` : formatCurrency(acc.balance, acc.currency)}
|
||||
</span>
|
||||
<span className="account-currency">{acc.currency}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user