Skip to content

Commit b63c81e

Browse files
More table conversions (#2115)
* Convert tables for Projects, Images, Instances * convert NetworkingTab * convert Storage tab * convert Snapshots page * Convert VPC page * convert VPC Subnet tab * convert SSH Keys page * convert Silo Images page * convert Disks tab * convert Racks tab, though not sure we're using it * convert Sleds tab * convert Sled Instances tab * convert IpPool page; unresolved tsc issue * convert SiloIDPs tab, but link not quite right yet * convert Silos page * small refactor * use proper LinkCell component for Identity Providers * Refactor Silo Identity Providers * Add DefaultPoolCell * Update type definitions for column helper * re-name QUeryTable2 and update imports * Make Floating IP table's name column just render the name, instead of a link * refactor * Convert argument in cell accessor from props to info * Tests passing; remove unused functions * Remove Cell type * Remove Cell type from BooleanCell * Remove LabelCell * clean up header / id, start extracting static cols * Extract more staticCols * clean up some id / header duplication * memoization of VPC page cols * consistent naming * Memoize ImagesPage * More momoization and moving things around * move instance networking cols out * pull out or memoize some more columns --------- Co-authored-by: David Crespo <david.crespo@oxidecomputer.com>
1 parent d5d70bd commit b63c81e

33 files changed

+479
-668
lines changed

app/pages/ProjectsPage.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { createColumnHelper } from '@tanstack/react-table'
89
import { useCallback, useMemo } from 'react'
910
import { Link, Outlet, useNavigate } from 'react-router-dom'
1011

@@ -19,8 +20,8 @@ import { Folder24Icon } from '@oxide/design-system/icons/react'
1920

2021
import { confirmDelete } from '~/stores/confirm-delete'
2122
import { DateCell } from '~/table/cells/DateCell'
22-
import { linkCell } from '~/table/cells/LinkCell'
23-
import type { MenuAction } from '~/table/columns/action-col'
23+
import { makeLinkCell } from '~/table/cells/LinkCell'
24+
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
2425
import { useQueryTable } from '~/table/QueryTable'
2526
import { buttonStyle } from '~/ui/lib/Button'
2627
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
@@ -45,11 +46,23 @@ ProjectsPage.loader = async () => {
4546
return null
4647
}
4748

49+
const colHelper = createColumnHelper<Project>()
50+
const staticCols = [
51+
colHelper.accessor('name', {
52+
cell: makeLinkCell((project) => pb.instances({ project })),
53+
}),
54+
colHelper.accessor('description', {}),
55+
colHelper.accessor('timeCreated', {
56+
header: 'created',
57+
cell: (info) => <DateCell value={info.getValue()} />,
58+
}),
59+
]
60+
4861
export function ProjectsPage() {
4962
const navigate = useNavigate()
5063

5164
const queryClient = useApiQueryClient()
52-
const { Table, Column } = useQueryTable('projectList', {})
65+
const { Table } = useQueryTable('projectList', {})
5366
const { data: projects } = usePrefetchedApiQuery('projectList', {
5467
query: { limit: 25 }, // limit to match QueryTable
5568
})
@@ -105,6 +118,7 @@ export function ProjectsPage() {
105118
)
106119
)
107120

121+
const columns = useColsWithActions(staticCols, makeActions)
108122
return (
109123
<>
110124
<PageHeader>
@@ -115,11 +129,7 @@ export function ProjectsPage() {
115129
New Project
116130
</Link>
117131
</TableActions>
118-
<Table emptyState={<EmptyState />} makeActions={makeActions}>
119-
<Column accessor="name" cell={linkCell((project) => pb.instances({ project }))} />
120-
<Column accessor="description" />
121-
<Column accessor="timeCreated" header="Created" cell={DateCell} />
122-
</Table>
132+
<Table columns={columns} emptyState={<EmptyState />} />
123133
<Outlet />
124134
</>
125135
)

app/pages/SiloAccessPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ export function SiloAccessPage() {
114114
colHelper.accessor('name', { header: 'Name' }),
115115
colHelper.accessor('identityType', {
116116
header: 'Type',
117-
cell: (props) => identityTypeLabel[props.getValue()],
117+
cell: (info) => identityTypeLabel[info.getValue()],
118118
}),
119119
colHelper.accessor('siloRole', {
120120
header: 'Role',
121-
cell: (props) => {
122-
const role = props.getValue()
121+
cell: (info) => {
122+
const role = info.getValue()
123123
return role ? <Badge color={roleColor[role]}>silo.{role}</Badge> : null
124124
},
125125
}),

app/pages/project/access/ProjectAccessPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export function ProjectAccessPage() {
128128
colHelper.accessor('name', { header: 'Name' }),
129129
colHelper.accessor('identityType', {
130130
header: 'Type',
131-
cell: (props) => identityTypeLabel[props.getValue()],
131+
cell: (info) => identityTypeLabel[info.getValue()],
132132
}),
133133
colHelper.accessor('roleBadges', {
134134
header: () => (
@@ -140,9 +140,9 @@ export function ProjectAccessPage() {
140140
</TipIcon>
141141
</span>
142142
),
143-
cell: (props) => (
143+
cell: (info) => (
144144
<ListPlusCell tooltipTitle="Other roles">
145-
{props.getValue().map(({ roleName, roleSource }) => (
145+
{info.getValue().map(({ roleName, roleSource }) => (
146146
<Badge key={roleSource} color={roleColor[roleName]}>
147147
{roleSource}.{roleName}
148148
</Badge>

app/pages/project/disks/DisksPage.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { DateCell } from '~/table/cells/DateCell'
2626
import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell'
2727
import { SizeCell } from '~/table/cells/SizeCell'
2828
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
29-
import { useQueryTable } from '~/table/QueryTable2'
29+
import { useQueryTable } from '~/table/QueryTable'
3030
import { buttonStyle } from '~/ui/lib/Button'
3131
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
3232
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
@@ -80,17 +80,17 @@ const staticCols = [
8080
(disk) => ('instance' in disk.state ? disk.state.instance : undefined),
8181
{
8282
header: 'Attached to',
83-
cell: (props) => <InstanceLinkCell instanceId={props.getValue()} />,
83+
cell: (info) => <InstanceLinkCell instanceId={info.getValue()} />,
8484
}
8585
),
86-
colHelper.accessor('size', { cell: (props) => <SizeCell value={props.getValue()} /> }),
86+
colHelper.accessor('size', { cell: (info) => <SizeCell value={info.getValue()} /> }),
8787
colHelper.accessor('state.state', {
8888
header: 'Status',
89-
cell: (props) => <DiskStatusBadge status={props.getValue()} />,
89+
cell: (info) => <DiskStatusBadge status={info.getValue()} />,
9090
}),
9191
colHelper.accessor('timeCreated', {
9292
header: 'Created',
93-
cell: (props) => <DateCell value={props.getValue()} />,
93+
cell: (info) => <DateCell value={info.getValue()} />,
9494
}),
9595
]
9696

@@ -172,7 +172,7 @@ export function DisksPage() {
172172
New Disk
173173
</Link>
174174
</TableActions>
175-
<Table emptyState={<EmptyState />} columns={columns} />
175+
<Table columns={columns} emptyState={<EmptyState />} />
176176
<Outlet />
177177
</>
178178
)

app/pages/project/floating-ips/FloatingIpsPage.tsx

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ import { confirmAction } from '~/stores/confirm-action'
2727
import { confirmDelete } from '~/stores/confirm-delete'
2828
import { addToast } from '~/stores/toast'
2929
import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell'
30-
import { makeLinkCell } from '~/table/cells/LinkCell'
3130
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
32-
import { useQueryTable } from '~/table/QueryTable2'
31+
import { useQueryTable } from '~/table/QueryTable'
3332
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
3433
import { Listbox } from '~/ui/lib/Listbox'
3534
import { Message } from '~/ui/lib/Message'
@@ -62,6 +61,17 @@ FloatingIpsPage.loader = async ({ params }: LoaderFunctionArgs) => {
6261
return null
6362
}
6463

64+
const colHelper = createColumnHelper<FloatingIp>()
65+
const staticCols = [
66+
colHelper.accessor('name', {}),
67+
colHelper.accessor('description', {}),
68+
colHelper.accessor('ip', {}),
69+
colHelper.accessor('instanceId', {
70+
cell: (info) => <InstanceLinkCell instanceId={info.getValue()} />,
71+
header: 'Attached to instance',
72+
}),
73+
]
74+
6575
export function FloatingIpsPage() {
6676
const [floatingIpToModify, setFloatingIpToModify] = useState<FloatingIp | null>(null)
6777
const queryClient = useApiQueryClient()
@@ -88,20 +98,6 @@ export function FloatingIpsPage() {
8898
},
8999
})
90100

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-
105101
const makeActions = useCallback(
106102
(floatingIp: FloatingIp): MenuAction[] => {
107103
const instanceName = floatingIp.instanceId
@@ -199,7 +195,7 @@ export function FloatingIpsPage() {
199195
New Floating IP
200196
</TableControlsLink>
201197
</TableControls>
202-
<Table emptyState={<EmptyState />} columns={columns} />
198+
<Table columns={columns} emptyState={<EmptyState />} />
203199
<Outlet />
204200
{floatingIpToModify && (
205201
<AttachFloatingIpModal

app/pages/project/images/ImagesPage.tsx

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import { useCallback, useState } from 'react'
8+
import { createColumnHelper } from '@tanstack/react-table'
9+
import { useCallback, useMemo, useState } from 'react'
910
import { Link, Outlet, type LoaderFunctionArgs } from 'react-router-dom'
1011

1112
import { apiQueryClient, useApiMutation, useApiQueryClient, type Image } from '@oxide/api'
@@ -14,9 +15,9 @@ import { Images24Icon } from '@oxide/design-system/icons/react'
1415
import { getProjectSelector, useProjectSelector, useToast } from '~/hooks'
1516
import { confirmDelete } from '~/stores/confirm-delete'
1617
import { DateCell } from '~/table/cells/DateCell'
17-
import { linkCell } from '~/table/cells/LinkCell'
18+
import { makeLinkCell } from '~/table/cells/LinkCell'
1819
import { SizeCell } from '~/table/cells/SizeCell'
19-
import type { MenuAction } from '~/table/columns/action-col'
20+
import { getActionsCol, type MenuAction } from '~/table/columns/action-col'
2021
import { useQueryTable } from '~/table/QueryTable'
2122
import { buttonStyle } from '~/ui/lib/Button'
2223
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
@@ -36,6 +37,8 @@ const EmptyState = () => (
3637
/>
3738
)
3839

40+
const columnHelper = createColumnHelper<Image>()
41+
3942
ImagesPage.loader = async ({ params }: LoaderFunctionArgs) => {
4043
const { project } = getProjectSelector(params)
4144
await apiQueryClient.prefetchQuery('imageList', {
@@ -46,7 +49,7 @@ ImagesPage.loader = async ({ params }: LoaderFunctionArgs) => {
4649

4750
export function ImagesPage() {
4851
const projectSelector = useProjectSelector()
49-
const { Table, Column } = useQueryTable('imageList', { query: projectSelector })
52+
const { Table } = useQueryTable('imageList', { query: projectSelector })
5053
const queryClient = useApiQueryClient()
5154
const addToast = useToast()
5255

@@ -80,6 +83,24 @@ export function ImagesPage() {
8083
[deleteImage, projectSelector]
8184
)
8285

86+
const columns = useMemo(() => {
87+
return [
88+
columnHelper.accessor('name', {
89+
cell: makeLinkCell((image) => pb.projectImageEdit({ ...projectSelector, image })),
90+
}),
91+
columnHelper.accessor('description', {}),
92+
columnHelper.accessor('size', {
93+
header: 'size',
94+
cell: (info) => <SizeCell value={info.getValue()} />,
95+
}),
96+
columnHelper.accessor('timeCreated', {
97+
header: 'created',
98+
cell: (info) => <DateCell value={info.getValue()} />,
99+
}),
100+
getActionsCol(makeActions),
101+
]
102+
}, [projectSelector, makeActions])
103+
83104
return (
84105
<>
85106
<PageHeader>
@@ -93,15 +114,7 @@ export function ImagesPage() {
93114
Upload image
94115
</Link>
95116
</TableActions>
96-
<Table emptyState={<EmptyState />} makeActions={makeActions}>
97-
<Column
98-
accessor="name"
99-
cell={linkCell((image) => pb.projectImageEdit({ ...projectSelector, image }))}
100-
/>
101-
<Column accessor="description" />
102-
<Column accessor="size" cell={SizeCell} />
103-
<Column accessor="timeCreated" header="Created" cell={DateCell} />
104-
</Table>
117+
<Table columns={columns} emptyState={<EmptyState />} />
105118
{promoteImageName && (
106119
<PromoteImageModal
107120
onDismiss={() => setPromoteImageName(null)}

app/pages/project/instances/InstancesPage.tsx

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,24 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8+
import { createColumnHelper } from '@tanstack/react-table'
89
import { useMemo } from 'react'
910
import { Link, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
1011

11-
import { apiQueryClient, useApiQueryClient, usePrefetchedApiQuery } from '@oxide/api'
12+
import {
13+
apiQueryClient,
14+
useApiQueryClient,
15+
usePrefetchedApiQuery,
16+
type Instance,
17+
} from '@oxide/api'
1218
import { Instances24Icon, Refresh16Icon } from '@oxide/design-system/icons/react'
1319

1420
import { getProjectSelector, useProjectSelector, useQuickActions } from '~/hooks'
1521
import { DateCell } from '~/table/cells/DateCell'
1622
import { InstanceResourceCell } from '~/table/cells/InstanceResourceCell'
1723
import { InstanceStatusCell } from '~/table/cells/InstanceStatusCell'
18-
import { linkCell } from '~/table/cells/LinkCell'
24+
import { makeLinkCell } from '~/table/cells/LinkCell'
25+
import { getActionsCol } from '~/table/columns/action-col'
1926
import { useQueryTable } from '~/table/QueryTable'
2027
import { Button, buttonStyle } from '~/ui/lib/Button'
2128
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
@@ -35,6 +42,8 @@ const EmptyState = () => (
3542
/>
3643
)
3744

45+
const colHelper = createColumnHelper<Instance>()
46+
3847
InstancesPage.loader = async ({ params }: LoaderFunctionArgs) => {
3948
await apiQueryClient.prefetchQuery('instanceList', {
4049
query: { ...getProjectSelector(params), limit: 25 },
@@ -75,12 +84,41 @@ export function InstancesPage() {
7584
)
7685
)
7786

78-
const { Table, Column } = useQueryTable(
87+
const { Table } = useQueryTable(
7988
'instanceList',
8089
{ query: projectSelector },
8190
{ placeholderData: (x) => x }
8291
)
8392

93+
const columns = useMemo(
94+
() => [
95+
colHelper.accessor('name', {
96+
cell: makeLinkCell((instance) => pb.instancePage({ ...projectSelector, instance })),
97+
}),
98+
colHelper.accessor((i) => ({ ncpus: i.ncpus, memory: i.memory }), {
99+
header: 'CPU, RAM',
100+
cell: (info) => <InstanceResourceCell value={info.getValue()} />,
101+
}),
102+
colHelper.accessor(
103+
(i) => ({
104+
runState: i.runState,
105+
timeRunStateUpdated: i.timeRunStateUpdated,
106+
}),
107+
{
108+
header: 'status',
109+
cell: (info) => <InstanceStatusCell value={info.getValue()} />,
110+
}
111+
),
112+
colHelper.accessor('hostname', {}),
113+
colHelper.accessor('timeCreated', {
114+
header: 'created',
115+
cell: (info) => <DateCell value={info.getValue()} />,
116+
}),
117+
getActionsCol(makeActions),
118+
],
119+
[projectSelector, makeActions]
120+
)
121+
84122
if (!instances) return null
85123

86124
return (
@@ -101,28 +139,7 @@ export function InstancesPage() {
101139
New Instance
102140
</Link>
103141
</TableActions>
104-
<Table makeActions={makeActions} emptyState={<EmptyState />}>
105-
<Column
106-
accessor="name"
107-
cell={linkCell((instance) => pb.instancePage({ ...projectSelector, instance }))}
108-
/>
109-
<Column
110-
id="resources"
111-
header="CPU, RAM"
112-
accessor={(i) => ({ ncpus: i.ncpus, memory: i.memory })}
113-
cell={InstanceResourceCell}
114-
/>
115-
<Column
116-
id="status"
117-
accessor={(i) => ({
118-
runState: i.runState,
119-
timeRunStateUpdated: i.timeRunStateUpdated,
120-
})}
121-
cell={InstanceStatusCell}
122-
/>
123-
<Column accessor="hostname" />
124-
<Column accessor="timeCreated" header="created" cell={DateCell} />
125-
</Table>
142+
<Table columns={columns} emptyState={<EmptyState />} />
126143
</>
127144
)
128145
}

0 commit comments

Comments
 (0)