@@ -2,7 +2,7 @@ import TextField from "@mui/material/TextField";
2
2
import * as TypesGen from "api/typesGenerated" ;
3
3
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete" ;
4
4
import { FormikContextType , useFormik } from "formik" ;
5
- import { FC , useEffect , useState } from "react" ;
5
+ import { type FC , useEffect , useState , useReducer } from "react" ;
6
6
import {
7
7
getFormHelpers ,
8
8
nameValidator ,
@@ -26,12 +26,18 @@ import {
26
26
ImmutableTemplateParametersSection ,
27
27
MutableTemplateParametersSection ,
28
28
} from "components/TemplateParameters/TemplateParameters" ;
29
- import { CreateWSPermissions } from "xServices/createWorkspace/createWorkspaceXService" ;
29
+ import {
30
+ CreateWSPermissions ,
31
+ CreateWorkspaceMode ,
32
+ } from "xServices/createWorkspace/createWorkspaceXService" ;
30
33
import { ExternalAuth } from "./ExternalAuth" ;
31
34
import { ErrorAlert } from "components/Alert/ErrorAlert" ;
32
35
import { Stack } from "components/Stack/Stack" ;
33
36
import { type ExternalAuthPollingState } from "./CreateWorkspacePage" ;
34
37
import { useSearchParams } from "react-router-dom" ;
38
+ import { Alert } from "components/Alert/Alert" ;
39
+ import { Margins } from "components/Margins/Margins" ;
40
+ import { useTheme } from "@emotion/react" ;
35
41
36
42
export interface CreateWorkspacePageViewProps {
37
43
error : unknown ;
@@ -47,6 +53,7 @@ export interface CreateWorkspacePageViewProps {
47
53
permissions : CreateWSPermissions ;
48
54
creatingWorkspace : boolean ;
49
55
onCancel : ( ) => void ;
56
+ mode : CreateWorkspaceMode ;
50
57
onSubmit : (
51
58
req : TypesGen . CreateWorkspaceRequest ,
52
59
owner : TypesGen . User ,
@@ -68,6 +75,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
68
75
creatingWorkspace,
69
76
onSubmit,
70
77
onCancel,
78
+ mode,
71
79
} ) => {
72
80
const styles = useStyles ( ) ;
73
81
const [ owner , setOwner ] = useState ( defaultOwner ) ;
@@ -113,133 +121,143 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
113
121
) ;
114
122
115
123
return (
116
- < FullPageHorizontalForm title = "New workspace" onCancel = { onCancel } >
117
- < HorizontalForm onSubmit = { form . handleSubmit } >
118
- { Boolean ( error ) && < ErrorAlert error = { error } /> }
119
- { /* General info */ }
120
- < FormSection
121
- title = "General"
122
- description = "The template and name of your new workspace."
123
- >
124
- < FormFields >
125
- < SelectedTemplate template = { template } />
126
- { versionId && versionId !== template . active_version_id && (
127
- < Stack spacing = { 1 } className = { styles . hasDescription } >
128
- < TextField
129
- disabled
130
- fullWidth
131
- value = { versionId }
132
- label = "Version ID"
133
- />
134
- < span className = { styles . description } >
135
- This parameter has been preset, and cannot be modified.
136
- </ span >
137
- </ Stack >
138
- ) }
139
- < TextField
140
- { ...getFieldHelpers ( "name" ) }
141
- disabled = { form . isSubmitting }
142
- onChange = { onChangeTrimmed ( form ) }
143
- autoFocus
144
- fullWidth
145
- label = "Workspace Name"
146
- />
147
- </ FormFields >
148
- </ FormSection >
124
+ < >
125
+ { mode === "duplicate" && < DuplicateWarningMessage /> }
149
126
150
- { permissions . createWorkspaceForUser && (
127
+ < FullPageHorizontalForm title = "New workspace" onCancel = { onCancel } >
128
+ < HorizontalForm onSubmit = { form . handleSubmit } >
129
+ { Boolean ( error ) && < ErrorAlert error = { error } /> }
130
+ { /* General info */ }
151
131
< FormSection
152
- title = "Workspace Owner "
153
- description = "Only admins can create workspace for other users ."
132
+ title = "General "
133
+ description = "The template and name of your new workspace ."
154
134
>
155
135
< FormFields >
156
- < UserAutocomplete
157
- value = { owner }
158
- onChange = { ( user ) => {
159
- setOwner ( user ?? defaultOwner ) ;
160
- } }
161
- label = "Owner"
162
- size = "medium"
136
+ < SelectedTemplate template = { template } />
137
+ { versionId && versionId !== template . active_version_id && (
138
+ < Stack spacing = { 1 } className = { styles . hasDescription } >
139
+ < TextField
140
+ disabled
141
+ fullWidth
142
+ value = { versionId }
143
+ label = "Version ID"
144
+ />
145
+ < span className = { styles . description } >
146
+ This parameter has been preset, and cannot be modified.
147
+ </ span >
148
+ </ Stack >
149
+ ) }
150
+ < TextField
151
+ { ...getFieldHelpers ( "name" ) }
152
+ disabled = { form . isSubmitting }
153
+ onChange = { onChangeTrimmed ( form ) }
154
+ autoFocus
155
+ fullWidth
156
+ label = "Workspace Name"
163
157
/>
164
158
</ FormFields >
165
159
</ FormSection >
166
- ) }
167
160
168
- { externalAuth && externalAuth . length > 0 && (
169
- < FormSection
170
- title = "External Authentication"
171
- description = "This template requires authentication to external services."
172
- >
173
- < FormFields >
174
- { externalAuth . map ( ( auth ) => (
175
- < ExternalAuth
176
- key = { auth . id }
177
- authenticateURL = { auth . authenticate_url }
178
- authenticated = { auth . authenticated }
179
- externalAuthPollingState = { externalAuthPollingState }
180
- startPollingExternalAuth = { startPollingExternalAuth }
181
- displayName = { auth . display_name }
182
- displayIcon = { auth . display_icon }
183
- error = { authErrors [ auth . id ] }
161
+ { permissions . createWorkspaceForUser && (
162
+ < FormSection
163
+ title = "Workspace Owner"
164
+ description = "Only admins can create workspace for other users."
165
+ >
166
+ < FormFields >
167
+ < UserAutocomplete
168
+ value = { owner }
169
+ onChange = { ( user ) => {
170
+ setOwner ( user ?? defaultOwner ) ;
171
+ } }
172
+ label = "Owner"
173
+ size = "medium"
184
174
/>
185
- ) ) }
186
- </ FormFields >
187
- </ FormSection >
188
- ) }
175
+ </ FormFields >
176
+ </ FormSection >
177
+ ) }
189
178
190
- { parameters && (
191
- < >
192
- < MutableTemplateParametersSection
193
- templateParameters = { parameters }
194
- getInputProps = { ( parameter , index ) => {
195
- return {
196
- ...getFieldHelpers (
197
- "rich_parameter_values[" + index + "].value" ,
198
- ) ,
199
- onChange : async ( value ) => {
200
- await form . setFieldValue ( "rich_parameter_values." + index , {
201
- name : parameter . name ,
202
- value : value ,
203
- } ) ;
204
- } ,
205
- disabled :
206
- disabledParamsList ?. includes (
207
- parameter . name . toLowerCase ( ) . replace ( / / g, "_" ) ,
208
- ) || form . isSubmitting ,
209
- } ;
210
- } }
211
- />
212
- < ImmutableTemplateParametersSection
213
- templateParameters = { parameters }
214
- classes = { { root : styles . warningSection } }
215
- getInputProps = { ( parameter , index ) => {
216
- return {
217
- ...getFieldHelpers (
218
- "rich_parameter_values[" + index + "].value" ,
219
- ) ,
220
- onChange : async ( value ) => {
221
- await form . setFieldValue ( "rich_parameter_values." + index , {
222
- name : parameter . name ,
223
- value : value ,
224
- } ) ;
225
- } ,
226
- disabled :
227
- disabledParamsList ?. includes (
228
- parameter . name . toLowerCase ( ) . replace ( / / g, "_" ) ,
229
- ) || form . isSubmitting ,
230
- } ;
231
- } }
232
- />
233
- </ >
234
- ) }
179
+ { externalAuth && externalAuth . length > 0 && (
180
+ < FormSection
181
+ title = "External Authentication"
182
+ description = "This template requires authentication to external services."
183
+ >
184
+ < FormFields >
185
+ { externalAuth . map ( ( auth ) => (
186
+ < ExternalAuth
187
+ key = { auth . id }
188
+ authenticateURL = { auth . authenticate_url }
189
+ authenticated = { auth . authenticated }
190
+ externalAuthPollingState = { externalAuthPollingState }
191
+ startPollingExternalAuth = { startPollingExternalAuth }
192
+ displayName = { auth . display_name }
193
+ displayIcon = { auth . display_icon }
194
+ error = { authErrors [ auth . id ] }
195
+ />
196
+ ) ) }
197
+ </ FormFields >
198
+ </ FormSection >
199
+ ) }
200
+
201
+ { parameters && (
202
+ < >
203
+ < MutableTemplateParametersSection
204
+ templateParameters = { parameters }
205
+ getInputProps = { ( parameter , index ) => {
206
+ return {
207
+ ...getFieldHelpers (
208
+ "rich_parameter_values[" + index + "].value" ,
209
+ ) ,
210
+ onChange : async ( value ) => {
211
+ await form . setFieldValue (
212
+ "rich_parameter_values." + index ,
213
+ {
214
+ name : parameter . name ,
215
+ value : value ,
216
+ } ,
217
+ ) ;
218
+ } ,
219
+ disabled :
220
+ disabledParamsList ?. includes (
221
+ parameter . name . toLowerCase ( ) . replace ( / / g, "_" ) ,
222
+ ) || form . isSubmitting ,
223
+ } ;
224
+ } }
225
+ />
226
+ < ImmutableTemplateParametersSection
227
+ templateParameters = { parameters }
228
+ classes = { { root : styles . warningSection } }
229
+ getInputProps = { ( parameter , index ) => {
230
+ return {
231
+ ...getFieldHelpers (
232
+ "rich_parameter_values[" + index + "].value" ,
233
+ ) ,
234
+ onChange : async ( value ) => {
235
+ await form . setFieldValue (
236
+ "rich_parameter_values." + index ,
237
+ {
238
+ name : parameter . name ,
239
+ value : value ,
240
+ } ,
241
+ ) ;
242
+ } ,
243
+ disabled :
244
+ disabledParamsList ?. includes (
245
+ parameter . name . toLowerCase ( ) . replace ( / / g, "_" ) ,
246
+ ) || form . isSubmitting ,
247
+ } ;
248
+ } }
249
+ />
250
+ </ >
251
+ ) }
235
252
236
- < FormFooter
237
- onCancel = { onCancel }
238
- isLoading = { creatingWorkspace }
239
- submitLabel = "Create Workspace"
240
- />
241
- </ HorizontalForm >
242
- </ FullPageHorizontalForm >
253
+ < FormFooter
254
+ onCancel = { onCancel }
255
+ isLoading = { creatingWorkspace }
256
+ submitLabel = "Create Workspace"
257
+ />
258
+ </ HorizontalForm >
259
+ </ FullPageHorizontalForm >
260
+ </ >
243
261
) ;
244
262
} ;
245
263
@@ -258,6 +276,26 @@ function getAuthErrors(
258
276
return authErrors ;
259
277
}
260
278
279
+ function DuplicateWarningMessage ( ) {
280
+ const [ isDismissed , dismiss ] = useReducer ( ( ) => true , false ) ;
281
+ const theme = useTheme ( ) ;
282
+
283
+ if ( isDismissed ) {
284
+ return null ;
285
+ }
286
+
287
+ return (
288
+ < div css = { { paddingTop : theme . spacing ( 6 ) } } >
289
+ < Margins size = "medium" >
290
+ < Alert severity = "warning" dismissible onDismiss = { dismiss } >
291
+ Duplicating a workspace only copies its parameters. No state from the
292
+ old workspace is copied over.
293
+ </ Alert >
294
+ </ Margins >
295
+ </ div >
296
+ ) ;
297
+ }
298
+
261
299
const useStyles = makeStyles ( ( theme ) => ( {
262
300
hasDescription : {
263
301
paddingBottom : theme . spacing ( 2 ) ,
0 commit comments