Files
explorer-monorepo/frontend/src/pages/transactions/index.tsx
2026-03-02 12:14:13 -08:00

104 lines
3.0 KiB
TypeScript

'use client'
import { useCallback, useEffect, useState } from 'react'
import { Table, Address } from '@/libs/frontend-ui-primitives'
import Link from 'next/link'
import { transactionsApi, Transaction } from '@/services/api/transactions'
export default function TransactionsPage() {
const [transactions, setTransactions] = useState<Transaction[]>([])
const [loading, setLoading] = useState(true)
const [page, setPage] = useState(1)
const chainId = parseInt(process.env.NEXT_PUBLIC_CHAIN_ID || '138')
const loadTransactions = useCallback(async () => {
setLoading(true)
try {
const { ok, data } = await transactionsApi.listSafe(chainId, page, 20)
setTransactions(ok ? data : [])
} catch (error) {
console.error('Failed to load transactions:', error)
setTransactions([])
} finally {
setLoading(false)
}
}, [chainId, page])
useEffect(() => {
loadTransactions()
}, [loadTransactions])
const columns = [
{
header: 'Hash',
accessor: (tx: Transaction) => (
<Link href={`/transactions/${tx.hash}`} className="text-primary-600 hover:underline">
<Address address={tx.hash} truncate />
</Link>
),
},
{
header: 'Block',
accessor: (tx: Transaction) => (
<Link href={`/blocks/${tx.block_number}`} className="text-primary-600 hover:underline">
{tx.block_number}
</Link>
),
},
{
header: 'From',
accessor: (tx: Transaction) => <Address address={tx.from_address} truncate />,
},
{
header: 'To',
accessor: (tx: Transaction) => tx.to_address ? <Address address={tx.to_address} truncate /> : <span className="text-gray-400">Contract Creation</span>,
},
{
header: 'Value',
accessor: (tx: Transaction) => {
const value = BigInt(tx.value)
const eth = Number(value) / 1e18
return eth > 0 ? `${eth.toFixed(4)} ETH` : '0 ETH'
},
},
{
header: 'Status',
accessor: (tx: Transaction) => (
<span className={tx.status === 1 ? 'text-green-600' : 'text-red-600'}>
{tx.status === 1 ? 'Success' : 'Failed'}
</span>
),
},
]
if (loading) {
return <div className="p-8">Loading transactions...</div>
}
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-6">Transactions</h1>
<Table columns={columns} data={transactions} keyExtractor={(tx) => tx.hash} />
<div className="mt-6 flex gap-4 justify-center">
<button
onClick={() => setPage((p) => Math.max(1, p - 1))}
disabled={page === 1}
className="px-4 py-2 bg-gray-200 rounded disabled:opacity-50"
>
Previous
</button>
<span className="px-4 py-2">Page {page}</span>
<button
onClick={() => setPage((p) => p + 1)}
className="px-4 py-2 bg-gray-200 rounded"
>
Next
</button>
</div>
</div>
)
}