1
1
import type { ProvisionerTiming } from "api/typesGenerated" ;
2
- import {
3
- Chart ,
4
- type Duration ,
5
- type ChartProps ,
6
- type Timing ,
7
- duration ,
8
- } from "./Chart/Chart" ;
2
+ import { Chart , type Duration , type Timing , duration } from "./Chart/Chart" ;
9
3
import { useState , type FC } from "react" ;
10
4
import type { Interpolation , Theme } from "@emotion/react" ;
11
5
import ChevronRight from "@mui/icons-material/ChevronRight" ;
12
6
import { YAxisSidePadding , YAxisWidth } from "./Chart/YAxis" ;
13
7
import { SearchField } from "components/SearchField/SearchField" ;
14
8
9
+ // TODO: Export provisioning stages from the BE to the generated types.
15
10
// We control the stages to be displayed in the chart so we can set the correct
16
11
// colors and labels.
17
12
const provisioningStages = [
@@ -21,73 +16,39 @@ const provisioningStages = [
21
16
{ name : "apply" } ,
22
17
] ;
23
18
19
+ // The advanced view is an expanded view of the stage, allowing the user to see
20
+ // which resources within a stage are taking the most time. It supports resource
21
+ // filtering and displays bars with different colors representing various states
22
+ // such as created, deleted, etc.
23
+ type TimingView =
24
+ | { name : "basic" }
25
+ | {
26
+ name : "advanced" ;
27
+ selectedStage : string ;
28
+ parentSection : string ;
29
+ filter : string ;
30
+ } ;
31
+
24
32
type WorkspaceTimingsProps = {
25
33
provisionerTimings : readonly ProvisionerTiming [ ] ;
26
34
} ;
27
35
28
- type TimingView =
29
- | { type : "basic" }
30
- // The advanced view enables users to filter results based on the XAxis label
31
- | { type : "advanced" ; selectedStage : string ; parentSection : string } ;
32
-
33
36
export const WorkspaceTimings : FC < WorkspaceTimingsProps > = ( {
34
37
provisionerTimings,
35
38
} ) => {
36
- const [ view , setView ] = useState < TimingView > ( { type : "basic" } ) ;
37
- let data : ChartProps [ "data" ] = [ ] ;
38
-
39
- if ( view . type === "basic" ) {
40
- data = [
41
- {
42
- name : "provisioning" ,
43
- timings : provisioningStages . map ( ( stage ) => {
44
- // Get all the timing durations for a stage
45
- const durations = provisionerTimings
46
- . filter ( ( t ) => t . stage === stage . name )
47
- . map ( extractDuration ) ;
48
- const stageDuration = duration ( durations ) ;
49
-
50
- // Mount the timing data that is required by the chart
51
- const stageTiming : Timing = {
52
- label : stage . name ,
53
- count : durations . length ,
54
- ...stageDuration ,
55
- } ;
56
- return stageTiming ;
57
- } ) ,
58
- } ,
59
- ] ;
60
- }
61
-
62
- if ( view . type === "advanced" ) {
63
- data = [
64
- {
65
- name : `${ view . selectedStage } stage` ,
66
- timings : provisionerTimings
67
- . filter ( ( t ) => t . stage === view . selectedStage )
68
- . map ( ( t ) => {
69
- console . log ( "-> RESOURCE" , t ) ;
70
- return {
71
- label : t . resource ,
72
- count : 0 , // Resource timings don't have inner timings
73
- ...extractDuration ( t ) ,
74
- } as Timing ;
75
- } ) ,
76
- } ,
77
- ] ;
78
- }
39
+ const [ view , setView ] = useState < TimingView > ( { name : "basic" } ) ;
79
40
80
41
return (
81
42
< div css = { styles . panelBody } >
82
- { view . type === "advanced" && (
43
+ { view . name === "advanced" && (
83
44
< div css = { styles . toolbar } >
84
45
< ul css = { styles . breadcrumbs } >
85
46
< li >
86
47
< button
87
48
type = "button"
88
49
css = { styles . breadcrumbButton }
89
50
onClick = { ( ) => {
90
- setView ( { type : "basic" } ) ;
51
+ setView ( { name : "basic" } ) ;
91
52
} }
92
53
>
93
54
{ view . parentSection }
@@ -101,26 +62,84 @@ export const WorkspaceTimings: FC<WorkspaceTimingsProps> = ({
101
62
102
63
< SearchField
103
64
css = { styles . searchField }
65
+ value = { view . filter }
104
66
placeholder = "Filter results..."
105
- onChange = { ( q : string ) => { } }
67
+ onChange = { ( q : string ) => {
68
+ setView ( ( v ) => ( {
69
+ ...v ,
70
+ filter : q ,
71
+ } ) ) ;
72
+ } }
106
73
/>
107
74
</ div >
108
75
) }
109
76
110
77
< Chart
111
- data = { data }
78
+ data = { selectChartData ( view , provisionerTimings ) }
112
79
onBarClick = { ( stage , section ) => {
113
80
setView ( {
114
- type : "advanced" ,
81
+ name : "advanced" ,
115
82
selectedStage : stage ,
116
83
parentSection : section ,
84
+ filter : "" ,
117
85
} ) ;
118
86
} }
119
87
/>
120
88
</ div >
121
89
) ;
122
90
} ;
123
91
92
+ export const selectChartData = (
93
+ view : TimingView ,
94
+ timings : readonly ProvisionerTiming [ ] ,
95
+ ) => {
96
+ switch ( view . name ) {
97
+ case "basic" : {
98
+ const groupedTimingsByStage = provisioningStages . map ( ( stage ) => {
99
+ const durations = timings
100
+ . filter ( ( t ) => t . stage === stage . name )
101
+ . map ( extractDuration ) ;
102
+ const stageDuration = duration ( durations ) ;
103
+ const stageTiming : Timing = {
104
+ label : stage . name ,
105
+ count : durations . length ,
106
+ ...stageDuration ,
107
+ } ;
108
+ return stageTiming ;
109
+ } ) ;
110
+
111
+ return [
112
+ {
113
+ name : "provisioning" ,
114
+ timings : groupedTimingsByStage ,
115
+ } ,
116
+ ] ;
117
+ }
118
+
119
+ case "advanced" : {
120
+ const selectedStageTimings = timings
121
+ . filter (
122
+ ( t ) =>
123
+ t . stage === view . selectedStage && t . resource . includes ( view . filter ) ,
124
+ )
125
+ . map ( ( t ) => {
126
+ return {
127
+ label : t . resource ,
128
+ count : 0 , // Resource timings don't have inner timings
129
+ ...extractDuration ( t ) ,
130
+ } as Timing ;
131
+ } ) ;
132
+
133
+ return [
134
+ {
135
+ name : `${ view . selectedStage } stage` ,
136
+ timings : selectedStageTimings ,
137
+ } ,
138
+ ] ;
139
+ }
140
+ }
141
+ } ;
142
+
124
143
const extractDuration = ( t : ProvisionerTiming ) : Duration => {
125
144
return {
126
145
startedAt : new Date ( t . started_at ) ,
@@ -148,6 +167,7 @@ const styles = {
148
167
alignItems : "center" ,
149
168
gap : 4 ,
150
169
lineHeight : 1 ,
170
+ flexShrink : 0 ,
151
171
152
172
"& li" : {
153
173
display : "block" ,
@@ -182,6 +202,8 @@ const styles = {
182
202
} ,
183
203
} ) ,
184
204
searchField : ( theme ) => ( {
205
+ width : "100%" ,
206
+
185
207
"& fieldset" : {
186
208
border : 0 ,
187
209
borderRadius : 0 ,
0 commit comments