Files
backroad/app/react/docker/containers/CreateView/BaseForm/BaseForm.tsx
2023-11-15 09:34:08 +02:00

205 lines
7.0 KiB
TypeScript

import { useFormikContext } from 'formik';
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
import { Authorized } from '@/react/hooks/useUser';
import { AccessControlForm } from '@/react/portainer/access-control';
import { AccessControlFormData } from '@/react/portainer/access-control/types';
import { EnvironmentType } from '@/react/portainer/environments/types';
import { NodeSelector } from '@/react/docker/agent/NodeSelector';
import { useIsSwarm } from '@/react/docker/proxy/queries/useInfo';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { isAgentEnvironment } from '@/react/portainer/environments/utils';
import { FeatureId } from '@/react/portainer/feature-flags/enums';
import { FormControl } from '@@/form-components/FormControl';
import { FormSection } from '@@/form-components/FormSection';
import { Input } from '@@/form-components/Input';
import { SwitchField } from '@@/form-components/SwitchField';
import { ImageConfigFieldset, ImageConfigValues } from '@@/ImageConfigFieldset';
import { LoadingButton } from '@@/buttons';
import { Widget } from '@@/Widget';
import {
PortsMappingField,
Values as PortMappingValue,
} from './PortsMappingField';
export interface Values {
name: string;
enableWebhook: boolean;
publishAllPorts: boolean;
image: ImageConfigValues;
alwaysPull: boolean;
ports: PortMappingValue;
accessControl: AccessControlFormData;
nodeName: string;
autoRemove: boolean;
}
function useIsAgentOnSwarm() {
const environmentId = useEnvironmentId();
const environmentQuery = useCurrentEnvironment();
const isSwarm = useIsSwarm(environmentId);
return (
!!environmentQuery.data &&
isAgentEnvironment(environmentQuery.data?.Type) &&
isSwarm
);
}
export function BaseForm({
isLoading,
onChangeName,
onChangeImageName,
onRateLimit,
}: {
isLoading: boolean;
onChangeName: (value: string) => void;
onChangeImageName: () => void;
onRateLimit: (limited?: boolean) => void;
}) {
const { setFieldValue, values, errors, isValid } = useFormikContext<Values>();
const environmentQuery = useCurrentEnvironment();
const isAgentOnSwarm = useIsAgentOnSwarm();
if (!environmentQuery.data) {
return null;
}
const environment = environmentQuery.data;
const canUseWebhook = environment.Type !== EnvironmentType.EdgeAgentOnDocker;
return (
<Widget>
<Widget.Body>
<FormControl label="Name" inputId="name-input" errors={errors?.name}>
<Input
id="name-input"
value={values.name}
onChange={(e) => {
const name = e.target.value;
onChangeName(name);
setFieldValue('name', name);
}}
placeholder="e.g. myContainer"
/>
</FormControl>
<FormSection title="Image Configuration">
<ImageConfigFieldset
values={values.image}
setFieldValue={(field, value) =>
setFieldValue(`image.${field}`, value)
}
autoComplete
onRateLimit={values.alwaysPull ? onRateLimit : undefined}
errors={errors?.image}
onChangeImage={onChangeImageName}
>
<div className="form-group">
<div className="col-sm-12">
<SwitchField
label="Always pull the image"
tooltip="When enabled, Portainer will automatically try to pull the specified image before creating the container."
checked={values.alwaysPull}
onChange={(alwaysPull) =>
setFieldValue('alwaysPull', alwaysPull)
}
labelClass="col-sm-3 col-lg-2"
/>
</div>
</div>
</ImageConfigFieldset>
</FormSection>
{canUseWebhook && (
<Authorized authorizations="PortainerWebhookCreate" adminOnlyCE>
<FormSection title="Webhook">
<div className="form-group">
<div className="col-sm-12">
<SwitchField
label="Create a container webhook"
tooltip="Create a webhook (or callback URI) to automate the recreate this container. Sending a POST request to this callback URI (without requiring any authentication) will pull the most up-to-date version of the associated image and recreate this container."
checked={values.enableWebhook}
onChange={(enableWebhook) =>
setFieldValue('enableWebhook', enableWebhook)
}
featureId={FeatureId.CONTAINER_WEBHOOK}
labelClass="col-sm-3 col-lg-2"
/>
</div>
</div>
</FormSection>
</Authorized>
)}
<FormSection title="Network ports configuration">
<div className="form-group">
<div className="col-sm-12">
<SwitchField
label="Publish all exposed ports to random host ports"
tooltip="When enabled, Portainer will let Docker automatically map a random port on the host to each one defined in the image Dockerfile."
checked={values.publishAllPorts}
onChange={(publishAllPorts) =>
setFieldValue('publishAllPorts', publishAllPorts)
}
labelClass="col-sm-3 col-lg-2"
/>
</div>
</div>
<PortsMappingField
value={values.ports}
onChange={(ports) => setFieldValue('ports', ports)}
errors={errors?.ports}
/>
</FormSection>
{isAgentOnSwarm && (
<FormSection title="Deployment">
<NodeSelector
value={values.nodeName}
onChange={(nodeName) => setFieldValue('nodeName', nodeName)}
/>
</FormSection>
)}
<AccessControlForm
onChange={(accessControl) =>
setFieldValue('accessControl', accessControl)
}
errors={errors?.accessControl}
values={values.accessControl}
environmentId={environment.Id}
/>
<div className="form-group">
<div className="col-sm-12">
<SwitchField
label="Auto remove"
tooltip="When enabled, Portainer will automatically remove the container when it exits. This is useful when you want to use the container only once."
checked={values.autoRemove}
onChange={(autoRemove) => setFieldValue('autoRemove', autoRemove)}
labelClass="col-sm-3 col-lg-2"
/>
</div>
</div>
<div className="form-group">
<div className="col-sm-12">
<LoadingButton
loadingText="Deployment in progress..."
isLoading={isLoading}
disabled={!isValid}
>
Deploy the container
</LoadingButton>
</div>
</div>
</Widget.Body>
</Widget>
);
}