Files
explorer-monorepo/frontend/src/pages/blocks/[number].tsx
defiQUG 0972178cc5 refactor: rename SolaceScanScout to Solace and update related configurations
- 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.
2026-04-10 12:52:17 -07:00

140 lines
4.9 KiB
TypeScript

'use client'
import { useCallback, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { blocksApi, Block } from '@/services/api/blocks'
import { Card, Address } from '@/libs/frontend-ui-primitives'
import Link from 'next/link'
import { DetailRow } from '@/components/common/DetailRow'
import PageIntro from '@/components/common/PageIntro'
import { formatTimestamp } from '@/utils/format'
export default function BlockDetailPage() {
const router = useRouter()
const rawNumber = typeof router.query.number === 'string' ? router.query.number : ''
const blockNumber = parseInt(rawNumber, 10)
const isValidBlock = rawNumber !== '' && !Number.isNaN(blockNumber) && blockNumber >= 0
const chainId = parseInt(process.env.NEXT_PUBLIC_CHAIN_ID || '138')
const [block, setBlock] = useState<Block | null>(null)
const [loading, setLoading] = useState(true)
const loadBlock = useCallback(async () => {
setLoading(true)
try {
const response = await blocksApi.getByNumber(chainId, blockNumber)
setBlock(response.data)
} catch (error) {
console.error('Failed to load block:', error)
} finally {
setLoading(false)
}
}, [chainId, blockNumber])
useEffect(() => {
if (!router.isReady) {
return
}
if (!isValidBlock) {
setLoading(false)
setBlock(null)
return
}
loadBlock()
}, [isValidBlock, loadBlock, router.isReady])
const gasUtilization = block && block.gas_limit > 0
? Math.round((block.gas_used / block.gas_limit) * 100)
: null
return (
<div className="container mx-auto px-4 py-6 sm:py-8">
<PageIntro
eyebrow="Block Detail"
title={block ? `Block #${block.number}` : 'Block'}
description="Inspect a single Chain 138 block, then move into its related miner address, adjacent block numbers, or broader explorer search flows."
actions={[
{ href: '/blocks', label: 'All blocks' },
{ href: '/transactions', label: 'Recent transactions' },
{ href: '/search', label: 'Search explorer' },
]}
/>
<div className="mb-6 flex flex-wrap gap-3 text-sm">
<Link href="/blocks" className="text-primary-600 hover:underline">
Back to blocks
</Link>
{block && block.number > 0 ? (
<Link href={`/blocks/${block.number - 1}`} className="text-primary-600 hover:underline">
Previous block
</Link>
) : null}
{block && (
<Link href={`/blocks/${block.number + 1}`} className="text-primary-600 hover:underline">
Next block
</Link>
)}
</div>
{!router.isReady || loading ? (
<Card>
<p className="text-sm text-gray-600 dark:text-gray-400">Loading block...</p>
</Card>
) : !isValidBlock ? (
<Card>
<p className="text-sm text-gray-600 dark:text-gray-400">Invalid block number. Please use a valid block number from the URL.</p>
<div className="mt-4 flex flex-wrap gap-3 text-sm">
<Link href="/blocks" className="text-primary-600 hover:underline">
Back to blocks
</Link>
<Link href="/search" className="text-primary-600 hover:underline">
Search by block number
</Link>
</div>
</Card>
) : !block ? (
<Card>
<p className="text-sm text-gray-600 dark:text-gray-400">Block not found.</p>
<div className="mt-4 flex flex-wrap gap-3 text-sm">
<Link href="/blocks" className="text-primary-600 hover:underline">
Browse recent blocks
</Link>
<Link href="/search" className="text-primary-600 hover:underline">
Search the explorer
</Link>
</div>
</Card>
) : (
<Card title="Block Information">
<dl className="space-y-4">
<DetailRow label="Hash">
<Address address={block.hash} />
</DetailRow>
<DetailRow label="Timestamp">
{formatTimestamp(block.timestamp)}
</DetailRow>
<DetailRow label="Miner">
<Link href={`/addresses/${block.miner}`} className="text-primary-600 hover:underline">
<Address address={block.miner} truncate showCopy={false} />
</Link>
</DetailRow>
<DetailRow label="Transactions">
<Link href={`/search?q=${block.number}`} className="text-primary-600 hover:underline">
{block.transaction_count}
</Link>
</DetailRow>
<DetailRow label="Gas Used">
{block.gas_used.toLocaleString()} / {block.gas_limit.toLocaleString()}
</DetailRow>
{gasUtilization != null && (
<DetailRow label="Gas Utilization">
{gasUtilization}%
</DetailRow>
)}
</dl>
</Card>
)}
</div>
)
}