synor/apps/explorer-web/src/pages/Block.tsx
Gulshan Yadav 48949ebb3f Initial commit: Synor blockchain monorepo
A complete blockchain implementation featuring:
- synord: Full node with GHOSTDAG consensus
- explorer-web: Modern React blockchain explorer with 3D DAG visualization
- CLI wallet and tools
- Smart contract SDK and example contracts (DEX, NFT, token)
- WASM crypto library for browser/mobile
2026-01-08 05:22:17 +05:30

224 lines
8.1 KiB
TypeScript

import { useParams, Link } from 'react-router-dom';
import { Box, Clock, ArrowUpRight, Layers, Activity, Zap } from 'lucide-react';
import { useBlock } from '../hooks/useApi';
import TransactionList from '../components/TransactionList';
import CopyButton from '../components/CopyButton';
import BlockRelationshipDiagram from '../components/BlockRelationshipDiagram';
import { formatDateTime, truncateHash } from '../lib/utils';
export default function Block() {
const { hash } = useParams<{ hash: string }>();
const { data: block, isLoading, error } = useBlock(hash || '');
if (!hash) {
return <div className="card p-6 text-red-400">Block hash is required</div>;
}
if (isLoading) {
return <BlockSkeleton />;
}
if (error) {
return (
<div className="card p-6 text-red-400">
Error loading block: {error.message}
</div>
);
}
if (!block) {
return <div className="card p-6 text-gray-400">Block not found</div>;
}
return (
<div className="space-y-6">
{/* Modern Header */}
<div className="relative">
{/* Background glow */}
<div className="absolute -top-10 left-0 w-[300px] h-[150px] bg-synor-500/20 rounded-full blur-[80px] pointer-events-none" />
<div className="relative flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-center gap-4">
<div className="p-3 rounded-xl bg-gradient-to-br from-synor-500/20 to-violet-500/20 border border-synor-500/30">
<Box size={28} className="text-synor-400" />
</div>
<div>
<h1 className="text-2xl md:text-3xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent">
Block Details
</h1>
<div className="flex items-center gap-3 mt-1">
<span className="text-sm text-gray-400 flex items-center gap-1.5">
<Activity size={14} className="text-synor-400" />
Blue Score: {block.blueScore.toLocaleString()}
</span>
{block.isChainBlock && (
<span className="px-2 py-0.5 text-xs font-medium bg-synor-500/20 text-synor-400 rounded-full border border-synor-500/30">
Chain Block
</span>
)}
</div>
</div>
</div>
{/* Quick stats */}
<div className="flex items-center gap-3">
<div className="px-4 py-2 rounded-xl bg-gray-800/50 border border-gray-700/50">
<div className="text-xs text-gray-500">Transactions</div>
<div className="text-lg font-bold text-white flex items-center gap-1.5">
<Zap size={14} className="text-amber-400" />
{block.transactionCount}
</div>
</div>
</div>
</div>
</div>
{/* Block Relationship Diagram */}
<BlockRelationshipDiagram
currentHash={block.hash}
parentHashes={block.parentHashes}
childrenHashes={block.childrenHashes}
isChainBlock={block.isChainBlock}
mergeSetBlues={block.mergeSetBlues}
mergeSetReds={block.mergeSetReds}
/>
{/* Block Info Card */}
<div className="card">
<div className="card-header">
<h2 className="font-semibold">Block Information</h2>
</div>
<div className="divide-y divide-gray-800">
<InfoRow label="Hash">
<div className="flex items-center gap-2">
<span className="hash text-synor-400">{block.hash}</span>
<CopyButton text={block.hash} />
</div>
</InfoRow>
<InfoRow label="Timestamp">
<div className="flex items-center gap-2">
<Clock size={16} className="text-gray-500" />
{formatDateTime(block.timestamp)}
</div>
</InfoRow>
<InfoRow label="Blue Score">
{block.blueScore.toLocaleString()}
</InfoRow>
<InfoRow label="DAA Score">
{block.daaScore.toLocaleString()}
</InfoRow>
<InfoRow label="Difficulty">
{block.difficulty.toLocaleString()}
</InfoRow>
<InfoRow label="Transactions">
{block.transactionCount}
</InfoRow>
<InfoRow label="Version">{block.version}</InfoRow>
<InfoRow label="Nonce">{block.nonce.toLocaleString()}</InfoRow>
<InfoRow label="Blue Work">
<span className="hash text-xs">{block.blueWork}</span>
</InfoRow>
</div>
</div>
{/* Parent Blocks */}
{block.parentHashes.length > 0 && (
<div className="card">
<div className="card-header flex items-center gap-2">
<Layers size={18} className="text-synor-400" />
<h2 className="font-semibold">
Parent Blocks ({block.parentHashes.length})
</h2>
</div>
<div className="divide-y divide-gray-800">
{block.parentHashes.map((parentHash, i) => (
<div key={parentHash} className="px-4 py-3 flex items-center justify-between">
<div className="flex items-center gap-2">
{i === 0 && (
<span className="badge badge-info">Selected</span>
)}
<Link
to={`/block/${parentHash}`}
className="hash text-synor-400 hover:text-synor-300"
>
{truncateHash(parentHash, 16, 16)}
</Link>
</div>
<ArrowUpRight size={16} className="text-gray-500" />
</div>
))}
</div>
</div>
)}
{/* Child Blocks */}
{block.childrenHashes.length > 0 && (
<div className="card">
<div className="card-header flex items-center gap-2">
<Layers size={18} className="text-synor-400" />
<h2 className="font-semibold">
Child Blocks ({block.childrenHashes.length})
</h2>
</div>
<div className="divide-y divide-gray-800">
{block.childrenHashes.map((childHash) => (
<div key={childHash} className="px-4 py-3 flex items-center justify-between">
<Link
to={`/block/${childHash}`}
className="hash text-synor-400 hover:text-synor-300"
>
{truncateHash(childHash, 16, 16)}
</Link>
<ArrowUpRight size={16} className="text-gray-500" />
</div>
))}
</div>
</div>
)}
{/* Transactions */}
{block.transactions && block.transactions.length > 0 && (
<TransactionList
transactions={block.transactions}
title={`Block Transactions (${block.transactions.length})`}
/>
)}
</div>
);
}
function InfoRow({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div className="px-4 py-3 flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-4">
<span className="text-sm text-gray-400 sm:w-32 flex-shrink-0">{label}</span>
<span className="text-gray-100 break-all">{children}</span>
</div>
);
}
function BlockSkeleton() {
return (
<div className="space-y-6 animate-pulse">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-lg bg-gray-800" />
<div>
<div className="h-7 w-40 bg-gray-800 rounded mb-2" />
<div className="h-4 w-32 bg-gray-800 rounded" />
</div>
</div>
<div className="card">
<div className="card-header">
<div className="h-5 w-36 bg-gray-800 rounded" />
</div>
<div className="divide-y divide-gray-800">
{Array.from({ length: 8 }).map((_, i) => (
<div key={i} className="px-4 py-3 flex items-center gap-4">
<div className="h-4 w-24 bg-gray-800 rounded" />
<div className="h-4 w-48 bg-gray-800 rounded" />
</div>
))}
</div>
</div>
</div>
);
}