Skip to content

Commit fed9f4d

Browse files
authored
Refresh button on instance detail page (#2159)
* refresh button invalidates all queries, add it to instance detail * use refreshData on onSuccess on actions too
1 parent aa75f57 commit fed9f4d

File tree

4 files changed

+42
-18
lines changed

4 files changed

+42
-18
lines changed

app/api/hooks.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,11 @@ export const wrapQueryClient = <A extends ApiClient>(api: A, queryClient: QueryC
239239
* accidentally overspecifying and therefore failing to match the desired
240240
* query. The params argument can be added back in if we ever have a use case
241241
* for it.
242+
*
243+
* Passing no arguments will invalidate all queries.
242244
*/
243-
invalidateQueries: <M extends keyof A>(method: M, filters?: InvalidateQueryFilters) =>
244-
queryClient.invalidateQueries({ queryKey: [method], ...filters }),
245+
invalidateQueries: <M extends keyof A>(method?: M, filters?: InvalidateQueryFilters) =>
246+
queryClient.invalidateQueries(method ? { queryKey: [method], ...filters } : undefined),
245247
setQueryData: <M extends keyof A>(method: M, params: Params<A[M]>, data: Result<A[M]>) =>
246248
queryClient.setQueryData([method, params], data),
247249
setQueryDataErrorsAllowed: <M extends keyof A>(

app/components/RefreshButton.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
9+
import { Refresh16Icon } from '@oxide/design-system/icons/react'
10+
11+
import { Button } from '~/ui/lib/Button'
12+
13+
export function RefreshButton({ onClick }: { onClick: () => void }) {
14+
return (
15+
<Button size="icon" variant="ghost" onClick={onClick} aria-label="Refresh data">
16+
<Refresh16Icon />
17+
</Button>
18+
)
19+
}

app/pages/project/instances/InstancesPage.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ import {
1515
usePrefetchedApiQuery,
1616
type Instance,
1717
} from '@oxide/api'
18-
import { Instances24Icon, Refresh16Icon } from '@oxide/design-system/icons/react'
18+
import { Instances24Icon } from '@oxide/design-system/icons/react'
1919

20+
import { RefreshButton } from '~/components/RefreshButton'
2021
import { getProjectSelector, useProjectSelector, useQuickActions } from '~/hooks'
2122
import { InstanceResourceCell } from '~/table/cells/InstanceResourceCell'
2223
import { InstanceStatusCell } from '~/table/cells/InstanceStatusCell'
2324
import { makeLinkCell } from '~/table/cells/LinkCell'
2425
import { getActionsCol } from '~/table/columns/action-col'
2526
import { Columns } from '~/table/columns/common'
2627
import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable'
27-
import { Button, buttonStyle } from '~/ui/lib/Button'
28+
import { buttonStyle } from '~/ui/lib/Button'
2829
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
2930
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
3031
import { TableActions } from '~/ui/lib/Table'
@@ -122,14 +123,7 @@ export function InstancesPage() {
122123
<PageTitle icon={<Instances24Icon />}>Instances</PageTitle>
123124
</PageHeader>
124125
<TableActions>
125-
<Button
126-
size="icon"
127-
variant="ghost"
128-
onClick={refetchInstances}
129-
aria-label="Refresh instances table"
130-
>
131-
<Refresh16Icon />
132-
</Button>
126+
<RefreshButton onClick={() => apiQueryClient.invalidateQueries('instanceList')} />
133127
<Link to={pb.instancesNew({ project })} className={buttonStyle({ size: 'sm' })}>
134128
New Instance
135129
</Link>

app/pages/project/instances/instance/InstancePage.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import { Link, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
1212
import {
1313
apiQueryClient,
1414
useApiQuery,
15-
useApiQueryClient,
1615
usePrefetchedApiQuery,
1716
type InstanceNetworkInterface,
1817
} from '@oxide/api'
1918
import { Instances24Icon } from '@oxide/design-system/icons/react'
2019

2120
import { ExternalIps } from '~/components/ExternalIps'
2221
import { MoreActionsMenu } from '~/components/MoreActionsMenu'
22+
import { RefreshButton } from '~/components/RefreshButton'
2323
import { RouteTabs, Tab } from '~/components/RouteTabs'
2424
import { InstanceStatusBadge } from '~/components/StatusBadge'
2525
import { getInstanceSelector, useInstanceSelector, useQuickActions } from '~/hooks'
@@ -37,6 +37,15 @@ function getPrimaryVpcId(nics: InstanceNetworkInterface[]) {
3737
return nic ? nic.vpcId : undefined
3838
}
3939

40+
// this is meant to cover everything that we fetch in the page
41+
function refreshData() {
42+
apiQueryClient.invalidateQueries('instanceView')
43+
apiQueryClient.invalidateQueries('instanceExternalIpList')
44+
apiQueryClient.invalidateQueries('instanceNetworkInterfaceList')
45+
apiQueryClient.invalidateQueries('instanceDiskList') // storage tab
46+
apiQueryClient.invalidateQueries('diskMetricsList') // metrics tab
47+
}
48+
4049
InstancePage.loader = async ({ params }: LoaderFunctionArgs) => {
4150
const { project, instance } = getInstanceSelector(params)
4251
await Promise.all([
@@ -75,11 +84,8 @@ export function InstancePage() {
7584
const instanceSelector = useInstanceSelector()
7685

7786
const navigate = useNavigate()
78-
const queryClient = useApiQueryClient()
7987
const makeActions = useMakeInstanceActions(instanceSelector, {
80-
onSuccess: () => {
81-
queryClient.invalidateQueries('instanceView')
82-
},
88+
onSuccess: refreshData,
8389
// go to project instances list since there's no more instance
8490
onDelete: () => navigate(pb.instances(instanceSelector)),
8591
})
@@ -137,7 +143,10 @@ export function InstancePage() {
137143
<>
138144
<PageHeader>
139145
<PageTitle icon={<Instances24Icon />}>{instance.name}</PageTitle>
140-
<MoreActionsMenu label="Instance actions" actions={actions} />
146+
<div className="inline-flex gap-2">
147+
<RefreshButton onClick={refreshData} />
148+
<MoreActionsMenu label="Instance actions" actions={actions} />
149+
</div>
141150
</PageHeader>
142151
<PropertiesTable.Group className="-mt-8 mb-16">
143152
<PropertiesTable>

0 commit comments

Comments
 (0)