diff --git a/app/pages/project/instances/InstancesPage.tsx b/app/pages/project/instances/InstancesPage.tsx index 94f940836..97de119b4 100644 --- a/app/pages/project/instances/InstancesPage.tsx +++ b/app/pages/project/instances/InstancesPage.tsx @@ -64,8 +64,7 @@ const POLL_INTERVAL_SLOW = 60 * sec export function InstancesPage() { const { project } = useProjectSelector() - - const { makeActions } = useMakeInstanceActions( + const { makeButtonActions, makeMenuActions } = useMakeInstanceActions( { project }, { onSuccess: refetchInstances, onDelete: refetchInstances } ) @@ -182,9 +181,12 @@ export function InstancesPage() { } ), colHelper.accessor('timeCreated', Columns.timeCreated), - getActionsCol(makeActions), + getActionsCol((instance: Instance) => [ + ...makeButtonActions(instance), + ...makeMenuActions(instance), + ]), ], - [project, makeActions] + [project, makeButtonActions, makeMenuActions] ) if (!instances) return null diff --git a/app/pages/project/instances/actions.tsx b/app/pages/project/instances/actions.tsx index db6c27e1b..0ec1ea684 100644 --- a/app/pages/project/instances/actions.tsx +++ b/app/pages/project/instances/actions.tsx @@ -5,7 +5,7 @@ * * Copyright Oxide Computer Company */ -import { useCallback, useMemo } from 'react' +import { useCallback } from 'react' import { useNavigate } from 'react-router-dom' import { instanceCan, useApiMutation, type Instance } from '@oxide/api' @@ -47,15 +47,14 @@ export const useMakeInstanceActions = ( onSuccess: options.onDelete, }) - const makeActions = useCallback( - (instance: Instance) => { - const instanceSelector = { project, instance: instance.name } - const instanceParams = { path: { instance: instance.name }, query: { project } } - return [ - { - label: 'Start', - onActivate() { - startInstance(instanceParams, { + const makeButtonActions = useCallback( + (instance: Instance) => [ + { + label: 'Start', + onActivate() { + startInstance( + { path: { instance: instance.name }, query: { project } }, + { onSuccess: () => addToast({ title: `Starting instance '${instance.name}'` }), onError: (error) => addToast({ @@ -63,41 +62,54 @@ export const useMakeInstanceActions = ( title: `Error starting instance '${instance.name}'`, content: error.message, }), - }) - }, - disabled: !instanceCan.start(instance) && ( - <>Only {fancifyStates(instanceCan.start.states)} instances can be started - ), + } + ) }, - { - label: 'Stop', - onActivate() { - confirmAction({ - actionType: 'danger', - doAction: () => - stopInstanceAsync(instanceParams, { + disabled: !instanceCan.start(instance) && ( + <>Only {fancifyStates(instanceCan.start.states)} instances can be started + ), + }, + { + label: 'Stop', + onActivate() { + confirmAction({ + actionType: 'danger', + doAction: () => + stopInstanceAsync( + { path: { instance: instance.name }, query: { project } }, + { onSuccess: () => addToast({ title: `Stopping instance '${instance.name}'` }), - }), - modalTitle: 'Confirm stop instance', - modalContent: ( -
-

- Are you sure you want to stop {instance.name}? -

-

- Stopped instances retain attached disks and IP addresses, but allocated - CPU and memory are freed. -

-
+ } ), - errorTitle: `Error stopping ${instance.name}`, - }) - }, - disabled: !instanceCan.stop(instance) && ( - <>Only {fancifyStates(instanceCan.stop.states)} instances can be stopped - ), + modalTitle: 'Confirm stop instance', + modalContent: ( +
+

+ Are you sure you want to stop {instance.name}? +

+

+ Stopped instances retain attached disks and IP addresses, but allocated + CPU and memory are freed. +

+
+ ), + errorTitle: `Error stopping ${instance.name}`, + }) }, + disabled: !instanceCan.stop(instance) && ( + <>Only {fancifyStates(instanceCan.stop.states)} instances can be stopped + ), + }, + ], + [project, startInstance, stopInstanceAsync] + ) + + const makeMenuActions = useCallback( + (instance: Instance) => { + const instanceSelector = { project, instance: instance.name } + const instanceParams = { path: { instance: instance.name }, query: { project } } + return [ { label: 'Reboot', onActivate() { @@ -140,31 +152,8 @@ export const useMakeInstanceActions = ( }, ] }, - [ - project, - deleteInstanceAsync, - navigate, - rebootInstance, - startInstance, - stopInstanceAsync, - ] + [project, deleteInstanceAsync, navigate, rebootInstance] ) - const useInstanceActions = (instance: Instance) => { - const allActions = useMemo(() => makeActions(instance), [instance]) - - const buttonActions = useMemo( - () => allActions.filter((a) => a.label === 'Start' || a.label === 'Stop'), - [allActions] - ) - - const menuActions = useMemo( - () => allActions.filter((a) => a.label !== 'Start' && a.label !== 'Stop'), - [allActions] - ) - - return { allActions, buttonActions, menuActions } - } - - return { useInstanceActions, makeActions } + return { makeButtonActions, makeMenuActions } } diff --git a/app/pages/project/instances/instance/InstancePage.tsx b/app/pages/project/instances/instance/InstancePage.tsx index 7bfb2c2de..f9c7fc30c 100644 --- a/app/pages/project/instances/instance/InstancePage.tsx +++ b/app/pages/project/instances/instance/InstancePage.tsx @@ -94,7 +94,7 @@ export function InstancePage() { const navigate = useNavigate() - const { useInstanceActions } = useMakeInstanceActions(instanceSelector, { + const { makeButtonActions, makeMenuActions } = useMakeInstanceActions(instanceSelector, { onSuccess: refreshData, // go to project instances list since there's no more instance onDelete: () => { @@ -134,7 +134,6 @@ export function InstancePage() { { enabled: !!primaryVpcId } ) - const { buttonActions, menuActions } = useInstanceActions(instance) const allMenuActions = useMemo( () => [ { @@ -143,9 +142,9 @@ export function InstancePage() { window.navigator.clipboard.writeText(instance.id || '') }, }, - ...menuActions, + ...makeMenuActions(instance), ], - [instance.id, menuActions] + [instance, makeMenuActions] ) const memory = filesize(instance.memory, { output: 'object', base: 2 }) @@ -158,13 +157,14 @@ export function InstancePage() {
- {buttonActions.map((action) => ( + {makeButtonActions(instance).map((action) => (