55 *
66 * Copyright Oxide Computer Company
77 */
8+ import { useEffect , useState } from 'react'
89import { useWatch } from 'react-hook-form'
910import { useNavigate , type LoaderFunctionArgs } from 'react-router-dom'
1011import type { SetRequired } from 'type-fest'
@@ -97,6 +98,7 @@ CreateInstanceForm.loader = async ({ params }: LoaderFunctionArgs) => {
9798}
9899
99100export function CreateInstanceForm ( ) {
101+ const [ isSubmitting , setIsSubmitting ] = useState ( false )
100102 const queryClient = useApiQueryClient ( )
101103 const addToast = useToast ( )
102104 const projectSelector = useProjectSelector ( )
@@ -138,6 +140,12 @@ export function CreateInstanceForm() {
138140 const image = allImages . find ( ( i ) => i . id === imageInput )
139141 const imageSize = image ?. size ? Math . ceil ( image . size / GiB ) : undefined
140142
143+ useEffect ( ( ) => {
144+ if ( createInstance . error ) {
145+ setIsSubmitting ( false )
146+ }
147+ } , [ createInstance . error ] )
148+
141149 return (
142150 < FullPageForm
143151 submitDisabled = { allImages . length ? undefined : 'Image required' }
@@ -146,6 +154,7 @@ export function CreateInstanceForm() {
146154 title = "Create instance"
147155 icon = { < Instances24Icon /> }
148156 onSubmit = { ( values ) => {
157+ setIsSubmitting ( true )
149158 // we should never have a presetId that's not in the list
150159 const preset = PRESETS . find ( ( option ) => option . id === values . presetId ) !
151160 const instance =
@@ -194,9 +203,14 @@ export function CreateInstanceForm() {
194203 loading = { createInstance . isPending }
195204 submitError = { createInstance . error }
196205 >
197- < NameField name = "name" control = { control } />
198- < DescriptionField name = "description" control = { control } />
199- < CheckboxField id = "start-instance" name = "start" control = { control } >
206+ < NameField name = "name" control = { control } disabled = { isSubmitting } />
207+ < DescriptionField name = "description" control = { control } disabled = { isSubmitting } />
208+ < CheckboxField
209+ id = "start-instance"
210+ name = "start"
211+ control = { control }
212+ disabled = { isSubmitting }
213+ >
200214 Start Instance
201215 </ CheckboxField >
202216
@@ -224,13 +238,21 @@ export function CreateInstanceForm() {
224238 } }
225239 >
226240 < Tabs . List aria-labelledby = "hardware" >
227- < Tabs . Trigger value = "general" > General Purpose</ Tabs . Trigger >
228- < Tabs . Trigger value = "highCPU" > High CPU</ Tabs . Trigger >
229- < Tabs . Trigger value = "highMemory" > High Memory</ Tabs . Trigger >
230- < Tabs . Trigger value = "custom" > Custom</ Tabs . Trigger >
241+ < Tabs . Trigger value = "general" disabled = { isSubmitting } >
242+ General Purpose
243+ </ Tabs . Trigger >
244+ < Tabs . Trigger value = "highCPU" disabled = { isSubmitting } >
245+ High CPU
246+ </ Tabs . Trigger >
247+ < Tabs . Trigger value = "highMemory" disabled = { isSubmitting } >
248+ High Memory
249+ </ Tabs . Trigger >
250+ < Tabs . Trigger value = "custom" disabled = { isSubmitting } >
251+ Custom
252+ </ Tabs . Trigger >
231253 </ Tabs . List >
232254 < Tabs . Content value = "general" >
233- < RadioFieldDyn name = "presetId" label = "" control = { control } >
255+ < RadioFieldDyn name = "presetId" label = "" control = { control } disabled = { isSubmitting } >
234256 { renderLargeRadioCards ( 'general' ) }
235257 </ RadioFieldDyn >
236258 </ Tabs . Content >
@@ -264,6 +286,7 @@ export function CreateInstanceForm() {
264286 return `CPUs capped to ${ INSTANCE_MAX_CPU } `
265287 }
266288 } }
289+ disabled = { isSubmitting }
267290 />
268291 < TextField
269292 units = "GiB"
@@ -282,6 +305,7 @@ export function CreateInstanceForm() {
282305 return `Can be at most ${ INSTANCE_MAX_RAM_GiB } GiB`
283306 }
284307 } }
308+ disabled = { isSubmitting }
285309 />
286310 </ Tabs . Content >
287311 </ Tabs . Root >
@@ -298,8 +322,12 @@ export function CreateInstanceForm() {
298322 }
299323 >
300324 < Tabs . List aria-describedby = "boot-disk" >
301- < Tabs . Trigger value = "silo" > Silo images</ Tabs . Trigger >
302- < Tabs . Trigger value = "project" > Project images</ Tabs . Trigger >
325+ < Tabs . Trigger value = "silo" disabled = { isSubmitting } >
326+ Silo images
327+ </ Tabs . Trigger >
328+ < Tabs . Trigger value = "project" disabled = { isSubmitting } >
329+ Project images
330+ </ Tabs . Trigger >
303331 </ Tabs . List >
304332 { allImages . length === 0 && (
305333 < Message
@@ -318,7 +346,11 @@ export function CreateInstanceForm() {
318346 />
319347 </ div >
320348 ) : (
321- < ImageSelectField images = { siloImages } control = { control } />
349+ < ImageSelectField
350+ images = { siloImages }
351+ control = { control }
352+ disabled = { isSubmitting }
353+ />
322354 ) }
323355 </ Tabs . Content >
324356 < Tabs . Content value = "project" className = "space-y-4" >
@@ -333,7 +365,11 @@ export function CreateInstanceForm() {
333365 />
334366 </ div >
335367 ) : (
336- < ImageSelectField images = { projectImages } control = { control } />
368+ < ImageSelectField
369+ images = { projectImages }
370+ control = { control }
371+ disabled = { isSubmitting }
372+ />
337373 ) }
338374 </ Tabs . Content >
339375 </ Tabs . Root >
@@ -349,18 +385,20 @@ export function CreateInstanceForm() {
349385 return `Must be as large as selected image (min. ${ imageSize } GiB)`
350386 }
351387 } }
388+ disabled = { isSubmitting }
352389 />
353390 < NameField
354391 name = "bootDiskName"
355392 label = "Disk name"
356393 description = "Will be autogenerated if name not provided"
357394 required = { false }
358395 control = { control }
396+ disabled = { isSubmitting }
359397 />
360398 < FormDivider />
361399 < Form . Heading id = "additional-disks" > Additional disks</ Form . Heading >
362400
363- < DisksTableField control = { control } />
401+ < DisksTableField control = { control } disabled = { isSubmitting } />
364402
365403 < FormDivider />
366404 < Form . Heading id = "authentication" > Authentication</ Form . Heading >
@@ -370,12 +408,13 @@ export function CreateInstanceForm() {
370408 < FormDivider />
371409 < Form . Heading id = "networking" > Networking</ Form . Heading >
372410
373- < NetworkInterfaceField control = { control } />
411+ < NetworkInterfaceField control = { control } disabled = { isSubmitting } />
374412
375413 < TextField
376414 name = "hostname"
377415 description = "Will be generated if not provided"
378416 control = { control }
417+ disabled = { isSubmitting }
379418 />
380419
381420 < Form . Actions >
0 commit comments