mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
update forms from FormRow to using FormLayout components
This commit is contained in:
parent
ff823c9fdb
commit
df13a8fea9
@ -3,6 +3,7 @@ import { string, bool } from 'prop-types';
|
||||
import { useField } from 'formik';
|
||||
import { Split, SplitItem } from '@patternfly/react-core';
|
||||
import { yamlToJson, jsonToYaml, isJson } from '@util/yaml';
|
||||
import { FormFullWidthLayout } from '@components/FormLayout';
|
||||
import CodeMirrorInput from './CodeMirrorInput';
|
||||
import YamlJsonToggle from './YamlJsonToggle';
|
||||
import { JSON_MODE, YAML_MODE } from './constants';
|
||||
@ -12,50 +13,46 @@ function VariablesField({ id, name, label, readOnly }) {
|
||||
const [mode, setMode] = useState(isJson(field.value) ? JSON_MODE : YAML_MODE);
|
||||
|
||||
return (
|
||||
<Field name={name}>
|
||||
{({ field, form }) => (
|
||||
<div className="pf-c-form__group">
|
||||
<Split gutter="sm">
|
||||
<SplitItem>
|
||||
<label htmlFor={id} className="pf-c-form__label">
|
||||
<span className="pf-c-form__label-text">{label}</span>
|
||||
</label>
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
<YamlJsonToggle
|
||||
mode={mode}
|
||||
onChange={newMode => {
|
||||
try {
|
||||
const newVal =
|
||||
newMode === YAML_MODE
|
||||
? jsonToYaml(field.value)
|
||||
: yamlToJson(field.value);
|
||||
helpers.setValue(newVal);
|
||||
setMode(newMode);
|
||||
} catch (err) {
|
||||
helpers.setError(err.message);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
<CodeMirrorInput
|
||||
<FormFullWidthLayout>
|
||||
<Split gutter="sm">
|
||||
<SplitItem>
|
||||
<label htmlFor={id} className="pf-c-form__label">
|
||||
<span className="pf-c-form__label-text">{label}</span>
|
||||
</label>
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
<YamlJsonToggle
|
||||
mode={mode}
|
||||
readOnly={readOnly}
|
||||
{...field}
|
||||
onChange={newVal => {
|
||||
helpers.setValue(newVal);
|
||||
onChange={newMode => {
|
||||
try {
|
||||
const newVal =
|
||||
newMode === YAML_MODE
|
||||
? jsonToYaml(field.value)
|
||||
: yamlToJson(field.value);
|
||||
helpers.setValue(newVal);
|
||||
setMode(newMode);
|
||||
} catch (err) {
|
||||
helpers.setError(err.message);
|
||||
}
|
||||
}}
|
||||
hasErrors={!!meta.error}
|
||||
/>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
<CodeMirrorInput
|
||||
mode={mode}
|
||||
readOnly={readOnly}
|
||||
{...field}
|
||||
onChange={newVal => {
|
||||
helpers.setValue(newVal);
|
||||
}}
|
||||
hasErrors={!!meta.error}
|
||||
/>
|
||||
{meta.error ? (
|
||||
<div className="pf-c-form__helper-text pf-m-error" aria-live="polite">
|
||||
{meta.error}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
) : null}
|
||||
</FormFullWidthLayout>
|
||||
);
|
||||
}
|
||||
VariablesField.propTypes = {
|
||||
|
||||
@ -9,6 +9,7 @@ const ActionGroup = styled(PFActionGroup)`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
--pf-c-form__group--m-action--MarginTop: 0;
|
||||
grid-column: 1 / -1;
|
||||
|
||||
.pf-c-form__actions {
|
||||
& > button {
|
||||
|
||||
44
awx/ui_next/src/components/FormLayout/FormLayout.jsx
Normal file
44
awx/ui_next/src/components/FormLayout/FormLayout.jsx
Normal file
@ -0,0 +1,44 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FormColumnLayout = styled.div`
|
||||
width: 100%;
|
||||
grid-column: 1 / 4;
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
|
||||
@media (min-width: 1210px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
`;
|
||||
|
||||
export const FormFullWidthLayout = styled.div`
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: 20px;
|
||||
`;
|
||||
|
||||
export const FormCheckboxLayout = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > * {
|
||||
margin-right: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SubFormLayout = styled.div`
|
||||
grid-column: 1 / -1;
|
||||
background-color: #f5f5f5;
|
||||
margin: 0 -24px;
|
||||
padding: 24px;
|
||||
|
||||
& > .pf-c-title {
|
||||
--pf-c-title--m-md--FontWeight: 700;
|
||||
grid-column: 1 / -1;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
`;
|
||||
6
awx/ui_next/src/components/FormLayout/index.js
Normal file
6
awx/ui_next/src/components/FormLayout/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
export {
|
||||
FormColumnLayout,
|
||||
FormFullWidthLayout,
|
||||
FormCheckboxLayout,
|
||||
SubFormLayout,
|
||||
} from './FormLayout';
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Row = styled.div`
|
||||
display: grid;
|
||||
grid-gap: 20px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
`;
|
||||
export default function FormRow({ children, className }) {
|
||||
return <Row className={className}>{children}</Row>;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
|
||||
describe('FormRow', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(<FormRow />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@ -1 +0,0 @@
|
||||
export { default } from './FormRow';
|
||||
@ -8,12 +8,12 @@ import { t } from '@lingui/macro';
|
||||
|
||||
import { Form } from '@patternfly/react-core';
|
||||
|
||||
import FormRow from '@components/FormRow';
|
||||
import FormField, { FormSubmitError } from '@components/FormField';
|
||||
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||
import { VariablesField } from '@components/CodeMirrorInput';
|
||||
import { required } from '@util/validators';
|
||||
import { InventoryLookup } from '@components/Lookup';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function HostFormFields({ host, i18n }) {
|
||||
const [inventory, setInventory] = useState(
|
||||
@ -30,43 +30,43 @@ function HostFormFields({ host, i18n }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="host-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={i18n._(t`Name`)}
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="host-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
{hostAddMatch && (
|
||||
<InventoryLookup
|
||||
value={inventory}
|
||||
<FormField
|
||||
id="host-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={i18n._(t`Name`)}
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="host-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
{hostAddMatch && (
|
||||
<InventoryLookup
|
||||
value={inventory}
|
||||
onBlur={() => inventoryHelpers.setTouched()}
|
||||
tooltip={i18n._(
|
||||
t`Select the inventory that this host will belong to.`
|
||||
)}
|
||||
tooltip={i18n._(
|
||||
t`Select the inventory that this host will belong to.`
|
||||
)}
|
||||
isValid={!inventoryMeta.touched || !inventoryMeta.error}
|
||||
helperTextInvalid={inventoryMeta.error}
|
||||
onChange={value => {
|
||||
onChange={value => {
|
||||
inventoryHelpers.setValuealue(value.id);
|
||||
setInventory(value);
|
||||
}}
|
||||
required
|
||||
setInventory(value);
|
||||
}}
|
||||
required
|
||||
touched={inventoryMeta.touched}
|
||||
error={inventoryMeta.error}
|
||||
/>
|
||||
)}
|
||||
<VariablesField
|
||||
id="host-variables"
|
||||
name="variables"
|
||||
label={i18n._(t`Variables`)}
|
||||
/>
|
||||
/>
|
||||
)}
|
||||
<VariablesField
|
||||
id="host-variables"
|
||||
name="variables"
|
||||
label={i18n._(t`Variables`)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -84,12 +84,14 @@ function HostForm({ handleSubmit, host, submitError, handleCancel, ...rest }) {
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<HostFormFields host={host} {...rest} />
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -12,6 +12,7 @@ import { required } from '@util/validators';
|
||||
import InstanceGroupsLookup from '@components/Lookup/InstanceGroupsLookup';
|
||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||
import CredentialLookup from '@components/Lookup/CredentialLookup';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function InventoryFormFields({ i18n, credentialTypeId }) {
|
||||
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
||||
@ -27,52 +28,52 @@ function InventoryFormFields({ i18n, credentialTypeId }) {
|
||||
const insightsCredentialHelpers = insightsCredentialFieldArr[2];
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="inventory-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="inventory-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
<FormField
|
||||
id="inventory-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="inventory-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={organizationMeta.error}
|
||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||
onBlur={() => organizationHelpers.setTouched()}
|
||||
onChange={value => {
|
||||
onChange={value => {
|
||||
organizationHelpers.setValue(value);
|
||||
}}
|
||||
}}
|
||||
value={organizationField.value}
|
||||
touched={organizationMeta.touched}
|
||||
error={organizationMeta.error}
|
||||
required
|
||||
/>
|
||||
<CredentialLookup
|
||||
label={i18n._(t`Insights Credential`)}
|
||||
credentialTypeId={credentialTypeId}
|
||||
required
|
||||
/>
|
||||
<CredentialLookup
|
||||
label={i18n._(t`Insights Credential`)}
|
||||
credentialTypeId={credentialTypeId}
|
||||
onChange={value => insightsCredentialHelpers.setValue(value)}
|
||||
value={insightsCredentialField.value}
|
||||
/>
|
||||
<InstanceGroupsLookup
|
||||
/>
|
||||
<InstanceGroupsLookup
|
||||
value={instanceGroupsField.value}
|
||||
onChange={value => {
|
||||
onChange={value => {
|
||||
instanceGroupsHelpers.setValue(value);
|
||||
}}
|
||||
/>
|
||||
<VariablesField
|
||||
tooltip={i18n._(
|
||||
t`Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax`
|
||||
)}
|
||||
id="inventory-variables"
|
||||
name="variables"
|
||||
label={i18n._(t`Variables`)}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
<VariablesField
|
||||
tooltip={i18n._(
|
||||
t`Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax`
|
||||
)}
|
||||
id="inventory-variables"
|
||||
name="variables"
|
||||
label={i18n._(t`Variables`)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -108,13 +109,14 @@ function InventoryForm({
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<InventoryFormFields {...rest} />
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={onCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormRow>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -6,11 +6,11 @@ import { Form, Card } from '@patternfly/react-core';
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
import { CardBody } from '@components/Card';
|
||||
import FormRow from '@components/FormRow';
|
||||
import FormField from '@components/FormField';
|
||||
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||
import { VariablesField } from '@components/CodeMirrorInput';
|
||||
import { required } from '@util/validators';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function InventoryGroupForm({
|
||||
i18n,
|
||||
@ -31,7 +31,7 @@ function InventoryGroupForm({
|
||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormRow css="grid-template-columns: repeat(auto-fit, minmax(300px, 500px));">
|
||||
<FormColumnLayout>
|
||||
<FormField
|
||||
id="inventoryGroup-name"
|
||||
name="name"
|
||||
@ -46,19 +46,17 @@ function InventoryGroupForm({
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<VariablesField
|
||||
id="host-variables"
|
||||
name="variables"
|
||||
label={i18n._(t`Variables`)}
|
||||
/>
|
||||
</FormRow>
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
{error ? <div>error</div> : null}
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
{error ? <div>error</div> : null}
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -11,12 +11,12 @@ import { ConfigContext } from '@contexts/Config';
|
||||
import AnsibleSelect from '@components/AnsibleSelect';
|
||||
import ContentError from '@components/ContentError';
|
||||
import ContentLoading from '@components/ContentLoading';
|
||||
import FormRow from '@components/FormRow';
|
||||
import FormField, { FormSubmitError } from '@components/FormField';
|
||||
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||
import { InstanceGroupsLookup } from '@components/Lookup/';
|
||||
import { getAddedAndRemoved } from '@util/lists';
|
||||
import { required, minMaxValue } from '@util/validators';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function OrganizationFormFields({
|
||||
i18n,
|
||||
@ -166,6 +166,7 @@ function OrganizationForm({
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<OrganizationFormFields
|
||||
instanceGroups={instanceGroups}
|
||||
setInstanceGroups={setInstanceGroups}
|
||||
@ -176,6 +177,7 @@ function OrganizationForm({
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -14,11 +14,10 @@ import FormField, {
|
||||
FieldTooltip,
|
||||
FormSubmitError,
|
||||
} from '@components/FormField';
|
||||
import FormRow from '@components/FormRow';
|
||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||
import { CredentialTypesAPI, ProjectsAPI } from '@api';
|
||||
import { required } from '@util/validators';
|
||||
import styled from 'styled-components';
|
||||
import { FormColumnLayout, SubFormLayout } from '@components/FormLayout';
|
||||
import {
|
||||
GitSubForm,
|
||||
HgSubForm,
|
||||
@ -146,147 +145,145 @@ function ProjectFormFields({
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="project-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="project-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
<FormField
|
||||
id="project-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="project-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={organizationMeta.error}
|
||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||
onBlur={() => organizationHelpers.setTouched()}
|
||||
onChange={value => {
|
||||
onChange={value => {
|
||||
organizationHelpers.setValue(value.id);
|
||||
setOrganization(value);
|
||||
}}
|
||||
value={organization}
|
||||
required
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="project-scm-type"
|
||||
setOrganization(value);
|
||||
}}
|
||||
value={organization}
|
||||
required
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="project-scm-type"
|
||||
helperTextInvalid={scmTypeMeta.error}
|
||||
isRequired
|
||||
isRequired
|
||||
isValid={!scmTypeMeta.touched || !scmTypeMeta.error}
|
||||
label={i18n._(t`SCM Type`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
label={i18n._(t`SCM Type`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
{...scmTypeField}
|
||||
id="scm_type"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
key: '',
|
||||
label: i18n._(t`Choose an SCM Type`),
|
||||
isDisabled: true,
|
||||
},
|
||||
...scmTypeOptions.map(([value, label]) => {
|
||||
if (label === 'Manual') {
|
||||
value = 'manual';
|
||||
}
|
||||
return {
|
||||
label,
|
||||
value,
|
||||
key: value,
|
||||
};
|
||||
}),
|
||||
]}
|
||||
onChange={(event, value) => {
|
||||
id="scm_type"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
key: '',
|
||||
label: i18n._(t`Choose an SCM Type`),
|
||||
isDisabled: true,
|
||||
},
|
||||
...scmTypeOptions.map(([value, label]) => {
|
||||
if (label === 'Manual') {
|
||||
value = 'manual';
|
||||
}
|
||||
return {
|
||||
label,
|
||||
value,
|
||||
key: value,
|
||||
};
|
||||
}),
|
||||
]}
|
||||
onChange={(event, value) => {
|
||||
scmTypeHelpers.setValue(value);
|
||||
resetScmTypeFields(value, formik);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
{formik.values.scm_type !== '' && (
|
||||
<ScmTypeFormRow>
|
||||
<SubFormTitle size="md">
|
||||
{i18n._(t`Type Details`)}
|
||||
</SubFormTitle>
|
||||
{
|
||||
{
|
||||
manual: (
|
||||
<ManualSubForm
|
||||
localPath={formik.initialValues.local_path}
|
||||
project_base_dir={project_base_dir}
|
||||
project_local_paths={project_local_paths}
|
||||
/>
|
||||
),
|
||||
git: (
|
||||
<GitSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
{formik.values.scm_type !== '' && (
|
||||
<SubFormLayout>
|
||||
<Title size="md">{i18n._(t`Type Details`)}</Title>
|
||||
<FormColumnLayout>
|
||||
{
|
||||
{
|
||||
manual: (
|
||||
<ManualSubForm
|
||||
localPath={formik.initialValues.local_path}
|
||||
project_base_dir={project_base_dir}
|
||||
project_local_paths={project_local_paths}
|
||||
/>
|
||||
),
|
||||
git: (
|
||||
<GitSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||
/>
|
||||
),
|
||||
hg: (
|
||||
<HgSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
/>
|
||||
),
|
||||
hg: (
|
||||
<HgSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||
/>
|
||||
),
|
||||
svn: (
|
||||
<SvnSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
/>
|
||||
),
|
||||
svn: (
|
||||
<SvnSubForm
|
||||
credential={credentials.scm}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||
/>
|
||||
),
|
||||
insights: (
|
||||
<InsightsSubForm
|
||||
credential={credentials.insights}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
/>
|
||||
),
|
||||
insights: (
|
||||
<InsightsSubForm
|
||||
credential={credentials.insights}
|
||||
onCredentialSelection={handleCredentialSelection}
|
||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||
/>
|
||||
),
|
||||
}[formik.values.scm_type]
|
||||
}
|
||||
</ScmTypeFormRow>
|
||||
)}
|
||||
<Config>
|
||||
{({ custom_virtualenvs }) =>
|
||||
custom_virtualenvs &&
|
||||
custom_virtualenvs.length > 1 && (
|
||||
<Field name="custom_virtualenv">
|
||||
{({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="project-custom-virtualenv"
|
||||
label={i18n._(t`Ansible Environment`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Select the playbook to be executed by
|
||||
this job.`)}
|
||||
/>
|
||||
<AnsibleSelect
|
||||
id="project-custom-virtualenv"
|
||||
data={[
|
||||
{
|
||||
/>
|
||||
),
|
||||
}[formik.values.scm_type]
|
||||
}
|
||||
</FormColumnLayout>
|
||||
</SubFormLayout>
|
||||
)}
|
||||
<Config>
|
||||
{({ custom_virtualenvs }) =>
|
||||
custom_virtualenvs &&
|
||||
custom_virtualenvs.length > 1 && (
|
||||
<FormGroup
|
||||
fieldId="project-custom-virtualenv"
|
||||
label={i18n._(t`Ansible Environment`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Select the playbook to be executed by
|
||||
this job.`)}
|
||||
/>
|
||||
<AnsibleSelect
|
||||
id="project-custom-virtualenv"
|
||||
data={[
|
||||
{
|
||||
label: i18n._(t`Use Default Ansible Environment`),
|
||||
value: '/venv/ansible/',
|
||||
key: 'default',
|
||||
},
|
||||
...custom_virtualenvs
|
||||
.filter(datum => datum !== '/venv/ansible/')
|
||||
.map(datum => ({
|
||||
label: datum,
|
||||
value: datum,
|
||||
key: datum,
|
||||
})),
|
||||
]}
|
||||
value: '/venv/ansible/',
|
||||
key: 'default',
|
||||
},
|
||||
...custom_virtualenvs
|
||||
.filter(datum => datum !== '/venv/ansible/')
|
||||
.map(datum => ({
|
||||
label: datum,
|
||||
value: datum,
|
||||
key: datum,
|
||||
})),
|
||||
]}
|
||||
{...venvField}
|
||||
/>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
</Config>
|
||||
/>
|
||||
</FormGroup>
|
||||
)
|
||||
}
|
||||
</Config>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -373,6 +370,7 @@ function ProjectForm({ i18n, project, submitError, ...props }) {
|
||||
onSubmit={formik.handleSubmit}
|
||||
css="padding: 0 24px"
|
||||
>
|
||||
<FormColumnLayout>
|
||||
<ProjectFormFields
|
||||
project_base_dir={project_base_dir}
|
||||
project_local_paths={project_local_paths}
|
||||
@ -386,11 +384,12 @@ function ProjectForm({ i18n, project, submitError, ...props }) {
|
||||
setOrganization={setOrganization}
|
||||
organization={organization}
|
||||
/>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -6,12 +6,10 @@ import CredentialLookup from '@components/Lookup/CredentialLookup';
|
||||
import FormField, { CheckboxField } from '@components/FormField';
|
||||
import { required } from '@util/validators';
|
||||
import { FormGroup, Title } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SubFormTitle = styled(Title)`
|
||||
--pf-c-title--m-md--FontWeight: 700;
|
||||
grid-column: 1 / -1;
|
||||
`;
|
||||
import {
|
||||
FormCheckboxLayout,
|
||||
FormFullWidthLayout,
|
||||
} from '@components/FormLayout';
|
||||
|
||||
export const UrlFormField = withI18n()(({ i18n, tooltip }) => (
|
||||
<FormField
|
||||
@ -44,28 +42,24 @@ export const ScmCredentialFormField = withI18n()(
|
||||
const credHelpers = useField('credential')[2];
|
||||
|
||||
return (
|
||||
<CredentialLookup
|
||||
credentialTypeId={credential.typeId}
|
||||
label={i18n._(t`SCM Credential`)}
|
||||
value={credential.value}
|
||||
onChange={value => {
|
||||
onCredentialSelection('scm', value);
|
||||
<CredentialLookup
|
||||
credentialTypeId={credential.typeId}
|
||||
label={i18n._(t`SCM Credential`)}
|
||||
value={credential.value}
|
||||
onChange={value => {
|
||||
onCredentialSelection('scm', value);
|
||||
credHelpers.setValue(value ? value.id : '');
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const ScmTypeOptions = withI18n()(
|
||||
({ i18n, scmUpdateOnLaunch, hideAllowOverride }) => (
|
||||
<>
|
||||
<FormGroup
|
||||
css="grid-column: 1/-1"
|
||||
fieldId="project-option-checkboxes"
|
||||
label={i18n._(t`Options`)}
|
||||
>
|
||||
<FormRow>
|
||||
<FormFullWidthLayout>
|
||||
<FormGroup fieldId="project-option-checkboxes" label={i18n._(t`Options`)}>
|
||||
<FormCheckboxLayout>
|
||||
<CheckboxField
|
||||
id="option-scm-clean"
|
||||
name="scm_clean"
|
||||
@ -80,9 +74,9 @@ export const ScmTypeOptions = withI18n()(
|
||||
label={i18n._(t`Delete`)}
|
||||
tooltip={i18n._(
|
||||
t`Delete the local repository in its entirety prior to
|
||||
performing an update. Depending on the size of the
|
||||
repository this may significantly increase the amount
|
||||
of time required to complete an update.`
|
||||
performing an update. Depending on the size of the
|
||||
repository this may significantly increase the amount
|
||||
of time required to complete an update.`
|
||||
)}
|
||||
/>
|
||||
<CheckboxField
|
||||
@ -91,7 +85,7 @@ export const ScmTypeOptions = withI18n()(
|
||||
label={i18n._(t`Update Revision on Launch`)}
|
||||
tooltip={i18n._(
|
||||
t`Each time a job runs using this project, update the
|
||||
revision of the project prior to starting the job.`
|
||||
revision of the project prior to starting the job.`
|
||||
)}
|
||||
/>
|
||||
{!hideAllowOverride && (
|
||||
@ -101,15 +95,16 @@ export const ScmTypeOptions = withI18n()(
|
||||
label={i18n._(t`Allow Branch Override`)}
|
||||
tooltip={i18n._(
|
||||
t`Allow changing the SCM branch or revision in a job
|
||||
template that uses this project.`
|
||||
template that uses this project.`
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</FormRow>
|
||||
</FormCheckboxLayout>
|
||||
</FormGroup>
|
||||
|
||||
{scmUpdateOnLaunch && (
|
||||
<>
|
||||
<SubFormTitle size="md">{i18n._(t`Option Details`)}</SubFormTitle>
|
||||
<Title size="md">{i18n._(t`Option Details`)}</Title>
|
||||
<FormField
|
||||
id="project-cache-timeout"
|
||||
name="scm_update_cache_timeout"
|
||||
@ -125,6 +120,6 @@ export const ScmTypeOptions = withI18n()(
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</FormFullWidthLayout>
|
||||
)
|
||||
);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export { SubFormTitle } from './SharedFields';
|
||||
export { default as GitSubForm } from './GitSubForm';
|
||||
export { default as HgSubForm } from './HgSubForm';
|
||||
export { default as InsightsSubForm } from './InsightsSubForm';
|
||||
|
||||
@ -8,6 +8,7 @@ import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||
import FormField, { FormSubmitError } from '@components/FormField';
|
||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||
import { required } from '@util/validators';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function TeamFormFields(props) {
|
||||
const { team, i18n } = props;
|
||||
@ -23,31 +24,31 @@ function TeamFormFields(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="team-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="team-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
<FormField
|
||||
id="team-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="team-description"
|
||||
label={i18n._(t`Description`)}
|
||||
name="description"
|
||||
type="text"
|
||||
/>
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={orgMeta.error}
|
||||
isValid={!orgMeta.touched || !orgMeta.error}
|
||||
onBlur={() => orgHelpers.setTouched('organization')}
|
||||
onChange={value => {
|
||||
onChange={value => {
|
||||
orgHelpers.setValue(value.id);
|
||||
setOrganization(value);
|
||||
}}
|
||||
value={organization}
|
||||
required
|
||||
/>
|
||||
setOrganization(value);
|
||||
}}
|
||||
value={organization}
|
||||
required
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -68,14 +69,15 @@ function TeamForm(props) {
|
||||
<Form
|
||||
autoComplete="off"
|
||||
onSubmit={formik.handleSubmit}
|
||||
css="padding: 0 24px"
|
||||
>
|
||||
<FormColumnLayout>
|
||||
<TeamFormFields team={team} {...rest} />
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
@ -22,10 +22,13 @@ import FormField, {
|
||||
FormSubmitError,
|
||||
} from '@components/FormField';
|
||||
import FieldWithPrompt from '@components/FieldWithPrompt';
|
||||
import FormRow from '@components/FormRow';
|
||||
import {
|
||||
FormColumnLayout,
|
||||
FormFullWidthLayout,
|
||||
FormCheckboxLayout,
|
||||
} from '@components/FormLayout';
|
||||
import CollapsibleSection from '@components/CollapsibleSection';
|
||||
import { required } from '@util/validators';
|
||||
import styled from 'styled-components';
|
||||
import { JobTemplate } from '@types';
|
||||
import {
|
||||
InventoryLookup,
|
||||
@ -37,17 +40,6 @@ import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
||||
import LabelSelect from './LabelSelect';
|
||||
import PlaybookSelect from './PlaybookSelect';
|
||||
|
||||
const GridFormGroup = styled(FormGroup)`
|
||||
& > label {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
&& {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
}
|
||||
`;
|
||||
|
||||
class JobTemplateForm extends Component {
|
||||
static propTypes = {
|
||||
template: JobTemplate,
|
||||
@ -211,9 +203,10 @@ class JobTemplateForm extends Component {
|
||||
}
|
||||
|
||||
const AdvancedFieldsWrapper = template.isNew ? CollapsibleSection : 'div';
|
||||
|
||||
return (
|
||||
<Form autoComplete="off" onSubmit={handleSubmit}>
|
||||
<FormRow>
|
||||
<FormColumnLayout>
|
||||
<FormField
|
||||
id="template-name"
|
||||
name="name"
|
||||
@ -334,266 +327,265 @@ class JobTemplateForm extends Component {
|
||||
);
|
||||
}}
|
||||
</Field>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<Field name="labels">
|
||||
{({ field }) => (
|
||||
<FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Optional labels that describe this job template,
|
||||
such as 'dev' or 'test'. Labels can be used to group and filter
|
||||
job templates and completed jobs.`)}
|
||||
/>
|
||||
<LabelSelect
|
||||
value={field.value}
|
||||
onChange={labels => setFieldValue('labels', labels)}
|
||||
onError={this.setContentError}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<Field name="credentials" fieldId="template-credentials">
|
||||
{({ field }) => (
|
||||
<MultiCredentialsLookup
|
||||
value={field.value}
|
||||
onChange={newCredentials =>
|
||||
setFieldValue('credentials', newCredentials)
|
||||
}
|
||||
onError={this.setContentError}
|
||||
tooltip={i18n._(
|
||||
t`Select credentials that allow Tower to access the nodes this job will be ran against. You can only select one credential of each type. For machine credentials (SSH), checking "Prompt on launch" without selecting credentials will require you to select a machine credential at run time. If you select credentials and check "Prompt on launch", the selected credential(s) become the defaults that can be updated at run time.`
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</FormRow>
|
||||
<AdvancedFieldsWrapper label="Advanced">
|
||||
<FormRow>
|
||||
<FormField
|
||||
id="template-forks"
|
||||
name="forks"
|
||||
type="number"
|
||||
min="0"
|
||||
label={i18n._(t`Forks`)}
|
||||
tooltip={
|
||||
<span>
|
||||
{i18n._(t`The number of parallel or simultaneous
|
||||
processes to use while executing the playbook. An empty value,
|
||||
or a value less than 1 will use the Ansible default which is
|
||||
usually 5. The default number of forks can be overwritten
|
||||
with a change to`)}{' '}
|
||||
<code>ansible.cfg</code>.{' '}
|
||||
{i18n._(t`Refer to the Ansible documentation for details
|
||||
about the configuration file.`)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<FormField
|
||||
id="template-limit"
|
||||
name="limit"
|
||||
type="text"
|
||||
label={i18n._(t`Limit`)}
|
||||
tooltip={i18n._(t`Provide a host pattern to further constrain
|
||||
the list of hosts that will be managed or affected by the
|
||||
playbook. Multiple patterns are allowed. Refer to Ansible
|
||||
documentation for more information and examples on patterns.`)}
|
||||
/>
|
||||
<Field name="verbosity">
|
||||
<FormFullWidthLayout>
|
||||
<Field name="labels">
|
||||
{({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="template-verbosity"
|
||||
label={i18n._(t`Verbosity`)}
|
||||
>
|
||||
<FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Control the level of output ansible will
|
||||
produce as the playbook executes.`)}
|
||||
content={i18n._(t`Optional labels that describe this job template,
|
||||
such as 'dev' or 'test'. Labels can be used to group and filter
|
||||
job templates and completed jobs.`)}
|
||||
/>
|
||||
<AnsibleSelect
|
||||
id="template-verbosity"
|
||||
data={verbosityOptions}
|
||||
{...field}
|
||||
<LabelSelect
|
||||
value={field.value}
|
||||
onChange={labels => setFieldValue('labels', labels)}
|
||||
onError={this.setContentError}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FormField
|
||||
id="template-job-slicing"
|
||||
name="job_slice_count"
|
||||
type="number"
|
||||
min="1"
|
||||
label={i18n._(t`Job Slicing`)}
|
||||
tooltip={i18n._(t`Divide the work done by this job template
|
||||
into the specified number of job slices, each running the
|
||||
same tasks against a portion of the inventory.`)}
|
||||
/>
|
||||
<FormField
|
||||
id="template-timeout"
|
||||
name="timeout"
|
||||
type="number"
|
||||
min="0"
|
||||
label={i18n._(t`Timeout`)}
|
||||
tooltip={i18n._(t`The amount of time (in seconds) to run
|
||||
before the task is canceled. Defaults to 0 for no job
|
||||
timeout.`)}
|
||||
/>
|
||||
<Field name="diff_mode">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
fieldId="template-show-changes"
|
||||
label={i18n._(t`Show Changes`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`If enabled, show the changes made by
|
||||
Ansible tasks, where supported. This is equivalent
|
||||
to Ansible’s --diff mode.`)}
|
||||
/>
|
||||
<div>
|
||||
<Switch
|
||||
id="template-show-changes"
|
||||
label={field.value ? i18n._(t`On`) : i18n._(t`Off`)}
|
||||
isChecked={field.value}
|
||||
onChange={checked =>
|
||||
form.setFieldValue(field.name, checked)
|
||||
}
|
||||
<Field name="credentials" fieldId="template-credentials">
|
||||
{({ field }) => (
|
||||
<MultiCredentialsLookup
|
||||
value={field.value}
|
||||
onChange={newCredentials =>
|
||||
setFieldValue('credentials', newCredentials)
|
||||
}
|
||||
onError={this.setContentError}
|
||||
tooltip={i18n._(
|
||||
t`Select credentials that allow Tower to access the nodes this job will be ran against. You can only select one credential of each type. For machine credentials (SSH), checking "Prompt on launch" without selecting credentials will require you to select a machine credential at run time. If you select credentials and check "Prompt on launch", the selected credential(s) become the defaults that can be updated at run time.`
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<AdvancedFieldsWrapper label="Advanced">
|
||||
<FormColumnLayout>
|
||||
<FormField
|
||||
id="template-forks"
|
||||
name="forks"
|
||||
type="number"
|
||||
min="0"
|
||||
label={i18n._(t`Forks`)}
|
||||
tooltip={
|
||||
<span>
|
||||
{i18n._(t`The number of parallel or simultaneous
|
||||
processes to use while executing the playbook. An empty value,
|
||||
or a value less than 1 will use the Ansible default which is
|
||||
usually 5. The default number of forks can be overwritten
|
||||
with a change to`)}{' '}
|
||||
<code>ansible.cfg</code>.{' '}
|
||||
{i18n._(t`Refer to the Ansible documentation for details
|
||||
about the configuration file.`)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<FormField
|
||||
id="template-limit"
|
||||
name="limit"
|
||||
type="text"
|
||||
label={i18n._(t`Limit`)}
|
||||
tooltip={i18n._(t`Provide a host pattern to further constrain
|
||||
the list of hosts that will be managed or affected by the
|
||||
playbook. Multiple patterns are allowed. Refer to Ansible
|
||||
documentation for more information and examples on patterns.`)}
|
||||
/>
|
||||
<Field name="verbosity">
|
||||
{({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="template-verbosity"
|
||||
label={i18n._(t`Verbosity`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Control the level of output ansible will
|
||||
produce as the playbook executes.`)}
|
||||
/>
|
||||
<AnsibleSelect
|
||||
id="template-verbosity"
|
||||
data={verbosityOptions}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FormField
|
||||
id="template-job-slicing"
|
||||
name="job_slice_count"
|
||||
type="number"
|
||||
min="1"
|
||||
label={i18n._(t`Job Slicing`)}
|
||||
tooltip={i18n._(t`Divide the work done by this job template
|
||||
into the specified number of job slices, each running the
|
||||
same tasks against a portion of the inventory.`)}
|
||||
/>
|
||||
<FormField
|
||||
id="template-timeout"
|
||||
name="timeout"
|
||||
type="number"
|
||||
min="0"
|
||||
label={i18n._(t`Timeout`)}
|
||||
tooltip={i18n._(t`The amount of time (in seconds) to run
|
||||
before the task is canceled. Defaults to 0 for no job
|
||||
timeout.`)}
|
||||
/>
|
||||
<Field name="diff_mode">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
fieldId="template-show-changes"
|
||||
label={i18n._(t`Show Changes`)}
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`If enabled, show the changes made by
|
||||
Ansible tasks, where supported. This is equivalent
|
||||
to Ansible’s --diff mode.`)}
|
||||
/>
|
||||
<div>
|
||||
<Switch
|
||||
id="template-show-changes"
|
||||
label={field.value ? i18n._(t`On`) : i18n._(t`Off`)}
|
||||
isChecked={field.value}
|
||||
onChange={checked =>
|
||||
form.setFieldValue(field.name, checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FormFullWidthLayout>
|
||||
<Field name="instanceGroups">
|
||||
{({ field, form }) => (
|
||||
<InstanceGroupsLookup
|
||||
value={field.value}
|
||||
onChange={value =>
|
||||
form.setFieldValue(field.name, value)
|
||||
}
|
||||
tooltip={i18n._(t`Select the Instance Groups for this Organization
|
||||
to run on.`)}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="job_tags">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
label={i18n._(t`Job Tags`)}
|
||||
fieldId="template-job-tags"
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Tags are useful when you have a large
|
||||
playbook, and you want to run a specific part of a
|
||||
play or task. Use commas to separate multiple tags.
|
||||
Refer to Ansible Tower documentation for details on
|
||||
the usage of tags.`)}
|
||||
/>
|
||||
<TagMultiSelect
|
||||
value={field.value}
|
||||
onChange={value =>
|
||||
form.setFieldValue(field.name, value)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="skip_tags">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
label={i18n._(t`Skip Tags`)}
|
||||
fieldId="template-skip-tags"
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Skip tags are useful when you have a
|
||||
large playbook, and you want to skip specific parts of a
|
||||
play or task. Use commas to separate multiple tags. Refer
|
||||
to Ansible Tower documentation for details on the usage
|
||||
of tags.`)}
|
||||
/>
|
||||
<TagMultiSelect
|
||||
value={field.value}
|
||||
onChange={value =>
|
||||
form.setFieldValue(field.name, value)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FormGroup
|
||||
fieldId="template-option-checkboxes"
|
||||
isInline
|
||||
label={i18n._(t`Options`)}
|
||||
>
|
||||
<FormCheckboxLayout>
|
||||
<CheckboxField
|
||||
id="option-privilege-escalation"
|
||||
name="become_enabled"
|
||||
label={i18n._(t`Privilege Escalation`)}
|
||||
tooltip={i18n._(t`If enabled, run this playbook as an
|
||||
administrator.`)}
|
||||
/>
|
||||
<Checkbox
|
||||
aria-label={i18n._(t`Provisioning Callbacks`)}
|
||||
label={
|
||||
<span>
|
||||
{i18n._(t`Provisioning Callbacks`)}
|
||||
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Enables creation of a provisioning
|
||||
callback URL. Using the URL a host can contact BRAND_NAME
|
||||
and request a configuration update using this job
|
||||
template.`)}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
id="option-callbacks"
|
||||
isChecked={allowCallbacks}
|
||||
onChange={checked => {
|
||||
this.setState({ allowCallbacks: checked });
|
||||
}}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="option-concurrent"
|
||||
name="allow_simultaneous"
|
||||
label={i18n._(t`Concurrent Jobs`)}
|
||||
tooltip={i18n._(t`If enabled, simultaneous runs of this job
|
||||
template will be allowed.`)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="option-fact-cache"
|
||||
name="use_fact_cache"
|
||||
label={i18n._(t`Fact Cache`)}
|
||||
tooltip={i18n._(t`If enabled, use cached facts if available
|
||||
and store discovered facts in the cache.`)}
|
||||
/>
|
||||
</FormCheckboxLayout>
|
||||
</FormGroup>
|
||||
</FormFullWidthLayout>
|
||||
{allowCallbacks && (
|
||||
<>
|
||||
{callbackUrl && (
|
||||
<FormGroup
|
||||
label={i18n._(t`Provisioning Callback URL`)}
|
||||
fieldId="template-callback-url"
|
||||
>
|
||||
<TextInput
|
||||
id="template-callback-url"
|
||||
isDisabled
|
||||
value={callbackUrl}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormField
|
||||
id="template-host-config-key"
|
||||
name="host_config_key"
|
||||
label={i18n._(t`Host Config Key`)}
|
||||
validate={allowCallbacks ? required(null, i18n) : null}
|
||||
/>
|
||||
</div>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
</FormRow>
|
||||
<Field name="instanceGroups">
|
||||
{({ field, form }) => (
|
||||
<InstanceGroupsLookup
|
||||
css="margin-top: 20px"
|
||||
value={field.value}
|
||||
onChange={value => form.setFieldValue(field.name, value)}
|
||||
tooltip={i18n._(t`Select the Instance Groups for this Organization
|
||||
to run on.`)}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="job_tags">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
label={i18n._(t`Job Tags`)}
|
||||
css="margin-top: 20px"
|
||||
fieldId="template-job-tags"
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Tags are useful when you have a large
|
||||
playbook, and you want to run a specific part of a
|
||||
play or task. Use commas to separate multiple tags.
|
||||
Refer to Ansible Tower documentation for details on
|
||||
the usage of tags.`)}
|
||||
/>
|
||||
<TagMultiSelect
|
||||
value={field.value}
|
||||
onChange={value => form.setFieldValue(field.name, value)}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="skip_tags">
|
||||
{({ field, form }) => (
|
||||
<FormGroup
|
||||
label={i18n._(t`Skip Tags`)}
|
||||
css="margin-top: 20px"
|
||||
fieldId="template-skip-tags"
|
||||
>
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Skip tags are useful when you have a
|
||||
large playbook, and you want to skip specific parts of a
|
||||
play or task. Use commas to separate multiple tags. Refer
|
||||
to Ansible Tower documentation for details on the usage
|
||||
of tags.`)}
|
||||
/>
|
||||
<TagMultiSelect
|
||||
value={field.value}
|
||||
onChange={value => form.setFieldValue(field.name, value)}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<GridFormGroup
|
||||
fieldId="template-option-checkboxes"
|
||||
isInline
|
||||
label={i18n._(t`Options`)}
|
||||
css="margin-top: 20px"
|
||||
>
|
||||
<CheckboxField
|
||||
id="option-privilege-escalation"
|
||||
name="become_enabled"
|
||||
label={i18n._(t`Privilege Escalation`)}
|
||||
tooltip={i18n._(t`If enabled, run this playbook as an
|
||||
administrator.`)}
|
||||
/>
|
||||
<Checkbox
|
||||
aria-label={i18n._(t`Provisioning Callbacks`)}
|
||||
label={
|
||||
<span>
|
||||
{i18n._(t`Provisioning Callbacks`)}
|
||||
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Enables creation of a provisioning
|
||||
callback URL. Using the URL a host can contact BRAND_NAME
|
||||
and request a configuration update using this job
|
||||
template.`)}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
id="option-callbacks"
|
||||
isChecked={allowCallbacks}
|
||||
onChange={checked => {
|
||||
this.setState({ allowCallbacks: checked });
|
||||
}}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="option-concurrent"
|
||||
name="allow_simultaneous"
|
||||
label={i18n._(t`Concurrent Jobs`)}
|
||||
tooltip={i18n._(t`If enabled, simultaneous runs of this job
|
||||
template will be allowed.`)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="option-fact-cache"
|
||||
name="use_fact_cache"
|
||||
label={i18n._(t`Fact Cache`)}
|
||||
tooltip={i18n._(t`If enabled, use cached facts if available
|
||||
and store discovered facts in the cache.`)}
|
||||
/>
|
||||
</GridFormGroup>
|
||||
<div
|
||||
css={`
|
||||
${allowCallbacks ? '' : 'display: none'}
|
||||
margin-top: 20px;
|
||||
`}
|
||||
>
|
||||
<FormRow>
|
||||
{callbackUrl && (
|
||||
<FormGroup
|
||||
label={i18n._(t`Provisioning Callback URL`)}
|
||||
fieldId="template-callback-url"
|
||||
>
|
||||
<TextInput
|
||||
id="template-callback-url"
|
||||
isDisabled
|
||||
value={callbackUrl}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormField
|
||||
id="template-host-config-key"
|
||||
name="host_config_key"
|
||||
label={i18n._(t`Host Config Key`)}
|
||||
validate={allowCallbacks ? required(null, i18n) : null}
|
||||
/>
|
||||
</FormRow>
|
||||
</div>
|
||||
</AdvancedFieldsWrapper>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup onCancel={handleCancel} onSubmit={handleSubmit} />
|
||||
</>
|
||||
)}
|
||||
</FormColumnLayout>
|
||||
</AdvancedFieldsWrapper>
|
||||
</FormFullWidthLayout>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup onCancel={handleCancel} onSubmit={handleSubmit} />
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@ import FormField, {
|
||||
PasswordField,
|
||||
FormSubmitError,
|
||||
} from '@components/FormField';
|
||||
import FormRow from '@components/FormRow';
|
||||
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
|
||||
import { required, requiredEmail } from '@util/validators';
|
||||
import { FormColumnLayout } from '@components/FormLayout';
|
||||
|
||||
function UserFormFields({ user, i18n }) {
|
||||
const [organization, setOrganization] = useState(null);
|
||||
@ -41,7 +41,7 @@ function UserFormFields({ user, i18n }) {
|
||||
const organizationFieldArr = useField({
|
||||
name: 'organization',
|
||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||
});
|
||||
});
|
||||
const organizationMeta = organizationFieldArr[1];
|
||||
const organizationHelpers = organizationFieldArr[2];
|
||||
|
||||
@ -99,6 +99,7 @@ function UserFormFields({ user, i18n }) {
|
||||
type="text"
|
||||
/>
|
||||
{!user.id && (
|
||||
<<<<<<< HEAD
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={organizationMeta.error}
|
||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||
@ -125,8 +126,36 @@ function UserFormFields({ user, i18n }) {
|
||||
{...userTypeField}
|
||||
/>
|
||||
</FormGroup>
|
||||
=======
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={organizationMeta.error}
|
||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||
onBlur={() => organizationHelpers.setTouched()}
|
||||
onChange={value => {
|
||||
organizationHelpers.setValue(value.id);
|
||||
setOrganization(value);
|
||||
}}
|
||||
value={organization}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
<FormGroup
|
||||
fieldId="user-type"
|
||||
helperTextInvalid={userTypeMeta.error}
|
||||
isRequired
|
||||
isValid={!userTypeMeta.touched || !userTypeMeta.error}
|
||||
label={i18n._(t`User Type`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
isValid={!userTypeMeta.touched || !userTypeMeta.error}
|
||||
id="user-type"
|
||||
data={userTypeOptions}
|
||||
{...userTypeField}
|
||||
/>
|
||||
</FormGroup>
|
||||
>>>>>>> update forms from FormRow to using FormLayout components
|
||||
</>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) {
|
||||
@ -173,12 +202,14 @@ function UserForm({ user, handleCancel, handleSubmit, submitError, i18n }) {
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<UserFormFields user={user} i18n={i18n} />
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user