@@ -3,6 +3,76 @@ import { useCyberStore } from '../cyber.store';
33import { useDynamicCyberData } from '../hooks/useCyberData' ;
44import { getEndpointDef , getCategoryForEndpoint } from '../config' ;
55
6+ // ─── Type Definitions ─────────────────────────────────────────────────────────
7+ interface RankedCountryRow {
8+ clientCountryName ?: string ;
9+ originCountryName ?: string ;
10+ location ?: string ;
11+ clientCountryAlpha2 ?: string ;
12+ originCountryAlpha2 ?: string ;
13+ value ?: string ;
14+ rank ?: number ;
15+ }
16+
17+ interface RankedAsnRow {
18+ asn ?: string ;
19+ originAsn ?: string ;
20+ ASName ?: string ;
21+ originAsnName ?: string ;
22+ value ?: string ;
23+ rank ?: number ;
24+ }
25+
26+ interface DomainCategory {
27+ name : string ;
28+ }
29+
30+ interface RankedDomainRow {
31+ rank : number ;
32+ domain : string ;
33+ categories ?: DomainCategory [ ] ;
34+ }
35+
36+ interface SpeedTableRow {
37+ clientCountryAlpha2 : string ;
38+ clientCountryName : string ;
39+ bandwidthDownload : string ;
40+ bandwidthUpload : string ;
41+ latencyIdle : string ;
42+ jitterIdle : string ;
43+ }
44+
45+ interface BgpStats {
46+ routes_total : number ;
47+ routes_valid : number ;
48+ routes_invalid : number ;
49+ routes_unknown : number ;
50+ distinct_prefixes : number ;
51+ distinct_prefixes_ipv4 : number ;
52+ distinct_prefixes_ipv6 : number ;
53+ distinct_origins : number ;
54+ distinct_origins_ipv4 : number ;
55+ distinct_origins_ipv6 : number ;
56+ }
57+
58+ interface AnomalyRow {
59+ type : string ;
60+ locationDetails ?: {
61+ name : string ;
62+ code : string ;
63+ } ;
64+ asnDetails ?: {
65+ name : string ;
66+ location ?: {
67+ code : string ;
68+ } ;
69+ } ;
70+ status : string ;
71+ startDate : string ;
72+ endDate ?: string ;
73+ visibleInDataSources ?: string [ ] ;
74+ }
75+
676// ─── Flag emoji from ISO alpha-2 ─────────────────────────────────────────────
777const flagEmoji = ( alpha2 : string ) =>
878 alpha2
@@ -22,7 +92,7 @@ const Bar: React.FC<{ pct: number; max?: number; color: string }> = ({ pct, max
2292) ;
2393
2494// ─── Ranked Country ───────────────────────────────────────────────────────────
25- const RankedCountryRenderer : React . FC < { rows : any [ ] ; color : string ; unit ?: string } > = ( { rows, color, unit } ) => {
95+ const RankedCountryRenderer : React . FC < { rows : RankedCountryRow [ ] ; color : string ; unit ?: string } > = ( { rows, color, unit } ) => {
2696 const max = rows . length > 0 ? parseFloat ( rows [ 0 ] . value ?? '0' ) : 100 ;
2797 return (
2898 < div className = "flex flex-col gap-1" >
@@ -47,7 +117,7 @@ const RankedCountryRenderer: React.FC<{ rows: any[]; color: string; unit?: strin
47117} ;
48118
49119// ─── Ranked ASN ───────────────────────────────────────────────────────────────
50- const RankedAsnRenderer : React . FC < { rows : any [ ] ; color : string ; unit ?: string } > = ( { rows, color, unit } ) => {
120+ const RankedAsnRenderer : React . FC < { rows : RankedAsnRow [ ] ; color : string ; unit ?: string } > = ( { rows, color, unit } ) => {
51121 const max = rows . length > 0 ? parseFloat ( rows [ 0 ] . value ?? '0' ) : 100 ;
52122 return (
53123 < div className = "flex flex-col gap-1" >
@@ -74,10 +144,10 @@ const RankedAsnRenderer: React.FC<{ rows: any[]; color: string; unit?: string }>
74144} ;
75145
76146// ─── Ranked Domain ────────────────────────────────────────────────────────────
77- const RankedDomainRenderer : React . FC < { rows : any [ ] ; color : string } > = ( { rows, color } ) => (
147+ const RankedDomainRenderer : React . FC < { rows : RankedDomainRow [ ] ; color : string } > = ( { rows, color } ) => (
78148 < div className = "flex flex-col gap-1" >
79149 { rows . map ( ( row , i ) => {
80- const cats = ( row . categories ?? [ ] ) . map ( ( c : any ) => c . name ) . join ( ' · ' ) ;
150+ const cats = ( row . categories ?? [ ] ) . map ( ( c : DomainCategory ) => c . name ) . join ( ' · ' ) ;
81151 return (
82152 < div key = { i } className = "flex items-center gap-3 bg-intel-bg/40 border border-white/5 px-3 py-1.5 rounded hover:bg-white/5 transition-colors" >
83153 < span className = "font-mono text-xs font-bold w-10 text-right tabular-nums shrink-0" style = { { color } } > #{ row . rank } </ span >
@@ -90,7 +160,7 @@ const RankedDomainRenderer: React.FC<{ rows: any[]; color: string }> = ({ rows,
90160) ;
91161
92162// ─── Speed Table ──────────────────────────────────────────────────────────────
93- const SpeedTableRenderer : React . FC < { rows : any [ ] ; color : string } > = ( { rows, color } ) => (
163+ const SpeedTableRenderer : React . FC < { rows : SpeedTableRow [ ] ; color : string } > = ( { rows, color } ) => (
94164 < div >
95165 < div className = "grid grid-cols-6 gap-2 px-3 py-1 mb-1" >
96166 { [ '#' , 'Country' , 'Download' , 'Upload' , 'Latency' , 'Jitter' ] . map ( h => (
@@ -123,7 +193,7 @@ const SpeedTableRenderer: React.FC<{ rows: any[]; color: string }> = ({ rows, co
123193) ;
124194
125195// ─── BGP Global Stats ─────────────────────────────────────────────────────────
126- const BgpStatsRenderer : React . FC < { stats : any ; color : string } > = ( { stats, color } ) => {
196+ const BgpStatsRenderer : React . FC < { stats : BgpStats ; color : string } > = ( { stats, color } ) => {
127197 const groups = [
128198 {
129199 label : 'Routes' ,
@@ -177,16 +247,20 @@ const BgpStatsRenderer: React.FC<{ stats: any; color: string }> = ({ stats, colo
177247} ;
178248
179249// ─── Anomaly Feed ─────────────────────────────────────────────────────────────
180- const AnomalyFeedRenderer : React . FC < { rows : any [ ] } > = ( { rows } ) => {
250+ const AnomalyFeedRenderer : React . FC < { rows : AnomalyRow [ ] } > = ( { rows } ) => {
181251 const statusColor = ( s : string ) => s === 'VERIFIED' ? '#ef4444' : '#f59e0b' ;
252+ // Compute current time for elapsed time display. This is intentionally called during render
253+ // to show accurate elapsed times. The component re-renders when rows change, updating times.
254+ // eslint-disable-next-line react-hooks/purity
255+ const now = Date . now ( ) ;
182256 return (
183257 < div className = "flex flex-col gap-2" >
184258 { rows . map ( ( a , i ) => {
185259 const isLocation = a . type === 'LOCATION' ;
186260 const name = isLocation ? a . locationDetails ?. name : a . asnDetails ?. name ;
187261 const code = isLocation ? a . locationDetails ?. code : a . asnDetails ?. location ?. code ;
188262 const sc = statusColor ( a . status ) ;
189- const elapsed = Date . now ( ) - new Date ( a . startDate ) . getTime ( ) ;
263+ const elapsed = now - new Date ( a . startDate ) . getTime ( ) ;
190264 const hours = Math . floor ( elapsed / 3_600_000 ) ;
191265 const minutes = Math . floor ( ( elapsed % 3_600_000 ) / 60_000 ) ;
192266 const duration = hours > 0 ? `${ hours } h ${ minutes } m ago` : `${ minutes } m ago` ;
0 commit comments