diff --git a/apps/visualizer/src/features/market/DlobDashboard.tsx b/apps/visualizer/src/features/market/DlobDashboard.tsx new file mode 100644 index 0000000..969c58c --- /dev/null +++ b/apps/visualizer/src/features/market/DlobDashboard.tsx @@ -0,0 +1,136 @@ +import type { ReactNode } from 'react'; +import type { DlobStats } from './useDlobStats'; +import type { DlobDepthBandRow } from './useDlobDepthBands'; +import type { DlobSlippageRow } from './useDlobSlippage'; +import DlobDepthBandsPanel from './DlobDepthBandsPanel'; +import DlobSlippageChart from './DlobSlippageChart'; + +function formatUsd(v: number | null | undefined): string { + if (v == null || !Number.isFinite(v)) return '—'; + if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(2)}M`; + if (v >= 1000) return `$${(v / 1000).toFixed(0)}K`; + if (v >= 1) return `$${v.toFixed(2)}`; + return `$${v.toPrecision(4)}`; +} + +function formatBps(v: number | null | undefined): string { + if (v == null || !Number.isFinite(v)) return '—'; + return `${v.toFixed(1)} bps`; +} + +function formatPct(v: number | null | undefined): string { + if (v == null || !Number.isFinite(v)) return '—'; + return `${(v * 100).toFixed(0)}%`; +} + +function statusLabel(connected: boolean, error: string | null): ReactNode { + if (error) return {error}; + return connected ? live : offline; +} + +export default function DlobDashboard({ + market, + stats, + statsConnected, + statsError, + depthBands, + depthBandsConnected, + depthBandsError, + slippageRows, + slippageConnected, + slippageError, +}: { + market: string; + stats: DlobStats | null; + statsConnected: boolean; + statsError: string | null; + depthBands: DlobDepthBandRow[]; + depthBandsConnected: boolean; + depthBandsError: string | null; + slippageRows: DlobSlippageRow[]; + slippageConnected: boolean; + slippageError: string | null; +}) { + const updatedAt = stats?.updatedAt || depthBands[0]?.updatedAt || slippageRows[0]?.updatedAt || null; + + return ( +