mirror of
https://github.com/ansible/awx.git
synced 2026-03-28 06:15:04 -02:30
Auto populate various required lookups on various forms
This commit is contained in:
@@ -10,6 +10,7 @@ import { getQSConfig, parseQueryString, mergeParams } from '../../util/qs';
|
|||||||
import { FieldTooltip } from '../FormField';
|
import { FieldTooltip } from '../FormField';
|
||||||
import Lookup from './Lookup';
|
import Lookup from './Lookup';
|
||||||
import OptionsList from '../OptionsList';
|
import OptionsList from '../OptionsList';
|
||||||
|
import useAutoPopulateLookup from '../../util/useAutoPopulateLookup';
|
||||||
import useRequest from '../../util/useRequest';
|
import useRequest from '../../util/useRequest';
|
||||||
import LookupErrorMessage from './shared/LookupErrorMessage';
|
import LookupErrorMessage from './shared/LookupErrorMessage';
|
||||||
|
|
||||||
@@ -34,7 +35,9 @@ function CredentialLookup({
|
|||||||
i18n,
|
i18n,
|
||||||
tooltip,
|
tooltip,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
|
autoPopulate,
|
||||||
}) {
|
}) {
|
||||||
|
const autoPopulateLookup = useAutoPopulateLookup(onChange);
|
||||||
const {
|
const {
|
||||||
result: { count, credentials, relatedSearchableKeys, searchableKeys },
|
result: { count, credentials, relatedSearchableKeys, searchableKeys },
|
||||||
error,
|
error,
|
||||||
@@ -62,6 +65,11 @@ function CredentialLookup({
|
|||||||
),
|
),
|
||||||
CredentialsAPI.readOptions,
|
CredentialsAPI.readOptions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (autoPopulate) {
|
||||||
|
autoPopulateLookup(data.results);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
count: data.count,
|
count: data.count,
|
||||||
credentials: data.results,
|
credentials: data.results,
|
||||||
@@ -73,6 +81,8 @@ function CredentialLookup({
|
|||||||
).filter(key => actionsResponse.data?.actions?.GET[key]?.filterable),
|
).filter(key => actionsResponse.data?.actions?.GET[key]?.filterable),
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
|
autoPopulate,
|
||||||
|
autoPopulateLookup,
|
||||||
credentialTypeId,
|
credentialTypeId,
|
||||||
credentialTypeKind,
|
credentialTypeKind,
|
||||||
credentialTypeNamespace,
|
credentialTypeNamespace,
|
||||||
@@ -182,6 +192,8 @@ CredentialLookup.propTypes = {
|
|||||||
onChange: func.isRequired,
|
onChange: func.isRequired,
|
||||||
required: bool,
|
required: bool,
|
||||||
value: Credential,
|
value: Credential,
|
||||||
|
isDisabled: bool,
|
||||||
|
autoPopulate: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
CredentialLookup.defaultProps = {
|
CredentialLookup.defaultProps = {
|
||||||
@@ -192,6 +204,8 @@ CredentialLookup.defaultProps = {
|
|||||||
onBlur: () => {},
|
onBlur: () => {},
|
||||||
required: false,
|
required: false,
|
||||||
value: null,
|
value: null,
|
||||||
|
isDisabled: false,
|
||||||
|
autoPopulate: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { CredentialLookup as _CredentialLookup };
|
export { CredentialLookup as _CredentialLookup };
|
||||||
|
|||||||
@@ -88,4 +88,66 @@ describe('CredentialLookup', () => {
|
|||||||
expect(_CredentialLookup.defaultProps.onBlur).toBeInstanceOf(Function);
|
expect(_CredentialLookup.defaultProps.onBlur).toBeInstanceOf(Function);
|
||||||
expect(_CredentialLookup.defaultProps.onBlur).not.toThrow();
|
expect(_CredentialLookup.defaultProps.onBlur).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should auto-select credential when only one available and autoPopulate prop is true', async () => {
|
||||||
|
CredentialsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialLookup
|
||||||
|
autoPopulate
|
||||||
|
credentialTypeId={1}
|
||||||
|
label="Foo"
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(onChange).toHaveBeenCalledWith({ id: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not auto-select credential when autoPopulate prop is false', async () => {
|
||||||
|
CredentialsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialLookup
|
||||||
|
credentialTypeId={1}
|
||||||
|
label="Foo"
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not auto-select credential when multiple available', async () => {
|
||||||
|
CredentialsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }, { id: 2 }],
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialLookup
|
||||||
|
credentialTypeId={1}
|
||||||
|
label="Foo"
|
||||||
|
autoPopulate
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { OrganizationsAPI } from '../../api';
|
|||||||
import { Organization } from '../../types';
|
import { Organization } from '../../types';
|
||||||
import { getQSConfig, parseQueryString } from '../../util/qs';
|
import { getQSConfig, parseQueryString } from '../../util/qs';
|
||||||
import useRequest from '../../util/useRequest';
|
import useRequest from '../../util/useRequest';
|
||||||
|
import useAutoPopulateLookup from '../../util/useAutoPopulateLookup';
|
||||||
import OptionsList from '../OptionsList';
|
import OptionsList from '../OptionsList';
|
||||||
import Lookup from './Lookup';
|
import Lookup from './Lookup';
|
||||||
import LookupErrorMessage from './shared/LookupErrorMessage';
|
import LookupErrorMessage from './shared/LookupErrorMessage';
|
||||||
@@ -27,7 +28,10 @@ function OrganizationLookup({
|
|||||||
required,
|
required,
|
||||||
value,
|
value,
|
||||||
history,
|
history,
|
||||||
|
autoPopulate,
|
||||||
}) {
|
}) {
|
||||||
|
const autoPopulateLookup = useAutoPopulateLookup(onChange);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
result: { itemCount, organizations, relatedSearchableKeys, searchableKeys },
|
result: { itemCount, organizations, relatedSearchableKeys, searchableKeys },
|
||||||
error: contentError,
|
error: contentError,
|
||||||
@@ -39,6 +43,11 @@ function OrganizationLookup({
|
|||||||
OrganizationsAPI.read(params),
|
OrganizationsAPI.read(params),
|
||||||
OrganizationsAPI.readOptions(),
|
OrganizationsAPI.readOptions(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (autoPopulate) {
|
||||||
|
autoPopulateLookup(response.data.results);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
organizations: response.data.results,
|
organizations: response.data.results,
|
||||||
itemCount: response.data.count,
|
itemCount: response.data.count,
|
||||||
@@ -49,7 +58,7 @@ function OrganizationLookup({
|
|||||||
actionsResponse.data.actions?.GET || {}
|
actionsResponse.data.actions?.GET || {}
|
||||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||||
};
|
};
|
||||||
}, [history.location.search]),
|
}, [autoPopulate, autoPopulateLookup, history.location.search]),
|
||||||
{
|
{
|
||||||
organizations: [],
|
organizations: [],
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
@@ -129,6 +138,7 @@ OrganizationLookup.propTypes = {
|
|||||||
onChange: func.isRequired,
|
onChange: func.isRequired,
|
||||||
required: bool,
|
required: bool,
|
||||||
value: Organization,
|
value: Organization,
|
||||||
|
autoPopulate: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
OrganizationLookup.defaultProps = {
|
OrganizationLookup.defaultProps = {
|
||||||
@@ -137,6 +147,7 @@ OrganizationLookup.defaultProps = {
|
|||||||
onBlur: () => {},
|
onBlur: () => {},
|
||||||
required: false,
|
required: false,
|
||||||
value: null,
|
value: null,
|
||||||
|
autoPopulate: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OrganizationLookup as _OrganizationLookup };
|
export { OrganizationLookup as _OrganizationLookup };
|
||||||
|
|||||||
@@ -48,4 +48,50 @@ describe('OrganizationLookup', () => {
|
|||||||
expect(_OrganizationLookup.defaultProps.onBlur).toBeInstanceOf(Function);
|
expect(_OrganizationLookup.defaultProps.onBlur).toBeInstanceOf(Function);
|
||||||
expect(_OrganizationLookup.defaultProps.onBlur).not.toThrow();
|
expect(_OrganizationLookup.defaultProps.onBlur).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should auto-select organization when only one available and autoPopulate prop is true', async () => {
|
||||||
|
OrganizationsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<OrganizationLookup autoPopulate onChange={onChange} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(onChange).toHaveBeenCalledWith({ id: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not auto-select organization when autoPopulate prop is false', async () => {
|
||||||
|
OrganizationsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<OrganizationLookup onChange={onChange} />);
|
||||||
|
});
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not auto-select organization when multiple available', async () => {
|
||||||
|
OrganizationsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }, { id: 2 }],
|
||||||
|
count: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<OrganizationLookup autoPopulate onChange={onChange} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ProjectsAPI } from '../../api';
|
|||||||
import { Project } from '../../types';
|
import { Project } from '../../types';
|
||||||
import { FieldTooltip } from '../FormField';
|
import { FieldTooltip } from '../FormField';
|
||||||
import OptionsList from '../OptionsList';
|
import OptionsList from '../OptionsList';
|
||||||
|
import useAutoPopulateLookup from '../../util/useAutoPopulateLookup';
|
||||||
import useRequest from '../../util/useRequest';
|
import useRequest from '../../util/useRequest';
|
||||||
import { getQSConfig, parseQueryString } from '../../util/qs';
|
import { getQSConfig, parseQueryString } from '../../util/qs';
|
||||||
import Lookup from './Lookup';
|
import Lookup from './Lookup';
|
||||||
@@ -21,7 +22,7 @@ const QS_CONFIG = getQSConfig('project', {
|
|||||||
|
|
||||||
function ProjectLookup({
|
function ProjectLookup({
|
||||||
helperTextInvalid,
|
helperTextInvalid,
|
||||||
autocomplete,
|
autoPopulate,
|
||||||
i18n,
|
i18n,
|
||||||
isValid,
|
isValid,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -31,6 +32,7 @@ function ProjectLookup({
|
|||||||
onBlur,
|
onBlur,
|
||||||
history,
|
history,
|
||||||
}) {
|
}) {
|
||||||
|
const autoPopulateLookup = useAutoPopulateLookup(onChange);
|
||||||
const {
|
const {
|
||||||
result: { projects, count, relatedSearchableKeys, searchableKeys, canEdit },
|
result: { projects, count, relatedSearchableKeys, searchableKeys, canEdit },
|
||||||
request: fetchProjects,
|
request: fetchProjects,
|
||||||
@@ -43,8 +45,8 @@ function ProjectLookup({
|
|||||||
ProjectsAPI.read(params),
|
ProjectsAPI.read(params),
|
||||||
ProjectsAPI.readOptions(),
|
ProjectsAPI.readOptions(),
|
||||||
]);
|
]);
|
||||||
if (data.count === 1 && autocomplete) {
|
if (autoPopulate) {
|
||||||
autocomplete(data.results[0]);
|
autoPopulateLookup(data.results);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
count: data.count,
|
count: data.count,
|
||||||
@@ -57,7 +59,7 @@ function ProjectLookup({
|
|||||||
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
).filter(key => actionsResponse.data.actions?.GET[key].filterable),
|
||||||
canEdit: Boolean(actionsResponse.data.actions.POST),
|
canEdit: Boolean(actionsResponse.data.actions.POST),
|
||||||
};
|
};
|
||||||
}, [history.location.search, autocomplete]),
|
}, [autoPopulate, autoPopulateLookup, history.location.search]),
|
||||||
{
|
{
|
||||||
count: 0,
|
count: 0,
|
||||||
projects: [],
|
projects: [],
|
||||||
@@ -151,7 +153,7 @@ function ProjectLookup({
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProjectLookup.propTypes = {
|
ProjectLookup.propTypes = {
|
||||||
autocomplete: func,
|
autoPopulate: bool,
|
||||||
helperTextInvalid: node,
|
helperTextInvalid: node,
|
||||||
isValid: bool,
|
isValid: bool,
|
||||||
onBlur: func,
|
onBlur: func,
|
||||||
@@ -162,7 +164,7 @@ ProjectLookup.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ProjectLookup.defaultProps = {
|
ProjectLookup.defaultProps = {
|
||||||
autocomplete: () => {},
|
autoPopulate: false,
|
||||||
helperTextInvalid: '',
|
helperTextInvalid: '',
|
||||||
isValid: true,
|
isValid: true,
|
||||||
onBlur: () => {},
|
onBlur: () => {},
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||||
import { sleep } from '../../../testUtils/testUtils';
|
|
||||||
import { ProjectsAPI } from '../../api';
|
import { ProjectsAPI } from '../../api';
|
||||||
import ProjectLookup from './ProjectLookup';
|
import ProjectLookup from './ProjectLookup';
|
||||||
|
|
||||||
jest.mock('../../api');
|
jest.mock('../../api');
|
||||||
|
|
||||||
describe('<ProjectLookup />', () => {
|
describe('<ProjectLookup />', () => {
|
||||||
test('should auto-select project when only one available', async () => {
|
test('should auto-select project when only one available and autoPopulate prop is true', async () => {
|
||||||
ProjectsAPI.read.mockReturnValue({
|
ProjectsAPI.read.mockReturnValue({
|
||||||
data: {
|
data: {
|
||||||
results: [{ id: 1 }],
|
results: [{ id: 1 }],
|
||||||
count: 1,
|
count: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const autocomplete = jest.fn();
|
const onChange = jest.fn();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
mountWithContexts(
|
mountWithContexts(<ProjectLookup autoPopulate onChange={onChange} />);
|
||||||
<ProjectLookup autocomplete={autocomplete} onChange={() => {}} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
await sleep(0);
|
expect(onChange).toHaveBeenCalledWith({ id: 1 });
|
||||||
expect(autocomplete).toHaveBeenCalledWith({ id: 1 });
|
});
|
||||||
|
|
||||||
|
test('should not auto-select project when autoPopulate prop is false', async () => {
|
||||||
|
ProjectsAPI.read.mockReturnValue({
|
||||||
|
data: {
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const onChange = jest.fn();
|
||||||
|
await act(async () => {
|
||||||
|
mountWithContexts(<ProjectLookup onChange={onChange} />);
|
||||||
|
});
|
||||||
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not auto-select project when multiple available', async () => {
|
test('should not auto-select project when multiple available', async () => {
|
||||||
@@ -32,13 +42,10 @@ describe('<ProjectLookup />', () => {
|
|||||||
count: 2,
|
count: 2,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const autocomplete = jest.fn();
|
const onChange = jest.fn();
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
mountWithContexts(
|
mountWithContexts(<ProjectLookup autoPopulate onChange={onChange} />);
|
||||||
<ProjectLookup autocomplete={autocomplete} onChange={() => {}} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
await sleep(0);
|
expect(onChange).not.toHaveBeenCalled();
|
||||||
expect(autocomplete).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useRouteMatch } from 'react-router-dom';
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { Form, FormGroup } from '@patternfly/react-core';
|
import { Form, FormGroup } from '@patternfly/react-core';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
@@ -18,10 +18,12 @@ import AnsibleSelect from '../../../components/AnsibleSelect';
|
|||||||
|
|
||||||
function ApplicationFormFields({
|
function ApplicationFormFields({
|
||||||
i18n,
|
i18n,
|
||||||
|
application,
|
||||||
authorizationOptions,
|
authorizationOptions,
|
||||||
clientTypeOptions,
|
clientTypeOptions,
|
||||||
}) {
|
}) {
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
validate: required(null, i18n),
|
validate: required(null, i18n),
|
||||||
@@ -40,6 +42,13 @@ function ApplicationFormFields({
|
|||||||
validate: required(null, i18n),
|
validate: required(null, i18n),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -60,11 +69,10 @@ function ApplicationFormFields({
|
|||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||||
onBlur={() => organizationHelpers.setTouched()}
|
onBlur={() => organizationHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={organizationField.value}
|
value={organizationField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!application?.id}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="authType"
|
fieldId="authType"
|
||||||
@@ -166,6 +174,7 @@ function ApplicationForm({
|
|||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<ApplicationFormFields
|
<ApplicationFormFields
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
application={application}
|
||||||
authorizationOptions={authorizationOptions}
|
authorizationOptions={authorizationOptions}
|
||||||
clientTypeOptions={clientTypeOptions}
|
clientTypeOptions={clientTypeOptions}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { arrayOf, func, object, shape } from 'prop-types';
|
import { arrayOf, func, object, shape } from 'prop-types';
|
||||||
@@ -21,6 +21,7 @@ function CredentialFormFields({
|
|||||||
formik,
|
formik,
|
||||||
initialValues,
|
initialValues,
|
||||||
}) {
|
}) {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [orgField, orgMeta, orgHelpers] = useField('organization');
|
const [orgField, orgMeta, orgHelpers] = useField('organization');
|
||||||
const [credTypeField, credTypeMeta, credTypeHelpers] = useField({
|
const [credTypeField, credTypeMeta, credTypeHelpers] = useField({
|
||||||
name: 'credential_type',
|
name: 'credential_type',
|
||||||
@@ -76,6 +77,13 @@ function CredentialFormFields({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -96,9 +104,7 @@ function CredentialFormFields({
|
|||||||
helperTextInvalid={orgMeta.error}
|
helperTextInvalid={orgMeta.error}
|
||||||
isValid={!orgMeta.touched || !orgMeta.error}
|
isValid={!orgMeta.touched || !orgMeta.error}
|
||||||
onBlur={() => orgHelpers.setTouched()}
|
onBlur={() => orgHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
orgHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={orgField.value}
|
value={orgField.value}
|
||||||
touched={orgMeta.touched}
|
touched={orgMeta.touched}
|
||||||
error={orgMeta.error}
|
error={orgMeta.error}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { func, number, shape } from 'prop-types';
|
import { func, number, shape } from 'prop-types';
|
||||||
@@ -17,18 +17,29 @@ import {
|
|||||||
FormFullWidthLayout,
|
FormFullWidthLayout,
|
||||||
} from '../../../components/FormLayout';
|
} from '../../../components/FormLayout';
|
||||||
|
|
||||||
function InventoryFormFields({ i18n, credentialTypeId }) {
|
function InventoryFormFields({ i18n, credentialTypeId, inventory }) {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
});
|
});
|
||||||
const instanceGroupsFieldArr = useField('instanceGroups');
|
const [instanceGroupsField, , instanceGroupsHelpers] = useField(
|
||||||
const instanceGroupsField = instanceGroupsFieldArr[0];
|
'instanceGroups'
|
||||||
const instanceGroupsHelpers = instanceGroupsFieldArr[2];
|
);
|
||||||
|
const [insightsCredentialField] = useField('insights_credential');
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
const onCredentialChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('insights_credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
const insightsCredentialFieldArr = useField('insights_credential');
|
|
||||||
const insightsCredentialField = insightsCredentialFieldArr[0];
|
|
||||||
const insightsCredentialHelpers = insightsCredentialFieldArr[2];
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -49,18 +60,17 @@ function InventoryFormFields({ i18n, credentialTypeId }) {
|
|||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||||
onBlur={() => organizationHelpers.setTouched()}
|
onBlur={() => organizationHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={organizationField.value}
|
value={organizationField.value}
|
||||||
touched={organizationMeta.touched}
|
touched={organizationMeta.touched}
|
||||||
error={organizationMeta.error}
|
error={organizationMeta.error}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!inventory?.id}
|
||||||
/>
|
/>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
label={i18n._(t`Insights Credential`)}
|
label={i18n._(t`Insights Credential`)}
|
||||||
credentialTypeId={credentialTypeId}
|
credentialTypeId={credentialTypeId}
|
||||||
onChange={value => insightsCredentialHelpers.setValue(value)}
|
onChange={onCredentialChange}
|
||||||
value={insightsCredentialField.value}
|
value={insightsCredentialField.value}
|
||||||
/>
|
/>
|
||||||
<InstanceGroupsLookup
|
<InstanceGroupsLookup
|
||||||
@@ -115,7 +125,7 @@ function InventoryForm({
|
|||||||
{formik => (
|
{formik => (
|
||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<InventoryFormFields {...rest} />
|
<InventoryFormFields {...rest} inventory={inventory} />
|
||||||
<FormSubmitError error={submitError} />
|
<FormSubmitError error={submitError} />
|
||||||
<FormActionGroup
|
<FormActionGroup
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const buildSourceChoiceOptions = options => {
|
|||||||
return sourceChoices.filter(({ key }) => key !== 'file');
|
return sourceChoices.filter(({ key }) => key !== 'file');
|
||||||
};
|
};
|
||||||
|
|
||||||
const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
|
const InventorySourceFormFields = ({ source, sourceOptions, i18n }) => {
|
||||||
const {
|
const {
|
||||||
values,
|
values,
|
||||||
initialValues,
|
initialValues,
|
||||||
@@ -170,16 +170,67 @@ const InventorySourceFormFields = ({ sourceOptions, i18n }) => {
|
|||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
azure_rm: <AzureSubForm sourceOptions={sourceOptions} />,
|
azure_rm: (
|
||||||
|
<AzureSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'azure_rm'
|
||||||
|
}
|
||||||
|
sourceOptions={sourceOptions}
|
||||||
|
/>
|
||||||
|
),
|
||||||
cloudforms: <CloudFormsSubForm />,
|
cloudforms: <CloudFormsSubForm />,
|
||||||
ec2: <EC2SubForm sourceOptions={sourceOptions} />,
|
ec2: <EC2SubForm sourceOptions={sourceOptions} />,
|
||||||
gce: <GCESubForm sourceOptions={sourceOptions} />,
|
gce: (
|
||||||
openstack: <OpenStackSubForm />,
|
<GCESubForm
|
||||||
rhv: <VirtualizationSubForm />,
|
autoPopulateCredential={
|
||||||
satellite6: <SatelliteSubForm />,
|
!source?.id || source?.source !== 'gce'
|
||||||
scm: <SCMSubForm />,
|
}
|
||||||
tower: <TowerSubForm />,
|
sourceOptions={sourceOptions}
|
||||||
vmware: <VMwareSubForm sourceOptions={sourceOptions} />,
|
/>
|
||||||
|
),
|
||||||
|
openstack: (
|
||||||
|
<OpenStackSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'openstack'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
rhv: (
|
||||||
|
<VirtualizationSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'rhv'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
satellite6: (
|
||||||
|
<SatelliteSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'satellite6'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
scm: (
|
||||||
|
<SCMSubForm
|
||||||
|
autoPopulateProject={
|
||||||
|
!source?.id || source?.source !== 'scm'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
tower: (
|
||||||
|
<TowerSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'tower'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
vmware: (
|
||||||
|
<VMwareSubForm
|
||||||
|
autoPopulateCredential={
|
||||||
|
!source?.id || source?.source !== 'vmware'
|
||||||
|
}
|
||||||
|
sourceOptions={sourceOptions}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}[sourceField.value]
|
}[sourceField.value]
|
||||||
}
|
}
|
||||||
</FormColumnLayout>
|
</FormColumnLayout>
|
||||||
@@ -255,6 +306,7 @@ const InventorySourceForm = ({
|
|||||||
<InventorySourceFormFields
|
<InventorySourceFormFields
|
||||||
formik={formik}
|
formik={formik}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
source={source}
|
||||||
sourceOptions={sourceOptions}
|
sourceOptions={sourceOptions}
|
||||||
/>
|
/>
|
||||||
{submitError && <FormSubmitError error={submitError} />}
|
{submitError && <FormSubmitError error={submitError} />}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -12,11 +12,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const AzureSubForm = ({ i18n }) => {
|
const AzureSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -25,11 +33,10 @@ const AzureSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -13,10 +13,18 @@ import {
|
|||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const CloudFormsSubForm = ({ i18n }) => {
|
const CloudFormsSubForm = ({ i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -25,9 +33,7 @@ const CloudFormsSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -13,16 +13,23 @@ import {
|
|||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const EC2SubForm = ({ i18n }) => {
|
const EC2SubForm = ({ i18n }) => {
|
||||||
const [credentialField, , credentialHelpers] = useField('credential');
|
const { setFieldValue } = useFormikContext();
|
||||||
|
const [credentialField] = useField('credential');
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
credentialTypeNamespace="aws"
|
credentialTypeNamespace="aws"
|
||||||
label={i18n._(t`Credential`)}
|
label={i18n._(t`Credential`)}
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -11,11 +11,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const GCESubForm = ({ i18n }) => {
|
const GCESubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -24,11 +32,10 @@ const GCESubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -12,11 +12,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const OpenStackSubForm = ({ i18n }) => {
|
const OpenStackSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -25,11 +33,10 @@ const OpenStackSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { FormGroup } from '@patternfly/react-core';
|
import { FormGroup } from '@patternfly/react-core';
|
||||||
@@ -20,8 +20,9 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const SCMSubForm = ({ i18n }) => {
|
const SCMSubForm = ({ autoPopulateProject, i18n }) => {
|
||||||
const [credentialField, , credentialHelpers] = useField('credential');
|
const { setFieldValue } = useFormikContext();
|
||||||
|
const [credentialField] = useField('credential');
|
||||||
const [projectField, projectMeta, projectHelpers] = useField({
|
const [projectField, projectMeta, projectHelpers] = useField({
|
||||||
name: 'source_project',
|
name: 'source_project',
|
||||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
@@ -51,21 +52,18 @@ const SCMSubForm = ({ i18n }) => {
|
|||||||
|
|
||||||
const handleProjectUpdate = useCallback(
|
const handleProjectUpdate = useCallback(
|
||||||
value => {
|
value => {
|
||||||
sourcePathHelpers.setValue('');
|
setFieldValue('source_path', '');
|
||||||
projectHelpers.setValue(value);
|
setFieldValue('source_project', value);
|
||||||
fetchSourcePath(value.id);
|
fetchSourcePath(value.id);
|
||||||
},
|
},
|
||||||
[] // eslint-disable-line react-hooks/exhaustive-deps
|
[fetchSourcePath, setFieldValue]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleProjectAutocomplete = useCallback(
|
const handleCredentialUpdate = useCallback(
|
||||||
val => {
|
value => {
|
||||||
projectHelpers.setValue(val);
|
setFieldValue('credential', value);
|
||||||
if (!projectMeta.initialValue) {
|
|
||||||
fetchSourcePath(val.id);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[] // eslint-disable-line react-hooks/exhaustive-deps
|
[setFieldValue]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -74,18 +72,16 @@ const SCMSubForm = ({ i18n }) => {
|
|||||||
credentialTypeKind="cloud"
|
credentialTypeKind="cloud"
|
||||||
label={i18n._(t`Credential`)}
|
label={i18n._(t`Credential`)}
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<ProjectLookup
|
<ProjectLookup
|
||||||
autocomplete={handleProjectAutocomplete}
|
|
||||||
value={projectField.value}
|
value={projectField.value}
|
||||||
isValid={!projectMeta.touched || !projectMeta.error}
|
isValid={!projectMeta.touched || !projectMeta.error}
|
||||||
helperTextInvalid={projectMeta.error}
|
helperTextInvalid={projectMeta.error}
|
||||||
onBlur={() => projectHelpers.setTouched()}
|
onBlur={() => projectHelpers.setTouched()}
|
||||||
onChange={handleProjectUpdate}
|
onChange={handleProjectUpdate}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateProject}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="source_path"
|
fieldId="source_path"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -12,11 +12,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const SatelliteSubForm = ({ i18n }) => {
|
const SatelliteSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -25,11 +33,10 @@ const SatelliteSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -11,11 +11,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const TowerSubForm = ({ i18n }) => {
|
const TowerSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -24,11 +32,10 @@ const TowerSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -12,11 +12,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const VMwareSubForm = ({ i18n }) => {
|
const VMwareSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -25,11 +33,10 @@ const VMwareSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
@@ -11,11 +11,19 @@ import {
|
|||||||
HostFilterField,
|
HostFilterField,
|
||||||
} from './SharedFields';
|
} from './SharedFields';
|
||||||
|
|
||||||
const VirtualizationSubForm = ({ i18n }) => {
|
const VirtualizationSubForm = ({ autoPopulateCredential, i18n }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
const [credentialField, credentialMeta, credentialHelpers] = useField(
|
||||||
'credential'
|
'credential'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCredentialUpdate = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('credential', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
@@ -24,11 +32,10 @@ const VirtualizationSubForm = ({ i18n }) => {
|
|||||||
helperTextInvalid={credentialMeta.error}
|
helperTextInvalid={credentialMeta.error}
|
||||||
isValid={!credentialMeta.touched || !credentialMeta.error}
|
isValid={!credentialMeta.touched || !credentialMeta.error}
|
||||||
onBlur={() => credentialHelpers.setTouched()}
|
onBlur={() => credentialHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={handleCredentialUpdate}
|
||||||
credentialHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={credentialField.value}
|
value={credentialField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<VerbosityField />
|
<VerbosityField />
|
||||||
<HostFilterField />
|
<HostFilterField />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useCallback } from 'react';
|
import React, { useEffect, useCallback } from 'react';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { func, shape, object, arrayOf } from 'prop-types';
|
import { func, shape, object, arrayOf } from 'prop-types';
|
||||||
@@ -20,7 +20,8 @@ import useRequest from '../../../util/useRequest';
|
|||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
import { InventoriesAPI } from '../../../api';
|
import { InventoriesAPI } from '../../../api';
|
||||||
|
|
||||||
const SmartInventoryFormFields = withI18n()(({ i18n }) => {
|
const SmartInventoryFormFields = withI18n()(({ i18n, inventory }) => {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
const [organizationField, organizationMeta, organizationHelpers] = useField({
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
@@ -32,6 +33,12 @@ const SmartInventoryFormFields = withI18n()(({ i18n }) => {
|
|||||||
name: 'host_filter',
|
name: 'host_filter',
|
||||||
validate: required(null, i18n),
|
validate: required(null, i18n),
|
||||||
});
|
});
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -53,11 +60,10 @@ const SmartInventoryFormFields = withI18n()(({ i18n }) => {
|
|||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||||
onBlur={() => organizationHelpers.setTouched()}
|
onBlur={() => organizationHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={organizationField.value}
|
value={organizationField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!inventory?.id}
|
||||||
/>
|
/>
|
||||||
<HostFilterLookup
|
<HostFilterLookup
|
||||||
value={hostFilterField.value}
|
value={hostFilterField.value}
|
||||||
@@ -144,7 +150,7 @@ function SmartInventoryForm({
|
|||||||
{formik => (
|
{formik => (
|
||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<SmartInventoryFormFields />
|
<SmartInventoryFormFields inventory={inventory} />
|
||||||
{submitError && <FormSubmitError error={submitError} />}
|
{submitError && <FormSubmitError error={submitError} />}
|
||||||
<FormActionGroup
|
<FormActionGroup
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/* eslint no-nested-ternary: 0 */
|
/* eslint no-nested-ternary: 0 */
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useCallback, useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { Form, FormGroup, Title } from '@patternfly/react-core';
|
import { Form, FormGroup, Title } from '@patternfly/react-core';
|
||||||
import { Config } from '../../../contexts/Config';
|
import { Config } from '../../../contexts/Config';
|
||||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||||
@@ -69,6 +69,7 @@ const fetchCredentials = async credential => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function ProjectFormFields({
|
function ProjectFormFields({
|
||||||
|
project,
|
||||||
project_base_dir,
|
project_base_dir,
|
||||||
project_local_paths,
|
project_local_paths,
|
||||||
formik,
|
formik,
|
||||||
@@ -91,6 +92,8 @@ function ProjectFormFields({
|
|||||||
scm_update_cache_timeout: 0,
|
scm_update_cache_timeout: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
const [scmTypeField, scmTypeMeta, scmTypeHelpers] = useField({
|
const [scmTypeField, scmTypeMeta, scmTypeHelpers] = useField({
|
||||||
name: 'scm_type',
|
name: 'scm_type',
|
||||||
validate: required(i18n._(t`Set a value for this field`), i18n),
|
validate: required(i18n._(t`Set a value for this field`), i18n),
|
||||||
@@ -133,15 +136,25 @@ function ProjectFormFields({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCredentialSelection = (type, value) => {
|
const handleCredentialSelection = useCallback(
|
||||||
setCredentials({
|
(type, value) => {
|
||||||
...credentials,
|
setCredentials({
|
||||||
[type]: {
|
...credentials,
|
||||||
...credentials[type],
|
[type]: {
|
||||||
value,
|
...credentials[type],
|
||||||
},
|
value,
|
||||||
});
|
},
|
||||||
};
|
});
|
||||||
|
},
|
||||||
|
[credentials, setCredentials]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -163,11 +176,10 @@ function ProjectFormFields({
|
|||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||||
onBlur={() => organizationHelpers.setTouched()}
|
onBlur={() => organizationHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value);
|
|
||||||
}}
|
|
||||||
value={organizationField.value}
|
value={organizationField.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!project?.id}
|
||||||
/>
|
/>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="project-scm-type"
|
fieldId="project-scm-type"
|
||||||
@@ -253,6 +265,9 @@ function ProjectFormFields({
|
|||||||
credential={credentials.insights}
|
credential={credentials.insights}
|
||||||
onCredentialSelection={handleCredentialSelection}
|
onCredentialSelection={handleCredentialSelection}
|
||||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
||||||
|
autoPopulateCredential={
|
||||||
|
!project?.id || project?.scm_type !== 'insights'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}[formik.values.scm_type]
|
}[formik.values.scm_type]
|
||||||
@@ -379,6 +394,7 @@ function ProjectForm({ i18n, project, submitError, ...props }) {
|
|||||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
<FormColumnLayout>
|
<FormColumnLayout>
|
||||||
<ProjectFormFields
|
<ProjectFormFields
|
||||||
|
project={project}
|
||||||
project_base_dir={project_base_dir}
|
project_base_dir={project_base_dir}
|
||||||
project_local_paths={project_local_paths}
|
project_local_paths={project_local_paths}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ describe('<ProjectForm />', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
act(() => {
|
await act(async () => {
|
||||||
wrapper.find('OrganizationLookup').invoke('onBlur')();
|
wrapper.find('OrganizationLookup').invoke('onBlur')();
|
||||||
wrapper.find('OrganizationLookup').invoke('onChange')({
|
wrapper.find('OrganizationLookup').invoke('onChange')({
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
import { required } from '../../../../util/validators';
|
import { required } from '../../../../util/validators';
|
||||||
import { ScmTypeOptions } from './SharedFields';
|
import { ScmTypeOptions } from './SharedFields';
|
||||||
@@ -11,13 +11,21 @@ const InsightsSubForm = ({
|
|||||||
credential,
|
credential,
|
||||||
onCredentialSelection,
|
onCredentialSelection,
|
||||||
scmUpdateOnLaunch,
|
scmUpdateOnLaunch,
|
||||||
|
autoPopulateCredential,
|
||||||
}) => {
|
}) => {
|
||||||
const credFieldArr = useField({
|
const { setFieldValue } = useFormikContext();
|
||||||
|
const [, credMeta, credHelpers] = useField({
|
||||||
name: 'credential',
|
name: 'credential',
|
||||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
});
|
});
|
||||||
const credMeta = credFieldArr[1];
|
|
||||||
const credHelpers = credFieldArr[2];
|
const onCredentialChange = useCallback(
|
||||||
|
value => {
|
||||||
|
onCredentialSelection('insights', value);
|
||||||
|
setFieldValue('credential', value.id);
|
||||||
|
},
|
||||||
|
[onCredentialSelection, setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -27,12 +35,10 @@ const InsightsSubForm = ({
|
|||||||
helperTextInvalid={credMeta.error}
|
helperTextInvalid={credMeta.error}
|
||||||
isValid={!credMeta.touched || !credMeta.error}
|
isValid={!credMeta.touched || !credMeta.error}
|
||||||
onBlur={() => credHelpers.setTouched()}
|
onBlur={() => credHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onCredentialChange}
|
||||||
onCredentialSelection('insights', value);
|
|
||||||
credHelpers.setValue(value.id);
|
|
||||||
}}
|
|
||||||
value={credential.value}
|
value={credential.value}
|
||||||
required
|
required
|
||||||
|
autoPopulate={autoPopulateCredential}
|
||||||
/>
|
/>
|
||||||
<ScmTypeOptions hideAllowOverride scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
<ScmTypeOptions hideAllowOverride scmUpdateOnLaunch={scmUpdateOnLaunch} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { useField } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { FormGroup, Title } from '@patternfly/react-core';
|
import { FormGroup, Title } from '@patternfly/react-core';
|
||||||
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
import CredentialLookup from '../../../../components/Lookup/CredentialLookup';
|
||||||
import FormField, { CheckboxField } from '../../../../components/FormField';
|
import FormField, { CheckboxField } from '../../../../components/FormField';
|
||||||
@@ -39,17 +39,22 @@ export const BranchFormField = withI18n()(({ i18n, label }) => (
|
|||||||
|
|
||||||
export const ScmCredentialFormField = withI18n()(
|
export const ScmCredentialFormField = withI18n()(
|
||||||
({ i18n, credential, onCredentialSelection }) => {
|
({ i18n, credential, onCredentialSelection }) => {
|
||||||
const credHelpers = useField('credential')[2];
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
|
const onCredentialChange = useCallback(
|
||||||
|
value => {
|
||||||
|
onCredentialSelection('scm', value);
|
||||||
|
setFieldValue('credential', value ? value.id : '');
|
||||||
|
},
|
||||||
|
[onCredentialSelection, setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CredentialLookup
|
<CredentialLookup
|
||||||
credentialTypeId={credential.typeId}
|
credentialTypeId={credential.typeId}
|
||||||
label={i18n._(t`Source Control Credential`)}
|
label={i18n._(t`Source Control Credential`)}
|
||||||
value={credential.value}
|
value={credential.value}
|
||||||
onChange={value => {
|
onChange={onCredentialChange}
|
||||||
onCredentialSelection('scm', value);
|
|
||||||
credHelpers.setValue(value ? value.id : '');
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { Form } from '@patternfly/react-core';
|
import { Form } from '@patternfly/react-core';
|
||||||
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
||||||
import FormField, { FormSubmitError } from '../../../components/FormField';
|
import FormField, { FormSubmitError } from '../../../components/FormField';
|
||||||
@@ -10,17 +10,23 @@ import OrganizationLookup from '../../../components/Lookup/OrganizationLookup';
|
|||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
import { FormColumnLayout } from '../../../components/FormLayout';
|
import { FormColumnLayout } from '../../../components/FormLayout';
|
||||||
|
|
||||||
function TeamFormFields(props) {
|
function TeamFormFields({ team, i18n }) {
|
||||||
const { team, i18n } = props;
|
const { setFieldValue } = useFormikContext();
|
||||||
const [organization, setOrganization] = useState(
|
const [organization, setOrganization] = useState(
|
||||||
team.summary_fields ? team.summary_fields.organization : null
|
team.summary_fields ? team.summary_fields.organization : null
|
||||||
);
|
);
|
||||||
const orgFieldArr = useField({
|
const [, orgMeta, orgHelpers] = useField({
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
});
|
});
|
||||||
const orgMeta = orgFieldArr[1];
|
|
||||||
const orgHelpers = orgFieldArr[2];
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value.id);
|
||||||
|
setOrganization(value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -42,12 +48,10 @@ function TeamFormFields(props) {
|
|||||||
helperTextInvalid={orgMeta.error}
|
helperTextInvalid={orgMeta.error}
|
||||||
isValid={!orgMeta.touched || !orgMeta.error}
|
isValid={!orgMeta.touched || !orgMeta.error}
|
||||||
onBlur={() => orgHelpers.setTouched('organization')}
|
onBlur={() => orgHelpers.setTouched('organization')}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
orgHelpers.setValue(value.id);
|
|
||||||
setOrganization(value);
|
|
||||||
}}
|
|
||||||
value={organization}
|
value={organization}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!team?.id}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -146,18 +146,11 @@ function JobTemplateForm({
|
|||||||
|
|
||||||
const handleProjectUpdate = useCallback(
|
const handleProjectUpdate = useCallback(
|
||||||
value => {
|
value => {
|
||||||
playbookHelpers.setValue(0);
|
setFieldValue('playbook', 0);
|
||||||
scmHelpers.setValue('');
|
setFieldValue('scm_branch', '');
|
||||||
projectHelpers.setValue(value);
|
setFieldValue('project', value);
|
||||||
},
|
},
|
||||||
[] // eslint-disable-line react-hooks/exhaustive-deps
|
[setFieldValue]
|
||||||
);
|
|
||||||
|
|
||||||
const handleProjectAutocomplete = useCallback(
|
|
||||||
val => {
|
|
||||||
projectHelpers.setValue(val);
|
|
||||||
},
|
|
||||||
[] // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const jobTypeOptions = [
|
const jobTypeOptions = [
|
||||||
@@ -270,8 +263,8 @@ function JobTemplateForm({
|
|||||||
isValid={!projectMeta.touched || !projectMeta.error}
|
isValid={!projectMeta.touched || !projectMeta.error}
|
||||||
helperTextInvalid={projectMeta.error}
|
helperTextInvalid={projectMeta.error}
|
||||||
onChange={handleProjectUpdate}
|
onChange={handleProjectUpdate}
|
||||||
autocomplete={handleProjectAutocomplete}
|
|
||||||
required
|
required
|
||||||
|
autoPopulate={!template?.id}
|
||||||
/>
|
/>
|
||||||
{projectField.value?.allow_override && (
|
{projectField.value?.allow_override && (
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
InputGroup,
|
InputGroup,
|
||||||
Button,
|
Button,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { useField } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import ContentError from '../../../components/ContentError';
|
import ContentError from '../../../components/ContentError';
|
||||||
import ContentLoading from '../../../components/ContentLoading';
|
import ContentLoading from '../../../components/ContentLoading';
|
||||||
import useRequest from '../../../util/useRequest';
|
import useRequest from '../../../util/useRequest';
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
} from '../../../api';
|
} from '../../../api';
|
||||||
|
|
||||||
function WebhookSubForm({ i18n, templateType }) {
|
function WebhookSubForm({ i18n, templateType }) {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const { origin } = document.location;
|
const { origin } = document.location;
|
||||||
@@ -82,6 +83,14 @@ function WebhookSubForm({ i18n, templateType }) {
|
|||||||
const changeWebhookKey = async () => {
|
const changeWebhookKey = async () => {
|
||||||
await fetchWebhookKey();
|
await fetchWebhookKey();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCredentialChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('webhook_credential', value || null);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
const isUpdateKeyDisabled =
|
const isUpdateKeyDisabled =
|
||||||
pathname.endsWith('/add') ||
|
pathname.endsWith('/add') ||
|
||||||
webhookKeyMeta.initialValue ===
|
webhookKeyMeta.initialValue ===
|
||||||
@@ -211,9 +220,7 @@ function WebhookSubForm({ i18n, templateType }) {
|
|||||||
t`Optionally select the credential to use to send status updates back to the webhook service.`
|
t`Optionally select the credential to use to send status updates back to the webhook service.`
|
||||||
)}
|
)}
|
||||||
credentialTypeId={credTypeId}
|
credentialTypeId={credTypeId}
|
||||||
onChange={value => {
|
onChange={onCredentialChange}
|
||||||
webhookCredentialHelpers.setValue(value || null);
|
|
||||||
}}
|
|
||||||
isValid={!webhookCredentialMeta.error}
|
isValid={!webhookCredentialMeta.error}
|
||||||
helperTextInvalid={webhookCredentialMeta.error}
|
helperTextInvalid={webhookCredentialMeta.error}
|
||||||
value={webhookCredentialField.value}
|
value={webhookCredentialField.value}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import PropTypes, { shape } from 'prop-types';
|
import PropTypes, { shape } from 'prop-types';
|
||||||
|
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { useField, withFormik } from 'formik';
|
import { useField, useFormikContext, withFormik } from 'formik';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
@@ -43,6 +43,7 @@ function WorkflowJobTemplateForm({
|
|||||||
i18n,
|
i18n,
|
||||||
submitError,
|
submitError,
|
||||||
}) {
|
}) {
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
const [enableWebhooks, setEnableWebhooks] = useState(
|
const [enableWebhooks, setEnableWebhooks] = useState(
|
||||||
Boolean(template.webhook_service)
|
Boolean(template.webhook_service)
|
||||||
);
|
);
|
||||||
@@ -53,9 +54,7 @@ function WorkflowJobTemplateForm({
|
|||||||
);
|
);
|
||||||
const [labelsField, , labelsHelpers] = useField('labels');
|
const [labelsField, , labelsHelpers] = useField('labels');
|
||||||
const [limitField, limitMeta, limitHelpers] = useField('limit');
|
const [limitField, limitMeta, limitHelpers] = useField('limit');
|
||||||
const [organizationField, organizationMeta, organizationHelpers] = useField(
|
const [organizationField, organizationMeta] = useField('organization');
|
||||||
'organization'
|
|
||||||
);
|
|
||||||
const [scmField, , scmHelpers] = useField('scm_branch');
|
const [scmField, , scmHelpers] = useField('scm_branch');
|
||||||
const [, webhookServiceMeta, webhookServiceHelpers] = useField(
|
const [, webhookServiceMeta, webhookServiceHelpers] = useField(
|
||||||
'webhook_service'
|
'webhook_service'
|
||||||
@@ -81,6 +80,13 @@ function WorkflowJobTemplateForm({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [enableWebhooks]);
|
}, [enableWebhooks]);
|
||||||
|
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
if (hasContentError) {
|
if (hasContentError) {
|
||||||
return <ContentError error={hasContentError} />;
|
return <ContentError error={hasContentError} />;
|
||||||
}
|
}
|
||||||
@@ -104,9 +110,7 @@ function WorkflowJobTemplateForm({
|
|||||||
/>
|
/>
|
||||||
<OrganizationLookup
|
<OrganizationLookup
|
||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value || null);
|
|
||||||
}}
|
|
||||||
value={organizationField.value}
|
value={organizationField.value}
|
||||||
isValid={!organizationMeta.error}
|
isValid={!organizationMeta.error}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Formik, useField } from 'formik';
|
import { Formik, useField, useFormikContext } from 'formik';
|
||||||
import { Form, FormGroup } from '@patternfly/react-core';
|
import { Form, FormGroup } from '@patternfly/react-core';
|
||||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||||
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
||||||
@@ -16,6 +16,7 @@ import { FormColumnLayout } from '../../../components/FormLayout';
|
|||||||
|
|
||||||
function UserFormFields({ user, i18n }) {
|
function UserFormFields({ user, i18n }) {
|
||||||
const [organization, setOrganization] = useState(null);
|
const [organization, setOrganization] = useState(null);
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
const userTypeOptions = [
|
const userTypeOptions = [
|
||||||
{
|
{
|
||||||
@@ -38,17 +39,23 @@ function UserFormFields({ user, i18n }) {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const organizationFieldArr = useField({
|
const [, organizationMeta, organizationHelpers] = useField({
|
||||||
name: 'organization',
|
name: 'organization',
|
||||||
validate: !user.id
|
validate: !user.id
|
||||||
? required(i18n._(t`Select a value for this field`), i18n)
|
? required(i18n._(t`Select a value for this field`), i18n)
|
||||||
: () => undefined,
|
: () => undefined,
|
||||||
});
|
});
|
||||||
const organizationMeta = organizationFieldArr[1];
|
|
||||||
const organizationHelpers = organizationFieldArr[2];
|
|
||||||
|
|
||||||
const [userTypeField, userTypeMeta] = useField('user_type');
|
const [userTypeField, userTypeMeta] = useField('user_type');
|
||||||
|
|
||||||
|
const onOrganizationChange = useCallback(
|
||||||
|
value => {
|
||||||
|
setFieldValue('organization', value.id);
|
||||||
|
setOrganization(value);
|
||||||
|
},
|
||||||
|
[setFieldValue]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -105,12 +112,10 @@ function UserFormFields({ user, i18n }) {
|
|||||||
helperTextInvalid={organizationMeta.error}
|
helperTextInvalid={organizationMeta.error}
|
||||||
isValid={!organizationMeta.touched || !organizationMeta.error}
|
isValid={!organizationMeta.touched || !organizationMeta.error}
|
||||||
onBlur={() => organizationHelpers.setTouched()}
|
onBlur={() => organizationHelpers.setTouched()}
|
||||||
onChange={value => {
|
onChange={onOrganizationChange}
|
||||||
organizationHelpers.setValue(value.id);
|
|
||||||
setOrganization(value);
|
|
||||||
}}
|
|
||||||
value={organization}
|
value={organization}
|
||||||
required
|
required
|
||||||
|
autoPopulate={!user?.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
|||||||
24
awx/ui_next/src/util/useAutoPopulateLookup.jsx
Normal file
24
awx/ui_next/src/util/useAutoPopulateLookup.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useAutoPopulateLookup hook [... insert description]
|
||||||
|
* Param: [... insert params]
|
||||||
|
* Returns: {
|
||||||
|
* [... insert returns]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function useAutoPopulateLookup(populateLookupField) {
|
||||||
|
const isFirst = useRef(true);
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
results => {
|
||||||
|
if (isFirst.current && results.length === 1) {
|
||||||
|
populateLookupField(results[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirst.current = false;
|
||||||
|
},
|
||||||
|
[populateLookupField]
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user