11import { CodeEditorPanel } from '@/components/common/code-editor-panel'
22import { CopyButton } from '@/components/common/copy-button'
3+ import { AlertDialog , AlertDialogAction , AlertDialogCancel , AlertDialogContent , AlertDialogDescription , AlertDialogFooter , AlertDialogHeader , AlertDialogTitle } from '@/components/ui/alert-dialog'
34import { Button } from '@/components/ui/button'
45import { Checkbox } from '@/components/ui/checkbox'
56import { Dialog , DialogContent , DialogFooter , DialogHeader , DialogTitle } from '@/components/ui/dialog'
@@ -79,6 +80,7 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
7980 const [ selectedVlessVariant , setSelectedVlessVariant ] = useState < VlessKeyVariant > ( 'x25519' )
8081 const [ vlessAdvancedSeed , setVlessAdvancedSeed ] = useState < VlessBuilderOptions | undefined > ( undefined )
8182 const [ isVlessAdvancedModalOpen , setIsVlessAdvancedModalOpen ] = useState ( false )
83+ const [ discardChangesOpen , setDiscardChangesOpen ] = useState ( false )
8284
8385 // Results dialog state
8486 const [ isResultsDialogOpen , setIsResultsDialogOpen ] = useState ( false )
@@ -278,6 +280,32 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
278280 generateWireGuardKeys ( )
279281 } , [ generateWireGuardKeys , generatedWireGuardKeyPair , showResultDialog ] )
280282
283+ const closeModal = useCallback ( ( ) => {
284+ setDiscardChangesOpen ( false )
285+ onOpenChange ( false )
286+ } , [ onOpenChange ] )
287+
288+ const handleDialogOpenChange = useCallback (
289+ ( open : boolean ) => {
290+ if ( open ) {
291+ onOpenChange ( true )
292+ return
293+ }
294+ if ( createCoreMutation . isPending || modifyCoreMutation . isPending || form . formState . isSubmitting ) return
295+ if ( form . formState . isDirty ) {
296+ setDiscardChangesOpen ( true )
297+ return
298+ }
299+ closeModal ( )
300+ } ,
301+ [ closeModal , createCoreMutation . isPending , form . formState . isDirty , form . formState . isSubmitting , modifyCoreMutation . isPending , onOpenChange ] ,
302+ )
303+
304+ const confirmDiscardChanges = useCallback ( ( ) => {
305+ form . reset ( )
306+ closeModal ( )
307+ } , [ closeModal , form ] )
308+
281309 const onSubmit = async ( values : CoreConfigFormValues ) => {
282310 try {
283311 // Validate JSON first
@@ -335,8 +363,8 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
335363 // Invalidate core config queries after successful action
336364 queryClient . invalidateQueries ( { queryKey : [ '/api/cores' ] } )
337365 queryClient . invalidateQueries ( { queryKey : [ '/api/cores/simple' ] } )
338- onOpenChange ( false )
339- form . reset ( )
366+ form . reset ( values )
367+ closeModal ( )
340368 } catch ( error : any ) {
341369 console . error ( 'Core config operation failed:' , error )
342370 console . error ( 'Error response:' , error ?. response )
@@ -782,7 +810,7 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
782810 } }
783811 />
784812 { renderResultDialog ( ) }
785- < Dialog open = { isDialogOpen } onOpenChange = { onOpenChange } >
813+ < Dialog open = { isDialogOpen } onOpenChange = { handleDialogOpenChange } >
786814 < DialogContent className = "h-full w-full max-w-5xl md:h-auto" >
787815 < DialogHeader >
788816 < DialogTitle className = "flex items-center gap-2" >
@@ -1072,7 +1100,7 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
10721100 ) }
10731101
10741102 < div className = "flex justify-end gap-2" >
1075- < Button type = "button" variant = "outline" onClick = { ( ) => onOpenChange ( false ) } disabled = { createCoreMutation . isPending || modifyCoreMutation . isPending } >
1103+ < Button type = "button" variant = "outline" onClick = { ( ) => handleDialogOpenChange ( false ) } disabled = { createCoreMutation . isPending || modifyCoreMutation . isPending } >
10761104 { t ( 'cancel' ) }
10771105 </ Button >
10781106 < LoaderButton
@@ -1090,6 +1118,22 @@ export default function CoreConfigModal({ isDialogOpen, onOpenChange, form, edit
10901118 </ Form >
10911119 </ DialogContent >
10921120 </ Dialog >
1121+ < AlertDialog open = { discardChangesOpen } onOpenChange = { setDiscardChangesOpen } >
1122+ < AlertDialogContent dir = { dir } >
1123+ < AlertDialogHeader >
1124+ < AlertDialogTitle > { t ( 'coreConfigModal.discardChangesTitle' , { defaultValue : 'Discard changes?' } ) } </ AlertDialogTitle >
1125+ < AlertDialogDescription >
1126+ { t ( 'coreConfigModal.discardChangesDescription' , {
1127+ defaultValue : 'Your unsaved kernel configuration changes will be lost if you close this editor.' ,
1128+ } ) }
1129+ </ AlertDialogDescription >
1130+ </ AlertDialogHeader >
1131+ < AlertDialogFooter >
1132+ < AlertDialogCancel > { t ( 'cancel' ) } </ AlertDialogCancel >
1133+ < AlertDialogAction onClick = { confirmDiscardChanges } > { t ( 'coreEditor.leave' , { defaultValue : 'Leave' } ) } </ AlertDialogAction >
1134+ </ AlertDialogFooter >
1135+ </ AlertDialogContent >
1136+ </ AlertDialog >
10931137 </ >
10941138 )
10951139}
0 commit comments