mirror of
https://github.com/ansible/awx.git
synced 2026-03-29 14:55:09 -02:30
Merge pull request #5740 from AlexSCorey/5257-WFJTMissingResource
Fixes InvGroup Form submission error and TemplateList Missing Resource Bug Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -1,28 +1,28 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { GroupsAPI } from '@api';
|
import { GroupsAPI } from '@api';
|
||||||
import { Card } from '@patternfly/react-core';
|
import { Card } from '@patternfly/react-core';
|
||||||
|
|
||||||
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
||||||
|
|
||||||
function InventoryGroupsAdd({ history, inventory, setBreadcrumb }) {
|
function InventoryGroupsAdd() {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const { id } = useParams();
|
||||||
useEffect(() => setBreadcrumb(inventory), [inventory, setBreadcrumb]);
|
const history = useHistory();
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
values.inventory = inventory.id;
|
values.inventory = id;
|
||||||
try {
|
try {
|
||||||
const { data } = await GroupsAPI.create(values);
|
const { data } = await GroupsAPI.create(values);
|
||||||
history.push(`/inventories/inventory/${inventory.id}/groups/${data.id}`);
|
history.push(`/inventories/inventory/${id}/groups/${data.id}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
history.push(`/inventories/inventory/${inventory.id}/groups`);
|
history.push(`/inventories/inventory/${id}/groups`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -35,5 +35,5 @@ function InventoryGroupsAdd({ history, inventory, setBreadcrumb }) {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default withI18n()(withRouter(InventoryGroupsAdd));
|
export default withI18n()(InventoryGroupsAdd);
|
||||||
export { InventoryGroupsAdd as _InventoryGroupsAdd };
|
export { InventoryGroupsAdd as _InventoryGroupsAdd };
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ describe('<InventoryGroupAdd />', () => {
|
|||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<Route
|
<Route
|
||||||
path="/inventories/inventory/:id/groups/add"
|
path="/inventories/inventory/:id/groups/add"
|
||||||
component={() => (
|
component={() => <InventoryGroupAdd />}
|
||||||
<InventoryGroupAdd setBreadcrumb={() => {}} inventory={{ id: 1 }} />
|
|
||||||
)}
|
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
context: {
|
context: {
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { GroupsAPI } from '@api';
|
import { GroupsAPI } from '@api';
|
||||||
|
|
||||||
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
||||||
|
|
||||||
function InventoryGroupEdit({ history, inventoryGroup, inventory, match }) {
|
function InventoryGroupEdit({ inventoryGroup }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
const { id, groupId } = useParams();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const handleSubmit = async values => {
|
const handleSubmit = async values => {
|
||||||
try {
|
try {
|
||||||
await GroupsAPI.update(match.params.groupId, values);
|
await GroupsAPI.update(groupId, values);
|
||||||
history.push(
|
history.push(`/inventories/inventory/${id}/groups/${groupId}`);
|
||||||
`/inventories/inventory/${inventory.id}/groups/${inventoryGroup.id}`
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
history.push(
|
history.push(`/inventories/inventory/${id}/groups/${groupId}`);
|
||||||
`/inventories/inventory/${inventory.id}/groups/${inventoryGroup.id}`
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -34,5 +32,5 @@ function InventoryGroupEdit({ history, inventoryGroup, inventory, match }) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default withI18n()(withRouter(InventoryGroupEdit));
|
export default withI18n()(InventoryGroupEdit);
|
||||||
export { InventoryGroupEdit as _InventoryGroupEdit };
|
export { InventoryGroupEdit as _InventoryGroupEdit };
|
||||||
|
|||||||
@@ -26,24 +26,12 @@ describe('<InventoryGroupEdit />', () => {
|
|||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<Route
|
<Route
|
||||||
path="/inventories/inventory/:id/groups/:groupId/edit"
|
path="/inventories/inventory/:id/groups/:groupId/edit"
|
||||||
component={() => (
|
component={() => <InventoryGroupEdit inventoryGroup={{ id: 2 }} />}
|
||||||
<InventoryGroupEdit
|
|
||||||
setBreadcrumb={() => {}}
|
|
||||||
inventory={{ id: 1 }}
|
|
||||||
inventoryGroup={{ id: 2 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
context: {
|
context: {
|
||||||
router: {
|
router: {
|
||||||
history,
|
history,
|
||||||
route: {
|
|
||||||
match: {
|
|
||||||
params: { groupId: 13 },
|
|
||||||
},
|
|
||||||
location: history.location,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
DataListItem,
|
DataListItem,
|
||||||
@@ -22,7 +22,6 @@ import ListActionButton from '@components/ListActionButton';
|
|||||||
import VerticalSeparator from '@components/VerticalSeparator';
|
import VerticalSeparator from '@components/VerticalSeparator';
|
||||||
import { Sparkline } from '@components/Sparkline';
|
import { Sparkline } from '@components/Sparkline';
|
||||||
import { toTitleCase } from '@util/strings';
|
import { toTitleCase } from '@util/strings';
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const rightStyle = `
|
const rightStyle = `
|
||||||
@@ -51,108 +50,104 @@ const LeftDataListCell = styled(DataListCell)`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const RightDataListCell = styled(DataListCell)`
|
const RightDataListCell = styled(DataListCell)`
|
||||||
${rightStyle}
|
${rightStyle}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const RightActionButtonCell = styled(ActionButtonCell)`
|
const RightActionButtonCell = styled(ActionButtonCell)`
|
||||||
${rightStyle}
|
${rightStyle}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class TemplateListItem extends Component {
|
function TemplateListItem({ i18n, template, isSelected, onSelect }) {
|
||||||
render() {
|
const canLaunch = template.summary_fields.user_capabilities.start;
|
||||||
const { i18n, template, isSelected, onSelect } = this.props;
|
|
||||||
const canLaunch = template.summary_fields.user_capabilities.start;
|
const missingResourceIcon =
|
||||||
const missingResourceIcon =
|
template.type === 'job_template' &&
|
||||||
|
(!template.summary_fields.project ||
|
||||||
(!template.summary_fields.inventory &&
|
(!template.summary_fields.inventory &&
|
||||||
!template.ask_inventory_on_launch) ||
|
!template.ask_inventory_on_launch));
|
||||||
!template.summary_fields.project;
|
|
||||||
return (
|
return (
|
||||||
<DataListItem
|
<DataListItem
|
||||||
aria-labelledby={`check-action-${template.id}`}
|
aria-labelledby={`check-action-${template.id}`}
|
||||||
css="--pf-c-data-list__expandable-content--BoxShadow: none;"
|
css="--pf-c-data-list__expandable-content--BoxShadow: none;"
|
||||||
id={`${template.id}`}
|
id={`${template.id}`}
|
||||||
>
|
>
|
||||||
<DataListItemRow>
|
<DataListItemRow>
|
||||||
<DataListCheck
|
<DataListCheck
|
||||||
id={`select-jobTemplate-${template.id}`}
|
id={`select-jobTemplate-${template.id}`}
|
||||||
checked={isSelected}
|
checked={isSelected}
|
||||||
onChange={onSelect}
|
onChange={onSelect}
|
||||||
aria-labelledby={`check-action-${template.id}`}
|
aria-labelledby={`check-action-${template.id}`}
|
||||||
/>
|
/>
|
||||||
<DataListItemCells
|
<DataListItemCells
|
||||||
dataListCells={[
|
dataListCells={[
|
||||||
<LeftDataListCell key="divider">
|
<LeftDataListCell key="divider">
|
||||||
<VerticalSeparator />
|
<VerticalSeparator />
|
||||||
|
<span>
|
||||||
|
<Link to={`/templates/${template.type}/${template.id}`}>
|
||||||
|
<b>{template.name}</b>
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
{missingResourceIcon && (
|
||||||
<span>
|
<span>
|
||||||
<Link to={`/templates/${template.type}/${template.id}`}>
|
<Tooltip
|
||||||
<b>{template.name}</b>
|
content={i18n._(
|
||||||
</Link>
|
t`Resources are missing from this template.`
|
||||||
|
)}
|
||||||
|
position="right"
|
||||||
|
>
|
||||||
|
<ExclamationTriangleIcon css="color: #c9190b; margin-left: 20px;" />
|
||||||
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
{missingResourceIcon && (
|
)}
|
||||||
<span>
|
</LeftDataListCell>,
|
||||||
<Tooltip
|
<RightDataListCell
|
||||||
content={i18n._(
|
css="padding-left: 40px;"
|
||||||
t`Resources are missing from this template.`
|
righthalf="true"
|
||||||
)}
|
key="type"
|
||||||
position="right"
|
>
|
||||||
>
|
{toTitleCase(template.type)}
|
||||||
<ExclamationTriangleIcon css="color: #c9190b; margin-left: 20px;" />
|
</RightDataListCell>,
|
||||||
</Tooltip>
|
<RightDataListCell css="flex: 1;" righthalf="true" key="sparkline">
|
||||||
</span>
|
<Sparkline jobs={template.summary_fields.recent_jobs} />
|
||||||
)}
|
</RightDataListCell>,
|
||||||
</LeftDataListCell>,
|
<RightActionButtonCell
|
||||||
<RightDataListCell
|
css="max-width: 80px;"
|
||||||
css="padding-left: 40px;"
|
righthalf="true"
|
||||||
righthalf="true"
|
lastcolumn="true"
|
||||||
key="type"
|
key="launch"
|
||||||
>
|
>
|
||||||
{toTitleCase(template.type)}
|
{canLaunch && template.type === 'job_template' && (
|
||||||
</RightDataListCell>,
|
<Tooltip content={i18n._(t`Launch Template`)} position="top">
|
||||||
<RightDataListCell
|
<LaunchButton resource={template}>
|
||||||
css="flex: 1;"
|
{({ handleLaunch }) => (
|
||||||
righthalf="true"
|
<ListActionButton variant="plain" onClick={handleLaunch}>
|
||||||
key="sparkline"
|
<RocketIcon />
|
||||||
>
|
</ListActionButton>
|
||||||
<Sparkline jobs={template.summary_fields.recent_jobs} />
|
)}
|
||||||
</RightDataListCell>,
|
</LaunchButton>
|
||||||
<RightActionButtonCell
|
</Tooltip>
|
||||||
css="max-width: 80px;"
|
)}
|
||||||
righthalf="true"
|
{template.summary_fields.user_capabilities.edit && (
|
||||||
lastcolumn="true"
|
<Tooltip content={i18n._(t`Edit Template`)} position="top">
|
||||||
key="launch"
|
<ListActionButton
|
||||||
>
|
variant="plain"
|
||||||
{canLaunch && template.type === 'job_template' && (
|
component={Link}
|
||||||
<Tooltip content={i18n._(t`Launch Template`)} position="top">
|
to={`/templates/${template.type}/${template.id}/edit`}
|
||||||
<LaunchButton resource={template}>
|
>
|
||||||
{({ handleLaunch }) => (
|
<PencilAltIcon />
|
||||||
<ListActionButton
|
</ListActionButton>
|
||||||
variant="plain"
|
</Tooltip>
|
||||||
onClick={handleLaunch}
|
)}
|
||||||
>
|
</RightActionButtonCell>,
|
||||||
<RocketIcon />
|
]}
|
||||||
</ListActionButton>
|
/>
|
||||||
)}
|
</DataListItemRow>
|
||||||
</LaunchButton>
|
</DataListItem>
|
||||||
</Tooltip>
|
);
|
||||||
)}
|
|
||||||
{template.summary_fields.user_capabilities.edit && (
|
|
||||||
<Tooltip content={i18n._(t`Edit Template`)} position="top">
|
|
||||||
<ListActionButton
|
|
||||||
variant="plain"
|
|
||||||
component={Link}
|
|
||||||
to={`/templates/${template.type}/${template.id}/edit`}
|
|
||||||
>
|
|
||||||
<PencilAltIcon />
|
|
||||||
</ListActionButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</RightActionButtonCell>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</DataListItemRow>
|
|
||||||
</DataListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { TemplateListItem as _TemplateListItem };
|
export { TemplateListItem as _TemplateListItem };
|
||||||
export default withI18n()(TemplateListItem);
|
export default withI18n()(TemplateListItem);
|
||||||
|
|||||||
@@ -142,4 +142,23 @@ describe('<TemplatesListItem />', () => {
|
|||||||
);
|
);
|
||||||
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
|
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
test('missing resource icon is not shown type is workflow_job_template', () => {
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<TemplatesListItem
|
||||||
|
isSelected={false}
|
||||||
|
template={{
|
||||||
|
id: 1,
|
||||||
|
name: 'Template 1',
|
||||||
|
url: '/templates/job_template/1',
|
||||||
|
type: 'workflow_job_template',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user