77 */
88import { createColumnHelper } from '@tanstack/react-table'
99import { useCallback , useMemo , useState } from 'react'
10- import { useForm , type FieldValues } from 'react-hook-form'
10+ import { useForm } from 'react-hook-form'
1111import { Outlet } from 'react-router'
1212
1313import {
@@ -36,7 +36,6 @@ import { Button } from '~/ui/lib/Button'
3636import { toComboboxItems } from '~/ui/lib/Combobox'
3737import { EmptyMessage } from '~/ui/lib/EmptyMessage'
3838import { Message } from '~/ui/lib/Message'
39- import { Modal } from '~/ui/lib/Modal'
4039import { PageHeader , PageTitle } from '~/ui/lib/PageHeader'
4140import { TableActions } from '~/ui/lib/Table'
4241import { docLinks } from '~/util/links'
@@ -202,27 +201,28 @@ const PromoteImageModal = ({ onDismiss }: { onDismiss: () => void }) => {
202201 )
203202}
204203
204+ type DemoteFormValues = {
205+ project : string | undefined
206+ }
207+
205208const DemoteImageModal = ( {
206209 onDismiss,
207210 image,
208211} : {
209212 onDismiss : ( ) => void
210213 image : Image
211214} ) => {
212- const { control, handleSubmit, watch } = useForm ( )
215+ const defaultValues : DemoteFormValues = { project : undefined }
216+ const form = useForm ( { defaultValues } )
213217
214- const selectedProject : string | undefined = watch ( 'project' )
218+ const selectedProject : string | undefined = form . watch ( 'project' )
215219
216220 const queryClient = useApiQueryClient ( )
217221
218222 const demoteImage = useApiMutation ( 'imageDemote' , {
219223 onSuccess ( data ) {
220224 addToast ( {
221- content : (
222- < >
223- Image < HL > { data . name } </ HL > demoted
224- </ >
225- ) ,
225+ content : < > Image < HL > { data . name } </ HL > demoted</ > , // prettier-ignore
226226 cta : selectedProject
227227 ? {
228228 text : `View images in ${ selectedProject } ` ,
@@ -239,51 +239,40 @@ const DemoteImageModal = ({
239239 onSettled : onDismiss ,
240240 } )
241241
242- const onSubmit = ( data : FieldValues ) => {
243- demoteImage . mutate ( { path : { image : image . id } , query : { project : data . project } } )
244- }
245-
246242 const projects = useApiQuery ( 'projectList' , { } )
247243 const projectItems = useMemo ( ( ) => toComboboxItems ( projects . data ?. items ) , [ projects . data ] )
248244
249245 return (
250- < Modal isOpen onDismiss = { onDismiss } title = "Demote image" >
251- < Modal . Body >
252- < Modal . Section >
253- < form
254- autoComplete = "off"
255- onSubmit = { ( e ) => {
256- e . stopPropagation ( )
257- handleSubmit ( onSubmit ) ( e )
258- } }
259- className = "space-y-4"
260- >
261- < p >
262- Demoting: < span className = "text-sans-semi-md text-raise" > { image . name } </ span >
263- </ p >
246+ < ModalForm
247+ title = "Demote image"
248+ form = { form }
249+ loading = { demoteImage . isPending }
250+ submitError = { demoteImage . error }
251+ onSubmit = { ( { project } ) => {
252+ if ( ! project ) return // shouldn't happen because of validation
253+ demoteImage . mutate ( { path : { image : image . id } , query : { project } } )
254+ } }
255+ onDismiss = { onDismiss }
256+ submitLabel = "Demote"
257+ >
258+ < p >
259+ Demoting: < span className = "text-sans-semi-md text-raise" > { image . name } </ span >
260+ </ p >
264261
265- < Message
266- variant = "info"
267- content = "Once an image has been demoted it is only visible to the project that it is demoted into. This will not affect disks already created with the image."
268- />
262+ < Message
263+ variant = "info"
264+ content = "Once an image has been demoted it is only visible to the project that it is demoted into. This will not affect disks already created with the image."
265+ />
269266
270- < ComboboxField
271- placeholder = "Select project for image"
272- name = "project"
273- label = "Project"
274- items = { projectItems }
275- isLoading = { projects . isPending }
276- required
277- control = { control }
278- />
279- </ form >
280- </ Modal . Section >
281- </ Modal . Body >
282- < Modal . Footer
283- onDismiss = { onDismiss }
284- onAction = { handleSubmit ( onSubmit ) }
285- actionText = "Demote"
267+ < ComboboxField
268+ placeholder = "Select project for image"
269+ name = "project"
270+ label = "Project"
271+ items = { projectItems }
272+ isLoading = { projects . isPending }
273+ required
274+ control = { form . control }
286275 />
287- </ Modal >
276+ </ ModalForm >
288277 )
289278}
0 commit comments