@@ -19,24 +19,20 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
1919 const [ loading , setLoading ] = useState ( false ) ;
2020 const [ error , setError ] = useState < string | null > ( null ) ;
2121 const [ selectedServer , setSelectedServer ] = useState < Server | null > ( null ) ;
22- const [ hasAutoExecuted , setHasAutoExecuted ] = useState ( false ) ;
2322 const [ settingsModalOpen , setSettingsModalOpen ] = useState ( false ) ;
2423
2524 useEffect ( ( ) => {
2625 if ( isOpen ) {
27- setHasAutoExecuted ( false ) ;
2826 void fetchServers ( ) ;
2927 }
3028 } , [ isOpen ] ) ;
3129
32- // Auto-execute when exactly one server is available
30+ // Auto-select server when exactly one server is available
3331 useEffect ( ( ) => {
34- if ( isOpen && ! loading && servers . length === 1 && ! hasAutoExecuted ) {
35- setHasAutoExecuted ( true ) ;
36- onExecute ( 'ssh' , servers [ 0 ] ) ;
37- onClose ( ) ;
32+ if ( isOpen && ! loading && servers . length === 1 ) {
33+ setSelectedServer ( servers [ 0 ] ?? null ) ;
3834 }
39- } , [ isOpen , loading , servers , hasAutoExecuted , onExecute , onClose ] ) ;
35+ } , [ isOpen , loading , servers ] ) ;
4036
4137 // Refresh servers when settings modal closes
4238 const handleSettingsModalClose = ( ) => {
@@ -54,7 +50,11 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
5450 throw new Error ( 'Failed to fetch servers' ) ;
5551 }
5652 const data = await response . json ( ) ;
57- setServers ( data as Server [ ] ) ;
53+ // Sort servers by name alphabetically
54+ const sortedServers = ( data as Server [ ] ) . sort ( ( a , b ) =>
55+ ( a . name ?? '' ) . localeCompare ( b . name ?? '' )
56+ ) ;
57+ setServers ( sortedServers ) ;
5858 } catch ( err ) {
5959 setError ( err instanceof Error ? err . message : 'An error occurred' ) ;
6060 } finally {
@@ -101,12 +101,6 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
101101
102102 { /* Content */ }
103103 < div className = "p-6" >
104- < div className = "mb-6" >
105- < h3 className = "text-lg font-medium text-foreground mb-2" >
106- Select server to execute "{ scriptName } "
107- </ h3 >
108- </ div >
109-
110104 { error && (
111105 < div className = "mb-4 p-3 bg-destructive/10 border border-destructive/20 rounded-md" >
112106 < div className = "flex" >
@@ -122,58 +116,113 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
122116 </ div >
123117 ) }
124118
125- { /* Server Selection */ }
126- < div className = "mb-6" >
127- < label htmlFor = "server" className = "block text-sm font-medium text-foreground mb-2" >
128- Select Server
129- </ label >
130- { loading ? (
131- < div className = "text-center py-4" >
132- < div className = "inline-block animate-spin rounded-full h-6 w-6 border-b-2 border-primary" > </ div >
133- < p className = "mt-2 text-sm text-muted-foreground" > Loading servers...</ p >
119+ { loading ? (
120+ < div className = "text-center py-8" >
121+ < div className = "inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-primary" > </ div >
122+ < p className = "mt-2 text-sm text-muted-foreground" > Loading servers...</ p >
123+ </ div >
124+ ) : servers . length === 0 ? (
125+ < div className = "text-center py-8 text-muted-foreground" >
126+ < p className = "text-sm" > No servers configured</ p >
127+ < p className = "text-xs mt-1" > Add servers in Settings to execute scripts</ p >
128+ < Button
129+ onClick = { ( ) => setSettingsModalOpen ( true ) }
130+ variant = "outline"
131+ size = "sm"
132+ className = "mt-3"
133+ >
134+ Open Server Settings
135+ </ Button >
136+ </ div >
137+ ) : servers . length === 1 ? (
138+ /* Single Server Confirmation View */
139+ < div className = "space-y-6" >
140+ < div className = "text-center" >
141+ < h3 className = "text-lg font-medium text-foreground mb-2" >
142+ Install Script Confirmation
143+ </ h3 >
144+ < p className = "text-sm text-muted-foreground" >
145+ Do you want to install "{ scriptName } " on the following server?
146+ </ p >
147+ </ div >
148+
149+ < div className = "bg-muted/50 rounded-lg p-4 border border-border" >
150+ < div className = "flex items-center space-x-3" >
151+ < div className = "flex-shrink-0" >
152+ < div className = "w-3 h-3 bg-green-500 rounded-full" > </ div >
153+ </ div >
154+ < div className = "flex-1 min-w-0" >
155+ < p className = "text-sm font-medium text-foreground truncate" >
156+ { selectedServer ?. name ?? 'Unnamed Server' }
157+ </ p >
158+ < p className = "text-sm text-muted-foreground" >
159+ { selectedServer ?. ip }
160+ </ p >
161+ </ div >
162+ </ div >
163+ </ div >
164+
165+ { /* Action Buttons */ }
166+ < div className = "flex justify-end space-x-3" >
167+ < Button
168+ onClick = { onClose }
169+ variant = "outline"
170+ size = "default"
171+ >
172+ Cancel
173+ </ Button >
174+ < Button
175+ onClick = { handleExecute }
176+ variant = "default"
177+ size = "default"
178+ >
179+ Install
180+ </ Button >
181+ </ div >
182+ </ div >
183+ ) : (
184+ /* Multiple Servers Selection View */
185+ < div className = "space-y-6" >
186+ < div className = "mb-6" >
187+ < h3 className = "text-lg font-medium text-foreground mb-2" >
188+ Select server to execute "{ scriptName } "
189+ </ h3 >
190+ </ div >
191+
192+ { /* Server Selection */ }
193+ < div className = "mb-6" >
194+ < label htmlFor = "server" className = "block text-sm font-medium text-foreground mb-2" >
195+ Select Server
196+ </ label >
197+ < ColorCodedDropdown
198+ servers = { servers }
199+ selectedServer = { selectedServer }
200+ onServerSelect = { handleServerSelect }
201+ placeholder = "Select a server..."
202+ />
134203 </ div >
135- ) : servers . length === 0 ? (
136- < div className = "text-center py-4 text-muted-foreground" >
137- < p className = "text-sm" > No servers configured</ p >
138- < p className = "text-xs mt-1" > Add servers in Settings to execute scripts</ p >
204+
205+ { /* Action Buttons */ }
206+ < div className = "flex justify-end space-x-3" >
139207 < Button
140- onClick = { ( ) => setSettingsModalOpen ( true ) }
208+ onClick = { onClose }
141209 variant = "outline"
142- size = "sm"
143- className = "mt-3"
210+ size = "default"
211+ >
212+ Cancel
213+ </ Button >
214+ < Button
215+ onClick = { handleExecute }
216+ disabled = { ! selectedServer }
217+ variant = "default"
218+ size = "default"
219+ className = { ! selectedServer ? 'bg-gray-400 cursor-not-allowed' : '' }
144220 >
145- Open Server Settings
221+ Run on Server
146222 </ Button >
147223 </ div >
148- ) : (
149- < ColorCodedDropdown
150- servers = { servers }
151- selectedServer = { selectedServer }
152- onServerSelect = { handleServerSelect }
153- placeholder = "Select a server..."
154- />
155- ) }
156- </ div >
157-
158- { /* Action Buttons */ }
159- < div className = "flex justify-end space-x-3" >
160- < Button
161- onClick = { onClose }
162- variant = "outline"
163- size = "default"
164- >
165- Cancel
166- </ Button >
167- < Button
168- onClick = { handleExecute }
169- disabled = { ! selectedServer }
170- variant = "default"
171- size = "default"
172- className = { ! selectedServer ? 'bg-gray-400 cursor-not-allowed' : '' }
173- >
174- Run on Server
175- </ Button >
176- </ div >
224+ </ div >
225+ ) }
177226 </ div >
178227 </ div >
179228 </ div >
0 commit comments