"use client"; import React, { useEffect, useState, useCallback } from "react"; import { motion, AnimatePresence } from "motion/react"; import { Link2, Shield, Activity, Hash, Clock, FileText, CheckCircle2, Search, RefreshCw, ExternalLink, Blocks, Copy, Check, AlertCircle, Upload, Zap, Fingerprint, Radio, ChevronRight, Terminal, Lock, Globe, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { getBlockchainTransactions, getBlockchainStats, verifyDocumentHashOnBlockchain, registerContractOnBlockchain, generateContractCertificate, } from "@/features/blockchain/api/blockchain.action"; import { getContracts } from "@/features/contracts/api/contract.action"; import type { BlockchainTransactionView, BlockchainStats, } from "@/lib/services/blockchain.types"; import { toast } from "sonner"; // ═══════════════════════════════════════════════════════════════ // Blockchain Explorer — 2026 Edition // ═══════════════════════════════════════════════════════════════ export default function BlockchainExplorerPage() { const [transactions, setTransactions] = useState( [], ); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [verifyHash, setVerifyHash] = useState(""); const [verifyResult, setVerifyResult] = useState<{ exists: boolean; timestamp: number; depositor: string; } | null>(null); const [verifying, setVerifying] = useState(false); const [copiedTx, setCopiedTx] = useState(null); const [unregisteredContracts, setUnregisteredContracts] = useState< Array<{ id: string; title: string | null; fileName: string }> >([]); const [registeringId, setRegisteringId] = useState(null); const [downloadingCertificateId, setDownloadingCertificateId] = useState< string | null >(null); const [activeTab, setActiveTab] = useState<"all" | "verified" | "pending">( "all", ); const loadData = useCallback(async () => { setLoading(true); try { const [txResult, statsResult, contractsResult] = await Promise.all([ getBlockchainTransactions(), getBlockchainStats(), getContracts({ status: "COMPLETED" }), ]); if (txResult.success && txResult.transactions) { setTransactions(txResult.transactions); } if (statsResult.success && statsResult.stats) { setStats(statsResult.stats); } if (contractsResult.success && contractsResult.contracts) { const registered = new Set( txResult.transactions?.map((tx) => tx.contractId) ?? [], ); const unregistered = contractsResult.contracts .filter( (c: { id: string; txHash?: string | null; status: string }) => !c.txHash && !registered.has(c.id) && c.status === "COMPLETED", ) .map((c: { id: string; title: string | null; fileName: string }) => ({ id: c.id, title: c.title, fileName: c.fileName, })); setUnregisteredContracts(unregistered); } } catch (error) { console.error("Failed to load blockchain data:", error); } finally { setLoading(false); } }, []); useEffect(() => { loadData(); }, [loadData]); const handleVerify = async () => { if (!verifyHash.trim()) return; setVerifying(true); setVerifyResult(null); try { const result = await verifyDocumentHashOnBlockchain(verifyHash.trim()); if (result.success && result.verification) { setVerifyResult(result.verification); } else { toast.error(result.error || "Verification failed"); } } catch { toast.error("Failed to verify hash"); } finally { setVerifying(false); } }; const handleRegister = async (contractId: string) => { setRegisteringId(contractId); try { const result = await registerContractOnBlockchain(contractId); if (result.success) { toast.success("Contract registered on blockchain!"); await loadData(); } else { toast.error(result.error || "Registration failed"); } } catch { toast.error("Failed to register on blockchain"); } finally { setRegisteringId(null); } }; const handleDownloadCertificate = async ( contractId: string, contractTitle: string, ) => { setDownloadingCertificateId(contractId); try { const result = await generateContractCertificate(contractId); if (result.success && result.certificatePdfBase64) { const base64 = result.certificatePdfBase64; const binary = atob(base64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i += 1) { bytes[i] = binary.charCodeAt(i); } const blob = new Blob([bytes], { type: "application/pdf" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = result.certificateFileName || `certificate-${contractTitle || contractId}.pdf`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); toast.success("Certificate downloaded successfully!"); } else { toast.error(result.error || "Failed to generate certificate"); } } catch { toast.error("Failed to download certificate"); } finally { setDownloadingCertificateId(null); } }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); setCopiedTx(text); setTimeout(() => setCopiedTx(null), 2000); }; const formatTimestamp = (iso: string) => { return new Date(iso).toLocaleString(); }; const truncateHash = (hash: string, chars = 8) => { if (!hash || hash.length <= chars * 2 + 3) return hash; return `${hash.slice(0, chars + 2)}...${hash.slice(-chars)}`; }; const filteredTransactions = transactions.filter((tx) => { if (activeTab === "verified") return tx.status === "CONFIRMED" || tx.status === "SUCCESS"; if (activeTab === "pending") return tx.status === "PENDING" || tx.status === "PROCESSING"; return true; }); return (
{/* Ambient Background */}
{/* Page Header */}

Blockchain Explorer

Immutable proof-of-existence registry. Verify document integrity, audit on-chain history, and register new contracts.

Live
{/* Stats Bento Grid */} } label="Verified Documents" value={stats?.totalVerified?.toString() ?? "0"} subtitle="On-chain proofs" gradient="from-emerald-500/20 to-emerald-500/5" border="border-emerald-500/20" iconColor="text-emerald-500" delay={0} /> } label="Latest Block" value={ stats?.latestBlockNumber ? `#${stats.latestBlockNumber.toLocaleString()}` : "—" } subtitle="Network height" gradient="from-blue-500/20 to-blue-500/5" border="border-blue-500/20" iconColor="text-blue-500" delay={0.05} /> } label="Network Status" value={stats?.networkName || "Not Configured"} subtitle={ stats?.chainId ? `Chain ID ${stats.chainId}` : "Disconnected" } gradient={ stats?.networkStatus === "connected" ? "from-emerald-500/20 to-emerald-500/5" : "from-red-500/20 to-red-500/5" } border={ stats?.networkStatus === "connected" ? "border-emerald-500/20" : "border-red-500/20" } iconColor={ stats?.networkStatus === "connected" ? "text-emerald-500" : "text-red-500" } badge={ stats?.networkStatus === "connected" ? ( LIVE ) : ( OFFLINE ) } delay={0.1} /> } label="Wallet" value={ stats?.walletAddress ? truncateHash(stats.walletAddress, 6) : "—" } subtitle="Connected address" gradient="from-violet-500/20 to-violet-500/5" border="border-violet-500/20" iconColor="text-violet-500" delay={0.15} /> {/* Unregistered Contracts Alert */} {unregisteredContracts.length > 0 && (

{unregisteredContracts.length} pending registration

{unregisteredContracts.map((contract) => (
{contract.title || contract.fileName}
))}
)}
{/* Transactions List */}

Transaction History

Immutable audit trail of all registered documents

{(["all", "verified", "pending"] as const).map((tab) => ( ))}
{loading ? (
{[1, 2, 3].map((i) => (
))}
) : filteredTransactions.length === 0 ? (

No transactions found

{activeTab !== "all" ? `No ${activeTab} transactions match your filter.` : "Upload and analyze a contract to register it on-chain."}

) : (
{filteredTransactions.map((tx, idx) => (
{/* Title Row */}
{tx.contractTitle || tx.contractFileName} {tx.status}
{/* Metadata Grid */}
{truncateHash(tx.documentHash, 14)}
{truncateHash(tx.txHash, 14)}
Block #{tx.blockNumber.toLocaleString()}
{formatTimestamp(tx.blockTimestamp)}
{/* Right Side Actions */}
{tx.network === "sepolia" ? "Sepolia" : "Hardhat"}
{tx.explorerUrl && ( Etherscan )}
))}
)} {/* Verification Panel */}

Verify Document

Cryptographic on-chain verification