diff --git a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx
index 3df2871ac6..965c5d33af 100644
--- a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx
+++ b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx
@@ -91,7 +91,7 @@ class ProjectListItem extends React.Component {
{project.name}
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
index 7478954a5e..ea1e784848 100644
--- a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
+++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
@@ -1,30 +1,27 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
-import { withRouter, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
-import { withFormik, Field } from 'formik';
+import { Formik, Field } from 'formik';
import { Config } from '@contexts/Config';
-import {
- Form as _Form,
- FormGroup,
- Title as _Title,
-} from '@patternfly/react-core';
+import { Form, FormGroup } from '@patternfly/react-core';
import AnsibleSelect from '@components/AnsibleSelect';
import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading';
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
-import FormField, { CheckboxField, FieldTooltip } from '@components/FormField';
+import FormField, { FieldTooltip } from '@components/FormField';
import FormRow from '@components/FormRow';
import OrganizationLookup from '@components/Lookup/OrganizationLookup';
-import CredentialLookup from '@components/Lookup/CredentialLookup';
import { CredentialTypesAPI, ProjectsAPI } from '@api';
import { required } from '@util/validators';
import styled from 'styled-components';
-
-const Form = styled(_Form)`
- padding: 0 24px;
-`;
+import {
+ GitSubForm,
+ HgSubForm,
+ SvnSubForm,
+ InsightsSubForm,
+ SubFormTitle,
+} from './ProjectSubForms';
const ScmTypeFormRow = styled(FormRow)`
background-color: #f5f5f5;
@@ -33,19 +30,10 @@ const ScmTypeFormRow = styled(FormRow)`
padding: 24px;
`;
-const OptionsFormGroup = styled.div`
- grid-column: 1/-1;
-`;
-
-const Title = styled(_Title)`
- --pf-c-title--m-md--FontWeight: 700;
- grid-column: 1 / -1;
-`;
-
function ProjectForm(props) {
- const { values, handleCancel, handleSubmit, i18n } = props;
+ const { project, handleCancel, handleSubmit, i18n } = props;
const [contentError, setContentError] = useState(null);
- const [hasContentLoading, setHasContentLoading] = useState(true);
+ const [isLoading, setIsLoading] = useState(true);
const [organization, setOrganization] = useState(null);
const [scmTypeOptions, setScmTypeOptions] = useState(null);
const [scmCredential, setScmCredential] = useState({
@@ -58,45 +46,48 @@ function ProjectForm(props) {
});
useEffect(() => {
- async function fetchCredTypeId(params) {
- try {
- const {
- data: {
- results: [credential],
- },
- } = await CredentialTypesAPI.read(params);
- return credential.id;
- } catch (error) {
- setContentError(error);
- return null;
- }
- }
-
async function fetchData() {
- const insightsTypeId = await fetchCredTypeId({ name: 'Insights' });
- const scmTypeId = await fetchCredTypeId({ kind: 'scm' });
- const {
- data: {
- actions: {
- GET: {
- scm_type: { choices },
+ try {
+ const [
+ {
+ data: {
+ results: [scmCredentialType],
},
},
- },
- } = await ProjectsAPI.readOptions();
- setInsightsCredential({ typeId: insightsTypeId });
- setScmCredential({ typeId: scmTypeId });
- setScmTypeOptions(choices);
- setHasContentLoading(false);
+ {
+ data: {
+ results: [insightsCredentialType],
+ },
+ },
+ {
+ data: {
+ actions: {
+ GET: {
+ scm_type: { choices },
+ },
+ },
+ },
+ },
+ ] = await Promise.all([
+ CredentialTypesAPI.read({ kind: 'scm' }),
+ CredentialTypesAPI.read({ name: 'Insights' }),
+ ProjectsAPI.readOptions(),
+ ]);
+
+ setScmCredential({ typeId: scmCredentialType.id });
+ setInsightsCredential({ typeId: insightsCredentialType.id });
+ setScmTypeOptions(choices);
+ } catch (error) {
+ setContentError(error);
+ } finally {
+ setIsLoading(false);
+ }
}
fetchData();
}, []);
- const resetScmTypeFields = (value, form) => {
- if (form.initialValues.scm_type === value) {
- return;
- }
+ const resetScmTypeFields = form => {
const scmFormFields = [
'scm_url',
'scm_branch',
@@ -115,63 +106,7 @@ function ProjectForm(props) {
});
};
- const gitScmTooltip = (
-
- {i18n._(t`Example URLs for GIT SCM include:`)}
-
- https://github.com/ansible/ansible.git
- git@github.com:ansible/ansible.git
- git://servername.example.com/ansible.git
-
-
- {i18n._(t`Note: When using SSH protocol for GitHub or
- Bitbucket, enter an SSH key only, do not enter a username
- (other than git). Additionally, GitHub and Bitbucket do
- not support password authentication when using SSH. GIT
- read only protocol (git://) does not use username or
- password information.`)}
-
- );
-
- const hgScmTooltip = (
-
- {i18n._(t`Example URLs for Mercurial SCM include:`)}
-
- https://bitbucket.org/username/project
- ssh://hg@bitbucket.org/username/project
- ssh://server.example.com/path
-
- {i18n._(t`Note: Mercurial does not support password authentication
- for SSH. Do not put the username and key in the URL. If using
- Bitbucket and SSH, do not supply your Bitbucket username.
- `)}
-
- );
-
- const svnScmTooltip = (
-
- {i18n._(t`Example URLs for Subversion SCM include:`)}
-
- https://github.com/ansible/ansible
- svn://servername.example.com/path
- svn+ssh://servername.example.com/path
-
-
- );
-
- const scmUrlTooltips = {
- git: gitScmTooltip,
- hg: hgScmTooltip,
- svn: svnScmTooltip,
- };
-
- const scmBranchLabels = {
- git: i18n._(t`SCM Branch/Tag/Commit`),
- hg: i18n._(t`SCM Branch/Tag/Revision`),
- svn: i18n._(t`Revision #`),
- };
-
- if (hasContentLoading) {
+ if (isLoading) {
return ;
}
@@ -180,343 +115,197 @@ function ProjectForm(props) {
}
return (
-
+
+ {({ custom_virtualenvs }) =>
+ custom_virtualenvs &&
+ custom_virtualenvs.length > 1 && (
+ (
+
+
+ datum !== '/venv/ansible/')
+ .map(datum => ({
+ label: datum,
+ value: datum,
+ key: datum,
+ })),
+ ]}
+ {...field}
+ />
+
+ )}
+ />
+ )
+ }
+
+
+
+
+ )}
+ />
);
}
-const FormikApp = withFormik({
- mapPropsToValues(props) {
- const { project = {} } = props;
-
- return {
- credential: project.credential || '',
- custom_virtualenv: project.custom_virtualenv || '',
- description: project.description || '',
- name: project.name || '',
- organization: project.organization || '',
- scm_branch: project.scm_branch || '',
- scm_clean: project.scm_clean || false,
- scm_delete_on_update: project.scm_delete_on_update || false,
- scm_refspec: project.scm_refspec || '',
- scm_type: project.scm_type || '',
- scm_update_on_launch: project.scm_update_on_launch || false,
- scm_url: project.scm_url || '',
- scm_update_cache_timeout: project.scm_update_cache_timeout || 0,
- allow_override: project.allow_override || false,
- };
- },
- handleSubmit: (values, { props }) => props.handleSubmit(values),
-})(ProjectForm);
-
ProjectForm.propTypes = {
handleCancel: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
@@ -527,4 +316,4 @@ ProjectForm.defaultProps = {
project: {},
};
-export default withI18n()(withRouter(FormikApp));
+export default withI18n()(ProjectForm);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
index 00ee3fbf99..18b4c5f680 100644
--- a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
+++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
@@ -243,7 +243,7 @@ describe(' ', () => {
});
wrapper.update();
await act(async () => {
- scmTypeSelect.props().onChange('git', { target: { name: 'insights' } });
+ scmTypeSelect.props().onChange('svn', { target: { name: 'Subversion' } });
});
wrapper.update();
expect(formik.state.values.scm_url).toEqual('');
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/GitSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/GitSubForm.jsx
new file mode 100644
index 0000000000..6bcbb83c48
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/GitSubForm.jsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import FormField from '@components/FormField';
+import {
+ UrlFormField,
+ BranchFormField,
+ ScmCredentialFormField,
+ ScmTypeOptions,
+} from './SharedFields';
+
+const GitSubForm = ({
+ i18n,
+ scmCredential,
+ setScmCredential,
+ scmUpdateOnLaunch,
+}) => (
+ <>
+
+ {i18n._(t`Example URLs for GIT SCM include:`)}
+
+ https://github.com/ansible/ansible.git
+ git@github.com:ansible/ansible.git
+ git://servername.example.com/ansible.git
+
+ {i18n._(t`Note: When using SSH protocol for GitHub or
+ Bitbucket, enter an SSH key only, do not enter a username
+ (other than git). Additionally, GitHub and Bitbucket do
+ not support password authentication when using SSH. GIT
+ read only protocol (git://) does not use username or
+ password information.`)}
+
+ }
+ />
+
+
+ {i18n._(t`A refspec to fetch (passed to the Ansible git
+ module). This parameter allows access to references via
+ the branch field not otherwise available.`)}
+
+
+ {i18n._(t`Note: This field assumes the remote name is "origin".`)}
+
+
+ {i18n._(t`Examples include:`)}
+
+ refs/*:refs/remotes/origin/*
+ refs/pull/62/head:refs/remotes/origin/pull/62/head
+
+ {i18n._(t`The first fetches all references. The second
+ fetches the Github pull request number 62, in this example
+ the branch needs to be "pull/62/head".`)}
+
+
+ {i18n._(t`For more information, refer to the`)}{' '}
+
+ {i18n._(t`Ansible Tower Documentation.`)}
+
+
+ }
+ />
+
+
+ >
+);
+
+export default withI18n()(GitSubForm);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/HgSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/HgSubForm.jsx
new file mode 100644
index 0000000000..6cc642686e
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/HgSubForm.jsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ UrlFormField,
+ BranchFormField,
+ ScmCredentialFormField,
+ ScmTypeOptions,
+} from './SharedFields';
+
+const HgSubForm = ({
+ i18n,
+ scmCredential,
+ setScmCredential,
+ scmUpdateOnLaunch,
+}) => (
+ <>
+
+ {i18n._(t`Example URLs for Mercurial SCM include:`)}
+
+ https://bitbucket.org/username/project
+ ssh://hg@bitbucket.org/username/project
+ ssh://server.example.com/path
+
+ {i18n._(t`Note: Mercurial does not support password authentication
+ for SSH. Do not put the username and key in the URL. If using
+ Bitbucket and SSH, do not supply your Bitbucket username.
+ `)}
+
+ }
+ />
+
+
+
+ >
+);
+
+export default withI18n()(HgSubForm);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx
new file mode 100644
index 0000000000..4e854c8b20
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/InsightsSubForm.jsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { Field } from 'formik';
+import CredentialLookup from '@components/Lookup/CredentialLookup';
+import { required } from '@util/validators';
+import { ScmTypeOptions } from './SharedFields';
+
+const InsightsSubForm = ({
+ i18n,
+ setInsightsCredential,
+ insightsCredential,
+ scmUpdateOnLaunch,
+}) => (
+ <>
+ (
+ form.setFieldTouched('credential')}
+ onChange={credential => {
+ form.setFieldValue('credential', credential.id);
+ setInsightsCredential({
+ ...insightsCredential,
+ value: credential,
+ });
+ }}
+ value={insightsCredential.value}
+ required
+ />
+ )}
+ />
+
+ >
+);
+
+export default withI18n()(InsightsSubForm);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx
new file mode 100644
index 0000000000..40b1dce826
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SharedFields.jsx
@@ -0,0 +1,135 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { Field } from 'formik';
+import CredentialLookup from '@components/Lookup/CredentialLookup';
+import FormField, { CheckboxField } from '@components/FormField';
+import { required } from '@util/validators';
+import FormRow from '@components/FormRow';
+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;
+`;
+
+export const UrlFormField = withI18n()(({ i18n, tooltip }) => (
+
+));
+
+export const BranchFormField = withI18n()(({ i18n, label }) => (
+
+));
+
+export const ScmCredentialFormField = withI18n()(
+ ({ i18n, setScmCredential, scmCredential }) => (
+ (
+ {
+ form.setFieldValue('credential', credential.id);
+ setScmCredential({
+ ...scmCredential,
+ value: credential,
+ });
+ }}
+ />
+ )}
+ />
+ )
+);
+
+export const ScmTypeOptions = withI18n()(
+ ({ i18n, scmUpdateOnLaunch, hideAllowOverride }) => (
+ <>
+
+
+
+
+
+ {!hideAllowOverride && (
+
+ )}
+
+
+ {scmUpdateOnLaunch && (
+ <>
+ {i18n._(t`Option Details`)}
+
+ >
+ )}
+ >
+ )
+);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SvnSubForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SvnSubForm.jsx
new file mode 100644
index 0000000000..9f7e9a4659
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/SvnSubForm.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import {
+ UrlFormField,
+ BranchFormField,
+ ScmCredentialFormField,
+ ScmTypeOptions,
+} from './SharedFields';
+
+const SvnSubForm = ({
+ i18n,
+ scmCredential,
+ setScmCredential,
+ scmUpdateOnLaunch,
+}) => (
+ <>
+
+ {i18n._(t`Example URLs for Subversion SCM include:`)}
+
+ https://github.com/ansible/ansible
+ svn://servername.example.com/path
+ svn+ssh://servername.example.com/path
+
+
+ }
+ />
+
+
+
+ >
+);
+
+export default withI18n()(SvnSubForm);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectSubForms/index.js b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/index.js
new file mode 100644
index 0000000000..9443f17054
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/shared/ProjectSubForms/index.js
@@ -0,0 +1,5 @@
+export { default as GitSubForm } from './GitSubForm';
+export { default as HgSubForm } from './HgSubForm';
+export { default as SvnSubForm } from './SvnSubForm';
+export { default as InsightsSubForm } from './InsightsSubForm';
+export { SubFormTitle } from './SharedFields';