@@ -124,8 +124,9 @@ const PeerDetail: React.FC = () => {
124124 block : '📦' ,
125125 // miningon: '⛏️', // Temporarily disabled
126126 subtree : '🌳' ,
127- handshake : '🤝' ,
127+ // handshake: '🤝', // Deprecated
128128 rejected_tx : '❌' ,
129+ node_status : '🖥️' ,
129130 bestblock : '🏆'
130131 } ;
131132 return icons [ type ] || '📄' ;
@@ -324,46 +325,75 @@ const PeerDetail: React.FC = () => {
324325 </ div >
325326 </ div >
326327
327- { /* Recent Handshakes */ }
328- { peerDetail . handshakes . length > 0 && (
328+ { /* Recent Node Status Updates */ }
329+ { peerDetail . nodeStatuses && peerDetail . nodeStatuses . length > 0 && (
329330 < div className = "bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-white/50 p-6 sm:p-8 mb-8" >
330331 < div className = "flex items-center gap-3 mb-6" >
331332 < div className = "w-10 h-10 bg-gradient-to-br from-blue-500 to-cyan-600 rounded-xl flex items-center justify-center" >
332- < span className = "text-2xl" > 🤝 </ span >
333+ < span className = "text-2xl" > 🖥️ </ span >
333334 </ div >
334- < h2 className = "text-xl font-semibold text-gray-900" > Recent Handshakes </ h2 >
335+ < h2 className = "text-xl font-semibold text-gray-900" > Recent Node Status </ h2 >
335336 </ div >
336-
337+
337338 < div className = "overflow-hidden rounded-xl border border-gray-200" >
338339 < div className = "overflow-x-auto" >
339340 < table className = "min-w-full divide-y divide-gray-200" >
340341 < thead className = "bg-gradient-to-r from-gray-50 to-gray-100" >
341342 < tr >
342- < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > Type </ th >
343- < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > User Agent </ th >
343+ < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > FSM State </ th >
344+ < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > Client / Version </ th >
344345 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > Best Height</ th >
346+ < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > Uptime</ th >
345347 < th className = "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" > Time</ th >
346348 </ tr >
347349 </ thead >
348350 < tbody className = "bg-white divide-y divide-gray-200" >
349- { peerDetail . handshakes . map ( ( handshake , idx ) => (
350- < tr key = { idx } className = "hover:bg-gray-50 transition-colors duration-150" >
351- < td className = "px-6 py-4 whitespace-nowrap" >
352- < span className = "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800" >
353- { handshake . Type }
354- </ span >
355- </ td >
356- < td className = "px-6 py-4 text-sm text-gray-600 max-w-xs truncate" title = { handshake . UserAgent } >
357- { handshake . UserAgent }
358- </ td >
359- < td className = "px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900" >
360- { handshake . BestHeight . toLocaleString ( ) }
361- </ td >
362- < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-500" >
363- { formatTime ( handshake . ReceivedAt ) }
364- </ td >
365- </ tr >
366- ) ) }
351+ { peerDetail . nodeStatuses . map ( ( status , idx ) => {
352+ const getFSMStateColor = ( state : string ) => {
353+ switch ( state ?. toUpperCase ( ) ) {
354+ case 'RUNNING' : return 'bg-green-100 text-green-800' ;
355+ case 'CATCHINGBLOCKS' : return 'bg-orange-100 text-orange-800' ;
356+ case 'LEGACYSYNC' : return 'bg-yellow-100 text-yellow-800' ;
357+ case 'IDLE' : return 'bg-gray-100 text-gray-800' ;
358+ default : return 'bg-blue-100 text-blue-800' ;
359+ }
360+ } ;
361+
362+ const formatUptime = ( seconds : number ) => {
363+ if ( ! seconds || seconds <= 0 ) return 'N/A' ;
364+ const days = Math . floor ( seconds / 86400 ) ;
365+ const hours = Math . floor ( ( seconds % 86400 ) / 3600 ) ;
366+ const minutes = Math . floor ( ( seconds % 3600 ) / 60 ) ;
367+ if ( days > 0 ) return `${ days } d ${ hours } h` ;
368+ if ( hours > 0 ) return `${ hours } h ${ minutes } m` ;
369+ return `${ minutes } m` ;
370+ } ;
371+
372+ return (
373+ < tr key = { idx } className = "hover:bg-gray-50 transition-colors duration-150" >
374+ < td className = "px-6 py-4 whitespace-nowrap" >
375+ < span className = { `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${ getFSMStateColor ( status . FSMState ) } ` } >
376+ { status . FSMState || 'UNKNOWN' }
377+ </ span >
378+ </ td >
379+ < td className = "px-6 py-4 text-sm text-gray-600" >
380+ < div >
381+ < div className = "font-medium" > { status . ClientName || 'Unknown' } </ div >
382+ < div className = "text-xs text-gray-500" > { status . Version || 'N/A' } </ div >
383+ </ div >
384+ </ td >
385+ < td className = "px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900" >
386+ { status . BestHeight ?. toLocaleString ( ) || 'N/A' }
387+ </ td >
388+ < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-600" >
389+ { formatUptime ( status . Uptime ) }
390+ </ td >
391+ < td className = "px-6 py-4 whitespace-nowrap text-sm text-gray-500" >
392+ { formatTime ( status . ReceivedAt ) }
393+ </ td >
394+ </ tr >
395+ ) ;
396+ } ) }
367397 </ tbody >
368398 </ table >
369399 </ div >
0 commit comments