1+ import { Accordion , AccordionContent , AccordionItem , AccordionTrigger } from '@/components/ui/accordion'
2+ import { Button } from '@/components/ui/button'
13import { Dialog , DialogContent , DialogHeader , DialogTitle } from '@/components/ui/dialog'
24import { Form , FormControl , FormField , FormItem , FormLabel , FormMessage } from '@/components/ui/form'
35import { Input } from '@/components/ui/input'
4- import { Button } from '@/components/ui/button'
5- import { Switch } from '@/components/ui/switch'
6- import { useTranslation } from 'react-i18next'
7- import { UseFormReturn } from 'react-hook-form'
8- import { useCreateNode , useModifyNode , NodeConnectionType , useGetAllCores , CoreResponse , getNode , DataLimitResetStrategy , useGetNode , NodeResponse } from '@/service/api'
9- import { toast } from 'sonner'
10- import { z } from 'zod'
11- import { cn } from '@/lib/utils'
126import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@/components/ui/select'
7+ import { Switch } from '@/components/ui/switch'
138import { Textarea } from '@/components/ui/textarea'
14- import { queryClient } from '@/utils/query-client'
159import useDirDetection from '@/hooks/use-dir-detection'
16- import React , { useState , useEffect , useRef } from 'react'
17- import { Loader2 , Settings , RefreshCw } from 'lucide-react'
18- import { v4 as uuidv4 , v5 as uuidv5 , v6 as uuidv6 , v7 as uuidv7 } from 'uuid'
19- import { LoaderButton } from '../ui/loader-button'
2010import useDynamicErrorHandler from '@/hooks/use-dynamic-errors.ts'
21- import { Accordion , AccordionContent , AccordionItem , AccordionTrigger } from '@/components/ui/accordion'
11+ import { cn } from '@/lib/utils'
12+ import { CoreResponse , DataLimitResetStrategy , getNode , NodeConnectionType , NodeResponse , useCreateNode , useGetAllCores , useGetNode , useModifyNode } from '@/service/api'
2213import { formatBytes , gbToBytes } from '@/utils/formatByte'
14+ import { queryClient } from '@/utils/query-client'
15+ import { Loader2 , RefreshCw , Settings } from 'lucide-react'
16+ import React , { useEffect , useRef , useState } from 'react'
17+ import { UseFormReturn } from 'react-hook-form'
18+ import { useTranslation } from 'react-i18next'
19+ import { toast } from 'sonner'
20+ import { v4 as uuidv4 } from 'uuid'
21+ import { z } from 'zod'
22+ import { LoaderButton } from '../ui/loader-button'
2323
2424export const nodeFormSchema = z . object ( {
2525 name : z . string ( ) . min ( 1 , 'Name is required' ) ,
@@ -83,7 +83,7 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
8383
8484 const currentNode = node || initialNodeData
8585 const lastSyncedNodeRef = useRef < NodeResponse | null > ( null )
86-
86+
8787 useEffect ( ( ) => {
8888 if ( isDialogOpen ) {
8989 setErrorDetails ( null )
@@ -97,32 +97,32 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
9797 // Update form when node data changes (from auto-refresh or external updates)
9898 useEffect ( ( ) => {
9999 if ( ! isDialogOpen || ! editingNode || ! editingNodeId || ! node ) return
100-
100+
101101 // Skip if form is dirty (user has made changes)
102102 if ( form . formState . isDirty ) return
103-
103+
104104 // Skip if this is the same node data we already synced
105105 // Compare key fields that change externally (status, message, versions, usage)
106106 const lastSynced = lastSyncedNodeRef . current
107- if ( lastSynced &&
108- lastSynced . id === node . id &&
109- lastSynced . status === node . status &&
110- lastSynced . message === node . message &&
111- lastSynced . xray_version === node . xray_version &&
112- lastSynced . node_version === node . node_version &&
113- lastSynced . uplink === node . uplink &&
114- lastSynced . downlink === node . downlink &&
115- lastSynced . name === node . name &&
116- lastSynced . address === node . address &&
117- lastSynced . port === node . port ) {
107+ if (
108+ lastSynced &&
109+ lastSynced . id === node . id &&
110+ lastSynced . status === node . status &&
111+ lastSynced . message === node . message &&
112+ lastSynced . xray_version === node . xray_version &&
113+ lastSynced . node_version === node . node_version &&
114+ lastSynced . uplink === node . uplink &&
115+ lastSynced . downlink === node . downlink &&
116+ lastSynced . name === node . name &&
117+ lastSynced . address === node . address &&
118+ lastSynced . port === node . port
119+ ) {
118120 return
119121 }
120122
121123 // Update form with new node data
122124 const dataLimitBytes = node . data_limit ?? null
123- const dataLimitGB = dataLimitBytes !== null && dataLimitBytes !== undefined && dataLimitBytes > 0
124- ? dataLimitBytes / ( 1024 * 1024 * 1024 )
125- : 0
125+ const dataLimitGB = dataLimitBytes !== null && dataLimitBytes !== undefined && dataLimitBytes > 0 ? dataLimitBytes / ( 1024 * 1024 * 1024 ) : 0
126126
127127 if ( dataLimitGB > 0 ) {
128128 const formatted = parseFloat ( dataLimitGB . toFixed ( 9 ) )
@@ -131,22 +131,25 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
131131 dataLimitInputRef . current = ''
132132 }
133133
134- form . reset ( {
135- name : node . name ,
136- address : node . address ,
137- port : node . port ,
138- usage_coefficient : node . usage_coefficient ,
139- connection_type : node . connection_type ,
140- server_ca : node . server_ca ,
141- keep_alive : node . keep_alive ,
142- api_key : ( node . api_key as string ) || '' ,
143- core_config_id : node . core_config_id ?? cores ?. cores ?. [ 0 ] ?. id ,
144- data_limit : dataLimitGB ,
145- data_limit_reset_strategy : node . data_limit_reset_strategy ?? DataLimitResetStrategy . no_reset ,
146- reset_time : node . reset_time ?? null ,
147- default_timeout : node . default_timeout ?? 10 ,
148- internal_timeout : node . internal_timeout ?? 15 ,
149- } , { keepDirty : false , keepValues : false } )
134+ form . reset (
135+ {
136+ name : node . name ,
137+ address : node . address ,
138+ port : node . port ,
139+ usage_coefficient : node . usage_coefficient ,
140+ connection_type : node . connection_type ,
141+ server_ca : node . server_ca ,
142+ keep_alive : node . keep_alive ,
143+ api_key : ( node . api_key as string ) || '' ,
144+ core_config_id : node . core_config_id ?? cores ?. cores ?. [ 0 ] ?. id ,
145+ data_limit : dataLimitGB ,
146+ data_limit_reset_strategy : node . data_limit_reset_strategy ?? DataLimitResetStrategy . no_reset ,
147+ reset_time : node . reset_time ?? null ,
148+ default_timeout : node . default_timeout ?? 10 ,
149+ internal_timeout : node . internal_timeout ?? 15 ,
150+ } ,
151+ { keepDirty : false , keepValues : false } ,
152+ )
150153
151154 lastSyncedNodeRef . current = node
152155 } , [ node , isDialogOpen , editingNode , editingNodeId , form , cores ] )
@@ -534,26 +537,9 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
534537 control = { form . control }
535538 name = "api_key"
536539 render = { ( { field } ) => {
537- const [ uuidVersion , setUuidVersion ] = useState < 'v4' | 'v5' | 'v6' | 'v7' > ( 'v4' )
538-
539540 const generateUUID = ( ) => {
540- switch ( uuidVersion ) {
541- case 'v4' :
542- field . onChange ( uuidv4 ( ) )
543- break
544- case 'v5' :
545- const namespace = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'
546- field . onChange ( uuidv5 ( field . value || 'default' , namespace ) )
547- break
548- case 'v6' :
549- field . onChange ( uuidv6 ( ) )
550- break
551- case 'v7' :
552- field . onChange ( uuidv7 ( ) )
553- break
554- }
541+ field . onChange ( uuidv4 ( ) )
555542 }
556-
557543 return (
558544 < FormItem className = { 'min-h-[100px]' } >
559545 < FormLabel > { t ( 'nodeModal.apiKey' ) } </ FormLabel >
@@ -568,17 +554,6 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
568554 onChange = { e => field . onChange ( e . target . value ) }
569555 />
570556 < div className = { cn ( 'flex items-center gap-0' , dir === 'rtl' && 'flex-row-reverse' ) } >
571- < Select value = { uuidVersion } onValueChange = { ( value : 'v4' | 'v5' | 'v6' | 'v7' ) => setUuidVersion ( value ) } >
572- < SelectTrigger className = "h-10 w-[60px] rounded-r-none border-r-0" >
573- < SelectValue />
574- </ SelectTrigger >
575- < SelectContent >
576- < SelectItem value = "v4" > v4</ SelectItem >
577- < SelectItem value = "v5" > v5</ SelectItem >
578- < SelectItem value = "v6" > v6</ SelectItem >
579- < SelectItem value = "v7" > v7</ SelectItem >
580- </ SelectContent >
581- </ Select >
582557 < Button type = "button" variant = "outline" onClick = { generateUUID } className = "h-10 rounded-l-none px-3" >
583558 < RefreshCw className = "h-3 w-3" />
584559 </ Button >
@@ -590,8 +565,7 @@ export default function NodeModal({ isDialogOpen, onOpenChange, form, editingNod
590565 )
591566 } }
592567 />
593-
594- < Accordion type = "single" collapsible className = "mb-4 !mt-0 w-full pb-4" >
568+ < Accordion type = "single" collapsible className = "!mt-0 mb-4 w-full pb-4" >
595569 < AccordionItem className = "rounded-sm border px-4 [&_[data-state=closed]]:no-underline [&_[data-state=open]]:no-underline" value = "advanced-settings" >
596570 < AccordionTrigger >
597571 < div className = "flex items-center gap-2" >
0 commit comments