Skip to content

Commit 59e6976

Browse files
Delete affinity groups / remove affinity group members (#2768)
* Enable removal of group member * Enable deletion of group * move away from useMemo for columns * Update copy in remove confirm modal * Update copy in delete modal * use apiq since we're not paginating * use the id for delete * merged main and reconciling diffs --------- Co-authored-by: David Crespo <[email protected]>
1 parent 4415745 commit 59e6976

File tree

3 files changed

+117
-21
lines changed

3 files changed

+117
-21
lines changed

app/pages/project/affinity/AffinityPage.tsx

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,26 @@
88

99
import { useQuery } from '@tanstack/react-query'
1010
import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table'
11-
import { useMemo } from 'react'
11+
import { useCallback } from 'react'
1212
import { Link, type LoaderFunctionArgs } from 'react-router'
1313

1414
import {
1515
apiq,
16-
getListQFn,
1716
queryClient,
17+
useApiMutation,
1818
usePrefetchedQuery,
1919
type AffinityPolicy,
2020
type AntiAffinityGroup,
2121
} from '@oxide/api'
2222
import { Affinity24Icon } from '@oxide/design-system/icons/react'
2323

24+
import { HL } from '~/components/HL'
2425
import { getProjectSelector, useProjectSelector } from '~/hooks/use-params'
26+
import { confirmAction } from '~/stores/confirm-action'
27+
import { addToast } from '~/stores/toast'
2528
import { EmptyCell, SkeletonCell } from '~/table/cells/EmptyCell'
2629
import { makeLinkCell } from '~/table/cells/LinkCell'
30+
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
2731
import { Columns } from '~/table/columns/common'
2832
import { Table } from '~/table/Table'
2933
import { Badge } from '~/ui/lib/Badge'
@@ -37,7 +41,7 @@ import { pb } from '~/util/path-builder'
3741
import type * as PP from '~/util/path-params'
3842

3943
const antiAffinityGroupList = ({ project }: PP.Project) =>
40-
getListQFn('antiAffinityGroupList', { query: { project, limit: ALL_ISH } })
44+
apiq('antiAffinityGroupList', { query: { project, limit: ALL_ISH } })
4145
const memberList = ({ antiAffinityGroup, project }: PP.AntiAffinityGroup) =>
4246
apiq('antiAffinityGroupMemberList', {
4347
path: { antiAffinityGroup },
@@ -47,9 +51,7 @@ const memberList = ({ antiAffinityGroup, project }: PP.AntiAffinityGroup) =>
4751

4852
export async function clientLoader({ params }: LoaderFunctionArgs) {
4953
const { project } = getProjectSelector(params)
50-
const groups = await queryClient.fetchQuery(
51-
antiAffinityGroupList({ project }).optionsFn()
52-
)
54+
const groups = await queryClient.fetchQuery(antiAffinityGroupList({ project }))
5355
const memberFetches = groups.items.map(({ name }) =>
5456
queryClient.prefetchQuery(memberList({ antiAffinityGroup: name, project }))
5557
)
@@ -98,8 +100,48 @@ const staticCols = [
98100

99101
export default function AffinityPage() {
100102
const { project } = useProjectSelector()
101-
const columns = useMemo(
102-
() => [
103+
const { data } = usePrefetchedQuery(antiAffinityGroupList({ project }))
104+
105+
const { mutateAsync: deleteGroup } = useApiMutation('antiAffinityGroupDelete', {
106+
onSuccess(_data, variables) {
107+
queryClient.invalidateEndpoint('antiAffinityGroupList')
108+
addToast(
109+
<>
110+
Anti-affinity group <HL>{variables.path.antiAffinityGroup}</HL> deleted
111+
</>
112+
)
113+
},
114+
})
115+
116+
const makeActions = useCallback(
117+
(antiAffinityGroup: AntiAffinityGroup): MenuAction[] => [
118+
{
119+
label: 'Delete',
120+
onActivate() {
121+
confirmAction({
122+
actionType: 'danger',
123+
doAction: () =>
124+
deleteGroup({
125+
path: { antiAffinityGroup: antiAffinityGroup.name },
126+
query: { project },
127+
}),
128+
modalTitle: 'Delete anti-affinity group',
129+
modalContent: (
130+
<p>
131+
Are you sure you want to delete the anti-affinity group{' '}
132+
<HL>{antiAffinityGroup.name}</HL>?
133+
</p>
134+
),
135+
errorTitle: `Error removing ${antiAffinityGroup.name}`,
136+
})
137+
},
138+
},
139+
],
140+
[project, deleteGroup]
141+
)
142+
143+
const columns = useColsWithActions(
144+
[
103145
colHelper.accessor('name', {
104146
cell: makeLinkCell((antiAffinityGroup) =>
105147
pb.antiAffinityGroup({ project, antiAffinityGroup })
@@ -108,9 +150,8 @@ export default function AffinityPage() {
108150
}),
109151
...staticCols,
110152
],
111-
[project]
153+
makeActions
112154
)
113-
const { data } = usePrefetchedQuery(antiAffinityGroupList({ project }).optionsFn())
114155

115156
const table = useReactTable({
116157
columns,

app/pages/project/affinity/AntiAffinityGroupPage.tsx

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,28 @@
77
*/
88

99
import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table'
10-
import { useMemo } from 'react'
10+
import { useCallback } from 'react'
1111
import type { LoaderFunctionArgs } from 'react-router'
1212

1313
import { Affinity24Icon } from '@oxide/design-system/icons/react'
1414

1515
import {
1616
apiq,
17-
getListQFn,
1817
queryClient,
18+
useApiMutation,
1919
usePrefetchedQuery,
2020
type AntiAffinityGroupMember,
2121
} from '~/api'
22+
import { HL } from '~/components/HL'
2223
import { makeCrumb } from '~/hooks/use-crumbs'
2324
import {
2425
getAntiAffinityGroupSelector,
2526
useAntiAffinityGroupSelector,
2627
} from '~/hooks/use-params'
28+
import { confirmAction } from '~/stores/confirm-action'
29+
import { addToast } from '~/stores/toast'
2730
import { makeLinkCell } from '~/table/cells/LinkCell'
31+
import { useColsWithActions, type MenuAction } from '~/table/columns/action-col'
2832
import { Columns } from '~/table/columns/common'
2933
import { Table } from '~/table/Table'
3034
import { Badge } from '~/ui/lib/Badge'
@@ -49,7 +53,7 @@ const colHelper = createColumnHelper<AntiAffinityGroupMember>()
4953
const antiAffinityGroupView = ({ antiAffinityGroup, project }: PP.AntiAffinityGroup) =>
5054
apiq('antiAffinityGroupView', { path: { antiAffinityGroup }, query: { project } })
5155
const memberList = ({ antiAffinityGroup, project }: PP.AntiAffinityGroup) =>
52-
getListQFn('antiAffinityGroupMemberList', {
56+
apiq('antiAffinityGroupMemberList', {
5357
path: { antiAffinityGroup },
5458
// member limit in DB is currently 32, so pagination isn't needed
5559
query: { project, limit: ALL_ISH },
@@ -59,7 +63,7 @@ export async function clientLoader({ params }: LoaderFunctionArgs) {
5963
const { antiAffinityGroup, project } = getAntiAffinityGroupSelector(params)
6064
await Promise.all([
6165
queryClient.fetchQuery(antiAffinityGroupView({ antiAffinityGroup, project })),
62-
queryClient.fetchQuery(memberList({ antiAffinityGroup, project }).optionsFn()),
66+
queryClient.fetchQuery(memberList({ antiAffinityGroup, project })),
6367
])
6468
return null
6569
}
@@ -80,19 +84,66 @@ export default function AntiAffinityPage() {
8084
antiAffinityGroupView({ antiAffinityGroup, project })
8185
)
8286
const { id, name, description, policy, timeCreated } = group
83-
const { data: members } = usePrefetchedQuery(
84-
memberList({ antiAffinityGroup, project }).optionsFn()
85-
)
87+
const { data: members } = usePrefetchedQuery(memberList({ antiAffinityGroup, project }))
8688
const membersCount = members.items.length
87-
const columns = useMemo(
88-
() => [
89+
90+
const { mutateAsync: removeMember } = useApiMutation(
91+
'antiAffinityGroupMemberInstanceDelete',
92+
{
93+
onSuccess(_data, variables) {
94+
queryClient.invalidateEndpoint('antiAffinityGroupMemberList')
95+
queryClient.invalidateEndpoint('antiAffinityGroupView')
96+
addToast(<>Member <HL>{variables.path.instance}</HL> removed from anti-affinity group <HL>{group.name}</HL></>) // prettier-ignore
97+
},
98+
}
99+
)
100+
101+
const makeActions = useCallback(
102+
(antiAffinityGroupMember: AntiAffinityGroupMember): MenuAction[] => [
103+
{
104+
label: 'Copy instance ID',
105+
onActivate() {
106+
navigator.clipboard.writeText(antiAffinityGroupMember.value.id)
107+
},
108+
},
109+
{
110+
label: 'Remove from group',
111+
onActivate() {
112+
confirmAction({
113+
actionType: 'danger',
114+
doAction: () =>
115+
removeMember({
116+
path: {
117+
antiAffinityGroup: antiAffinityGroup,
118+
instance: antiAffinityGroupMember.value.name,
119+
},
120+
query: { project },
121+
}),
122+
modalTitle: 'Remove instance from anti-affinity group',
123+
modalContent: (
124+
<p>
125+
Are you sure you want to remove{' '}
126+
<HL>{antiAffinityGroupMember.value.name}</HL> from the anti-affinity group{' '}
127+
<HL>{antiAffinityGroup}</HL>?
128+
</p>
129+
),
130+
errorTitle: `Error removing ${antiAffinityGroupMember.value.name}`,
131+
})
132+
},
133+
},
134+
],
135+
[project, removeMember, antiAffinityGroup]
136+
)
137+
138+
const columns = useColsWithActions(
139+
[
89140
colHelper.accessor('value.name', {
90141
header: 'Name',
91142
cell: makeLinkCell((instance) => pb.instance({ project, instance })),
92143
}),
93144
colHelper.accessor('value.runState', Columns.instanceState),
94145
],
95-
[project]
146+
makeActions
96147
)
97148

98149
const table = useReactTable({

mock-api/msw/handlers.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,11 @@ export const handlers = makeHandlers({
16371637
},
16381638
antiAffinityGroupView: ({ path, query }) =>
16391639
lookup.antiAffinityGroup({ ...path, ...query }),
1640+
antiAffinityGroupDelete: ({ path, query }) => {
1641+
const group = lookup.antiAffinityGroup({ ...path, ...query })
1642+
db.antiAffinityGroups = db.antiAffinityGroups.filter((i) => i.id !== group.id)
1643+
return 204
1644+
},
16401645
antiAffinityGroupMemberList: ({ path, query }) => {
16411646
const antiAffinityGroup = lookup.antiAffinityGroup({ ...path, ...query })
16421647
const members: Json<AntiAffinityGroupMember>[] = db.antiAffinityGroupMemberLists
@@ -1672,7 +1677,6 @@ export const handlers = makeHandlers({
16721677
affinityGroupMemberInstanceView: NotImplemented,
16731678
affinityGroupUpdate: NotImplemented,
16741679
antiAffinityGroupCreate: NotImplemented,
1675-
antiAffinityGroupDelete: NotImplemented,
16761680
antiAffinityGroupMemberInstanceAdd: NotImplemented,
16771681
antiAffinityGroupMemberInstanceView: NotImplemented,
16781682
antiAffinityGroupUpdate: NotImplemented,

0 commit comments

Comments
 (0)