From 72d7eb3d9a8bb6bb9234cf8f737aeda22c90dbec Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 11:20:19 -0500 Subject: [PATCH 1/7] convert silo IP pools tab to useQueryTable2 --- app/pages/system/silos/SiloIpPoolsTab.tsx | 176 ++++++++++++---------- 1 file changed, 93 insertions(+), 83 deletions(-) diff --git a/app/pages/system/silos/SiloIpPoolsTab.tsx b/app/pages/system/silos/SiloIpPoolsTab.tsx index 5bc44e87ce..eb81844bc0 100644 --- a/app/pages/system/silos/SiloIpPoolsTab.tsx +++ b/app/pages/system/silos/SiloIpPoolsTab.tsx @@ -6,7 +6,8 @@ * Copyright Oxide Computer Company */ -import { useMemo, useState } from 'react' +import { createColumnHelper } from '@tanstack/react-table' +import { useCallback, useMemo, useState } from 'react' import { useApiMutation, useApiQuery, useApiQueryClient, type SiloIpPool } from '@oxide/api' import { Networking24Icon, Success12Icon } from '@oxide/design-system/icons/react' @@ -17,9 +18,10 @@ import { HL } from '~/components/HL' import { useForm, useSiloSelector } from '~/hooks' import { confirmAction } from '~/stores/confirm-action' import { addToast } from '~/stores/toast' -import { linkCell } from '~/table/cells/LinkCell' -import type { MenuAction } from '~/table/columns/action-col' -import { useQueryTable } from '~/table/QueryTable' +import { defaultCell } from '~/table/cells/DefaultCell' +import { makeLinkCell } from '~/table/cells/LinkCell' +import { useColsWithActions, type MenuAction } from '~/table/columns/action-col' +import { useQueryTable2 } from '~/table/QueryTable2' import { Badge } from '~/ui/lib/Badge' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { Message } from '~/ui/lib/Message' @@ -38,10 +40,28 @@ const EmptyState = () => ( /> ) +const DefaultBadge = () => ( + <> + + default + +) + +const colHelper = createColumnHelper() + +const staticCols = [ + colHelper.accessor('name', { cell: makeLinkCell((pool) => pb.ipPool({ pool })) }), + colHelper.accessor('description', { cell: defaultCell }), + colHelper.accessor('isDefault', { + header: 'Default', + cell: (props) => (props.getValue() ? : null), + }), +] + export function SiloIpPoolsTab() { const { silo } = useSiloSelector() const [showLinkModal, setShowLinkModal] = useState(false) - const { Table, Column } = useQueryTable('siloIpPoolList', { path: { silo } }) + const { Table } = useQueryTable2('siloIpPoolList', { path: { silo } }) const queryClient = useApiQueryClient() // Fetch 1000 to we can be sure to get them all. There should only be a few @@ -71,76 +91,81 @@ export function SiloIpPoolsTab() { }) // this is all very extra. I'm sorry. it's for the users - const makeActions = (pool: SiloIpPool): MenuAction[] => [ - { - label: pool.isDefault ? 'Clear default' : 'Make default', - className: pool.isDefault ? 'destructive' : undefined, - onActivate() { - if (pool.isDefault) { + const makeActions = useCallback( + (pool: SiloIpPool): MenuAction[] => [ + { + label: pool.isDefault ? 'Clear default' : 'Make default', + className: pool.isDefault ? 'destructive' : undefined, + onActivate() { + if (pool.isDefault) { + confirmAction({ + doAction: () => + updatePoolLink.mutateAsync({ + path: { silo, pool: pool.id }, + body: { isDefault: false }, + }), + modalTitle: 'Confirm clear default', + modalContent: ( +

+ Are you sure you want {pool.name} to stop being the default pool + for this silo? If there is no default, users in this silo will have to + specify a pool when allocating IPs. +

+ ), + errorTitle: 'Could not clear default', + actionType: 'danger', + }) + } else { + const modalContent = defaultPool ? ( +

+ Are you sure you want to change the default pool from {defaultPool}{' '} + to {pool.name}? +

+ ) : ( +

+ Are you sure you want to make {pool.name} the default pool for this + silo? +

+ ) + const verb = defaultPool ? 'change' : 'make' + confirmAction({ + doAction: () => + updatePoolLink.mutateAsync({ + path: { silo, pool: pool.id }, + body: { isDefault: true }, + }), + modalTitle: `Confirm ${verb} default`, + modalContent, + errorTitle: `Could not ${verb} default`, + actionType: 'primary', + }) + } + }, + }, + { + label: 'Unlink', + className: 'destructive', + onActivate() { confirmAction({ - doAction: () => - updatePoolLink.mutateAsync({ - path: { silo, pool: pool.id }, - body: { isDefault: false }, - }), - modalTitle: 'Confirm clear default', + doAction: () => unlinkPool.mutateAsync({ path: { silo, pool: pool.id } }), + modalTitle: `Confirm unlink pool`, modalContent: (

- Are you sure you want {pool.name} to stop being the default pool - for this silo? If there is no default, users in this silo will have to - specify a pool when allocating IPs. + Are you sure you want to unlink {pool.name}? Users in this silo + will no longer be able to allocate IPs from this pool. Unlink will fail if + there are any IPs from {pool.name} in use in this silo.

), - errorTitle: 'Could not clear default', + errorTitle: `Could not unlink pool`, actionType: 'danger', }) - } else { - const modalContent = defaultPool ? ( -

- Are you sure you want to change the default pool from {defaultPool}{' '} - to {pool.name}? -

- ) : ( -

- Are you sure you want to make {pool.name} the default pool for this - silo? -

- ) - const verb = defaultPool ? 'change' : 'make' - confirmAction({ - doAction: () => - updatePoolLink.mutateAsync({ - path: { silo, pool: pool.id }, - body: { isDefault: true }, - }), - modalTitle: `Confirm ${verb} default`, - modalContent, - errorTitle: `Could not ${verb} default`, - actionType: 'primary', - }) - } - }, - }, - { - label: 'Unlink', - className: 'destructive', - onActivate() { - confirmAction({ - doAction: () => unlinkPool.mutateAsync({ path: { silo, pool: pool.id } }), - modalTitle: `Confirm unlink pool`, - modalContent: ( -

- Are you sure you want to unlink {pool.name}? Users in this silo will - no longer be able to allocate IPs from this pool. Unlink will fail if there - are any IPs from {pool.name} in use in this silo. -

- ), - errorTitle: `Could not unlink pool`, - actionType: 'danger', - }) + }, }, - }, - ] + ], + [defaultPool, silo, unlinkPool, updatePoolLink] + ) + + const columns = useColsWithActions(staticCols, makeActions) return ( <> @@ -155,22 +180,7 @@ export function SiloIpPoolsTab() { Link pool - } makeActions={makeActions}> - pb.ipPool({ pool }))} /> - - - value && ( - <> - - default - - ) - } - /> -
+ } columns={columns}>
{showLinkModal && setShowLinkModal(false)} />} ) From e05fefdfb1708bafae9b8c197df0716591efd89f Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 12:18:01 -0500 Subject: [PATCH 2/7] set defaultCell inside QueryTable --- app/pages/project/disks/DisksPage.tsx | 3 +-- app/pages/system/networking/IpPoolsTab.tsx | 3 +-- app/pages/system/silos/SiloIpPoolsTab.tsx | 3 +-- app/table/QueryTable2.tsx | 2 ++ app/table/cells/DefaultCell.tsx | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/pages/project/disks/DisksPage.tsx b/app/pages/project/disks/DisksPage.tsx index 3bd98fd444..2941a3df57 100644 --- a/app/pages/project/disks/DisksPage.tsx +++ b/app/pages/project/disks/DisksPage.tsx @@ -23,7 +23,6 @@ import { DiskStatusBadge } from '~/components/StatusBadge' import { getProjectSelector, useProjectSelector, useToast } from '~/hooks' import { confirmDelete } from '~/stores/confirm-delete' import { DateCell } from '~/table/cells/DateCell' -import { defaultCell } from '~/table/cells/DefaultCell' import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell' import { SizeCell } from '~/table/cells/SizeCell' import { useColsWithActions, type MenuAction } from '~/table/columns/action-col' @@ -74,7 +73,7 @@ DisksPage.loader = async ({ params }: LoaderFunctionArgs) => { const colHelper = createColumnHelper() const staticCols = [ - colHelper.accessor('name', { cell: defaultCell }), + colHelper.accessor('name', {}), // sneaky: rather than looking at particular states, just look at // whether it has an instance field colHelper.accessor((disk) => ('instance' in disk.state ? disk.state.instance : null), { diff --git a/app/pages/system/networking/IpPoolsTab.tsx b/app/pages/system/networking/IpPoolsTab.tsx index 1a0e1db44a..75b68b1fe4 100644 --- a/app/pages/system/networking/IpPoolsTab.tsx +++ b/app/pages/system/networking/IpPoolsTab.tsx @@ -23,7 +23,6 @@ import { IpUtilCell } from '~/components/IpPoolUtilization' import { useQuickActions } from '~/hooks' import { confirmDelete } from '~/stores/confirm-delete' import { DateCell } from '~/table/cells/DateCell' -import { defaultCell } from '~/table/cells/DefaultCell' import { SkeletonCell } from '~/table/cells/EmptyCell' import { makeLinkCell } from '~/table/cells/LinkCell' import { useColsWithActions, type MenuAction } from '~/table/columns/action-col' @@ -53,7 +52,7 @@ const colHelper = createColumnHelper() const staticColumns = [ colHelper.accessor('name', { cell: makeLinkCell((pool) => pb.ipPool({ pool })) }), - colHelper.accessor('description', { cell: defaultCell }), + colHelper.accessor('description', {}), colHelper.accessor('name', { id: 'Utilization', header: 'Utilization', diff --git a/app/pages/system/silos/SiloIpPoolsTab.tsx b/app/pages/system/silos/SiloIpPoolsTab.tsx index eb81844bc0..e824651d3b 100644 --- a/app/pages/system/silos/SiloIpPoolsTab.tsx +++ b/app/pages/system/silos/SiloIpPoolsTab.tsx @@ -18,7 +18,6 @@ import { HL } from '~/components/HL' import { useForm, useSiloSelector } from '~/hooks' import { confirmAction } from '~/stores/confirm-action' import { addToast } from '~/stores/toast' -import { defaultCell } from '~/table/cells/DefaultCell' import { makeLinkCell } from '~/table/cells/LinkCell' import { useColsWithActions, type MenuAction } from '~/table/columns/action-col' import { useQueryTable2 } from '~/table/QueryTable2' @@ -51,7 +50,7 @@ const colHelper = createColumnHelper() const staticCols = [ colHelper.accessor('name', { cell: makeLinkCell((pool) => pb.ipPool({ pool })) }), - colHelper.accessor('description', { cell: defaultCell }), + colHelper.accessor('description', {}), colHelper.accessor('isDefault', { header: 'Default', cell: (props) => (props.getValue() ? : null), diff --git a/app/table/QueryTable2.tsx b/app/table/QueryTable2.tsx index 8f53be328e..4371656b51 100644 --- a/app/table/QueryTable2.tsx +++ b/app/table/QueryTable2.tsx @@ -25,6 +25,7 @@ import { usePagination } from '~/hooks/use-pagination' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { TableEmptyBox } from '~/ui/lib/Table' +import { defaultCell } from './cells/DefaultCell' import { Table } from './Table' interface UseQueryTableResult> { @@ -90,6 +91,7 @@ const makeQueryTable = >( const table = useReactTable({ columns, + defaultColumn: { cell: defaultCell }, data: tableData, getRowId, getCoreRowModel: getCoreRowModel(), diff --git a/app/table/cells/DefaultCell.tsx b/app/table/cells/DefaultCell.tsx index 366286f2ca..2d0afa948d 100644 --- a/app/table/cells/DefaultCell.tsx +++ b/app/table/cells/DefaultCell.tsx @@ -13,6 +13,6 @@ export const DefaultCell = ({ value }: Cell) => ( {value} ) -export const defaultCell = (props: CellContext) => ( +export const defaultCell = (props: CellContext) => ( {props.getValue()} ) From 9ac6b444af96c7ac27415b60fc6c65b2cd35d768 Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 12:27:07 -0500 Subject: [PATCH 3/7] we don't even need defaultCell. default cell color in table.css is already text-secondary --- app/table/QueryTable2.tsx | 2 -- app/table/cells/DefaultCell.tsx | 6 ------ 2 files changed, 8 deletions(-) diff --git a/app/table/QueryTable2.tsx b/app/table/QueryTable2.tsx index 4371656b51..8f53be328e 100644 --- a/app/table/QueryTable2.tsx +++ b/app/table/QueryTable2.tsx @@ -25,7 +25,6 @@ import { usePagination } from '~/hooks/use-pagination' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { TableEmptyBox } from '~/ui/lib/Table' -import { defaultCell } from './cells/DefaultCell' import { Table } from './Table' interface UseQueryTableResult> { @@ -91,7 +90,6 @@ const makeQueryTable = >( const table = useReactTable({ columns, - defaultColumn: { cell: defaultCell }, data: tableData, getRowId, getCoreRowModel: getCoreRowModel(), diff --git a/app/table/cells/DefaultCell.tsx b/app/table/cells/DefaultCell.tsx index 2d0afa948d..8f045bea0d 100644 --- a/app/table/cells/DefaultCell.tsx +++ b/app/table/cells/DefaultCell.tsx @@ -5,14 +5,8 @@ * * Copyright Oxide Computer Company */ -import type { CellContext } from '@tanstack/react-table' - import type { Cell } from './Cell' export const DefaultCell = ({ value }: Cell) => ( {value} ) - -export const defaultCell = (props: CellContext) => ( - {props.getValue()} -) From 51b53376b2973f9140065b2813c02de53570dcfa Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Fri, 29 Mar 2024 13:30:12 -0700 Subject: [PATCH 4/7] Convert FloatingIps page --- app/pages/project/disks/DisksPage.tsx | 11 +- .../project/floating-ips/FloatingIpsPage.tsx | 178 ++++++++++-------- app/table/cells/InstanceLinkCell.tsx | 2 +- 3 files changed, 104 insertions(+), 87 deletions(-) diff --git a/app/pages/project/disks/DisksPage.tsx b/app/pages/project/disks/DisksPage.tsx index 2941a3df57..eb796a6b61 100644 --- a/app/pages/project/disks/DisksPage.tsx +++ b/app/pages/project/disks/DisksPage.tsx @@ -76,10 +76,13 @@ const staticCols = [ colHelper.accessor('name', {}), // sneaky: rather than looking at particular states, just look at // whether it has an instance field - colHelper.accessor((disk) => ('instance' in disk.state ? disk.state.instance : null), { - header: 'Attached to', - cell: (props) => , - }), + colHelper.accessor( + (disk) => ('instance' in disk.state ? disk.state.instance : undefined), + { + header: 'Attached to', + cell: (props) => , + } + ), colHelper.accessor('size', { cell: (props) => }), colHelper.accessor('state.state', { header: 'Status', diff --git a/app/pages/project/floating-ips/FloatingIpsPage.tsx b/app/pages/project/floating-ips/FloatingIpsPage.tsx index 4696c3f28b..99a5075933 100644 --- a/app/pages/project/floating-ips/FloatingIpsPage.tsx +++ b/app/pages/project/floating-ips/FloatingIpsPage.tsx @@ -5,7 +5,8 @@ * * Copyright Oxide Computer Company */ -import { useState } from 'react' +import { createColumnHelper } from '@tanstack/react-table' +import { useCallback, useState } from 'react' import { useForm } from 'react-hook-form' import { Outlet, useNavigate, type LoaderFunctionArgs } from 'react-router-dom' @@ -26,8 +27,9 @@ import { confirmAction } from '~/stores/confirm-action' import { confirmDelete } from '~/stores/confirm-delete' import { addToast } from '~/stores/toast' import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell' -import type { MenuAction } from '~/table/columns/action-col' -import { useQueryTable } from '~/table/QueryTable' +import { makeLinkCell } from '~/table/cells/LinkCell' +import { useColsWithActions, type MenuAction } from '~/table/columns/action-col' +import { useQueryTable2 } from '~/table/QueryTable2' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { Listbox } from '~/ui/lib/Listbox' import { Message } from '~/ui/lib/Message' @@ -68,8 +70,6 @@ export function FloatingIpsPage() { query: { project }, }) const navigate = useNavigate() - const getInstanceName = (instanceId: string) => - instances.items.find((i) => i.id === instanceId)?.name const floatingIpDetach = useApiMutation('floatingIpDetach', { onSuccess() { @@ -88,76 +88,99 @@ export function FloatingIpsPage() { }, }) - const makeActions = (floatingIp: FloatingIp): MenuAction[] => { - const isAttachedToAnInstance = !!floatingIp.instanceId - const attachOrDetachAction = isAttachedToAnInstance - ? { - label: 'Detach', - onActivate: () => - confirmAction({ - actionType: 'danger', - doAction: () => - floatingIpDetach.mutateAsync({ - path: { floatingIp: floatingIp.name }, - query: { project }, - }), - modalTitle: 'Detach Floating IP', - modalContent: ( -

- Are you sure you want to detach floating IP {floatingIp.name}{' '} - from instance{' '} - - { - // instanceId is guaranteed to be non-null here - getInstanceName(floatingIp.instanceId!) - } - - ? The instance will no longer be reachable at {floatingIp.ip}. -

- ), - errorTitle: 'Error detaching floating IP', - }), - } - : { - label: 'Attach', - onActivate() { - setFloatingIpToModify(floatingIp) - }, - } - return [ - { - label: 'Edit', - onActivate: () => { - apiQueryClient.setQueryData( - 'floatingIpView', - { - path: { floatingIp: floatingIp.name }, - query: { project }, + const colHelper = createColumnHelper() + + const staticCols = [ + colHelper.accessor('name', { + cell: makeLinkCell((name) => pb.floatingIp({ floatingIp: name, project })), + }), + colHelper.accessor('description', {}), + colHelper.accessor('ip', {}), + colHelper.accessor('instanceId', { + cell: (props) => , + header: 'Attached to instance', + }), + ] + + const makeActions = useCallback( + (floatingIp: FloatingIp): MenuAction[] => { + const getInstanceName = (instanceId: string) => + instances.items.find((i) => i.id === instanceId)?.name + + const isAttachedToAnInstance = !!floatingIp.instanceId + const attachOrDetachAction = isAttachedToAnInstance + ? { + label: 'Detach', + onActivate: () => + confirmAction({ + actionType: 'danger', + doAction: () => + floatingIpDetach.mutateAsync({ + path: { floatingIp: floatingIp.name }, + query: { project }, + }), + modalTitle: 'Detach Floating IP', + modalContent: ( +

+ Are you sure you want to detach floating IP {floatingIp.name}{' '} + from instance{' '} + + { + // instanceId is guaranteed to be non-null here + getInstanceName(floatingIp.instanceId!) + } + + ? The instance will no longer be reachable at {floatingIp.ip}. +

+ ), + errorTitle: 'Error detaching floating IP', + }), + } + : { + label: 'Attach', + onActivate() { + setFloatingIpToModify(floatingIp) }, - floatingIp - ) - navigate(pb.floatingIpEdit({ project, floatingIp: floatingIp.name })) + } + return [ + { + label: 'Edit', + onActivate: () => { + apiQueryClient.setQueryData( + 'floatingIpView', + { + path: { floatingIp: floatingIp.name }, + query: { project }, + }, + floatingIp + ) + navigate(pb.floatingIpEdit({ project, floatingIp: floatingIp.name })) + }, + }, + attachOrDetachAction, + { + label: 'Delete', + disabled: isAttachedToAnInstance + ? 'This floating IP must be detached from the instance before it can be deleted' + : false, + onActivate: confirmDelete({ + doDelete: () => + deleteFloatingIp.mutateAsync({ + path: { floatingIp: floatingIp.name }, + query: { project }, + }), + label: floatingIp.name, + }), }, - }, - attachOrDetachAction, - { - label: 'Delete', - disabled: isAttachedToAnInstance - ? 'This floating IP must be detached from the instance before it can be deleted' - : false, - onActivate: confirmDelete({ - doDelete: () => - deleteFloatingIp.mutateAsync({ - path: { floatingIp: floatingIp.name }, - query: { project }, - }), - label: floatingIp.name, - }), - }, - ] - } + ] + }, + [deleteFloatingIp, floatingIpDetach, navigate, project, instances] + ) + + const { Table } = useQueryTable2('floatingIpList', { query: { project } }) + + const columns = useColsWithActions(staticCols, makeActions) - const { Table, Column } = useQueryTable('floatingIpList', { query: { project } }) return ( <> @@ -173,16 +196,7 @@ export function FloatingIpsPage() { New Floating IP - } makeActions={makeActions}> - - - - -
+ } columns={columns} /> {floatingIpToModify && ( { +export const InstanceLinkCell = ({ value: instanceId }: { value?: string }) => { const { project } = useProjectSelector() const { data: instance } = useApiQuery( 'instanceView', From 671923a4e8fc22f4a1be9755e4d6d702de162b6f Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 15:44:08 -0500 Subject: [PATCH 5/7] comment on makeLinkCell about static or memoize --- app/table/cells/LinkCell.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/table/cells/LinkCell.tsx b/app/table/cells/LinkCell.tsx index 5a7f5f15df..ffa2b0f63d 100644 --- a/app/table/cells/LinkCell.tsx +++ b/app/table/cells/LinkCell.tsx @@ -24,6 +24,12 @@ export const linkCell = return {value} } +/** + * Because this returns a component, it should only be used in a static context + * or memoized with useCallback. It should not be used unmemoized inside the + * render loop. It's probably better to inline the contents directly at the call + * site if it needs to be called inside render. + */ export const makeLinkCell = (makeHref: (value: string) => string) => (props: CellContext) => { From 68fbd3a6e8684aa2f5b2466b7aa7c3da42632a93 Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 15:54:50 -0500 Subject: [PATCH 6/7] tweak floating IP instance name logic in detach modal --- .../project/floating-ips/FloatingIpsPage.tsx | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/pages/project/floating-ips/FloatingIpsPage.tsx b/app/pages/project/floating-ips/FloatingIpsPage.tsx index 99a5075933..a3c4406427 100644 --- a/app/pages/project/floating-ips/FloatingIpsPage.tsx +++ b/app/pages/project/floating-ips/FloatingIpsPage.tsx @@ -104,8 +104,16 @@ export function FloatingIpsPage() { const makeActions = useCallback( (floatingIp: FloatingIp): MenuAction[] => { - const getInstanceName = (instanceId: string) => - instances.items.find((i) => i.id === instanceId)?.name + const instanceName = floatingIp.instanceId + ? instances.items.find((i) => i.id === floatingIp.instanceId)?.name + : undefined + // handling the rather unlikely case where the instance is not in the 1000 we fetched + const fromInstance = instanceName ? ( + <> + {' ' /* important */} + from instance {instanceName} + + ) : null const isAttachedToAnInstance = !!floatingIp.instanceId const attachOrDetachAction = isAttachedToAnInstance @@ -120,17 +128,12 @@ export function FloatingIpsPage() { query: { project }, }), modalTitle: 'Detach Floating IP', + // instanceName! non-null because we only see this if there is an instance modalContent: (

- Are you sure you want to detach floating IP {floatingIp.name}{' '} - from instance{' '} - - { - // instanceId is guaranteed to be non-null here - getInstanceName(floatingIp.instanceId!) - } - - ? The instance will no longer be reachable at {floatingIp.ip}. + Are you sure you want to detach floating IP {floatingIp.name} + {fromInstance}? The instance will no longer be reachable at{' '} + {floatingIp.ip}.

), errorTitle: 'Error detaching floating IP', From 0f4836a9655d767c037af81f664e77499625d471 Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 29 Mar 2024 15:57:10 -0500 Subject: [PATCH 7/7] rename value prop of InstanceLinkCell to instanceId --- app/pages/project/disks/DisksPage.tsx | 2 +- app/pages/project/floating-ips/FloatingIpsPage.tsx | 2 +- app/table/cells/InstanceLinkCell.tsx | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/pages/project/disks/DisksPage.tsx b/app/pages/project/disks/DisksPage.tsx index eb796a6b61..075a7809ca 100644 --- a/app/pages/project/disks/DisksPage.tsx +++ b/app/pages/project/disks/DisksPage.tsx @@ -80,7 +80,7 @@ const staticCols = [ (disk) => ('instance' in disk.state ? disk.state.instance : undefined), { header: 'Attached to', - cell: (props) => , + cell: (props) => , } ), colHelper.accessor('size', { cell: (props) => }), diff --git a/app/pages/project/floating-ips/FloatingIpsPage.tsx b/app/pages/project/floating-ips/FloatingIpsPage.tsx index a3c4406427..f148cc82cc 100644 --- a/app/pages/project/floating-ips/FloatingIpsPage.tsx +++ b/app/pages/project/floating-ips/FloatingIpsPage.tsx @@ -97,7 +97,7 @@ export function FloatingIpsPage() { colHelper.accessor('description', {}), colHelper.accessor('ip', {}), colHelper.accessor('instanceId', { - cell: (props) => , + cell: (props) => , header: 'Attached to instance', }), ] diff --git a/app/table/cells/InstanceLinkCell.tsx b/app/table/cells/InstanceLinkCell.tsx index 9a4ffe8087..ed144b8629 100644 --- a/app/table/cells/InstanceLinkCell.tsx +++ b/app/table/cells/InstanceLinkCell.tsx @@ -14,7 +14,7 @@ import { pb } from '~/util/path-builder' import { SkeletonCell } from './EmptyCell' import { LinkCell } from './LinkCell' -export const InstanceLinkCell = ({ value: instanceId }: { value?: string }) => { +export const InstanceLinkCell = ({ instanceId }: { instanceId?: string }) => { const { project } = useProjectSelector() const { data: instance } = useApiQuery( 'instanceView', @@ -22,6 +22,7 @@ export const InstanceLinkCell = ({ value: instanceId }: { value?: string }) => { { enabled: !!instanceId } ) + // has to be after the hooks because hooks can't be executed conditionally if (!instanceId) return null if (!instance) return