@@ -11,20 +11,23 @@ import {
1111 ResponsiveContainer ,
1212 AreaChart ,
1313 Area ,
14+ PieChart ,
15+ Pie ,
16+ Cell
1417} from "recharts" ;
1518
16- interface ChartData {
17- connections ?: number [ ] ;
18- requests ?: number [ ] ;
19- storage ?: number [ ] ;
20- labels ?: string [ ] ;
19+ interface MetricPoint {
20+ ts : number ;
21+ v : number ;
2122}
2223
2324function Charts ( ) {
2425 const { isAuthenticated, getAuthHeaders } = useAuth ( ) ;
2526 const [ loading , setLoading ] = useState ( true ) ;
2627 const [ stats , setStats ] = useState < Record < string , any > > ( { } ) ;
27- const [ history , setHistory ] = useState < any [ ] > ( [ ] ) ;
28+
29+ // Local history for metrics not historically tracked by backend
30+ const [ localHistory , setLocalHistory ] = useState < any [ ] > ( [ ] ) ;
2831
2932 const loadStats = useCallback ( async ( ) => {
3033 try {
@@ -33,17 +36,16 @@ function Charts() {
3336 const data = await response . json ( ) ;
3437 setStats ( data ) ;
3538
36- setHistory ( ( prev ) => {
39+ setLocalHistory ( ( prev ) => {
3740 const now = new Date ( ) . toLocaleTimeString ( ) ;
3841 const newPoint = {
3942 time : now ,
4043 memory : ( data . memory ?. heapUsed || 0 ) / 1024 / 1024 ,
41- peers : data . peers ?. count || 0 ,
42- damIn : data . dam ?. in ?. count || 0 ,
43- damOut : data . dam ?. out ?. count || 0 ,
44+ peers : data . connectedPeers || 0 ,
45+ totalMsgs : data . totalMessages || 0 ,
4446 } ;
4547 const newHistory = [ ...prev , newPoint ] ;
46- return newHistory . slice ( - 20 ) ;
48+ return newHistory . slice ( - 30 ) ;
4749 } ) ;
4850 }
4951 } catch ( error ) {
@@ -56,43 +58,63 @@ function Charts() {
5658 useEffect ( ( ) => {
5759 if ( isAuthenticated ) {
5860 loadStats ( ) ;
59- const interval = setInterval ( loadStats , 5000 ) ;
61+ const interval = setInterval ( loadStats , 2000 ) ;
6062 return ( ) => clearInterval ( interval ) ;
6163 }
6264 } , [ isAuthenticated , loadStats ] ) ;
6365
6466 const formatBytes = ( bytes : number ) => {
65- if ( ! bytes ) return "0 B" ;
67+ if ( ! bytes && bytes !== 0 ) return "0 B" ;
6668 const k = 1024 ;
6769 const sizes = [ "B" , "KB" , "MB" , "GB" ] ;
6870 const i = Math . floor ( Math . log ( bytes ) / Math . log ( k ) ) ;
6971 return parseFloat ( ( bytes / Math . pow ( k , i ) ) . toFixed ( 2 ) ) + " " + sizes [ i ] ;
7072 } ;
7173
72- const formatUptime = ( seconds : number ) => {
73- if ( ! seconds ) return "-" ;
74+ const formatUptime = ( ms : number ) => {
75+ if ( ! ms ) return "-" ;
76+ const seconds = Math . floor ( ms / 1000 ) ;
7477 const days = Math . floor ( seconds / 86400 ) ;
7578 const hours = Math . floor ( ( seconds % 86400 ) / 3600 ) ;
7679 const mins = Math . floor ( ( seconds % 3600 ) / 60 ) ;
7780 return `${ days } d ${ hours } h ${ mins } m` ;
7881 } ;
7982
80- if ( loading && history . length === 0 ) {
83+ if ( loading && localHistory . length === 0 ) {
8184 return (
8285 < div className = "flex justify-center p-8" >
8386 < span className = "loading loading-spinner loading-lg" > </ span >
8487 </ div >
8588 ) ;
8689 }
8790
91+ // Map backend history into chart-friendly formats
92+ const msgHistoryData = ( stats . msgHistory || [ ] ) . map ( ( pt : MetricPoint ) => ( {
93+ time : new Date ( pt . ts ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' , second : '2-digit' } ) ,
94+ rate : pt . v
95+ } ) ) ;
96+
97+ const byteHistoryData = ( stats . byteHistory || [ ] ) . map ( ( pt : MetricPoint ) => ( {
98+ time : new Date ( pt . ts ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' , second : '2-digit' } ) ,
99+ rate : pt . v
100+ } ) ) ;
101+
102+ const opMixData = [
103+ { name : 'PUT' , value : stats . putCount || 0 } ,
104+ { name : 'GET' , value : stats . getCount || 0 } ,
105+ { name : 'ACK' , value : stats . ackCount || 0 } ,
106+ { name : 'ERR' , value : stats . errorCount || 0 } ,
107+ ] ;
108+ const COLORS = [ '#00ffe5' , '#00b4ff' , '#00ff8c' , '#ff3a5c' ] ;
109+
88110 return (
89111 < div className = "flex flex-col gap-6 max-w-6xl" >
90112 { /* Header */ }
91113 < div className = "card bg-base-100 shadow" >
92114 < div className = "card-body flex-row items-center justify-between flex-wrap gap-4" >
93115 < div >
94- < h2 className = "card-title text-2xl" > 📊 Charts & Metrics </ h2 >
95- < p className = "text-base-content/70" > Real-time system performance monitoring</ p >
116+ < h2 className = "card-title text-2xl" > 📊 Live Telemetry Charts </ h2 >
117+ < p className = "text-base-content/70" > Real-time GunDB protocol monitoring</ p >
96118 </ div >
97119 < div className = "flex gap-2" >
98120 < span className = "badge badge-ghost" > { new Date ( ) . toLocaleTimeString ( ) } </ span >
@@ -105,51 +127,62 @@ function Charts() {
105127 < div className = "stats stats-vertical lg:stats-horizontal shadow w-full" >
106128 < div className = "stat" >
107129 < div className = "stat-title" > Uptime</ div >
108- < div className = "stat-value text-lg" > { formatUptime ( stats . up ?. time / 1000 ) } </ div >
130+ < div className = "stat-value text-lg" > { formatUptime ( stats . uptime ) } </ div >
109131 </ div >
110132 < div className = "stat" >
111133 < div className = "stat-title" > Heap Used</ div >
112134 < div className = "stat-value text-lg" > { formatBytes ( stats . memory ?. heapUsed ) } </ div >
113135 </ div >
114136 < div className = "stat" >
115- < div className = "stat-title" > CPU User </ div >
137+ < div className = "stat-title" > CPU System </ div >
116138 < div className = "stat-value text-lg" >
117- { stats . cpu ?. user ? ( stats . cpu . user / 1000000 ) . toFixed ( 2 ) + "s" : "-" }
139+ { stats . cpu ?. system ? ( stats . cpu . system / 1000000 ) . toFixed ( 2 ) + "s" : "-" }
118140 </ div >
119141 </ div >
120142 < div className = "stat" >
121- < div className = "stat-title" > Version </ div >
122- < div className = "stat-value text-lg" > { stats . version || "1.0.0" } </ div >
143+ < div className = "stat-title" > Total Messages </ div >
144+ < div className = "stat-value text-lg text-primary " > { stats . totalMessages ?. toLocaleString ( ) || 0 } </ div >
123145 </ div >
124146 </ div >
125147
126148 { /* Charts Grid */ }
127149 < div className = "grid grid-cols-1 lg:grid-cols-2 gap-6" >
128- { /* Memory Usage Chart */ }
129- < div className = "card bg-base-100 shadow" >
150+ { /* Message Rate Chart */ }
151+ < div className = "card bg-base-100 shadow border border-primary/20 " >
130152 < div className = "card-body" >
131- < h3 className = "card-title text-lg" > 🧠 Memory Usage (MB )</ h3 >
132- < div className = "h-72 " >
153+ < h3 className = "card-title text-lg text-primary" > ⚡ Message Rate (msg/s )</ h3 >
154+ < div className = "h-64 " >
133155 < ResponsiveContainer width = "100%" height = "100%" >
134- < AreaChart data = { history } >
135- < CartesianGrid strokeDasharray = "3 3" stroke = "#444" />
136- < XAxis dataKey = "time" stroke = "#888" fontSize = { 12 } />
137- < YAxis stroke = "#888" fontSize = { 12 } />
156+ < AreaChart data = { msgHistoryData } >
157+ < CartesianGrid strokeDasharray = "3 3" stroke = "#444" opacity = { 0.3 } />
158+ < XAxis dataKey = "time" stroke = "#888" fontSize = { 10 } tick = { { fill : '#2a6070' } } />
159+ < YAxis stroke = "#888" fontSize = { 10 } tick = { { fill : '#2a6070' } } />
138160 < Tooltip
139- contentStyle = { {
140- backgroundColor : "#2a2a2a" ,
141- border : "none" ,
142- borderRadius : "8px" ,
143- } }
144- itemStyle = { { color : "#fff" } }
161+ contentStyle = { { backgroundColor : "#050f14" , border : "1px solid #00ffe5" , borderRadius : "4px" } }
162+ itemStyle = { { color : "#7ecfdf" } }
145163 />
146- < Area
147- type = "monotone"
148- dataKey = "memory"
149- stroke = "#8884d8"
150- fill = "#8884d8"
151- fillOpacity = { 0.3 }
164+ < Area type = "monotone" dataKey = "rate" stroke = "#00ffe5" fill = "#00ffe5" fillOpacity = { 0.15 } isAnimationActive = { false } />
165+ </ AreaChart >
166+ </ ResponsiveContainer >
167+ </ div >
168+ </ div >
169+ </ div >
170+
171+ { /* Bandwidth Chart */ }
172+ < div className = "card bg-base-100 shadow border border-secondary/20" >
173+ < div className = "card-body" >
174+ < h3 className = "card-title text-lg text-secondary" > 🌊 Bandwidth (bytes/s)</ h3 >
175+ < div className = "h-64" >
176+ < ResponsiveContainer width = "100%" height = "100%" >
177+ < AreaChart data = { byteHistoryData } >
178+ < CartesianGrid strokeDasharray = "3 3" stroke = "#444" opacity = { 0.3 } />
179+ < XAxis dataKey = "time" stroke = "#888" fontSize = { 10 } tick = { { fill : '#2a6070' } } />
180+ < YAxis stroke = "#888" fontSize = { 10 } tick = { { fill : '#2a6070' } } />
181+ < Tooltip
182+ contentStyle = { { backgroundColor : "#050f14" , border : "1px solid #00b4ff" , borderRadius : "4px" } }
183+ itemStyle = { { color : "#7ecfdf" } }
152184 />
185+ < Area type = "monotone" dataKey = "rate" stroke = "#00b4ff" fill = "#00b4ff" fillOpacity = { 0.15 } isAnimationActive = { false } />
153186 </ AreaChart >
154187 </ ResponsiveContainer >
155188 </ div >
@@ -159,65 +192,57 @@ function Charts() {
159192 { /* Peers Chart */ }
160193 < div className = "card bg-base-100 shadow" >
161194 < div className = "card-body" >
162- < h3 className = "card-title text-lg" > 👥 Connected Peers</ h3 >
163- < div className = "h-72 " >
195+ < h3 className = "card-title text-lg" > 👥 Connected Peers Trend </ h3 >
196+ < div className = "h-64 " >
164197 < ResponsiveContainer width = "100%" height = "100%" >
165- < LineChart data = { history } >
166- < CartesianGrid strokeDasharray = "3 3" stroke = "#444" />
167- < XAxis dataKey = "time" stroke = "#888" fontSize = { 12 } />
168- < YAxis allowDecimals = { false } stroke = "#888" fontSize = { 12 } />
198+ < LineChart data = { localHistory } >
199+ < CartesianGrid strokeDasharray = "3 3" stroke = "#444" opacity = { 0.3 } />
200+ < XAxis dataKey = "time" stroke = "#888" fontSize = { 10 } />
201+ < YAxis allowDecimals = { false } stroke = "#888" fontSize = { 10 } />
169202 < Tooltip
170- contentStyle = { {
171- backgroundColor : "#2a2a2a" ,
172- border : "none" ,
173- borderRadius : "8px" ,
174- } }
175- itemStyle = { { color : "#fff" } }
203+ contentStyle = { { backgroundColor : "#050f14" , border : "1px solid #00ff8c" , borderRadius : "4px" } }
204+ itemStyle = { { color : "#7ecfdf" } }
176205 />
177- < Line type = "step " dataKey = "peers" stroke = "#82ca9d " strokeWidth = { 2 } />
206+ < Line type = "stepAfter " dataKey = "peers" stroke = "#00ff8c " strokeWidth = { 2 } isAnimationActive = { false } />
178207 </ LineChart >
179208 </ ResponsiveContainer >
180209 </ div >
181210 </ div >
182211 </ div >
183- </ div >
184212
185- { /* Full Width Chart */ }
186- < div className = "card bg-base-100 shadow" >
187- < div className = "card-body" >
188- < h3 className = "card-title text-lg" > 📡 DAM Request Traffic</ h3 >
189- < div className = "h-72" >
190- < ResponsiveContainer width = "100%" height = "100%" >
191- < AreaChart data = { history } >
192- < CartesianGrid strokeDasharray = "3 3" stroke = "#444" />
193- < XAxis dataKey = "time" stroke = "#888" fontSize = { 12 } />
194- < YAxis stroke = "#888" fontSize = { 12 } />
195- < Tooltip
196- contentStyle = { { backgroundColor : "#2a2a2a" , border : "none" , borderRadius : "8px" } }
197- itemStyle = { { color : "#fff" } }
198- />
199- < Legend />
200- < Area
201- type = "monotone"
202- dataKey = "damIn"
203- name = "Incoming (In)"
204- stroke = "#ffc658"
205- fill = "#ffc658"
206- stackId = "1"
207- />
208- < Area
209- type = "monotone"
210- dataKey = "damOut"
211- name = "Outgoing (Out)"
212- stroke = "#ff7300"
213- fill = "#ff7300"
214- stackId = "1"
215- />
216- </ AreaChart >
217- </ ResponsiveContainer >
213+ { /* Operation Mix (Doughnut) */ }
214+ < div className = "card bg-base-100 shadow" >
215+ < div className = "card-body items-center" >
216+ < h3 className = "card-title text-lg w-full text-left" > 🔄 Operation Mix</ h3 >
217+ < div className = "h-64 w-full" >
218+ < ResponsiveContainer width = "100%" height = "100%" >
219+ < PieChart >
220+ < Pie
221+ data = { opMixData }
222+ cx = "50%"
223+ cy = "50%"
224+ innerRadius = { 60 }
225+ outerRadius = { 90 }
226+ paddingAngle = { 2 }
227+ dataKey = "value"
228+ stroke = "none"
229+ >
230+ { opMixData . map ( ( entry , index ) => (
231+ < Cell key = { `cell-${ index } ` } fill = { COLORS [ index % COLORS . length ] } />
232+ ) ) }
233+ </ Pie >
234+ < Tooltip
235+ contentStyle = { { backgroundColor : "#050f14" , border : "1px solid #333" , borderRadius : "4px" } }
236+ itemStyle = { { color : "#fff" } }
237+ />
238+ < Legend verticalAlign = "bottom" height = { 36 } />
239+ </ PieChart >
240+ </ ResponsiveContainer >
241+ </ div >
218242 </ div >
219243 </ div >
220244 </ div >
245+
221246 </ div >
222247 ) ;
223248}
0 commit comments