Skip to content

Commit 788389a

Browse files
authored
Add confirmation modal to instance stop (#2157)
1 parent fed9f4d commit 788389a

File tree

6 files changed

+34
-9
lines changed

6 files changed

+34
-9
lines changed

app/pages/project/instances/actions.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { useNavigate } from 'react-router-dom'
1010

1111
import { instanceCan, useApiMutation, type Instance } from '@oxide/api'
1212

13+
import { HL } from '~/components/HL'
14+
import { confirmAction } from '~/stores/confirm-action'
1315
import { confirmDelete } from '~/stores/confirm-delete'
1416
import { addToast } from '~/stores/toast'
1517
import type { MakeActions } from '~/table/columns/action-col'
@@ -65,14 +67,28 @@ export const useMakeInstanceActions = (
6567
{
6668
label: 'Stop',
6769
onActivate() {
68-
stopInstance.mutate(instanceParams, {
69-
onSuccess: () => addToast({ title: `Stopping instance '${instance.name}'` }),
70-
onError: (error) =>
71-
addToast({
72-
variant: 'error',
73-
title: `Error stopping instance '${instance.name}'`,
74-
content: error.message,
70+
confirmAction({
71+
actionType: 'danger',
72+
doAction: async () =>
73+
stopInstance.mutate(instanceParams, {
74+
onSuccess: () =>
75+
addToast({ title: `Stopping instance '${instance.name}'` }),
76+
onError: (error) =>
77+
addToast({
78+
variant: 'error',
79+
title: `Error stopping instance '${instance.name}'`,
80+
content: error.message,
81+
}),
7582
}),
83+
modalTitle: 'Confirm stop instance',
84+
modalContent: (
85+
<p>
86+
Are you sure you want to stop <HL>{instance.name}</HL>? Stopped instances
87+
retain attached disks and IP addresses, but allocated CPU and memory are
88+
freed.
89+
</p>
90+
),
91+
errorTitle: `Could not stop ${instance.name}`,
7692
})
7793
},
7894
disabled: !instanceCan.stop(instance) && (
@@ -113,6 +129,7 @@ export const useMakeInstanceActions = (
113129
},
114130
}),
115131
label: instance.name,
132+
resourceKind: 'instance',
116133
}),
117134
disabled: !instanceCan.delete(instance) && (
118135
<>Only {fancifyStates(instanceCan.delete.states)} instances can be deleted</>

app/stores/confirm-delete.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@ type DeleteConfig = {
2323
* directly.
2424
*/
2525
label: React.ReactNode
26+
resourceKind?: string
2627
}
2728

2829
export const confirmDelete =
29-
({ doDelete, label }: DeleteConfig) =>
30+
({ doDelete, label, resourceKind }: DeleteConfig) =>
3031
() => {
3132
const displayLabel = typeof label === 'string' ? <HL>{label}</HL> : label
33+
const modalTitle = resourceKind ? `Confirm delete ${resourceKind}` : 'Confirm delete'
3234
useConfirmAction.setState({
3335
actionConfig: {
3436
doAction: doDelete,
3537
modalContent: <p>Are you sure you want to delete {displayLabel}?</p>,
3638
errorTitle: 'Could not delete resource',
37-
modalTitle: 'Confirm delete',
39+
modalTitle,
3840
actionType: 'danger',
3941
},
4042
})

test/e2e/instance/list.e2e.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ test('can stop and delete a running instance', async ({ page }) => {
3434
await row.getByRole('button', { name: 'Row actions' }).click()
3535
await expect(page.getByRole('menuitem', { name: 'Delete' })).toBeDisabled()
3636
await page.getByRole('menuitem', { name: 'Stop' }).click()
37+
await page.getByRole('button', { name: 'Confirm' }).click()
3738

3839
// now it's stopped
3940
await expect(row.getByRole('cell', { name: /stopped/ })).toBeVisible()
@@ -55,6 +56,7 @@ test('can stop a starting instance', async ({ page }) => {
5556

5657
await row.getByRole('button', { name: 'Row actions' }).click()
5758
await page.getByRole('menuitem', { name: 'Stop' }).click()
59+
await page.getByRole('button', { name: 'Confirm' }).click()
5860

5961
await expect(row.getByRole('cell', { name: /stopped/ })).toBeVisible()
6062
})

test/e2e/network-interface-create.e2e.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ test('can create a NIC with a specified IP address', async ({ page }) => {
1616
// stop the instance
1717
await page.getByRole('button', { name: 'Instance actions' }).click()
1818
await page.getByRole('menuitem', { name: 'Stop' }).click()
19+
await page.getByRole('button', { name: 'Confirm' }).click()
1920

2021
// open the add network interface side modal
2122
await page.getByRole('button', { name: 'Add network interface' }).click()
@@ -45,6 +46,7 @@ test('can create a NIC with a blank IP address', async ({ page }) => {
4546
// stop the instance
4647
await page.getByRole('button', { name: 'Instance actions' }).click()
4748
await page.getByRole('menuitem', { name: 'Stop' }).click()
49+
await page.getByRole('button', { name: 'Confirm' }).click()
4850

4951
// open the add network interface side modal
5052
await page.getByRole('button', { name: 'Add network interface' }).click()

test/e2e/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export async function expectRowVisible(
106106
export async function stopInstance(page: Page) {
107107
await page.click('role=button[name="Instance actions"]')
108108
await page.click('role=menuitem[name="Stop"]')
109+
await page.click('role=button[name="Confirm"]')
109110
await closeToast(page)
110111
}
111112

test/e2e/z-index.e2e.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ test('Dropdown content in SidebarModal shows on screen', async ({ page }) => {
4848
// stop the instance
4949
await page.getByRole('button', { name: 'Instance actions' }).click()
5050
await page.getByRole('menuitem', { name: 'Stop' }).click()
51+
await page.getByRole('button', { name: 'Confirm' }).click()
5152

5253
// open the add network interface side modal
5354
await page.getByRole('button', { name: 'Add network interface' }).click()

0 commit comments

Comments
 (0)