mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Add project manual scm type subform
This commit is contained in:
@@ -97,7 +97,13 @@ class App extends Component {
|
|||||||
MeAPI.read(),
|
MeAPI.read(),
|
||||||
]);
|
]);
|
||||||
const {
|
const {
|
||||||
data: { ansible_version, custom_virtualenvs, version },
|
data: {
|
||||||
|
ansible_version,
|
||||||
|
custom_virtualenvs,
|
||||||
|
project_base_dir,
|
||||||
|
project_local_paths,
|
||||||
|
version,
|
||||||
|
},
|
||||||
} = configRes;
|
} = configRes;
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
@@ -105,7 +111,14 @@ class App extends Component {
|
|||||||
},
|
},
|
||||||
} = meRes;
|
} = meRes;
|
||||||
|
|
||||||
this.setState({ ansible_version, custom_virtualenvs, version, me });
|
this.setState({
|
||||||
|
ansible_version,
|
||||||
|
custom_virtualenvs,
|
||||||
|
project_base_dir,
|
||||||
|
project_local_paths,
|
||||||
|
version,
|
||||||
|
me,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ configError: err });
|
this.setState({ configError: err });
|
||||||
}
|
}
|
||||||
@@ -115,6 +128,8 @@ class App extends Component {
|
|||||||
const {
|
const {
|
||||||
ansible_version,
|
ansible_version,
|
||||||
custom_virtualenvs,
|
custom_virtualenvs,
|
||||||
|
project_base_dir,
|
||||||
|
project_local_paths,
|
||||||
isAboutModalOpen,
|
isAboutModalOpen,
|
||||||
isNavOpen,
|
isNavOpen,
|
||||||
me,
|
me,
|
||||||
@@ -169,7 +184,14 @@ class App extends Component {
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<Page usecondensed="True" header={header} sidebar={sidebar}>
|
<Page usecondensed="True" header={header} sidebar={sidebar}>
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
value={{ ansible_version, custom_virtualenvs, me, version }}
|
value={{
|
||||||
|
ansible_version,
|
||||||
|
custom_virtualenvs,
|
||||||
|
project_base_dir,
|
||||||
|
project_local_paths,
|
||||||
|
me,
|
||||||
|
version,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{render({ routeGroups })}
|
{render({ routeGroups })}
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
|
|||||||
@@ -189,10 +189,6 @@
|
|||||||
z-index: 20;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pf-c-alert__icon {
|
|
||||||
--pf-c-alert__icon--Color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.at-u-textRight {
|
.at-u-textRight {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ function ProjectAdd({ history, i18n }) {
|
|||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
|
if (values.scm_type === 'manual') {
|
||||||
|
values.scm_type = '';
|
||||||
|
}
|
||||||
setFormSubmitError(null);
|
setFormSubmitError(null);
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ describe('<ProjectAdd />', () => {
|
|||||||
scm_url: 'https://foo.bar',
|
scm_url: 'https://foo.bar',
|
||||||
scm_clean: true,
|
scm_clean: true,
|
||||||
credential: 100,
|
credential: 100,
|
||||||
|
local_path: '',
|
||||||
organization: 2,
|
organization: 2,
|
||||||
scm_update_on_launch: true,
|
scm_update_on_launch: true,
|
||||||
scm_update_cache_timeout: 3,
|
scm_update_cache_timeout: 3,
|
||||||
@@ -116,9 +117,15 @@ describe('<ProjectAdd />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit should throw an error', async () => {
|
test('handleSubmit should throw an error', async () => {
|
||||||
|
const config = {
|
||||||
|
project_local_paths: ['foobar', 'qux'],
|
||||||
|
project_base_dir: 'dir/foo/bar',
|
||||||
|
};
|
||||||
ProjectsAPI.create.mockImplementation(() => Promise.reject(new Error()));
|
ProjectsAPI.create.mockImplementation(() => Promise.reject(new Error()));
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ProjectAdd />);
|
wrapper = mountWithContexts(<ProjectAdd />, {
|
||||||
|
context: { config },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
const formik = wrapper.find('Formik').instance();
|
const formik = wrapper.find('Formik').instance();
|
||||||
@@ -127,6 +134,7 @@ describe('<ProjectAdd />', () => {
|
|||||||
{
|
{
|
||||||
values: {
|
values: {
|
||||||
...projectData,
|
...projectData,
|
||||||
|
scm_type: 'manual',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
() => resolve()
|
() => resolve()
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import { t } from '@lingui/macro';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Project } from '@types';
|
import { Project } from '@types';
|
||||||
import { formatDateString } from '@util/dates';
|
import { formatDateString } from '@util/dates';
|
||||||
|
import { Config } from '@contexts/Config';
|
||||||
import { Button, CardBody, List, ListItem } from '@patternfly/react-core';
|
import { Button, CardBody, List, ListItem } from '@patternfly/react-core';
|
||||||
import { DetailList, Detail } from '@components/DetailList';
|
import { DetailList, Detail } from '@components/DetailList';
|
||||||
import { CredentialChip } from '@components/Chip';
|
import { CredentialChip } from '@components/Chip';
|
||||||
|
import { toTitleCase } from '@util/strings';
|
||||||
|
|
||||||
const ActionButtonWrapper = styled.div`
|
const ActionButtonWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -25,6 +27,7 @@ function ProjectDetail({ project, i18n }) {
|
|||||||
custom_virtualenv,
|
custom_virtualenv,
|
||||||
description,
|
description,
|
||||||
id,
|
id,
|
||||||
|
local_path,
|
||||||
modified,
|
modified,
|
||||||
name,
|
name,
|
||||||
scm_branch,
|
scm_branch,
|
||||||
@@ -93,10 +96,21 @@ function ProjectDetail({ project, i18n }) {
|
|||||||
{summary_fields.organization && (
|
{summary_fields.organization && (
|
||||||
<Detail
|
<Detail
|
||||||
label={i18n._(t`Organization`)}
|
label={i18n._(t`Organization`)}
|
||||||
value={summary_fields.organization.name}
|
value={
|
||||||
|
<Link
|
||||||
|
to={`/organizations/${summary_fields.organization.id}/details`}
|
||||||
|
>
|
||||||
|
{summary_fields.organization.name}
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Detail label={i18n._(t`SCM Type`)} value={scm_type} />
|
<Detail
|
||||||
|
label={i18n._(t`SCM Type`)}
|
||||||
|
value={
|
||||||
|
scm_type === '' ? i18n._(t`Manual`) : toTitleCase(project.scm_type)
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Detail label={i18n._(t`SCM URL`)} value={scm_url} />
|
<Detail label={i18n._(t`SCM URL`)} value={scm_url} />
|
||||||
<Detail label={i18n._(t`SCM Branch`)} value={scm_branch} />
|
<Detail label={i18n._(t`SCM Branch`)} value={scm_branch} />
|
||||||
<Detail label={i18n._(t`SCM Refspec`)} value={scm_refspec} />
|
<Detail label={i18n._(t`SCM Refspec`)} value={scm_refspec} />
|
||||||
@@ -123,6 +137,15 @@ function ProjectDetail({ project, i18n }) {
|
|||||||
label={i18n._(t`Ansible Environment`)}
|
label={i18n._(t`Ansible Environment`)}
|
||||||
value={custom_virtualenv}
|
value={custom_virtualenv}
|
||||||
/>
|
/>
|
||||||
|
<Config>
|
||||||
|
{({ project_base_dir }) => (
|
||||||
|
<Detail
|
||||||
|
label={i18n._(t`Project Base Path`)}
|
||||||
|
value={project_base_dir}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Config>
|
||||||
|
<Detail label={i18n._(t`Playbook Directory`)} value={local_path} />
|
||||||
{/* TODO: Link to user in users */}
|
{/* TODO: Link to user in users */}
|
||||||
<Detail label={i18n._(t`Created`)} value={createdBy} />
|
<Detail label={i18n._(t`Created`)} value={createdBy} />
|
||||||
{/* TODO: Link to user in users */}
|
{/* TODO: Link to user in users */}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ describe('<ProjectDetail />', () => {
|
|||||||
assertDetail('Name', mockProject.name);
|
assertDetail('Name', mockProject.name);
|
||||||
assertDetail('Description', mockProject.description);
|
assertDetail('Description', mockProject.description);
|
||||||
assertDetail('Organization', mockProject.summary_fields.organization.name);
|
assertDetail('Organization', mockProject.summary_fields.organization.name);
|
||||||
assertDetail('SCM Type', mockProject.scm_type);
|
assertDetail('SCM Type', 'Git');
|
||||||
assertDetail('SCM URL', mockProject.scm_url);
|
assertDetail('SCM URL', mockProject.scm_url);
|
||||||
assertDetail('SCM Branch', mockProject.scm_branch);
|
assertDetail('SCM Branch', mockProject.scm_branch);
|
||||||
assertDetail('SCM Refspec', mockProject.scm_refspec);
|
assertDetail('SCM Refspec', mockProject.scm_refspec);
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ function ProjectEdit({ project, history, i18n }) {
|
|||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
|
if (values.scm_type === 'manual') {
|
||||||
|
values.scm_type = '';
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { id },
|
data: { id },
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe('<ProjectEdit />', () => {
|
|||||||
scm_url: 'https://foo.bar',
|
scm_url: 'https://foo.bar',
|
||||||
scm_clean: true,
|
scm_clean: true,
|
||||||
credential: 100,
|
credential: 100,
|
||||||
|
local_path: '',
|
||||||
organization: 2,
|
organization: 2,
|
||||||
scm_update_on_launch: true,
|
scm_update_on_launch: true,
|
||||||
scm_update_cache_timeout: 3,
|
scm_update_cache_timeout: 3,
|
||||||
@@ -115,9 +116,18 @@ describe('<ProjectEdit />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit should throw an error', async () => {
|
test('handleSubmit should throw an error', async () => {
|
||||||
|
const config = {
|
||||||
|
project_local_paths: [],
|
||||||
|
project_base_dir: 'foo/bar',
|
||||||
|
};
|
||||||
ProjectsAPI.update.mockImplementation(() => Promise.reject(new Error()));
|
ProjectsAPI.update.mockImplementation(() => Promise.reject(new Error()));
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ProjectEdit project={projectData} />);
|
wrapper = mountWithContexts(
|
||||||
|
<ProjectEdit project={{ ...projectData, scm_type: 'manual' }} />,
|
||||||
|
{
|
||||||
|
context: { config },
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import ListActionButton from '@components/ListActionButton';
|
|||||||
import ProjectSyncButton from '../shared/ProjectSyncButton';
|
import ProjectSyncButton from '../shared/ProjectSyncButton';
|
||||||
import { StatusIcon } from '@components/Sparkline';
|
import { StatusIcon } from '@components/Sparkline';
|
||||||
import VerticalSeparator from '@components/VerticalSeparator';
|
import VerticalSeparator from '@components/VerticalSeparator';
|
||||||
|
import { toTitleCase } from '@util/strings';
|
||||||
import { Project } from '@types';
|
import { Project } from '@types';
|
||||||
|
|
||||||
class ProjectListItem extends React.Component {
|
class ProjectListItem extends React.Component {
|
||||||
@@ -97,7 +98,9 @@ class ProjectListItem extends React.Component {
|
|||||||
</Link>
|
</Link>
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell key="type">
|
<DataListCell key="type">
|
||||||
{project.scm_type.toUpperCase()}
|
{project.scm_type === ''
|
||||||
|
? i18n._(t`Manual`)
|
||||||
|
: toTitleCase(project.scm_type)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell key="revision">
|
<DataListCell key="revision">
|
||||||
{project.scm_revision.substring(0, 7)}
|
{project.scm_revision.substring(0, 7)}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
SvnSubForm,
|
SvnSubForm,
|
||||||
InsightsSubForm,
|
InsightsSubForm,
|
||||||
SubFormTitle,
|
SubFormTitle,
|
||||||
|
ManualSubForm,
|
||||||
} from './ProjectSubForms';
|
} from './ProjectSubForms';
|
||||||
|
|
||||||
const ScmTypeFormRow = styled(FormRow)`
|
const ScmTypeFormRow = styled(FormRow)`
|
||||||
@@ -173,12 +174,16 @@ function ProjectForm({ project, ...props }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Config>
|
||||||
|
{({ project_base_dir, project_local_paths }) => (
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
allow_override: project.allow_override || false,
|
allow_override: project.allow_override || false,
|
||||||
|
base_dir: project_base_dir || '',
|
||||||
credential: project.credential || '',
|
credential: project.credential || '',
|
||||||
custom_virtualenv: project.custom_virtualenv || '',
|
custom_virtualenv: project.custom_virtualenv || '',
|
||||||
description: project.description || '',
|
description: project.description || '',
|
||||||
|
local_path: project.local_path || null,
|
||||||
name: project.name || '',
|
name: project.name || '',
|
||||||
organization: project.organization || '',
|
organization: project.organization || '',
|
||||||
scm_branch: project.scm_branch || '',
|
scm_branch: project.scm_branch || '',
|
||||||
@@ -284,35 +289,52 @@ function ProjectForm({ project, ...props }) {
|
|||||||
/>
|
/>
|
||||||
{formik.values.scm_type !== '' && (
|
{formik.values.scm_type !== '' && (
|
||||||
<ScmTypeFormRow>
|
<ScmTypeFormRow>
|
||||||
<SubFormTitle size="md">{i18n._(t`Type Details`)}</SubFormTitle>
|
<SubFormTitle size="md">
|
||||||
|
{i18n._(t`Type Details`)}
|
||||||
|
</SubFormTitle>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
manual: (
|
||||||
|
<ManualSubForm
|
||||||
|
localPath={formik.initialValues.local_path}
|
||||||
|
project_base_dir={project_base_dir}
|
||||||
|
project_local_paths={project_local_paths}
|
||||||
|
/>
|
||||||
|
),
|
||||||
git: (
|
git: (
|
||||||
<GitSubForm
|
<GitSubForm
|
||||||
credential={credentials.scm}
|
credential={credentials.scm}
|
||||||
onCredentialSelection={handleCredentialSelection}
|
onCredentialSelection={handleCredentialSelection}
|
||||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
scmUpdateOnLaunch={
|
||||||
|
formik.values.scm_update_on_launch
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
hg: (
|
hg: (
|
||||||
<HgSubForm
|
<HgSubForm
|
||||||
credential={credentials.scm}
|
credential={credentials.scm}
|
||||||
onCredentialSelection={handleCredentialSelection}
|
onCredentialSelection={handleCredentialSelection}
|
||||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
scmUpdateOnLaunch={
|
||||||
|
formik.values.scm_update_on_launch
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
svn: (
|
svn: (
|
||||||
<SvnSubForm
|
<SvnSubForm
|
||||||
credential={credentials.scm}
|
credential={credentials.scm}
|
||||||
onCredentialSelection={handleCredentialSelection}
|
onCredentialSelection={handleCredentialSelection}
|
||||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
scmUpdateOnLaunch={
|
||||||
|
formik.values.scm_update_on_launch
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
insights: (
|
insights: (
|
||||||
<InsightsSubForm
|
<InsightsSubForm
|
||||||
credential={credentials.insights}
|
credential={credentials.insights}
|
||||||
onCredentialSelection={handleCredentialSelection}
|
onCredentialSelection={handleCredentialSelection}
|
||||||
scmUpdateOnLaunch={formik.values.scm_update_on_launch}
|
scmUpdateOnLaunch={
|
||||||
|
formik.values.scm_update_on_launch
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}[formik.values.scm_type]
|
}[formik.values.scm_type]
|
||||||
@@ -338,7 +360,9 @@ function ProjectForm({ project, ...props }) {
|
|||||||
id="project-custom-virtualenv"
|
id="project-custom-virtualenv"
|
||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
label: i18n._(t`Use Default Ansible Environment`),
|
label: i18n._(
|
||||||
|
t`Use Default Ansible Environment`
|
||||||
|
),
|
||||||
value: '/venv/ansible/',
|
value: '/venv/ansible/',
|
||||||
key: 'default',
|
key: 'default',
|
||||||
},
|
},
|
||||||
@@ -366,6 +390,8 @@ function ProjectForm({ project, ...props }) {
|
|||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</Config>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ describe('<ProjectAdd />', () => {
|
|||||||
id: 100,
|
id: 100,
|
||||||
credential_type_id: 4,
|
credential_type_id: 4,
|
||||||
kind: 'scm',
|
kind: 'scm',
|
||||||
|
name: 'alpha',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -216,6 +217,59 @@ describe('<ProjectAdd />', () => {
|
|||||||
expect(formik.state.values.credential).toEqual(123);
|
expect(formik.state.values.credential).toEqual(123);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('manual subform should display expected fields', async () => {
|
||||||
|
const config = {
|
||||||
|
project_local_paths: ['foobar', 'qux'],
|
||||||
|
project_base_dir: 'dir/foo/bar',
|
||||||
|
};
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ProjectForm
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
project={{ scm_type: '', local_path: '/_foo__bar' }}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: { config },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
const playbookDirectorySelect = wrapper.find(
|
||||||
|
'FormGroup[label="Playbook Directory"] FormSelect'
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
playbookDirectorySelect
|
||||||
|
.props()
|
||||||
|
.onChange('foobar', { target: { name: 'foobar' } });
|
||||||
|
});
|
||||||
|
expect(wrapper.find('FormGroup[label="Project Base Path"]').length).toBe(1);
|
||||||
|
expect(wrapper.find('FormGroup[label="Playbook Directory"]').length).toBe(
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('manual subform should display warning message when playbook directory is empty', async () => {
|
||||||
|
const config = {
|
||||||
|
project_local_paths: [],
|
||||||
|
project_base_dir: 'dir/foo/bar',
|
||||||
|
};
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ProjectForm
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
project={{ scm_type: '', local_path: '' }}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: { config },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
expect(wrapper.find('ManualSubForm Alert').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('should reset scm subform values when scm type changes', async () => {
|
test('should reset scm subform values when scm type changes', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
|
import FormField, { FieldTooltip } from '@components/FormField';
|
||||||
|
import { FormGroup, Alert } from '@patternfly/react-core';
|
||||||
|
import { BrandName } from '../../../../variables';
|
||||||
|
|
||||||
|
// Setting BrandName to a variable here is necessary to get the jest tests
|
||||||
|
// passing. Attempting to use BrandName in the template literal results
|
||||||
|
// in failing tests.
|
||||||
|
const brandName = BrandName;
|
||||||
|
|
||||||
|
const ManualSubForm = ({
|
||||||
|
i18n,
|
||||||
|
localPath,
|
||||||
|
project_base_dir,
|
||||||
|
project_local_paths,
|
||||||
|
}) => {
|
||||||
|
const localPaths = [...new Set([...project_local_paths, localPath])];
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
value: '',
|
||||||
|
key: '',
|
||||||
|
label: i18n._(t`Choose a Playbook Directory`),
|
||||||
|
},
|
||||||
|
...localPaths
|
||||||
|
.filter(path => path)
|
||||||
|
.map(path => ({
|
||||||
|
value: path,
|
||||||
|
key: path,
|
||||||
|
label: path,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{options.length === 1 && (
|
||||||
|
<Alert
|
||||||
|
title={i18n._(t`WARNING: `)}
|
||||||
|
css="grid-column: 1/-1"
|
||||||
|
variant="warning"
|
||||||
|
isInline
|
||||||
|
>
|
||||||
|
{i18n._(t`
|
||||||
|
There are no available playbook directories in ${project_base_dir}.
|
||||||
|
Either that directory is empty, or all of the contents are already
|
||||||
|
assigned to other projects. Create a new directory there and make
|
||||||
|
sure the playbook files can be read by the "awx" system user,
|
||||||
|
or have ${brandName} directly retrieve your playbooks from
|
||||||
|
source control using the SCM Type option above.`)}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<FormField
|
||||||
|
id="project-base-dir"
|
||||||
|
label={i18n._(t`Project Base Path`)}
|
||||||
|
name="base_dir"
|
||||||
|
type="text"
|
||||||
|
isReadOnly
|
||||||
|
tooltip={
|
||||||
|
<span>
|
||||||
|
{i18n._(t`Base path used for locating playbooks. Directories
|
||||||
|
found inside this path will be listed in the playbook directory drop-down.
|
||||||
|
Together the base path and selected playbook directory provide the full
|
||||||
|
path used to locate playbooks.`)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{i18n._(t`Change PROJECTS_ROOT when deploying
|
||||||
|
${brandName} to change this location.`)}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{options.length !== 1 && (
|
||||||
|
<Field
|
||||||
|
name="local_path"
|
||||||
|
render={({ field, form }) => (
|
||||||
|
<FormGroup
|
||||||
|
fieldId="project-local-path"
|
||||||
|
label={i18n._(t`Playbook Directory`)}
|
||||||
|
>
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(t`Select from the list of directories found in
|
||||||
|
the Project Base Path. Together the base path and the playbook
|
||||||
|
directory provide the full path used to locate playbooks.`)}
|
||||||
|
/>
|
||||||
|
<AnsibleSelect
|
||||||
|
{...field}
|
||||||
|
id="local_path"
|
||||||
|
data={options}
|
||||||
|
onChange={(event, value) => {
|
||||||
|
form.setFieldValue('local_path', value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(ManualSubForm);
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
export { SubFormTitle } from './SharedFields';
|
||||||
export { default as GitSubForm } from './GitSubForm';
|
export { default as GitSubForm } from './GitSubForm';
|
||||||
export { default as HgSubForm } from './HgSubForm';
|
export { default as HgSubForm } from './HgSubForm';
|
||||||
export { default as SvnSubForm } from './SvnSubForm';
|
|
||||||
export { default as InsightsSubForm } from './InsightsSubForm';
|
export { default as InsightsSubForm } from './InsightsSubForm';
|
||||||
export { SubFormTitle } from './SharedFields';
|
export { default as ManualSubForm } from './ManualSubForm';
|
||||||
|
export { default as SvnSubForm } from './SvnSubForm';
|
||||||
|
|||||||
Reference in New Issue
Block a user