@@ -5,12 +5,13 @@ import {
5
5
} from '@mui/icons-material' ;
6
6
import { darken , useTheme } from '@mui/material' ;
7
7
import { FC , useMemo } from 'react' ;
8
+
9
+ import { Chart2 , ChartSeries } from '@/components/Chart2' ;
8
10
import { FlexBox } from '@/components/FlexBox' ;
9
11
import { Line } from '@/components/Text' ;
10
12
import { useSelector } from '@/store' ;
11
13
import { Deployment } from '@/types/resources' ;
12
14
import { percentageToMultiplier } from '@/utils/datatype' ;
13
- import { Chart2 , ChartSeries } from '@/components/Chart2' ;
14
15
15
16
const MEANINGFUL_CHANGE_THRESHOLD = 0.5 ;
16
17
@@ -22,11 +23,14 @@ interface TrendData {
22
23
}
23
24
24
25
const getDeploymentDurationInSeconds = ( deployment : Deployment ) : number => {
25
- if ( deployment . id ?. startsWith ( 'WORKFLOW' ) && typeof deployment . run_duration === 'number' && deployment . run_duration > 0 ) {
26
+ if (
27
+ deployment . id ?. startsWith ( 'WORKFLOW' ) &&
28
+ typeof deployment . run_duration === 'number' &&
29
+ deployment . run_duration > 0
30
+ ) {
26
31
return deployment . run_duration ;
27
32
}
28
-
29
-
33
+
30
34
try {
31
35
const conductedAt = new Date ( deployment . conducted_at ) ;
32
36
const createdAt = new Date ( deployment . created_at ) ;
@@ -36,14 +40,14 @@ const getDeploymentDurationInSeconds = (deployment: Deployment): number => {
36
40
const durationMs = conductedAt . getTime ( ) - createdAt . getTime ( ) ;
37
41
return Math . max ( 0 , Math . floor ( durationMs / 1000 ) ) ;
38
42
} catch ( e ) {
39
- console . error ( " Error calculating deployment duration" , e ) ;
43
+ console . error ( ' Error calculating deployment duration' , e ) ;
40
44
return 0 ;
41
45
}
42
46
} ;
43
47
44
- const getDeploymentDurationInHours = ( deployment : Deployment ) : number => {
48
+ const getDeploymentDurationInMinutes = ( deployment : Deployment ) : number => {
45
49
const seconds = getDeploymentDurationInSeconds ( deployment ) ;
46
- return + ( seconds / 3600 ) . toFixed ( 2 ) ;
50
+ return + ( seconds / 60 ) . toFixed ( 2 ) ;
47
51
} ;
48
52
49
53
export const calculateDeploymentTrends = (
@@ -60,13 +64,15 @@ export const calculateDeploymentTrends = (
60
64
}
61
65
62
66
// Filter valid deployments early
63
- const validDeployments = deployments . filter ( dep => {
64
- const hasValidDates = dep . conducted_at && new Date ( dep . conducted_at ) . toString ( ) !== 'Invalid Date' ;
67
+ const validDeployments = deployments . filter ( ( dep ) => {
68
+ const hasValidDates =
69
+ dep . conducted_at &&
70
+ new Date ( dep . conducted_at ) . toString ( ) !== 'Invalid Date' ;
65
71
66
72
if ( dep . id . startsWith ( 'WORKFLOW' ) ) {
67
73
return hasValidDates && typeof dep . run_duration === 'number' ;
68
74
}
69
-
75
+
70
76
return hasValidDates ;
71
77
} ) ;
72
78
@@ -86,45 +92,53 @@ export const calculateDeploymentTrends = (
86
92
const firstHalf = sortedDeployments . slice ( 0 , midpoint ) ;
87
93
const secondHalf = sortedDeployments . slice ( midpoint ) ;
88
94
89
-
90
95
// Calculate average duration for each half
91
96
const getAvgDuration = ( deps : Deployment [ ] ) => {
92
- const totalDuration = deps . reduce ( ( sum , dep ) => sum + getDeploymentDurationInSeconds ( dep ) , 0 ) ;
97
+ const totalDuration = deps . reduce (
98
+ ( sum , dep ) => sum + getDeploymentDurationInSeconds ( dep ) ,
99
+ 0
100
+ ) ;
93
101
return deps . length > 0 ? totalDuration / deps . length : 0 ;
94
102
} ;
95
103
96
104
const firstHalfAvgDuration = getAvgDuration ( firstHalf ) ;
97
105
const secondHalfAvgDuration = getAvgDuration ( secondHalf ) ;
98
-
99
- const durationChange = firstHalfAvgDuration
100
- ? ( ( secondHalfAvgDuration - firstHalfAvgDuration ) / firstHalfAvgDuration ) * 100
106
+
107
+ const durationChange = firstHalfAvgDuration
108
+ ? ( ( secondHalfAvgDuration - firstHalfAvgDuration ) / firstHalfAvgDuration ) *
109
+ 100
101
110
: 0 ;
102
-
111
+
103
112
const avgDuration = getAvgDuration ( sortedDeployments ) ;
104
113
105
114
const getAvgPrCount = ( deps : Deployment [ ] ) : number => {
106
115
if ( ! deps || deps . length === 0 ) return 0 ;
107
-
116
+
108
117
// Filter deployments that have valid PR count data
109
- const depsWithPrCount = deps . filter ( dep => dep . pr_count >= 0 ) ;
110
-
118
+ const depsWithPrCount = deps . filter ( ( dep ) => dep . pr_count >= 0 ) ;
119
+
120
+ console . log ( 'prCount' , deployments [ 0 ] ) ;
121
+
111
122
if ( depsWithPrCount . length === 0 ) return 0 ;
112
-
113
- const deploymentsByDate = depsWithPrCount . reduce ( ( acc , dep ) => {
114
- const date = new Date ( dep . conducted_at ) . toLocaleDateString ( 'en-US' ) ;
115
- if ( ! acc [ date ] ) {
116
- acc [ date ] = { totalPRs : 0 , count : 0 } ;
117
- }
118
- acc [ date ] . totalPRs += dep . pr_count || 0 ;
119
- acc [ date ] . count ++ ;
120
- return acc ;
121
- } , { } as Record < string , { totalPRs : number , count : number } > ) ;
122
-
123
+
124
+ const deploymentsByDate = depsWithPrCount . reduce (
125
+ ( acc , dep ) => {
126
+ const date = new Date ( dep . conducted_at ) . toLocaleDateString ( 'en-US' ) ;
127
+ if ( ! acc [ date ] ) {
128
+ acc [ date ] = { totalPRs : 0 , count : 0 } ;
129
+ }
130
+ acc [ date ] . totalPRs += dep . pr_count || 0 ;
131
+ acc [ date ] . count ++ ;
132
+ return acc ;
133
+ } ,
134
+ { } as Record < string , { totalPRs : number ; count : number } >
135
+ ) ;
136
+
123
137
const dailyTotals = Object . values ( deploymentsByDate ) ;
124
-
125
- const avgPrPerDay = dailyTotals . map ( day => day . totalPRs / day . count ) ;
138
+
139
+ const avgPrPerDay = dailyTotals . map ( ( day ) => day . totalPRs / day . count ) ;
126
140
const totalAvgPr = avgPrPerDay . reduce ( ( sum , avg ) => sum + avg , 0 ) ;
127
-
141
+
128
142
return avgPrPerDay . length > 0 ? totalAvgPr / avgPrPerDay . length : 0 ;
129
143
} ;
130
144
@@ -139,7 +153,7 @@ export const calculateDeploymentTrends = (
139
153
140
154
return {
141
155
durationTrend : {
142
- value : avgDuration ,
156
+ value : avgDuration / 60 ,
143
157
change : durationChange ,
144
158
state : determineTrendState ( durationChange , false )
145
159
} ,
@@ -176,22 +190,24 @@ export const DeploymentTrendPill: FC<{
176
190
} > = ( { label, change, state } ) => {
177
191
const theme = useTheme ( ) ;
178
192
179
- const text = (
180
- state === 'positive' ? 'Increasing ' + label : state === 'negative' ? 'Decreasing ' + label : 'Stable ' + label
181
- )
193
+ const text =
194
+ state === 'positive'
195
+ ? 'Increasing ' + label
196
+ : state === 'negative'
197
+ ? 'Decreasing ' + label
198
+ : 'Stable ' + label ;
182
199
183
200
const useMultiplierFormat = Math . abs ( change ) > 100 ;
184
201
const formattedChange = useMultiplierFormat
185
202
? `${ percentageToMultiplier ( change ) } `
186
203
: `${ Math . round ( change ) } %` ;
187
204
188
- const color = darken (
189
- state === 'positive'
190
- ? theme . colors . success . main
191
- : theme . colors . warning . main ,
192
- state === 'neutral' ? 0.5 : 0 ,
193
-
194
- )
205
+ const color = darken (
206
+ state === 'positive'
207
+ ? theme . colors . success . main
208
+ : theme . colors . warning . main ,
209
+ state === 'neutral' ? 0.5 : 0
210
+ ) ;
195
211
196
212
const icon =
197
213
state === 'positive' ? (
@@ -215,10 +231,8 @@ export const DeploymentTrendPill: FC<{
215
231
>
216
232
< Line bold > { text } </ Line >
217
233
< FlexBox alignCenter >
218
- < FlexBox color = { color } alignCenter >
219
- < Line bold >
220
- { formattedChange }
221
- </ Line >
234
+ < FlexBox color = { color } alignCenter >
235
+ < Line bold > { formattedChange } </ Line >
222
236
{ icon }
223
237
</ FlexBox >
224
238
</ FlexBox >
@@ -241,60 +255,82 @@ export const DoraMetricsTrend: FC = () => {
241
255
} , [ allDeployments ] ) ;
242
256
243
257
const chartData = useMemo ( ( ) => {
244
- const validDeployments = allDeployments . filter ( dep => {
245
- const hasValidDates = dep . conducted_at && new Date ( dep . conducted_at ) . toString ( ) !== 'Invalid Date' ;
246
-
258
+ const validDeployments = allDeployments . filter ( ( dep ) => {
259
+ const hasValidDates =
260
+ dep . conducted_at &&
261
+ new Date ( dep . conducted_at ) . toString ( ) !== 'Invalid Date' ;
262
+
247
263
if ( dep . id ?. startsWith ( 'WORKFLOW' ) ) {
248
- return hasValidDates && typeof dep . run_duration === 'number' && dep . run_duration >= 0 ;
264
+ return (
265
+ hasValidDates &&
266
+ typeof dep . run_duration === 'number' &&
267
+ dep . run_duration >= 0
268
+ ) ;
249
269
}
250
-
251
- return hasValidDates && dep . created_at && new Date ( dep . created_at ) . toString ( ) !== 'Invalid Date' ;
270
+
271
+ return (
272
+ hasValidDates &&
273
+ dep . created_at &&
274
+ new Date ( dep . created_at ) . toString ( ) !== 'Invalid Date'
275
+ ) ;
252
276
} ) ;
253
277
254
278
if ( ! validDeployments . length ) {
255
279
return { labels : [ ] , series : [ ] , yAxisMax : 0 } ;
256
280
}
257
281
258
282
const sortedDeployments = [ ...validDeployments ] . sort (
259
- ( a , b ) => new Date ( a . conducted_at ) . getTime ( ) - new Date ( b . conducted_at ) . getTime ( )
283
+ ( a , b ) =>
284
+ new Date ( a . conducted_at ) . getTime ( ) - new Date ( b . conducted_at ) . getTime ( )
260
285
) ;
261
286
262
- const deploymentsByDate = sortedDeployments . reduce ( ( acc , deployment ) => {
263
- const date = new Date ( deployment . conducted_at ) . toLocaleDateString ( 'en-US' , {
264
- day : 'numeric' ,
265
- month : 'short'
266
- } ) ;
267
-
268
- if ( ! acc [ date ] ) {
269
- acc [ date ] = {
270
- deployments : [ ] ,
271
- totalDuration : 0 ,
272
- totalPRs : 0 ,
273
- prDeploymentCount : 0
274
- } ;
275
- }
276
-
277
- const durationInHours = getDeploymentDurationInHours ( deployment ) ;
278
- acc [ date ] . deployments . push ( deployment ) ;
279
- acc [ date ] . totalDuration += durationInHours ;
280
-
281
- if ( deployment . pr_count >= 0 ) {
282
- acc [ date ] . totalPRs += deployment . pr_count || 0 ;
283
- acc [ date ] . prDeploymentCount ++ ;
284
- }
285
-
286
- return acc ;
287
- } , { } as Record < string , {
288
- deployments : Deployment [ ] ,
289
- totalDuration : number ,
290
- totalPRs : number ,
291
- prDeploymentCount : number
292
- } > ) ;
287
+ const deploymentsByDate = sortedDeployments . reduce (
288
+ ( acc , deployment ) => {
289
+ const date = new Date ( deployment . conducted_at ) . toLocaleDateString (
290
+ 'en-US' ,
291
+ {
292
+ day : 'numeric' ,
293
+ month : 'short'
294
+ }
295
+ ) ;
296
+
297
+ if ( ! acc [ date ] ) {
298
+ acc [ date ] = {
299
+ deployments : [ ] ,
300
+ totalDuration : 0 ,
301
+ totalPRs : 0 ,
302
+ prDeploymentCount : 0
303
+ } ;
304
+ }
305
+
306
+ const durationInMinutes = getDeploymentDurationInMinutes ( deployment ) ;
307
+ acc [ date ] . deployments . push ( deployment ) ;
308
+ acc [ date ] . totalDuration += durationInMinutes ;
309
+
310
+ if ( deployment . pr_count >= 0 ) {
311
+ acc [ date ] . totalPRs += deployment . pr_count || 0 ;
312
+ acc [ date ] . prDeploymentCount ++ ;
313
+ }
314
+
315
+ return acc ;
316
+ } ,
317
+ { } as Record <
318
+ string ,
319
+ {
320
+ deployments : Deployment [ ] ;
321
+ totalDuration : number ;
322
+ totalPRs : number ;
323
+ prDeploymentCount : number ;
324
+ }
325
+ >
326
+ ) ;
293
327
294
328
const dates = Object . keys ( deploymentsByDate ) ;
295
- const durations = dates . map ( date => deploymentsByDate [ date ] . totalDuration ) ;
296
-
297
- const prCounts = dates . map ( date => {
329
+ const durations = dates . map (
330
+ ( date ) => deploymentsByDate [ date ] . totalDuration
331
+ ) ;
332
+
333
+ const prCounts = dates . map ( ( date ) => {
298
334
const { totalPRs, prDeploymentCount } = deploymentsByDate [ date ] ;
299
335
return prDeploymentCount > 0 ? totalPRs / prDeploymentCount : 0 ;
300
336
} ) ;
@@ -305,11 +341,11 @@ export const DoraMetricsTrend: FC = () => {
305
341
const series : ChartSeries = [
306
342
{
307
343
type : 'bar' ,
308
- label : 'Deployment Duration (hours )' ,
344
+ label : 'Deployment Duration (minutes )' ,
309
345
data : durations ,
310
346
yAxisID : 'y' ,
311
- borderColor : theme . colors . success . main ,
312
- order : 0
347
+ order : 0 ,
348
+ color : 'white'
313
349
} ,
314
350
{
315
351
type : 'bar' ,
@@ -319,7 +355,8 @@ export const DoraMetricsTrend: FC = () => {
319
355
backgroundColor : theme . colors . info . main ,
320
356
borderWidth : 2 ,
321
357
tension : 0.4 ,
322
- order : 1
358
+ order : 1 ,
359
+ color : 'white'
323
360
}
324
361
] ;
325
362
@@ -361,12 +398,12 @@ export const DoraMetricsTrend: FC = () => {
361
398
position : 'left' ,
362
399
title : {
363
400
display : true ,
364
- text : 'Duration (hours )' ,
401
+ text : 'Duration (minutes )' ,
365
402
color : theme . colors . success . main
366
403
} ,
367
404
ticks : {
368
405
color : theme . colors . success . main ,
369
- callback : ( value ) => value + 'h '
406
+ callback : ( value ) => value + 'm '
370
407
} ,
371
408
max : chartData . yAxisMax ,
372
409
grid : {
@@ -402,7 +439,7 @@ export const DoraMetricsTrend: FC = () => {
402
439
const label = context . dataset . label || '' ;
403
440
const value = context . parsed . y ;
404
441
if ( label . includes ( 'Duration' ) ) {
405
- return `${ label } : ${ value . toFixed ( 2 ) } h ` ;
442
+ return `${ label } : ${ value . toFixed ( 2 ) } m ` ;
406
443
}
407
444
return `${ label } : ${ value . toFixed ( 0 ) } ` ;
408
445
}
0 commit comments