Refine homepage freshness layout
This commit is contained in:
@@ -49,9 +49,11 @@ function renderHeadline(context: ChainActivityContext): string {
|
||||
export default function ActivityContextPanel({
|
||||
context,
|
||||
title = 'Chain Activity Context',
|
||||
compact = false,
|
||||
}: {
|
||||
context: ChainActivityContext
|
||||
title?: string
|
||||
compact?: boolean
|
||||
}) {
|
||||
const { mode } = useUiMode()
|
||||
const tone = resolveTone(context.state)
|
||||
@@ -62,7 +64,7 @@ export default function ActivityContextPanel({
|
||||
|
||||
return (
|
||||
<Card className="border border-sky-200 bg-sky-50/60 dark:border-sky-900/40 dark:bg-sky-950/20" title={title}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className={`flex flex-col ${compact ? 'gap-3' : 'gap-4'}`}>
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<div className="text-base font-semibold text-gray-900 dark:text-white">{renderHeadline(context)}</div>
|
||||
@@ -75,48 +77,49 @@ export default function ActivityContextPanel({
|
||||
<EntityBadge label={resolveLabel(context.state)} tone={tone} />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Latest Block</div>
|
||||
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{compact ? (
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 px-4 py-3 text-sm leading-6 text-gray-700 dark:border-white/10 dark:bg-black/10 dark:text-gray-300">
|
||||
Latest block{' '}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
{context.latest_block_number != null ? `#${context.latest_block_number}` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{formatRelativeAge(context.latest_block_timestamp)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Latest Transaction</div>
|
||||
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
|
||||
</span>{' '}
|
||||
was {formatRelativeAge(context.latest_block_timestamp)}. Latest visible transaction was{' '}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
{context.latest_transaction_block_number != null ? `#${context.latest_transaction_block_number}` : 'Unknown'}
|
||||
</span>{' '}
|
||||
{formatRelativeAge(context.latest_transaction_timestamp)}.{' '}
|
||||
{mode === 'guided'
|
||||
? 'Use the block gap and last non-empty block above to tell low activity apart from an indexing issue.'
|
||||
: dualTimelineLabel}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-3 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Timeline Summary</div>
|
||||
<div className="mt-2 text-sm leading-6 text-gray-700 dark:text-gray-300">
|
||||
Latest block{' '}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
{context.latest_block_number != null ? `#${context.latest_block_number}` : 'Unknown'}
|
||||
</span>{' '}
|
||||
was {formatRelativeAge(context.latest_block_timestamp)}. Latest visible transaction was{' '}
|
||||
<span className="font-semibold text-gray-900 dark:text-white">
|
||||
{context.latest_transaction_block_number != null ? `#${context.latest_transaction_block_number}` : 'Unknown'}
|
||||
</span>{' '}
|
||||
{formatRelativeAge(context.latest_transaction_timestamp)}.
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{formatRelativeAge(context.latest_transaction_timestamp)}
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Visibility Signal</div>
|
||||
<div className="mt-2 text-sm leading-6 text-gray-700 dark:text-gray-300">
|
||||
{mode === 'guided'
|
||||
? 'Use the block gap and the last non-empty block above to tell low activity apart from an indexing issue.'
|
||||
: dualTimelineLabel}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Last Non-Empty Block</div>
|
||||
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{context.last_non_empty_block_number != null ? `#${context.last_non_empty_block_number}` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{formatRelativeAge(context.last_non_empty_block_timestamp)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/50 bg-white/70 p-4 dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Block Gap</div>
|
||||
<div className="mt-2 text-xl font-semibold text-gray-900 dark:text-white">
|
||||
{context.block_gap_to_latest_transaction != null ? context.block_gap_to_latest_transaction.toLocaleString() : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{mode === 'guided'
|
||||
? 'Difference between the current tip and the latest visible transaction block.'
|
||||
: dualTimelineLabel}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap gap-4 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className={`flex flex-wrap text-sm text-gray-600 dark:text-gray-400 ${compact ? 'gap-x-4 gap-y-2' : 'gap-4'}`}>
|
||||
{context.latest_transaction_block_number != null ? (
|
||||
<Link href={`/blocks/${context.latest_transaction_block_number}`} className="text-primary-600 hover:underline">
|
||||
Open latest transaction block →
|
||||
|
||||
@@ -413,6 +413,37 @@ export default function Home({
|
||||
note: compactStatNote(networkUtilizationSummary.note, networkUtilization != null ? 'Latest stats sample' : 'Unavailable', mode),
|
||||
},
|
||||
]
|
||||
const activityMetricCards = [
|
||||
{
|
||||
label: 'Latest Transaction',
|
||||
value: activityContext.latest_transaction_block_number != null ? `#${activityContext.latest_transaction_block_number}` : 'Unknown',
|
||||
note: latestTransactionAgeLabel,
|
||||
detail: `Latest visible transaction freshness${txCompleteness ? ` · ${txCompleteness}` : ''}.`,
|
||||
},
|
||||
{
|
||||
label: 'Last Non-Empty Block',
|
||||
value: activityContext.last_non_empty_block_number != null ? `#${activityContext.last_non_empty_block_number}` : 'Unknown',
|
||||
note: formatRelativeAge(activityContext.last_non_empty_block_timestamp),
|
||||
detail:
|
||||
activityContext.latest_transaction_block_number != null &&
|
||||
activityContext.last_non_empty_block_number != null &&
|
||||
activityContext.latest_transaction_block_number === activityContext.last_non_empty_block_number
|
||||
? 'Matches the latest visible transaction block.'
|
||||
: 'Most recent block with at least one indexed transaction.',
|
||||
},
|
||||
{
|
||||
label: 'Block Gap',
|
||||
value:
|
||||
activityContext.block_gap_to_latest_transaction != null
|
||||
? activityContext.block_gap_to_latest_transaction.toLocaleString()
|
||||
: 'Unknown',
|
||||
note:
|
||||
activityContext.block_gap_to_latest_transaction != null
|
||||
? `${activityContext.block_gap_to_latest_transaction.toLocaleString()} blocks behind tip`
|
||||
: 'Gap unavailable',
|
||||
detail: 'Difference between the current tip and the latest visible transaction block.',
|
||||
},
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
setRelayPage(1)
|
||||
@@ -540,48 +571,27 @@ export default function Home({
|
||||
</div>
|
||||
|
||||
{chainStatus ? (
|
||||
<div className="grid gap-3 md:grid-cols-3 xl:grid-cols-5">
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Chain 138 Status</div>
|
||||
<div className="mt-2 text-lg font-semibold">{chainStatus.status || 'unknown'}</div>
|
||||
<div className="mt-1 text-sm opacity-80">{chainStatus.name || 'Defi Oracle Meta Mainnet'}</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Head Age</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{chainStatus.head_age_sec != null ? `${Math.round(chainStatus.head_age_sec)}s` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">Latest public RPC head freshness.</div>
|
||||
<div className="mt-2 text-xs opacity-75">Chain visibility is currently {chainVisibilityState}.</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">RPC Latency</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{chainStatus.latency_ms != null ? `${Math.round(chainStatus.latency_ms)}ms` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">Public Chain 138 RPC probe latency.</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Latest Transaction</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{activityContext.latest_transaction_block_number != null ? `#${activityContext.latest_transaction_block_number}` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">{latestTransactionAgeLabel}</div>
|
||||
<div className="mt-2 text-xs opacity-75">
|
||||
Latest visible transaction freshness{txCompleteness ? ` · ${txCompleteness}` : ''}.
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Last Non-Empty Block</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{activityContext.last_non_empty_block_number != null ? `#${activityContext.last_non_empty_block_number}` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">
|
||||
{activityContext.block_gap_to_latest_transaction != null
|
||||
? `${activityContext.block_gap_to_latest_transaction.toLocaleString()} blocks behind tip`
|
||||
: 'Gap unavailable'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Chain 138 Status</div>
|
||||
<div className="mt-2 text-lg font-semibold">{chainStatus.status || 'unknown'}</div>
|
||||
<div className="mt-1 text-sm opacity-80">{chainStatus.name || 'Defi Oracle Meta Mainnet'}</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">Head Age</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{chainStatus.head_age_sec != null ? `${Math.round(chainStatus.head_age_sec)}s` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">Latest public RPC head freshness.</div>
|
||||
<div className="mt-2 text-xs opacity-75">Chain visibility is currently {chainVisibilityState}.</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/40 bg-white/55 p-4 shadow-sm backdrop-blur dark:border-white/10 dark:bg-black/10">
|
||||
<div className="text-xs font-semibold uppercase tracking-wide opacity-70">RPC Latency</div>
|
||||
<div className="mt-2 text-lg font-semibold">
|
||||
{chainStatus.latency_ms != null ? `${Math.round(chainStatus.latency_ms)}ms` : 'Unknown'}
|
||||
</div>
|
||||
<div className="mt-1 text-sm opacity-80">Public Chain 138 RPC probe latency.</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -683,6 +693,17 @@ export default function Home({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 sm:gap-4 md:grid-cols-3">
|
||||
{activityMetricCards.map((card) => (
|
||||
<Card key={card.label}>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400">{card.label}</div>
|
||||
<div className="text-xl font-bold sm:text-2xl">{card.value}</div>
|
||||
<div className="mt-2 text-sm text-gray-700 dark:text-gray-300">{card.note}</div>
|
||||
<div className="mt-2 text-xs text-gray-500 dark:text-gray-400">{card.detail}</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{mode === 'guided' ? (
|
||||
<div className="grid grid-cols-1 gap-3 sm:gap-4 md:grid-cols-4">
|
||||
{secondaryMetricCards.map((card) => (
|
||||
@@ -718,7 +739,11 @@ export default function Home({
|
||||
)}
|
||||
|
||||
<div className="mb-8">
|
||||
<ActivityContextPanel context={activityContext} title={mode === 'guided' ? 'Chain Activity Context' : 'Freshness & Activity'} />
|
||||
<ActivityContextPanel
|
||||
context={activityContext}
|
||||
title="Freshness Interpretation"
|
||||
compact
|
||||
/>
|
||||
<FreshnessTrustNote
|
||||
className="mt-3"
|
||||
context={activityContext}
|
||||
|
||||
Reference in New Issue
Block a user