Skip to content

Commit

Permalink
refactor: Convert all remaining tables to new useQueryTable (#2575)
Browse files Browse the repository at this point in the history
* redo useQueryTable so that the types are legit and it returns the data

* fix junky getRowId and use new QueryTable on sleds and physical disks

* get wild with a list-specific helper to make the call sites clean

* encapsulate pageSize in the query config so it is defined in one place

* do the placeholderData thing for all lists

* scroll to top when page changes

* loading spinner on page changes!

* fix the pagination test, test lovely new scroll reset logic

* fix other e2es, don't scroll reset on browser forward/back

* fix bug found while converting other tables, extract useScrollReset

* move columns up

* convert instance list to new QueryTable, fix polling bug

* convert the rest of the tables

* convert a few more

* a hard one: IpPoolPage, have to handle rows with no ID field

* last few easy ones

* last ones and delete QueryTable

* rename file back to QueryTable
  • Loading branch information
david-crespo authored Nov 23, 2024
1 parent 3e40ff7 commit 175d75b
Show file tree
Hide file tree
Showing 26 changed files with 394 additions and 414 deletions.
7 changes: 6 additions & 1 deletion app/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import {
wrapQueryClient,
} from './hooks'

export { ensurePrefetched, PAGE_SIZE, type PaginatedQuery } from './hooks'
export {
ensurePrefetched,
usePrefetchedQuery,
PAGE_SIZE,
type PaginatedQuery,
} from './hooks'

export const api = new Api({
// unit tests run in Node, whose fetch implementation requires a full URL
Expand Down
3 changes: 3 additions & 0 deletions app/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ export function ensurePrefetched<TData, TError>(
return result as SetNonNullable<typeof result, 'data'>
}

export const usePrefetchedQuery = <TData>(options: UseQueryOptions<TData, ApiError>) =>
ensurePrefetched(useQuery(options), options.queryKey)

const ERRORS_ALLOWED = 'errors-allowed'

/** Result that includes both success and error so it can be cached by RQ */
Expand Down
28 changes: 14 additions & 14 deletions app/pages/ProjectsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { Outlet, useNavigate } from 'react-router-dom'

import {
apiQueryClient,
getListQFn,
queryClient,
useApiMutation,
useApiQueryClient,
usePrefetchedApiQuery,
type Project,
} from '@oxide/api'
import { Folder16Icon, Folder24Icon } from '@oxide/design-system/icons/react'
Expand All @@ -24,7 +24,7 @@ import { confirmDelete } from '~/stores/confirm-delete'
import { makeLinkCell } from '~/table/cells/LinkCell'
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable'
import { useQueryTable } from '~/table/QueryTable'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
Expand All @@ -42,8 +42,10 @@ const EmptyState = () => (
/>
)

const projectList = getListQFn('projectList', {})

export async function loader() {
await apiQueryClient.prefetchQuery('projectList', { query: { limit: PAGE_SIZE } })
await queryClient.prefetchQuery(projectList.optionsFn())
return null
}

Expand All @@ -60,17 +62,11 @@ Component.displayName = 'ProjectsPage'
export function Component() {
const navigate = useNavigate()

const queryClient = useApiQueryClient()
const { Table } = useQueryTable('projectList', {})
const { data: projects } = usePrefetchedApiQuery('projectList', {
query: { limit: PAGE_SIZE },
})

const { mutateAsync: deleteProject } = useApiMutation('projectDelete', {
onSuccess() {
// TODO: figure out if this is invalidating as expected, can we leave out the query
// altogether, etc. Look at whether limit param matters.
queryClient.invalidateQueries('projectList')
apiQueryClient.invalidateQueries('projectList')
},
})

Expand Down Expand Up @@ -100,6 +96,12 @@ export function Component() {
[deleteProject, navigate]
)

const columns = useColsWithActions(staticCols, makeActions)
const {
table,
query: { data: projects },
} = useQueryTable({ query: projectList, columns, emptyState: <EmptyState /> })

useQuickActions(
useMemo(
() => [
Expand All @@ -117,8 +119,6 @@ export function Component() {
)
)

const columns = useColsWithActions(staticCols, makeActions)

return (
<>
<PageHeader>
Expand All @@ -133,7 +133,7 @@ export function Component() {
<TableActions>
<CreateLink to={pb.projectsNew()}>New Project</CreateLink>
</TableActions>
<Table columns={columns} emptyState={<EmptyState />} />
{table}
<Outlet />
</>
)
Expand Down
18 changes: 12 additions & 6 deletions app/pages/project/disks/DisksPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
apiQueryClient,
diskCan,
genName,
getListQFn,
queryClient,
useApiMutation,
useApiQueryClient,
type Disk,
Expand All @@ -28,7 +30,7 @@ import { addToast } from '~/stores/toast'
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 { useQueryTable } from '~/table/QueryTable'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
Expand All @@ -48,12 +50,12 @@ const EmptyState = () => (
/>
)

const diskList = (project: string) => getListQFn('diskList', { query: { project } })

DisksPage.loader = async ({ params }: LoaderFunctionArgs) => {
const { project } = getProjectSelector(params)
await Promise.all([
apiQueryClient.prefetchQuery('diskList', {
query: { project, limit: PAGE_SIZE },
}),
queryClient.prefetchQuery(diskList(project).optionsFn()),

// fetch instances and preload into RQ cache so fetches by ID in
// InstanceLinkCell can be mostly instant yet gracefully fall back to
Expand Down Expand Up @@ -97,7 +99,6 @@ const staticCols = [
export function DisksPage() {
const queryClient = useApiQueryClient()
const { project } = useProjectSelector()
const { Table } = useQueryTable('diskList', { query: { project } })

const { mutateAsync: deleteDisk } = useApiMutation('diskDelete', {
onSuccess(_data, variables) {
Expand Down Expand Up @@ -160,6 +161,11 @@ export function DisksPage() {
)

const columns = useColsWithActions(staticCols, makeActions)
const { table } = useQueryTable({
query: diskList(project),
columns,
emptyState: <EmptyState />,
})

return (
<>
Expand All @@ -175,7 +181,7 @@ export function DisksPage() {
<TableActions>
<CreateLink to={pb.disksNew({ project })}>New Disk</CreateLink>
</TableActions>
<Table columns={columns} emptyState={<EmptyState />} />
{table}
<Outlet />
</>
)
Expand Down
31 changes: 17 additions & 14 deletions app/pages/project/floating-ips/FloatingIpsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import { Outlet, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'

import {
apiQueryClient,
getListQFn,
queryClient,
useApiMutation,
useApiQueryClient,
usePrefetchedApiQuery,
usePrefetchedQuery,
type FloatingIp,
type Instance,
} from '@oxide/api'
Expand All @@ -31,7 +33,7 @@ import { InstanceLinkCell } from '~/table/cells/InstanceLinkCell'
import { IpPoolCell } from '~/table/cells/IpPoolCell'
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable'
import { useQueryTable } from '~/table/QueryTable'
import { CopyableIp } from '~/ui/lib/CopyableIp'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
Expand All @@ -53,15 +55,15 @@ const EmptyState = () => (
/>
)

const fipList = (project: string) => getListQFn('floatingIpList', { query: { project } })
const instanceList = (project: string) =>
getListQFn('instanceList', { query: { project, limit: ALL_ISH } })

FloatingIpsPage.loader = async ({ params }: LoaderFunctionArgs) => {
const { project } = getProjectSelector(params)
await Promise.all([
apiQueryClient.prefetchQuery('floatingIpList', {
query: { project, limit: PAGE_SIZE },
}),
apiQueryClient.prefetchQuery('instanceList', {
query: { project },
}),
queryClient.prefetchQuery(fipList(project).optionsFn()),
queryClient.prefetchQuery(instanceList(project).optionsFn()),
// fetch IP Pools and preload into RQ cache so fetches by ID in
// IpPoolCell can be mostly instant yet gracefully fall back to
// fetching individually if we don't fetch them all here
Expand Down Expand Up @@ -102,9 +104,7 @@ export function FloatingIpsPage() {
const [floatingIpToModify, setFloatingIpToModify] = useState<FloatingIp | null>(null)
const queryClient = useApiQueryClient()
const { project } = useProjectSelector()
const { data: instances } = usePrefetchedApiQuery('instanceList', {
query: { project },
})
const { data: instances } = usePrefetchedQuery(instanceList(project).optionsFn())
const navigate = useNavigate()

const { mutateAsync: floatingIpDetach } = useApiMutation('floatingIpDetach', {
Expand Down Expand Up @@ -202,9 +202,12 @@ export function FloatingIpsPage() {
[deleteFloatingIp, floatingIpDetach, navigate, project, instances]
)

const { Table } = useQueryTable('floatingIpList', { query: { project } })

const columns = useColsWithActions(staticCols, makeActions)
const { table } = useQueryTable({
query: fipList(project),
columns,
emptyState: <EmptyState />,
})

return (
<>
Expand All @@ -220,7 +223,7 @@ export function FloatingIpsPage() {
<TableActions>
<CreateLink to={pb.floatingIpsNew({ project })}>New Floating IP</CreateLink>
</TableActions>
<Table columns={columns} emptyState={<EmptyState />} />
{table}
<Outlet />
{floatingIpToModify && (
<AttachFloatingIpModal
Expand Down
29 changes: 20 additions & 9 deletions app/pages/project/images/ImagesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ import { createColumnHelper } from '@tanstack/react-table'
import { useCallback, useMemo, useState } from 'react'
import { Outlet, type LoaderFunctionArgs } from 'react-router-dom'

import { apiQueryClient, useApiMutation, useApiQueryClient, type Image } from '@oxide/api'
import {
apiQueryClient,
getListQFn,
queryClient,
useApiMutation,
useApiQueryClient,
type Image,
} from '@oxide/api'
import { Images16Icon, Images24Icon } from '@oxide/design-system/icons/react'

import { DocsPopover } from '~/components/DocsPopover'
Expand All @@ -20,7 +27,7 @@ import { addToast } from '~/stores/toast'
import { makeLinkCell } from '~/table/cells/LinkCell'
import { getActionsCol, type MenuAction } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable'
import { useQueryTable } from '~/table/QueryTable'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
import { Message } from '~/ui/lib/Message'
Expand All @@ -42,25 +49,23 @@ const EmptyState = () => (

const colHelper = createColumnHelper<Image>()

const imageList = (project: string) => getListQFn('imageList', { query: { project } })

ImagesPage.loader = async ({ params }: LoaderFunctionArgs) => {
const { project } = getProjectSelector(params)
await apiQueryClient.prefetchQuery('imageList', {
query: { project, limit: PAGE_SIZE },
})
await queryClient.prefetchQuery(imageList(project).optionsFn())
return null
}

export function ImagesPage() {
const { project } = useProjectSelector()
const { Table } = useQueryTable('imageList', { query: { project } })
const queryClient = useApiQueryClient()

const [promoteImageName, setPromoteImageName] = useState<string | null>(null)

const { mutateAsync: deleteImage } = useApiMutation('imageDelete', {
onSuccess(_data, variables) {
addToast(<>Image <HL>{variables.path.image}</HL> deleted</>) // prettier-ignore
queryClient.invalidateQueries('imageList')
apiQueryClient.invalidateQueries('imageList')
},
})

Expand Down Expand Up @@ -97,6 +102,12 @@ export function ImagesPage() {
]
}, [project, makeActions])

const { table } = useQueryTable({
query: imageList(project),
columns,
emptyState: <EmptyState />,
})

return (
<>
<PageHeader>
Expand All @@ -111,7 +122,7 @@ export function ImagesPage() {
<TableActions>
<CreateLink to={pb.projectImagesNew({ project })}>Upload image</CreateLink>
</TableActions>
<Table columns={columns} emptyState={<EmptyState />} />
{table}
{promoteImageName && (
<PromoteImageModal
onDismiss={() => setPromoteImageName(null)}
Expand Down
2 changes: 1 addition & 1 deletion app/pages/project/instances/InstancesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { InstanceStateCell } from '~/table/cells/InstanceStateCell'
import { makeLinkCell } from '~/table/cells/LinkCell'
import { getActionsCol } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { useQueryTable } from '~/table/QueryTable2'
import { useQueryTable } from '~/table/QueryTable'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
Expand Down
2 changes: 1 addition & 1 deletion app/pages/project/snapshots/SnapshotsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { confirmDelete } from '~/stores/confirm-delete'
import { SkeletonCell } from '~/table/cells/EmptyCell'
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
import { Columns } from '~/table/columns/common'
import { useQueryTable } from '~/table/QueryTable2'
import { useQueryTable } from '~/table/QueryTable'
import { Badge } from '~/ui/lib/Badge'
import { CreateLink } from '~/ui/lib/CreateButton'
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
Expand Down
Loading

0 comments on commit 175d75b

Please sign in to comment.