@@ -3,10 +3,10 @@ import { Link } from 'react-router-dom';
3
3
import { FiClock , FiTag , FiGitCommit , FiZap , FiHash , FiCalendar } from 'react-icons/fi' ;
4
4
import { getTimeAgo } from '../utils/lmpUtils' ;
5
5
import VersionBadge from './VersionBadge' ;
6
- import { AreaChart , Area , XAxis , YAxis , Tooltip , ResponsiveContainer , CartesianGrid } from 'recharts' ;
7
- import { format } from 'date-fns' ;
6
+ import MetricChart from './MetricChart' ;
8
7
import { useInvocationsFromLMP } from '../hooks/useBackend' ;
9
8
import { LMPCardTitle } from './depgraph/LMPCardTitle' ;
9
+ import { subDays , format , addDays , endOfMonth } from 'date-fns' ;
10
10
11
11
function StatItem ( { icon : Icon , label, value } ) {
12
12
return (
@@ -18,68 +18,26 @@ function StatItem({ icon: Icon, label, value }) {
18
18
}
19
19
20
20
function LMPDetailsSidePanel ( { lmp, uses, versionHistory } ) {
21
- const [ timeScale , setTimeScale ] = useState ( '1h' ) ;
22
- const [ movingAverage , setMovingAverage ] = useState ( 1 ) ;
23
- const { data : invocations } = useInvocationsFromLMP ( lmp . name , lmp . lmp_id ) ;
21
+ const { data : invocations } = useInvocationsFromLMP ( lmp . name , lmp . lmp_id , 0 , 100 ) ;
24
22
25
23
const chartData = useMemo ( ( ) => {
26
- if ( ! invocations ) return [ ] ;
27
-
28
- const now = new Date ( ) ;
29
- const timeScaleDays = {
30
- '15m' : 15 / ( 24 * 60 ) , '1h' : 1 / 24 , '6h' : 6 / 24 , '1d' : 1 , '1w' : 7 , '1m' : 30 , '3m' : 90 , '1y' : 365
31
- } [ timeScale ] ;
32
- const startDate = new Date ( now . getTime ( ) - timeScaleDays * 24 * 60 * 60 * 1000 ) ;
33
-
34
- const filteredInvocations = invocations . filter ( inv => new Date ( inv . created_at ) >= startDate ) ;
35
-
36
- // Group invocations by time intervals
37
- const intervalMs = timeScaleDays * 24 * 60 * 60 * 1000 / 20 ; // Divide the time range into 20 intervals
38
- const groupedData = filteredInvocations . reduce ( ( acc , inv ) => {
39
- const date = new Date ( inv . created_at ) ;
40
- const intervalIndex = Math . floor ( ( date - startDate ) / intervalMs ) ;
41
- const intervalDate = new Date ( startDate . getTime ( ) + intervalIndex * intervalMs ) ;
42
- const key = intervalDate . toISOString ( ) ;
43
- acc [ key ] = ( acc [ key ] || 0 ) + 1 ;
44
- return acc ;
45
- } , { } ) ;
46
-
47
- const sortedData = Object . entries ( groupedData )
48
- . map ( ( [ date , count ] ) => ( { date, count } ) )
24
+ if ( ! invocations || invocations . length === 0 ) return [ ] ;
25
+
26
+ return invocations
27
+ . map ( inv => ( {
28
+ date : new Date ( inv . created_at ) ,
29
+ count : 1 ,
30
+ latency : inv . latency_ms
31
+ } ) )
49
32
. sort ( ( a , b ) => new Date ( a . date ) - new Date ( b . date ) ) ;
33
+ } , [ invocations ] ) ;
50
34
51
- // Calculate moving average
52
- return sortedData . map ( ( item , index , array ) => {
53
- const start = Math . max ( 0 , index - movingAverage + 1 ) ;
54
- const windowSlice = array . slice ( start , index + 1 ) ;
55
- const avg = windowSlice . reduce ( ( sum , i ) => sum + i . count , 0 ) / windowSlice . length ;
56
- return { ...item , movingAvg : avg } ;
57
- } ) ;
58
- } , [ invocations , timeScale , movingAverage ] ) ;
59
-
60
- const formatXAxis = ( tickItem ) => {
61
- const date = new Date ( tickItem ) ;
62
- switch ( timeScale ) {
63
- case '15m' :
64
- case '1h' :
65
- case '6h' :
66
- return format ( date , 'HH:mm' ) ;
67
- case '1d' :
68
- return format ( date , 'HH:mm' ) ;
69
- case '1w' :
70
- return format ( date , 'EEE' ) ;
71
- case '1m' :
72
- case '3m' :
73
- return format ( date , 'MMM d' ) ;
74
- case '1y' :
75
- return format ( date , 'MMM' ) ;
76
- default :
77
- return format ( date , 'MMM d' ) ;
78
- }
79
- } ;
80
-
81
- const totalInvocations = invocations ?. length || 0 ;
82
- const avgLatency = invocations ?. reduce ( ( sum , inv ) => sum + inv . latency_ms , 0 ) / totalInvocations || 0 ;
35
+ const totalInvocations = useMemo ( ( ) => invocations ?. length || 0 , [ invocations ] ) ;
36
+ const avgLatency = useMemo ( ( ) => {
37
+ if ( ! invocations || invocations . length === 0 ) return 0 ;
38
+ const sum = invocations . reduce ( ( acc , inv ) => acc + inv . latency_ms , 0 ) ;
39
+ return sum / invocations . length ;
40
+ } , [ invocations ] ) ;
83
41
84
42
return (
85
43
< aside className = "w-[500px] bg-[#0d1117] p-4 overflow-y-auto" >
@@ -122,65 +80,21 @@ function LMPDetailsSidePanel({ lmp, uses, versionHistory }) {
122
80
) }
123
81
</ div >
124
82
125
- < div className = "bg-[#161b22] p-3 rounded" >
126
- < h3 className = "text-sm font-semibold mb-2 text-white" > Invocations Over Time</ h3 >
127
- < div className = "flex space-x-2 mb-2" >
128
- < select
129
- className = "bg-[#0d1117] text-white text-xs p-1 rounded"
130
- value = { timeScale }
131
- onChange = { ( e ) => setTimeScale ( e . target . value ) }
132
- >
133
- < option value = "15m" > 15 Minutes</ option >
134
- < option value = "1h" > 1 Hour</ option >
135
- < option value = "6h" > 6 Hours</ option >
136
- < option value = "1d" > 1 Day</ option >
137
- < option value = "1w" > 1 Week</ option >
138
- < option value = "1m" > 1 Month</ option >
139
- < option value = "3m" > 3 Months</ option >
140
- < option value = "1y" > 1 Year</ option >
141
- </ select >
142
- < select
143
- className = "bg-[#0d1117] text-white text-xs p-1 rounded"
144
- value = { movingAverage }
145
- onChange = { ( e ) => setMovingAverage ( Number ( e . target . value ) ) }
146
- >
147
- < option value = "1" > No Average</ option >
148
- < option value = "3" > 3-Day Avg</ option >
149
- < option value = "7" > 7-Day Avg</ option >
150
- < option value = "30" > 30-Day Avg</ option >
151
- </ select >
152
- </ div >
153
- < div className = "h-60" >
154
- < ResponsiveContainer width = "100%" height = "100%" >
155
- < AreaChart data = { chartData } margin = { { top : 10 , right : 30 , left : 0 , bottom : 0 } } >
156
- < defs >
157
- < linearGradient id = "colorCount" x1 = "0" y1 = "0" x2 = "0" y2 = "1" >
158
- < stop offset = "5%" stopColor = "#8884d8" stopOpacity = { 0.8 } />
159
- < stop offset = "95%" stopColor = "#8884d8" stopOpacity = { 0 } />
160
- </ linearGradient >
161
- < linearGradient id = "colorAvg" x1 = "0" y1 = "0" x2 = "0" y2 = "1" >
162
- < stop offset = "5%" stopColor = "#82ca9d" stopOpacity = { 0.8 } />
163
- < stop offset = "95%" stopColor = "#82ca9d" stopOpacity = { 0 } />
164
- </ linearGradient >
165
- </ defs >
166
- < XAxis
167
- dataKey = "date"
168
- stroke = "#4a5568"
169
- tick = { { fill : '#4a5568' } }
170
- tickFormatter = { formatXAxis }
171
- />
172
- < YAxis stroke = "#4a5568" tick = { { fill : '#4a5568' } } />
173
- < CartesianGrid strokeDasharray = "3 3" stroke = "#2d3748" />
174
- < Tooltip
175
- contentStyle = { { backgroundColor : '#1f2937' , border : 'none' , color : '#fff' } }
176
- labelFormatter = { ( label ) => format ( new Date ( label ) , 'PPpp' ) }
177
- />
178
- < Area type = "monotone" dataKey = "count" stroke = "#8884d8" fillOpacity = { 1 } fill = "url(#colorCount)" name = "Count" />
179
- < Area type = "monotone" dataKey = "movingAvg" stroke = "#82ca9d" fillOpacity = { 1 } fill = "url(#colorAvg)" name = "Moving Avg" />
180
- </ AreaChart >
181
- </ ResponsiveContainer >
182
- </ div >
183
- </ div >
83
+ < MetricChart
84
+ rawData = { chartData }
85
+ dataKey = "count"
86
+ color = "#8884d8"
87
+ title = "Invocations"
88
+ yAxisLabel = "Count"
89
+ />
90
+
91
+ < MetricChart
92
+ rawData = { chartData }
93
+ dataKey = "latency"
94
+ color = "#82ca9d"
95
+ title = "Latency"
96
+ yAxisLabel = "ms"
97
+ />
184
98
185
99
< div className = "bg-[#161b22] p-3 rounded" >
186
100
< h3 className = "text-sm font-semibold mb-2 text-white" > Version History</ h3 >
0 commit comments