5
5
*
6
6
* Copyright Oxide Computer Company
7
7
*/
8
- import { useState } from 'react'
8
+ import { createColumnHelper } from '@tanstack/react-table'
9
+ import { useCallback , useState } from 'react'
9
10
import { useForm } from 'react-hook-form'
10
11
import { Outlet , useNavigate , type LoaderFunctionArgs } from 'react-router-dom'
11
12
@@ -26,8 +27,9 @@ import { confirmAction } from '~/stores/confirm-action'
26
27
import { confirmDelete } from '~/stores/confirm-delete'
27
28
import { addToast } from '~/stores/toast'
28
29
import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell'
29
- import type { MenuAction } from '~/table/columns/action-col'
30
- import { useQueryTable } from '~/table/QueryTable'
30
+ import { makeLinkCell } from '~/table/cells/LinkCell'
31
+ import { useColsWithActions , type MenuAction } from '~/table/columns/action-col'
32
+ import { useQueryTable2 } from '~/table/QueryTable2'
31
33
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
32
34
import { Listbox } from '~/ui/lib/Listbox'
33
35
import { Message } from '~/ui/lib/Message'
@@ -68,8 +70,6 @@ export function FloatingIpsPage() {
68
70
query : { project } ,
69
71
} )
70
72
const navigate = useNavigate ( )
71
- const getInstanceName = ( instanceId : string ) =>
72
- instances . items . find ( ( i ) => i . id === instanceId ) ?. name
73
73
74
74
const floatingIpDetach = useApiMutation ( 'floatingIpDetach' , {
75
75
onSuccess ( ) {
@@ -88,76 +88,102 @@ export function FloatingIpsPage() {
88
88
} ,
89
89
} )
90
90
91
- const makeActions = ( floatingIp : FloatingIp ) : MenuAction [ ] => {
92
- const isAttachedToAnInstance = ! ! floatingIp . instanceId
93
- const attachOrDetachAction = isAttachedToAnInstance
94
- ? {
95
- label : 'Detach' ,
96
- onActivate : ( ) =>
97
- confirmAction ( {
98
- actionType : 'danger' ,
99
- doAction : ( ) =>
100
- floatingIpDetach . mutateAsync ( {
101
- path : { floatingIp : floatingIp . name } ,
102
- query : { project } ,
103
- } ) ,
104
- modalTitle : 'Detach Floating IP' ,
105
- modalContent : (
106
- < p >
107
- Are you sure you want to detach floating IP < HL > { floatingIp . name } </ HL > { ' ' }
108
- from instance{ ' ' }
109
- < HL >
110
- {
111
- // instanceId is guaranteed to be non-null here
112
- getInstanceName ( floatingIp . instanceId ! )
113
- }
114
- </ HL >
115
- ? The instance will no longer be reachable at < HL > { floatingIp . ip } </ HL > .
116
- </ p >
117
- ) ,
118
- errorTitle : 'Error detaching floating IP' ,
119
- } ) ,
120
- }
121
- : {
122
- label : 'Attach' ,
123
- onActivate ( ) {
124
- setFloatingIpToModify ( floatingIp )
125
- } ,
126
- }
127
- return [
128
- {
129
- label : 'Edit' ,
130
- onActivate : ( ) => {
131
- apiQueryClient . setQueryData (
132
- 'floatingIpView' ,
133
- {
134
- path : { floatingIp : floatingIp . name } ,
135
- query : { project } ,
91
+ const colHelper = createColumnHelper < FloatingIp > ( )
92
+
93
+ const staticCols = [
94
+ colHelper . accessor ( 'name' , {
95
+ cell : makeLinkCell ( ( name ) => pb . floatingIp ( { floatingIp : name , project } ) ) ,
96
+ } ) ,
97
+ colHelper . accessor ( 'description' , { } ) ,
98
+ colHelper . accessor ( 'ip' , { } ) ,
99
+ colHelper . accessor ( 'instanceId' , {
100
+ cell : ( props ) => < InstanceLinkCell instanceId = { props . getValue ( ) } /> ,
101
+ header : 'Attached to instance' ,
102
+ } ) ,
103
+ ]
104
+
105
+ const makeActions = useCallback (
106
+ ( floatingIp : FloatingIp ) : MenuAction [ ] => {
107
+ const instanceName = floatingIp . instanceId
108
+ ? instances . items . find ( ( i ) => i . id === floatingIp . instanceId ) ?. name
109
+ : undefined
110
+ // handling the rather unlikely case where the instance is not in the 1000 we fetched
111
+ const fromInstance = instanceName ? (
112
+ < >
113
+ { ' ' /* important */ }
114
+ from instance < HL > { instanceName } </ HL >
115
+ </ >
116
+ ) : null
117
+
118
+ const isAttachedToAnInstance = ! ! floatingIp . instanceId
119
+ const attachOrDetachAction = isAttachedToAnInstance
120
+ ? {
121
+ label : 'Detach' ,
122
+ onActivate : ( ) =>
123
+ confirmAction ( {
124
+ actionType : 'danger' ,
125
+ doAction : ( ) =>
126
+ floatingIpDetach . mutateAsync ( {
127
+ path : { floatingIp : floatingIp . name } ,
128
+ query : { project } ,
129
+ } ) ,
130
+ modalTitle : 'Detach Floating IP' ,
131
+ // instanceName! non-null because we only see this if there is an instance
132
+ modalContent : (
133
+ < p >
134
+ Are you sure you want to detach floating IP < HL > { floatingIp . name } </ HL >
135
+ { fromInstance } ? The instance will no longer be reachable at{ ' ' }
136
+ < HL > { floatingIp . ip } </ HL > .
137
+ </ p >
138
+ ) ,
139
+ errorTitle : 'Error detaching floating IP' ,
140
+ } ) ,
141
+ }
142
+ : {
143
+ label : 'Attach' ,
144
+ onActivate ( ) {
145
+ setFloatingIpToModify ( floatingIp )
136
146
} ,
137
- floatingIp
138
- )
139
- navigate ( pb . floatingIpEdit ( { project, floatingIp : floatingIp . name } ) )
147
+ }
148
+ return [
149
+ {
150
+ label : 'Edit' ,
151
+ onActivate : ( ) => {
152
+ apiQueryClient . setQueryData (
153
+ 'floatingIpView' ,
154
+ {
155
+ path : { floatingIp : floatingIp . name } ,
156
+ query : { project } ,
157
+ } ,
158
+ floatingIp
159
+ )
160
+ navigate ( pb . floatingIpEdit ( { project, floatingIp : floatingIp . name } ) )
161
+ } ,
162
+ } ,
163
+ attachOrDetachAction ,
164
+ {
165
+ label : 'Delete' ,
166
+ disabled : isAttachedToAnInstance
167
+ ? 'This floating IP must be detached from the instance before it can be deleted'
168
+ : false ,
169
+ onActivate : confirmDelete ( {
170
+ doDelete : ( ) =>
171
+ deleteFloatingIp . mutateAsync ( {
172
+ path : { floatingIp : floatingIp . name } ,
173
+ query : { project } ,
174
+ } ) ,
175
+ label : floatingIp . name ,
176
+ } ) ,
140
177
} ,
141
- } ,
142
- attachOrDetachAction ,
143
- {
144
- label : 'Delete' ,
145
- disabled : isAttachedToAnInstance
146
- ? 'This floating IP must be detached from the instance before it can be deleted'
147
- : false ,
148
- onActivate : confirmDelete ( {
149
- doDelete : ( ) =>
150
- deleteFloatingIp . mutateAsync ( {
151
- path : { floatingIp : floatingIp . name } ,
152
- query : { project } ,
153
- } ) ,
154
- label : floatingIp . name ,
155
- } ) ,
156
- } ,
157
- ]
158
- }
178
+ ]
179
+ } ,
180
+ [ deleteFloatingIp , floatingIpDetach , navigate , project , instances ]
181
+ )
182
+
183
+ const { Table } = useQueryTable2 ( 'floatingIpList' , { query : { project } } )
184
+
185
+ const columns = useColsWithActions ( staticCols , makeActions )
159
186
160
- const { Table, Column } = useQueryTable ( 'floatingIpList' , { query : { project } } )
161
187
return (
162
188
< >
163
189
< PageHeader >
@@ -173,16 +199,7 @@ export function FloatingIpsPage() {
173
199
New Floating IP
174
200
</ TableControlsLink >
175
201
</ TableControls >
176
- < Table emptyState = { < EmptyState /> } makeActions = { makeActions } >
177
- < Column accessor = "name" />
178
- < Column accessor = "description" />
179
- < Column accessor = "ip" />
180
- < Column
181
- accessor = "instanceId"
182
- header = "Attached to instance"
183
- cell = { InstanceLinkCell }
184
- />
185
- </ Table >
202
+ < Table emptyState = { < EmptyState /> } columns = { columns } />
186
203
< Outlet />
187
204
{ floatingIpToModify && (
188
205
< AttachFloatingIpModal
0 commit comments