diff --git a/app/forms/disk-attach.tsx b/app/forms/disk-attach.tsx index 02f730fc97..2ef2988856 100644 --- a/app/forms/disk-attach.tsx +++ b/app/forms/disk-attach.tsx @@ -1,8 +1,5 @@ -import { useParams } from 'react-router-dom' -import invariant from 'tiny-invariant' - -import type { Disk, DiskIdentifier } from '@oxide/api' -import { useApiMutation, useApiQuery, useApiQueryClient } from '@oxide/api' +import type { DiskIdentifier, ErrorResult } from '@oxide/api' +import { useApiQuery } from '@oxide/api' import { ListboxField, SideModalForm } from 'app/components/form' import { useProjectSelector } from 'app/hooks' @@ -11,35 +8,24 @@ const defaultValues = { name: '' } type AttachDiskProps = { /** If defined, this overrides the usual mutation */ - onSubmit?: (diskAttach: DiskIdentifier) => void + onSubmit: (diskAttach: DiskIdentifier) => void onDismiss: () => void - onSuccess?: (disk: Disk) => void + loading?: boolean + submitError?: ErrorResult | null } +/** + * Can be used with either a `setState` or a real mutation as `onSubmit`, hence + * the optional `loading` and `submitError` + */ export function AttachDiskSideModalForm({ onSubmit, - onSuccess, onDismiss, + loading, + submitError = null, }: AttachDiskProps) { - const queryClient = useApiQueryClient() - // instance name undefined when this form is called from DisksTableField on - // instance create, which passes in its own onSubmit, bypassing the attachDisk mutation - const { instanceName } = useParams() const projectSelector = useProjectSelector() - // TODO: pass in this mutation from outside so we don't have to do the instanceName check - const attachDisk = useApiMutation('instanceDiskAttachV1', { - onSuccess(data) { - invariant(instanceName, 'instanceName is required') - queryClient.invalidateQueries('instanceDiskListV1', { - path: { instance: instanceName }, - query: projectSelector, - }) - onSuccess?.(data) - onDismiss() - }, - }) - // TODO: loading state? because this fires when the modal opens and not when // they focus the combobox, it will almost always be done by the time they // click in @@ -54,19 +40,9 @@ export function AttachDiskSideModalForm({ id="form-disk-attach" title="Attach Disk" formOptions={{ defaultValues }} - onSubmit={ - onSubmit || - (({ name }) => { - invariant(instanceName, 'instanceName is required') - attachDisk.mutate({ - path: { instance: instanceName }, - query: projectSelector, - body: { disk: name }, - }) - }) - } - loading={attachDisk.isLoading} - submitError={attachDisk.error} + onSubmit={onSubmit} + loading={loading} + submitError={submitError} onDismiss={onDismiss} > {({ control }) => ( diff --git a/app/forms/network-interface-create.tsx b/app/forms/network-interface-create.tsx index 667b702257..27503e2dce 100644 --- a/app/forms/network-interface-create.tsx +++ b/app/forms/network-interface-create.tsx @@ -1,9 +1,7 @@ import { useMemo } from 'react' -import { useParams } from 'react-router-dom' -import invariant from 'tiny-invariant' -import type { NetworkInterfaceCreate } from '@oxide/api' -import { useApiMutation, useApiQuery, useApiQueryClient } from '@oxide/api' +import type { ErrorResult, NetworkInterfaceCreate } from '@oxide/api' +import { useApiQuery } from '@oxide/api' import { Divider } from '@oxide/ui' import { @@ -26,28 +24,23 @@ const defaultValues: NetworkInterfaceCreate = { type CreateNetworkInterfaceFormProps = { onDismiss: () => void - onSubmit?: (values: NetworkInterfaceCreate) => void + onSubmit: (values: NetworkInterfaceCreate) => void + loading?: boolean + submitError?: ErrorResult | null } +/** + * Can be used with either a `setState` or a real mutation as `onSubmit`, hence + * the optional `loading` and `submitError` + */ export default function CreateNetworkInterfaceForm({ onSubmit, onDismiss, + loading, + submitError = null, }: CreateNetworkInterfaceFormProps) { - const queryClient = useApiQueryClient() - const { instanceName } = useParams() const projectSelector = useProjectSelector() - // TODO: pass in this mutation from outside so we don't have to do the instanceName check - const createNetworkInterface = useApiMutation('instanceNetworkInterfaceCreateV1', { - onSuccess() { - invariant(instanceName, 'instanceName is required when posting a network interface') - queryClient.invalidateQueries('instanceNetworkInterfaceListV1', { - query: { instance: instanceName, ...projectSelector }, - }) - onDismiss() - }, - }) - const { data: vpcsData } = useApiQuery('vpcListV1', { query: projectSelector }) const vpcs = useMemo(() => vpcsData?.items || [], [vpcsData]) @@ -57,22 +50,9 @@ export default function CreateNetworkInterfaceForm({ title="Add network interface" formOptions={{ defaultValues }} onDismiss={onDismiss} - onSubmit={ - onSubmit || - ((body) => { - invariant( - instanceName, - 'instanceName is required when posting a network interface' - ) - - createNetworkInterface.mutate({ - query: { ...projectSelector, instance: instanceName }, - body, - }) - }) - } - loading={createNetworkInterface.isLoading} - submitError={createNetworkInterface.error} + onSubmit={onSubmit} + loading={loading} + submitError={submitError} > {({ control }) => ( <> diff --git a/app/pages/project/instances/instance/tabs/NetworkingTab.tsx b/app/pages/project/instances/instance/tabs/NetworkingTab.tsx index 5a630c189a..83c5070354 100644 --- a/app/pages/project/instances/instance/tabs/NetworkingTab.tsx +++ b/app/pages/project/instances/instance/tabs/NetworkingTab.tsx @@ -82,6 +82,15 @@ export function NetworkingTab() { const getQuery = ['instanceNetworkInterfaceListV1', { query: instanceSelector }] as const + const createNic = useApiMutation('instanceNetworkInterfaceCreateV1', { + onSuccess() { + queryClient.invalidateQueries('instanceNetworkInterfaceListV1', { + query: instanceSelector, + }) + setCreateModalOpen(false) + }, + }) + const deleteNic = useApiMutation('instanceNetworkInterfaceDeleteV1', { onSuccess() { queryClient.invalidateQueries(...getQuery) @@ -200,7 +209,10 @@ export function NetworkingTab() { {createModalOpen && ( - setCreateModalOpen(false)} /> + setCreateModalOpen(false)} + onSubmit={(body) => createNic.mutate({ query: instanceSelector, body })} + /> )} {editing && ( setEditing(null)} /> diff --git a/app/pages/project/instances/instance/tabs/StorageTab.tsx b/app/pages/project/instances/instance/tabs/StorageTab.tsx index 961bbaf018..556ea76432 100644 --- a/app/pages/project/instances/instance/tabs/StorageTab.tsx +++ b/app/pages/project/instances/instance/tabs/StorageTab.tsx @@ -105,8 +105,10 @@ export function StorageTab() { const attachDisk = useApiMutation('instanceDiskAttachV1', { onSuccess() { - console.log('disk atttach success') queryClient.invalidateQueries('instanceDiskListV1', instancePathQuery) + // cover all our bases. this is called by both modals + setShowDiskCreate(false) + setShowDiskAttach(false) }, onError(err) { addToast({ @@ -193,7 +195,14 @@ export function StorageTab() { /> )} {showDiskAttach && ( - setShowDiskAttach(false)} /> + setShowDiskAttach(false)} + onSubmit={({ name }) => { + attachDisk.mutate({ ...instancePathQuery, body: { disk: name } }) + }} + loading={attachDisk.isLoading} + submitError={attachDisk.error} + /> )} ) diff --git a/libs/api-mocks/msw/db.ts b/libs/api-mocks/msw/db.ts index 75c88f1afc..6722021d87 100644 --- a/libs/api-mocks/msw/db.ts +++ b/libs/api-mocks/msw/db.ts @@ -94,7 +94,6 @@ export const lookup = { return snapshot }, vpc({ vpc: id, ...projectSelector }: PP.Vpc): Json { - console.log({ id, ...projectSelector }) if (!id) throw notFoundErr if (isUuid(id)) return lookupById(db.vpcs, id) diff --git a/libs/api-mocks/msw/handlers.ts b/libs/api-mocks/msw/handlers.ts index 15026a3a46..a462c71d0d 100644 --- a/libs/api-mocks/msw/handlers.ts +++ b/libs/api-mocks/msw/handlers.ts @@ -355,7 +355,6 @@ export const handlers = makeHandlers({ state: 'attached', instance: instance.id, } - console.log(disk) return disk }, instanceDiskDetachV1({ body, path, query: projectParams }) {