44/* eslint-disable no-nested-ternary */
55
66import { useCallback , useEffect , useState } from "react" ;
7- import { Blocks , Zap , Link , Clock , Database } from "lucide-react" ;
7+ import { useRouter } from "next/navigation" ;
8+ import { Blocks , Zap , Link , Clock , Database , ArrowRight } from "lucide-react" ;
89
910import {
1011 Card ,
@@ -13,6 +14,7 @@ import {
1314 CardHeader ,
1415 CardTitle ,
1516} from "@/components/ui/card" ;
17+ import { Button } from "@/components/ui/button" ;
1618import { Skeleton } from "@/components/ui/skeleton" ;
1719import config from "@/config" ;
1820import { cn } from "@/lib/utils" ;
@@ -38,6 +40,11 @@ export interface GetDashboardStatsResponse {
3840 stateRoot : string ;
3941 } ;
4042 } > ;
43+ recentBlocks : Array < {
44+ height : number ;
45+ hash : string ;
46+ timestamp ?: string ;
47+ } > ;
4148 settlements : Array < {
4249 transactionHash : string ;
4350 promisedMessagesHash : string ;
@@ -57,6 +64,11 @@ interface DashboardStatsData {
5764 promisedMessagesHash : string ;
5865 } | null ;
5966 currentStateRoot : string ;
67+ recentBlocks : Array < {
68+ height : number ;
69+ hash : string ;
70+ timestamp ?: string ;
71+ } > ;
6072}
6173
6274interface StatCardProps {
@@ -111,6 +123,7 @@ function StatCard({
111123export default function DashboardStats ( ) {
112124 const [ stats , setStats ] = useState < DashboardStatsData | null > ( null ) ;
113125 const [ loading , setLoading ] = useState ( true ) ;
126+ const router = useRouter ( ) ;
114127
115128 const fetchStats = useCallback ( async ( ) => {
116129 setLoading ( true ) ;
@@ -124,6 +137,10 @@ export default function DashboardStats() {
124137 fromStateRoot
125138 result { stateRoot }
126139 }
140+ recentBlocks: blocks(take: 10, orderBy: { height: desc }) {
141+ height
142+ hash
143+ }
127144 settlements(take: 1, orderBy: { transactionHash: desc }) {
128145 transactionHash
129146 promisedMessagesHash
@@ -161,6 +178,7 @@ export default function DashboardStats() {
161178 data ?. blocks ?. [ 0 ] ?. result ?. stateRoot ||
162179 data ?. blocks ?. [ 0 ] ?. fromStateRoot ||
163180 "—" ,
181+ recentBlocks : data . recentBlocks ,
164182 } ) ;
165183 } catch ( error ) {
166184 console . error ( "Failed to fetch dashboard stats:" , error ) ;
@@ -206,7 +224,10 @@ export default function DashboardStats() {
206224 loading = { loading }
207225 >
208226 { stats ?. latestBlock ? (
209- < >
227+ < div
228+ className = "cursor-pointer hover:opacity-75 transition-opacity"
229+ onClick = { ( ) => router . push ( `/blocks/${ stats . latestBlock ?. hash } ` ) }
230+ >
210231 < div >
211232 < p className = "text-xs text-muted-foreground mb-1" > Height</ p >
212233 < p className = "text-2xl font-bold" >
@@ -219,7 +240,7 @@ export default function DashboardStats() {
219240 < CopyToClipboard text = { stats . latestBlock . hash } />
220241 </ div >
221242 </ div >
222- </ >
243+ </ div >
223244 ) : (
224245 < p className = "text-sm text-muted-foreground" > No data available</ p >
225246 ) }
@@ -230,7 +251,12 @@ export default function DashboardStats() {
230251 loading = { loading }
231252 >
232253 { stats ?. latestSettlement ? (
233- < >
254+ < div
255+ className = "cursor-pointer hover:opacity-75 transition-opacity"
256+ onClick = { ( ) =>
257+ router . push ( `/settlements/${ stats . latestSettlement ?. hash } ` )
258+ }
259+ >
234260 < div >
235261 < p className = "text-xs text-muted-foreground mb-1" >
236262 Transaction Hash
@@ -249,12 +275,84 @@ export default function DashboardStats() {
249275 />
250276 </ div >
251277 </ div >
252- </ >
278+ </ div >
253279 ) : (
254280 < p className = "text-sm text-muted-foreground" > No data available</ p >
255281 ) }
256282 </ StatCard >
257283 </ div >
284+ < Card >
285+ < CardHeader className = "flex flex-row items-center justify-between space-y-0 pb-3" >
286+ < div >
287+ < CardTitle className = "flex items-center gap-2" >
288+ < Blocks className = "w-5 h-5" />
289+ Latest Blocks
290+ </ CardTitle >
291+ < CardDescription > The 10 most recently mined blocks</ CardDescription >
292+ </ div >
293+ < Button
294+ variant = "outline"
295+ size = "sm"
296+ onClick = { ( ) => router . push ( "/blocks" ) }
297+ className = "gap-2"
298+ >
299+ See all blocks
300+ < ArrowRight className = "w-4 h-4" />
301+ </ Button >
302+ </ CardHeader >
303+ < CardContent >
304+ < div className = "overflow-x-auto" >
305+ < table className = "w-full text-sm" >
306+ < thead >
307+ < tr className = "border-b" >
308+ < th className = "text-left py-3 px-4 font-medium text-muted-foreground" >
309+ Height
310+ </ th >
311+ < th className = "text-left py-3 px-4 font-medium text-muted-foreground" >
312+ Hash
313+ </ th >
314+ </ tr >
315+ </ thead >
316+ < tbody >
317+ { loading ? (
318+ Array . from ( { length : 5 } ) . map ( ( _ , i ) => (
319+ < tr key = { i } className = "border-b hover:bg-muted/50" >
320+ < td className = "py-3 px-4" >
321+ < Skeleton className = "h-4 w-12" />
322+ </ td >
323+ < td className = "py-3 px-4" >
324+ < Skeleton className = "h-4 w-48" />
325+ </ td >
326+ </ tr >
327+ ) )
328+ ) : stats ?. recentBlocks && stats . recentBlocks . length > 0 ? (
329+ stats . recentBlocks . map ( ( block ) => (
330+ < tr
331+ key = { block . hash }
332+ className = "border-b hover:bg-muted/50 cursor-pointer transition-colors"
333+ onClick = { ( ) => router . push ( `/blocks/${ block . hash } ` ) }
334+ >
335+ < td className = "py-3 px-4 font-medium" > #{ block . height } </ td >
336+ < td className = "py-3 px-4 text-muted-foreground truncate max-w-xs" >
337+ < CopyToClipboard text = { block . hash } />
338+ </ td >
339+ </ tr >
340+ ) )
341+ ) : (
342+ < tr >
343+ < td
344+ colSpan = { 2 }
345+ className = "py-4 px-4 text-center text-muted-foreground"
346+ >
347+ No blocks available
348+ </ td >
349+ </ tr >
350+ ) }
351+ </ tbody >
352+ </ table >
353+ </ div >
354+ </ CardContent >
355+ </ Card >
258356 </ div >
259357 ) ;
260358}
0 commit comments