feat: bridge lane health API and dual-chain token list updates
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import SubsystemPosturePanel from '@/components/common/SubsystemPosturePanel'
|
||||
import TokenListSurfaceNote from '@/components/common/TokenListSurfaceNote'
|
||||
import OperationsSurfaceNav from '@/components/explorer/OperationsSurfaceNav'
|
||||
import OperationsActionGrid from '@/components/explorer/OperationsActionGrid'
|
||||
import BridgeLaneHealthPanel from '@/components/explorer/BridgeLaneHealthPanel'
|
||||
import { resolveEffectiveFreshness } from '@/utils/explorerFreshness'
|
||||
import { statsApi, type ExplorerStats } from '@/services/api/stats'
|
||||
|
||||
@@ -156,6 +157,25 @@ export default function OperationsHubPage({
|
||||
new Set((tokenList?.tokens || []).map((token) => token.symbol).filter(Boolean) as string[])
|
||||
).slice(0, 8)
|
||||
}, [tokenList])
|
||||
const laneSummary = useMemo(() => {
|
||||
const lanes = bridgeStatus?.data?.bridge_lanes?.lanes || []
|
||||
if (lanes.length === 0) return null
|
||||
|
||||
const funded = lanes.filter((lane) => String(lane.status || '').toLowerCase() === 'funded').length
|
||||
const unfunded = lanes.filter((lane) => String(lane.status || '').toLowerCase() === 'unfunded').length
|
||||
const proofRecorded = lanes.filter(
|
||||
(lane) => String(lane.proof_status || '').toLowerCase() === 'proof-recorded'
|
||||
).length
|
||||
|
||||
return {
|
||||
total: lanes.length,
|
||||
funded,
|
||||
unfunded,
|
||||
proofRecorded,
|
||||
updatedAt: bridgeStatus?.data?.bridge_lanes?.updated_at,
|
||||
}
|
||||
}, [bridgeStatus])
|
||||
|
||||
const activityContext = useMemo(
|
||||
() =>
|
||||
summarizeChainActivity({
|
||||
@@ -231,6 +251,14 @@ export default function OperationsHubPage({
|
||||
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
{relayCount} managed lanes · queue {totalQueue}
|
||||
</div>
|
||||
{laneSummary ? (
|
||||
<div className="mt-1 text-sm text-gray-700 dark:text-gray-300">
|
||||
{laneSummary.funded}/{laneSummary.total} config-ready funded
|
||||
{laneSummary.unfunded > 0 ? ` · ${laneSummary.unfunded} unfunded` : ''}
|
||||
{' · '}
|
||||
{laneSummary.proofRecorded}/{laneSummary.total} proof-recorded
|
||||
</div>
|
||||
) : null}
|
||||
</Card>
|
||||
|
||||
<Card className="border border-emerald-200 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20">
|
||||
@@ -312,6 +340,23 @@ export default function OperationsHubPage({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{laneSummary ? (
|
||||
<div className="mt-4 rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40">
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">Config-ready lanes</div>
|
||||
<div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{laneSummary.funded}/{laneSummary.total} funded · {laneSummary.proofRecorded}/{laneSummary.total} proof-recorded
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
{mode === 'guided'
|
||||
? 'Remote CCIP bridge LINK balances and operator proof hashes. Full lane table below.'
|
||||
: 'Lane funding and proof posture from bridge status API.'}
|
||||
{' '}
|
||||
<Link href="/bridge" className="font-semibold text-primary-600 hover:underline">
|
||||
Open bridge monitoring
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</Card>
|
||||
|
||||
<Card title="Public Config Highlights">
|
||||
@@ -342,6 +387,8 @@ export default function OperationsHubPage({
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<BridgeLaneHealthPanel laneHealth={bridgeStatus?.data?.bridge_lanes} />
|
||||
|
||||
<OperationsActionGrid actions={page.actions} />
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user