@@ -19,7 +19,7 @@ import {
19
19
type ExternalIp ,
20
20
type InstanceNetworkInterface ,
21
21
} from '@oxide/api'
22
- import { Networking24Icon } from '@oxide/design-system/icons/react'
22
+ import { IpGlobal24Icon , Networking24Icon } from '@oxide/design-system/icons/react'
23
23
24
24
import { HL } from '~/components/HL'
25
25
import { CreateNetworkInterfaceForm } from '~/forms/network-interface-create'
@@ -255,6 +255,16 @@ export function NetworkingTab() {
255
255
} ) ,
256
256
]
257
257
258
+ const ephemeralIpDetach = useApiMutation ( 'instanceEphemeralIpDetach' , {
259
+ onSuccess ( ) {
260
+ queryClient . invalidateQueries ( 'instanceExternalIpList' )
261
+ addToast ( { content : 'Your ephemeral IP has been detached' } )
262
+ } ,
263
+ onError : ( err ) => {
264
+ addToast ( { title : 'Error' , content : err . message , variant : 'error' } )
265
+ } ,
266
+ } )
267
+
258
268
const floatingIpDetach = useApiMutation ( 'floatingIpDetach' , {
259
269
onSuccess ( ) {
260
270
queryClient . invalidateQueries ( 'floatingIpList' )
@@ -275,35 +285,50 @@ export function NetworkingTab() {
275
285
} ,
276
286
}
277
287
278
- if ( externalIp . kind === 'floating' ) {
279
- return [
280
- copyAction ,
281
- {
282
- label : 'Detach' ,
283
- onActivate : ( ) =>
284
- confirmAction ( {
285
- actionType : 'danger' ,
286
- doAction : ( ) =>
287
- floatingIpDetach . mutateAsync ( {
288
- path : { floatingIp : externalIp . name } ,
289
- query : { project } ,
290
- } ) ,
291
- modalTitle : 'Detach Floating IP' ,
292
- modalContent : (
293
- < p >
294
- Are you sure you want to detach floating IP < HL > { externalIp . name } </ HL > { ' ' }
295
- from < HL > { instanceName } </ HL > ? The instance will no longer be reachable
296
- at < HL > { externalIp . ip } </ HL > .
297
- </ p >
298
- ) ,
299
- errorTitle : 'Error detaching floating IP' ,
300
- } ) ,
301
- } ,
302
- ]
303
- }
288
+ const doAction =
289
+ externalIp . kind === 'floating'
290
+ ? ( ) =>
291
+ floatingIpDetach . mutateAsync ( {
292
+ path : { floatingIp : externalIp . name } ,
293
+ query : { project } ,
294
+ } )
295
+ : ( ) =>
296
+ ephemeralIpDetach . mutateAsync ( {
297
+ path : { instance : instanceName } ,
298
+ query : { project } ,
299
+ } )
300
+
301
+ return [
302
+ copyAction ,
303
+ {
304
+ label : 'Detach' ,
305
+ onActivate : ( ) =>
306
+ confirmAction ( {
307
+ actionType : 'danger' ,
308
+ doAction,
309
+ modalTitle : `Confirm detach ${ externalIp . kind } IP` ,
310
+ modalContent : (
311
+ < p >
312
+ Are you sure you want to detach{ ' ' }
313
+ { externalIp . kind === 'ephemeral' ? (
314
+ 'this ephemeral IP'
315
+ ) : (
316
+ < >
317
+ floating IP < HL > { externalIp . name } </ HL >
318
+ </ >
319
+ ) } { ' ' }
320
+ from < HL > { instanceName } </ HL > ? The instance will no longer be reachable at{ ' ' }
321
+ < HL > { externalIp . ip } </ HL > .
322
+ </ p >
323
+ ) ,
324
+ errorTitle : `Error detaching ${ externalIp . kind } IP` ,
325
+ } ) ,
326
+ } ,
327
+ ]
328
+
304
329
return [ copyAction ]
305
330
} ,
306
- [ floatingIpDetach , instanceName , project ]
331
+ [ ephemeralIpDetach , floatingIpDetach , instanceName , project ]
307
332
)
308
333
309
334
const ipTableInstance = useReactTable ( {
@@ -338,7 +363,17 @@ export function NetworkingTab() {
338
363
/>
339
364
) }
340
365
</ TableControls >
341
- < Table aria-labelledby = "attached-ips-label" table = { ipTableInstance } />
366
+ { eips . items . length > 0 ? (
367
+ < Table aria-labelledby = "attached-ips-label" table = { ipTableInstance } />
368
+ ) : (
369
+ < TableEmptyBox >
370
+ < EmptyMessage
371
+ icon = { < IpGlobal24Icon /> }
372
+ title = "No external IPs"
373
+ body = "You need to attach an external IP to be able to see it here"
374
+ />
375
+ </ TableEmptyBox >
376
+ ) }
342
377
343
378
< TableControls className = "mt-8" >
344
379
< TableTitle id = "nics-label" > Network interfaces</ TableTitle >
0 commit comments