1
1
import { provisionerDaemons } from "api/queries/organizations" ;
2
- import type {
3
- Organization ,
4
- ProvisionerDaemon ,
5
- ProvisionerDaemonStatus ,
6
- } from "api/typesGenerated" ;
7
- import { Badge } from "components/Badge/Badge" ;
2
+ import type { Organization , ProvisionerDaemon } from "api/typesGenerated" ;
8
3
import { Link } from "components/Link/Link" ;
9
4
import {
10
5
StatusIndicator ,
@@ -20,14 +15,18 @@ import {
20
15
TableRow ,
21
16
} from "components/Table/Table" ;
22
17
import { TableEmpty } from "components/TableEmpty/TableEmpty" ;
23
- import { TableLoader } from "components/TableLoader/TableLoader" ;
24
18
import { ChevronDownIcon , ChevronRightIcon } from "lucide-react" ;
25
19
import { useState , type FC } from "react" ;
26
20
import { useQuery } from "react-query" ;
27
21
import { cn } from "utils/cn" ;
28
22
import { docs } from "utils/docs" ;
29
23
import { relativeTime } from "utils/time" ;
30
24
import { JobStatusIndicator } from "./JobStatusIndicator" ;
25
+ import { Avatar } from "components/Avatar/Avatar" ;
26
+ import { DataGrid , DataGridSpace } from "./DataGrid" ;
27
+ import { ShrinkTags , Tag , Tags } from "./Tags" ;
28
+ import { Loader } from "components/Loader/Loader" ;
29
+ import { EmptyState } from "components/EmptyState/EmptyState" ;
31
30
32
31
type ProvisionerDaemonsPageProps = {
33
32
org : Organization ;
@@ -36,9 +35,18 @@ type ProvisionerDaemonsPageProps = {
36
35
export const ProvisionerDaemonsPage : FC < ProvisionerDaemonsPageProps > = ( {
37
36
org,
38
37
} ) => {
39
- const { data : daemons , isLoadingError } = useQuery (
40
- provisionerDaemons ( org . id ) ,
41
- ) ;
38
+ const { data : daemons , isLoadingError } = useQuery ( {
39
+ ...provisionerDaemons ( org . id ) ,
40
+ select : ( data ) =>
41
+ data . toSorted ( ( a , b ) => {
42
+ if ( ! a . last_seen_at ) return 1 ;
43
+ if ( ! b . last_seen_at ) return - 1 ;
44
+ return (
45
+ new Date ( b . last_seen_at ) . getTime ( ) -
46
+ new Date ( a . last_seen_at ) . getTime ( )
47
+ ) ;
48
+ } ) ,
49
+ } ) ;
42
50
43
51
return (
44
52
< section className = "flex flex-col gap-8" >
@@ -69,12 +77,24 @@ export const ProvisionerDaemonsPage: FC<ProvisionerDaemonsPageProps> = ({
69
77
daemons . length > 0 ? (
70
78
daemons . map ( ( d ) => < DaemonRow key = { d . id } daemon = { d } /> )
71
79
) : (
72
- < TableEmpty message = "No provisioner daemons found" />
80
+ < TableRow >
81
+ < TableCell colSpan = { 999 } >
82
+ < EmptyState message = "No provisioner daemons found" />
83
+ </ TableCell >
84
+ </ TableRow >
73
85
)
74
86
) : isLoadingError ? (
75
- < TableEmpty message = "Error loading the provisioner daemons" />
87
+ < TableRow >
88
+ < TableCell colSpan = { 999 } >
89
+ < EmptyState message = "Error loading the provisioner daemons" />
90
+ </ TableCell >
91
+ </ TableRow >
76
92
) : (
77
- < TableLoader />
93
+ < TableRow >
94
+ < TableCell colSpan = { 999 } >
95
+ < Loader />
96
+ </ TableCell >
97
+ </ TableRow >
78
98
) }
79
99
</ TableBody >
80
100
</ Table >
@@ -116,39 +136,46 @@ const DaemonRow: FC<DaemonRowProps> = ({ daemon }) => {
116
136
</ span >
117
137
</ button >
118
138
</ TableCell >
119
- < TableCell > { daemon . name } </ TableCell >
120
- < TableCell > Template</ TableCell >
121
139
< TableCell >
122
- < div className = "flex items-center gap-1 flex-wrap" >
123
- { Object . entries ( daemon . tags ) . map ( ( [ k , v ] ) => (
124
- < Badge size = "sm" key = { k } className = "whitespace-nowrap" >
125
- [{ k }
126
- { v && `=${ v } ` } ]
127
- </ Badge >
128
- ) ) }
129
- </ div >
140
+ < span className = "block whitespace-nowrap text-ellipsis overflow-hidden" >
141
+ { daemon . name }
142
+ </ span >
130
143
</ TableCell >
131
144
< TableCell >
132
- < StatusIndicator
133
- size = "sm"
134
- variant = { statusIndicatorVariant ( daemon . status ) }
135
- >
145
+ { daemon . current_job ? (
146
+ < div className = "flex items-center gap-1 whitespace-nowrap" >
147
+ < Avatar
148
+ variant = "icon"
149
+ src = { daemon . current_job . template_icon }
150
+ fallback = {
151
+ daemon . current_job . template_display_name ||
152
+ daemon . current_job . template_name
153
+ }
154
+ />
155
+ { daemon . current_job . template_display_name ??
156
+ daemon . current_job . template_name }
157
+ </ div >
158
+ ) : (
159
+ < span className = "whitespace-nowrap" > Not linked</ span >
160
+ ) }
161
+ </ TableCell >
162
+ < TableCell >
163
+ < ShrinkTags tags = { daemon . tags } />
164
+ </ TableCell >
165
+ < TableCell >
166
+ < StatusIndicator size = "sm" variant = { statusIndicatorVariant ( daemon ) } >
136
167
< StatusIndicatorDot />
137
- < span className = "[&:first-letter]:uppercase" > { daemon . status } </ span >
168
+ < span className = "[&:first-letter]:uppercase" >
169
+ { statusLabel ( daemon ) }
170
+ </ span >
138
171
</ StatusIndicator >
139
172
</ TableCell >
140
173
</ TableRow >
141
174
142
175
{ isOpen && (
143
176
< TableRow >
144
177
< TableCell colSpan = { 999 } className = "p-4 border-t-0" >
145
- < div
146
- className = { cn ( [
147
- "grid grid-cols-[auto_1fr] gap-x-4 items-center" ,
148
- "[&_span:nth-child(even)]:text-content-primary [&_span:nth-child(even)]:font-mono" ,
149
- "[&_span:nth-child(even)]:leading-[22px]" ,
150
- ] ) }
151
- >
178
+ < DataGrid >
152
179
< span > Last seen:</ span >
153
180
< span > { daemon . last_seen_at } </ span >
154
181
@@ -158,8 +185,19 @@ const DaemonRow: FC<DaemonRowProps> = ({ daemon }) => {
158
185
< span > Version:</ span >
159
186
< span > { daemon . version } </ span >
160
187
188
+ < span > Tags:</ span >
189
+ < span >
190
+ < Tags >
191
+ { Object . entries ( daemon . tags ) . map ( ( [ key , value ] ) => (
192
+ < Tag key = { key } label = { key } value = { value } />
193
+ ) ) }
194
+ </ Tags >
195
+ </ span >
196
+
161
197
{ daemon . current_job && (
162
198
< >
199
+ < DataGridSpace />
200
+
163
201
< span > Last job:</ span >
164
202
< span > { daemon . current_job . id } </ span >
165
203
@@ -172,6 +210,8 @@ const DaemonRow: FC<DaemonRowProps> = ({ daemon }) => {
172
210
173
211
{ daemon . previous_job && (
174
212
< >
213
+ < DataGridSpace />
214
+
175
215
< span > Previous job:</ span >
176
216
< span > { daemon . previous_job . id } </ span >
177
217
@@ -181,7 +221,7 @@ const DaemonRow: FC<DaemonRowProps> = ({ daemon }) => {
181
221
</ span >
182
222
</ >
183
223
) }
184
- </ div >
224
+ </ DataGrid >
185
225
</ TableCell >
186
226
</ TableRow >
187
227
) }
@@ -190,9 +230,13 @@ const DaemonRow: FC<DaemonRowProps> = ({ daemon }) => {
190
230
} ;
191
231
192
232
function statusIndicatorVariant (
193
- status : ProvisionerDaemonStatus | null ,
233
+ daemon : ProvisionerDaemon ,
194
234
) : StatusIndicatorProps [ "variant" ] {
195
- switch ( status ) {
235
+ if ( daemon . previous_job && daemon . previous_job . status === "failed" ) {
236
+ return "failed" ;
237
+ }
238
+
239
+ switch ( daemon . status ) {
196
240
case "idle" :
197
241
return "success" ;
198
242
case "busy" :
@@ -202,3 +246,20 @@ function statusIndicatorVariant(
202
246
return "inactive" ;
203
247
}
204
248
}
249
+
250
+ function statusLabel ( daemon : ProvisionerDaemon ) {
251
+ if ( daemon . previous_job && daemon . previous_job . status === "failed" ) {
252
+ return "Last job failed" ;
253
+ }
254
+
255
+ switch ( daemon . status ) {
256
+ case "idle" :
257
+ return "Idle" ;
258
+ case "busy" :
259
+ return "Busy..." ;
260
+ case "offline" :
261
+ return "Disconnected" ;
262
+ case null :
263
+ return "Unknown" ;
264
+ }
265
+ }
0 commit comments