diff --git a/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx new file mode 100644 index 000000000..2d01601a2 --- /dev/null +++ b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx @@ -0,0 +1,43 @@ +import userEvent from '@testing-library/user-event'; + +import { UserContext } from '@/portainer/hooks/useUser'; +import { UserViewModel } from '@/portainer/models/user'; +import { renderWithQueryClient } from '@/react-tools/test-utils'; + +import { CreateContainerInstanceForm } from './CreateContainerInstanceForm'; + +jest.mock('@uirouter/react', () => ({ + ...jest.requireActual('@uirouter/react'), + useCurrentStateAndParams: jest.fn(() => ({ + params: { endpointId: 5 }, + })), +})); + +test('submit button should be disabled when name or image is missing', async () => { + const user = new UserViewModel({ Username: 'user' }); + + const { findByText, getByText, getByLabelText } = renderWithQueryClient( + + + + ); + + await expect(findByText(/Azure settings/)).resolves.toBeVisible(); + + const button = getByText(/Deploy the container/); + expect(button).toBeVisible(); + expect(button).toBeDisabled(); + + const nameInput = getByLabelText(/name/i); + userEvent.type(nameInput, 'name'); + + const imageInput = getByLabelText(/image/i); + userEvent.type(imageInput, 'image'); + + await expect(findByText(/Deploy the container/)).resolves.toBeEnabled(); + + expect(nameInput).toHaveValue('name'); + userEvent.clear(nameInput); + + await expect(findByText(/Deploy the container/)).resolves.toBeDisabled(); +}); diff --git a/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx new file mode 100644 index 000000000..0aa9aa0e2 --- /dev/null +++ b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx @@ -0,0 +1,219 @@ +import { Field, Form, Formik } from 'formik'; +import { useCurrentStateAndParams, useRouter } from '@uirouter/react'; + +import { FormControl } from '@/portainer/components/form-components/FormControl'; +import { Input, Select } from '@/portainer/components/form-components/Input'; +import { FormSectionTitle } from '@/portainer/components/form-components/FormSectionTitle'; +import { LoadingButton } from '@/portainer/components/Button/LoadingButton'; +import { InputListError } from '@/portainer/components/form-components/InputList/InputList'; +import { AccessControlForm } from '@/portainer/components/accessControlForm'; +import { ContainerInstanceFormValues } from '@/azure/types'; +import * as notifications from '@/portainer/services/notifications'; +import { isAdmin, useUser } from '@/portainer/hooks/useUser'; + +import { validationSchema } from './CreateContainerInstanceForm.validation'; +import { PortMapping, PortsMappingField } from './PortsMappingField'; +import { useLoadFormState } from './useLoadFormState'; +import { + getSubscriptionLocations, + getSubscriptionResourceGroups, +} from './utils'; +import { useCreateInstance } from './useCreateInstanceMutation'; + +export function CreateContainerInstanceForm() { + const { + params: { endpointId: environmentId }, + } = useCurrentStateAndParams(); + + if (!environmentId) { + throw new Error('endpointId url param is required'); + } + + const { user } = useUser(); + const isUserAdmin = isAdmin(user); + + const { initialValues, isLoading, providers, subscriptions, resourceGroups } = + useLoadFormState(environmentId, isUserAdmin); + + const router = useRouter(); + + const { mutateAsync } = useCreateInstance( + resourceGroups, + environmentId, + user?.Id + ); + + if (isLoading) { + return null; + } + + return ( + + initialValues={initialValues} + validationSchema={() => validationSchema(isUserAdmin)} + onSubmit={onSubmit} + validateOnMount + validateOnChange + enableReinitialize + > + {({ + errors, + handleSubmit, + isSubmitting, + isValid, + values, + setFieldValue, + }) => ( +
+ Azure settings + + + + + + + + + + + + + Container configuration + + + + + + + + + + + + + + setFieldValue('ports', value)} + errors={errors.ports as InputListError[]} + /> + +
+
+ This will automatically deploy a container with a public IP + address +
+
+ + Container Resources + + + + + + + + + + setFieldValue('accessControl', values)} + values={values.accessControl} + errors={errors.accessControl} + /> + +
+
+ +