@@ -8,6 +8,7 @@ import { Loader } from "components/Loader/Loader";
8
8
import { ErrorAlert } from "components/Alert/ErrorAlert" ;
9
9
import { Avatar } from "components/Avatar/Avatar" ;
10
10
import { cn } from "utils/cn" ;
11
+ import _ from "lodash" ;
11
12
12
13
/**
13
14
* @todo Need to decide if we should include the template display name here, or
@@ -45,13 +46,15 @@ function groupWorkspacesByTemplateVersionId(
45
46
return [ ...grouped . values ( ) ] ;
46
47
}
47
48
48
- type Separation = Readonly < {
49
+ type UpdateTypePartition = Readonly < {
49
50
dormant : readonly Workspace [ ] ;
50
51
noUpdateNeeded : readonly Workspace [ ] ;
51
52
readyToUpdate : readonly Workspace [ ] ;
52
53
} > ;
53
54
54
- function separateWorkspaces ( workspaces : readonly Workspace [ ] ) : Separation {
55
+ function separateWorkspacesByUpdateType (
56
+ workspaces : readonly Workspace [ ] ,
57
+ ) : UpdateTypePartition {
55
58
const noUpdateNeeded : Workspace [ ] = [ ] ;
56
59
const dormant : Workspace [ ] = [ ] ;
57
60
const readyToUpdate : Workspace [ ] = [ ] ;
@@ -74,6 +77,7 @@ function separateWorkspaces(workspaces: readonly Workspace[]): Separation {
74
77
type WorkspacePanelProps = Readonly < {
75
78
workspaceName : string ;
76
79
workspaceIconUrl : string ;
80
+ running ?: boolean ;
77
81
label ?: ReactNode ;
78
82
adornment ?: ReactNode ;
79
83
className ?: string ;
@@ -144,12 +148,17 @@ const ReviewForm: FC<ReviewFormProps> = ({
144
148
// they can be changed by another user + be subject to a query invalidation
145
149
// while the form is open
146
150
const [ cachedWorkspaces , setCachedWorkspaces ] = useState ( workspacesToUpdate ) ;
151
+ // Used to force the user to acknowledge that batch updating has risks in
152
+ // certain situations and could destroy their data. Initial value
153
+ // deliberately *not* based on any derived values to avoid state sync issues
154
+ // as cachedWorkspaces gets refreshed
155
+ const [ acceptedConsequences , setAcceptedConsequences ] = useState ( false ) ;
147
156
148
157
// Dormant workspaces can't be activated without activating them first. For
149
158
// now, we'll only show the user that some workspaces can't be updated, and
150
159
// then skip over them for all other update logic
151
160
const { dormant, noUpdateNeeded, readyToUpdate } =
152
- separateWorkspaces ( cachedWorkspaces ) ;
161
+ separateWorkspacesByUpdateType ( cachedWorkspaces ) ;
153
162
154
163
// The workspaces don't have all necessary data by themselves, so we need to
155
164
// fetch the unique template versions, and massage the results
@@ -167,18 +176,21 @@ const ReviewForm: FC<ReviewFormProps> = ({
167
176
? templateVersionQueries . map ( ( q ) => q . data )
168
177
: undefined ;
169
178
170
- // Also need to tease apart workspaces that are actively running, because
171
- // there's a whole set of warnings we need to issue about them
172
- const running = readyToUpdate . filter (
179
+ const [ running , notRunning ] = _ . partition (
180
+ readyToUpdate ,
173
181
( ws ) => ws . latest_build . status === "running" ,
174
182
) ;
175
183
176
184
const workspacesChangedWhileOpen = workspacesToUpdate !== cachedWorkspaces ;
177
- const updateIsReady = error === undefined && readyToUpdate . length > 0 ;
185
+ const consequencesResolved = running . length === 0 || acceptedConsequences ;
186
+ const canSubmit =
187
+ consequencesResolved &&
188
+ error === undefined &&
189
+ ( running . length > 0 || notRunning . length > 0 ) ;
178
190
179
191
return (
180
192
< form
181
- className = "max-h-[90vh ]"
193
+ className = "max-h-[80vh ]"
182
194
onSubmit = { ( e ) => {
183
195
e . preventDefault ( ) ;
184
196
onSubmit ( ) ;
@@ -200,7 +212,10 @@ const ReviewForm: FC<ReviewFormProps> = ({
200
212
variant = "outline"
201
213
size = "sm"
202
214
disabled = { ! workspacesChangedWhileOpen }
203
- onClick = { ( ) => setCachedWorkspaces ( workspacesToUpdate ) }
215
+ onClick = { ( ) => {
216
+ setCachedWorkspaces ( workspacesToUpdate ) ;
217
+ setAcceptedConsequences ( false ) ;
218
+ } }
204
219
>
205
220
Refresh list
206
221
</ Button >
@@ -211,13 +226,43 @@ const ReviewForm: FC<ReviewFormProps> = ({
211
226
< div className = "max-w-prose" >
212
227
< h4 className = "m-0" > Ready to update</ h4 >
213
228
< p className = "m-0 text-sm leading-snug text-content-secondary" >
214
- These workspaces have available updates and require no
215
- additional action before updating.
229
+ These workspaces require no additional action before
230
+ updating.
216
231
</ p >
217
232
</ div >
218
233
219
234
< ul className = "list-none p-0 flex flex-col rounded-md border border-solid border-border" >
220
- { readyToUpdate . map ( ( ws ) => {
235
+ { running . map ( ( ws ) => {
236
+ const matchedQuery = templateVersionQueries . find (
237
+ ( q ) => q . data ?. id === ws . template_active_version_id ,
238
+ ) ;
239
+ const newTemplateName = matchedQuery ?. data ?. name ;
240
+
241
+ return (
242
+ < li
243
+ key = { ws . id }
244
+ className = "[&:not(:last-child)]:border-b-border [&:not(:last-child)]:border-b [&:not(:last-child)]:border-solid border-0"
245
+ >
246
+ < ReviewPanel
247
+ running
248
+ className = "border-none"
249
+ workspaceName = { ws . name }
250
+ workspaceIconUrl = { ws . template_icon }
251
+ label = {
252
+ newTemplateName !== undefined && (
253
+ < TemplateNameChange
254
+ newTemplateName = { newTemplateName }
255
+ oldTemplateName = {
256
+ ws . latest_build . template_version_name
257
+ }
258
+ />
259
+ )
260
+ }
261
+ />
262
+ </ li >
263
+ ) ;
264
+ } ) }
265
+ { notRunning . map ( ( ws ) => {
221
266
const matchedQuery = templateVersionQueries . find (
222
267
( q ) => q . data ?. id === ws . template_active_version_id ,
223
268
) ;
@@ -305,11 +350,11 @@ const ReviewForm: FC<ReviewFormProps> = ({
305
350
) }
306
351
</ div >
307
352
308
- < div className = "flex flex-row flex-wrap justify-end gap-4 border-0 border-t border-solid border-t-border pt-4 " >
353
+ < div className = "flex flex-row flex-wrap justify-end gap-4 border-0 border-t border-solid border-t-border pt-8 " >
309
354
< Button variant = "outline" onClick = { onCancel } >
310
355
Cancel
311
356
</ Button >
312
- < Button variant = "default" type = "submit" disabled = { ! updateIsReady } >
357
+ < Button variant = "default" type = "submit" disabled = { ! canSubmit } >
313
358
Update
314
359
</ Button >
315
360
</ div >
0 commit comments