diff --git a/app/pages/project/floating-ips/FloatingIpsPage.tsx b/app/pages/project/floating-ips/FloatingIpsPage.tsx
index 9f7b14a456..685c461ab0 100644
--- a/app/pages/project/floating-ips/FloatingIpsPage.tsx
+++ b/app/pages/project/floating-ips/FloatingIpsPage.tsx
@@ -13,6 +13,7 @@ import { Outlet, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
import {
apiQueryClient,
useApiMutation,
+ useApiQuery,
useApiQueryClient,
usePrefetchedApiQuery,
type FloatingIp,
@@ -26,10 +27,12 @@ import { getProjectSelector, useProjectSelector } from '~/hooks'
import { confirmAction } from '~/stores/confirm-action'
import { confirmDelete } from '~/stores/confirm-delete'
import { addToast } from '~/stores/toast'
+import { EmptyCell } from '~/table/cells/EmptyCell'
import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell'
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable'
+import { Badge } from '~/ui/lib/Badge'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
import { Listbox } from '~/ui/lib/Listbox'
@@ -37,6 +40,7 @@ import { Message } from '~/ui/lib/Message'
import { Modal } from '~/ui/lib/Modal'
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
import { TableActions } from '~/ui/lib/Table'
+import { Tooltip } from '~/ui/lib/Tooltip'
import { docLinks } from '~/util/links'
import { pb } from '~/util/path-builder'
@@ -59,15 +63,45 @@ FloatingIpsPage.loader = async ({ params }: LoaderFunctionArgs) => {
apiQueryClient.prefetchQuery('instanceList', {
query: { project },
}),
+ apiQueryClient
+ .fetchQuery('projectIpPoolList', { query: { limit: 1000 } })
+ .then((pools) => {
+ for (const pool of pools.items) {
+ apiQueryClient.setQueryData(
+ 'projectIpPoolView',
+ { path: { pool: pool.id } },
+ pool
+ )
+ }
+ }),
])
return null
}
+const IpPoolCell = ({ ipPoolId }: { ipPoolId: string }) => {
+ const pool = useApiQuery('projectIpPoolView', { path: { pool: ipPoolId } }).data
+ if (!pool) return
+ const badge = {pool.name}
+ return pool.description ? (
+
+ {badge}
+
+ ) : (
+ badge
+ )
+}
+
const colHelper = createColumnHelper()
const staticCols = [
colHelper.accessor('name', {}),
colHelper.accessor('description', Columns.description),
- colHelper.accessor('ip', {}),
+ colHelper.accessor('ip', {
+ header: 'IP address',
+ }),
+ colHelper.accessor('ipPoolId', {
+ cell: (info) => ,
+ header: 'IP pool',
+ }),
colHelper.accessor('instanceId', {
cell: (info) => ,
header: 'Attached to instance',
diff --git a/app/ui/lib/Tooltip.tsx b/app/ui/lib/Tooltip.tsx
index 101f34b237..ece688b0c7 100644
--- a/app/ui/lib/Tooltip.tsx
+++ b/app/ui/lib/Tooltip.tsx
@@ -36,7 +36,8 @@ import { usePopoverZIndex } from './SideModal'
export interface TooltipProps {
delay?: number
- children?: React.ReactNode
+ /** The target the tooltip hovers near; can not be a raw string. */
+ children?: ReactElement
/** The text to appear on hover/focus */
content: string | React.ReactNode
/**
diff --git a/mock-api/ip-pool.ts b/mock-api/ip-pool.ts
index 0296782ae9..534125ff80 100644
--- a/mock-api/ip-pool.ts
+++ b/mock-api/ip-pool.ts
@@ -14,7 +14,7 @@ import { defaultSilo } from './silo'
export const ipPool1: Json = {
id: '69b5c583-74a9-451a-823d-0741c1ec66e2',
name: 'ip-pool-1',
- description: '',
+ description: 'public IPs',
time_created: new Date().toISOString(),
time_modified: new Date().toISOString(),
}
@@ -22,7 +22,7 @@ export const ipPool1: Json = {
const ipPool2: Json = {
id: 'af2fbe06-b21d-4364-96b7-a58220bc3242',
name: 'ip-pool-2',
- description: '',
+ description: 'VPN IPs',
time_created: new Date().toISOString(),
time_modified: new Date().toISOString(),
}
diff --git a/test/e2e/floating-ip-create.e2e.ts b/test/e2e/floating-ip-create.e2e.ts
index d8dd2c9ea7..0f847df0ad 100644
--- a/test/e2e/floating-ip-create.e2e.ts
+++ b/test/e2e/floating-ip-create.e2e.ts
@@ -49,6 +49,7 @@ test('can create a floating IP', async ({ page }) => {
await expectRowVisible(page.getByRole('table'), {
name: floatingIpName,
description: 'A description for this Floating IP',
+ 'IP pool': 'ip-pool-1',
})
})
@@ -62,7 +63,7 @@ test('can detach and attach a floating IP', async ({ page }) => {
await expectRowVisible(page.getByRole('table'), {
name: 'cola-float',
- ip: '123.4.56.5',
+ 'IP address': '123.4.56.5',
'Attached to instance': 'db1',
})
await clickRowAction(page, 'cola-float', 'Detach')
@@ -91,7 +92,7 @@ test('can detach and attach a floating IP', async ({ page }) => {
await expect(page.getByRole('dialog')).toBeHidden()
await expectRowVisible(page.getByRole('table'), {
name: 'cola-float',
- ip: '123.4.56.5',
+ 'IP address': '123.4.56.5',
'Attached to instance': 'db1',
})
})