- Updated branding from "SolaceScanScout" to "Solace" across various files including deployment scripts, API responses, and documentation. - Changed default base URL for Playwright tests and updated security headers to reflect the new branding. - Enhanced README and API documentation to include new authentication endpoints and product access details. This refactor aligns the project branding and improves clarity in the API documentation.
206 lines
9.0 KiB
TypeScript
206 lines
9.0 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react'
|
|
import { Card } from '@/libs/frontend-ui-primitives'
|
|
import { explorerFeaturePages } from '@/data/explorerOperations'
|
|
import {
|
|
getMissionControlRelays,
|
|
getMissionControlRelayLabel,
|
|
missionControlApi,
|
|
type MissionControlBridgeStatusResponse,
|
|
} from '@/services/api/missionControl'
|
|
import { plannerApi, type InternalExecutionPlanResponse, type PlannerCapabilitiesResponse } from '@/services/api/planner'
|
|
import { routesApi, type RouteMatrixResponse } from '@/services/api/routes'
|
|
import OperationsPageShell, {
|
|
MetricCard,
|
|
StatusBadge,
|
|
formatNumber,
|
|
relativeAge,
|
|
truncateMiddle,
|
|
} from './OperationsPageShell'
|
|
|
|
interface OperatorOperationsPageProps {
|
|
initialBridgeStatus?: MissionControlBridgeStatusResponse | null
|
|
initialRouteMatrix?: RouteMatrixResponse | null
|
|
initialPlannerCapabilities?: PlannerCapabilitiesResponse | null
|
|
initialInternalPlan?: InternalExecutionPlanResponse | null
|
|
}
|
|
|
|
function relayTone(status?: string): 'normal' | 'warning' | 'danger' {
|
|
const normalized = String(status || 'unknown').toLowerCase()
|
|
if (['degraded', 'stale', 'stopped', 'down'].includes(normalized)) return 'danger'
|
|
if (['paused', 'starting', 'unknown'].includes(normalized)) return 'warning'
|
|
return 'normal'
|
|
}
|
|
|
|
export default function OperatorOperationsPage({
|
|
initialBridgeStatus = null,
|
|
initialRouteMatrix = null,
|
|
initialPlannerCapabilities = null,
|
|
initialInternalPlan = null,
|
|
}: OperatorOperationsPageProps) {
|
|
const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
|
|
const [routeMatrix, setRouteMatrix] = useState<RouteMatrixResponse | null>(initialRouteMatrix)
|
|
const [plannerCapabilities, setPlannerCapabilities] = useState<PlannerCapabilitiesResponse | null>(initialPlannerCapabilities)
|
|
const [internalPlan, setInternalPlan] = useState<InternalExecutionPlanResponse | null>(initialInternalPlan)
|
|
const [loadingError, setLoadingError] = useState<string | null>(null)
|
|
const page = explorerFeaturePages.operator
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
|
|
const load = async () => {
|
|
const [bridgeResult, routesResult, capabilitiesResult, planResult] = await Promise.allSettled([
|
|
missionControlApi.getBridgeStatus(),
|
|
routesApi.getRouteMatrix(),
|
|
plannerApi.getCapabilities(),
|
|
plannerApi.getInternalExecutionPlan(),
|
|
])
|
|
|
|
if (cancelled) return
|
|
|
|
if (bridgeResult.status === 'fulfilled') setBridgeStatus(bridgeResult.value)
|
|
if (routesResult.status === 'fulfilled') setRouteMatrix(routesResult.value)
|
|
if (capabilitiesResult.status === 'fulfilled') setPlannerCapabilities(capabilitiesResult.value)
|
|
if (planResult.status === 'fulfilled') setInternalPlan(planResult.value)
|
|
|
|
const failedCount = [bridgeResult, routesResult, capabilitiesResult, planResult].filter(
|
|
(result) => result.status === 'rejected'
|
|
).length
|
|
|
|
if (failedCount === 4) {
|
|
setLoadingError('Operator telemetry is temporarily unavailable from the public explorer APIs.')
|
|
}
|
|
}
|
|
|
|
load().catch((error) => {
|
|
if (!cancelled) {
|
|
setLoadingError(error instanceof Error ? error.message : 'Operator telemetry is temporarily unavailable from the public explorer APIs.')
|
|
}
|
|
})
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [])
|
|
|
|
const relays = useMemo(() => getMissionControlRelays(bridgeStatus), [bridgeStatus])
|
|
const relayEntries = useMemo(() => Object.entries(relays || {}), [relays])
|
|
const totalQueue = useMemo(
|
|
() =>
|
|
relayEntries.reduce((sum, [, relay]) => {
|
|
const queueSize = relay.url_probe?.body?.queue?.size ?? relay.file_snapshot?.queue?.size ?? 0
|
|
return sum + queueSize
|
|
}, 0),
|
|
[relayEntries]
|
|
)
|
|
const providers = plannerCapabilities?.providers || []
|
|
const liveProviders = providers.filter((provider) => provider.live)
|
|
|
|
return (
|
|
<OperationsPageShell page={page}>
|
|
{loadingError ? (
|
|
<Card className="mb-6 border border-red-200 bg-red-50/70 dark:border-red-900/50 dark:bg-red-950/20">
|
|
<p className="text-sm leading-6 text-red-900 dark:text-red-100">{loadingError}</p>
|
|
</Card>
|
|
) : null}
|
|
|
|
<div className="mb-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
<MetricCard
|
|
title="Relay Fleet"
|
|
value={bridgeStatus?.data?.status || 'unknown'}
|
|
description={`${relayEntries.length} managed lanes · queue ${formatNumber(totalQueue)}`}
|
|
className="border border-sky-200 bg-sky-50/70 dark:border-sky-900/50 dark:bg-sky-950/20"
|
|
/>
|
|
<MetricCard
|
|
title="Live Routes"
|
|
value={formatNumber(routeMatrix?.counts?.filteredLiveRoutes)}
|
|
description={`${formatNumber(routeMatrix?.counts?.blockedOrPlannedRoutes)} planned or blocked routes remain in the matrix.`}
|
|
className="border border-emerald-200 bg-emerald-50/70 dark:border-emerald-900/50 dark:bg-emerald-950/20"
|
|
/>
|
|
<MetricCard
|
|
title="Planner Providers"
|
|
value={formatNumber(liveProviders.length)}
|
|
description={`${formatNumber(providers.length)} published providers in planner v2 capabilities.`}
|
|
/>
|
|
<MetricCard
|
|
title="Fallback Decision"
|
|
value={internalPlan?.plannerResponse?.decision || 'unknown'}
|
|
description={
|
|
internalPlan?.execution?.contractAddress
|
|
? `Execution contract ${truncateMiddle(internalPlan.execution.contractAddress)}`
|
|
: 'Latest internal execution plan posture.'
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="mb-8 grid gap-6 lg:grid-cols-[1.15fr_0.85fr]">
|
|
<Card title="Managed Relay Lanes">
|
|
<div className="space-y-4">
|
|
{relayEntries.map(([key, relay]) => {
|
|
const snapshot = relay.url_probe?.body || relay.file_snapshot
|
|
const status = snapshot?.status || 'unknown'
|
|
return (
|
|
<div
|
|
key={key}
|
|
className="rounded-2xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-900/40"
|
|
>
|
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
|
<div>
|
|
<div className="text-base font-semibold text-gray-900 dark:text-white">
|
|
{getMissionControlRelayLabel(key)}
|
|
</div>
|
|
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
{snapshot?.destination?.chain_name || 'Unknown destination'} · queue {formatNumber(snapshot?.queue?.size ?? 0)}
|
|
</div>
|
|
</div>
|
|
<StatusBadge status={status} tone={relayTone(status)} />
|
|
</div>
|
|
<div className="mt-3 text-sm text-gray-600 dark:text-gray-400">
|
|
Last source poll {relativeAge(snapshot?.last_source_poll?.at)} · processed {formatNumber(snapshot?.queue?.processed ?? 0)}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
{relayEntries.length === 0 ? (
|
|
<p className="text-sm text-gray-600 dark:text-gray-400">No relay lane data available.</p>
|
|
) : null}
|
|
</div>
|
|
</Card>
|
|
|
|
<Card title="Execution Readiness">
|
|
<div className="space-y-4">
|
|
<div className="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">Internal execution plan</div>
|
|
<div className="mt-2 flex items-center gap-3">
|
|
<StatusBadge
|
|
status={internalPlan?.plannerResponse?.decision || 'unknown'}
|
|
tone={internalPlan?.plannerResponse?.decision === 'direct-pool' ? 'normal' : 'warning'}
|
|
/>
|
|
</div>
|
|
<div className="mt-3 text-sm text-gray-600 dark:text-gray-400">
|
|
Contract {truncateMiddle(internalPlan?.execution?.contractAddress)} · {formatNumber(internalPlan?.plannerResponse?.steps?.length)} planner steps
|
|
</div>
|
|
</div>
|
|
|
|
<div className="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">Live providers</div>
|
|
<div className="mt-3 flex flex-wrap gap-2">
|
|
{liveProviders.map((provider) => (
|
|
<span
|
|
key={provider.provider}
|
|
className="rounded-full bg-primary-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-primary-700 dark:bg-primary-900/30 dark:text-primary-300"
|
|
>
|
|
{provider.provider}
|
|
</span>
|
|
))}
|
|
{liveProviders.length === 0 ? (
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">No live providers reported.</span>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</OperationsPageShell>
|
|
)
|
|
}
|