diff --git a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx
index aa278fedf8..dec5055c60 100644
--- a/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx
+++ b/awx/ui_next/src/screens/Template/TemplateList/TemplateList.jsx
@@ -1,18 +1,23 @@
import React, { Component } from 'react';
-import { withRouter } from 'react-router-dom';
+import { withRouter, Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
-import { t } from '@lingui/macro';
-import { Card, PageSection, PageSectionVariants } from '@patternfly/react-core';
+import { t } from '@lingui/macro';
import {
- JobTemplatesAPI,
- UnifiedJobTemplatesAPI,
- WorkflowJobTemplatesAPI,
-} from '@api';
+ Card,
+ PageSection,
+ PageSectionVariants,
+ Dropdown,
+ DropdownItem,
+ DropdownPosition,
+} from '@patternfly/react-core';
+
+import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api';
import AlertModal from '@components/AlertModal';
import DatalistToolbar from '@components/DataListToolbar';
import PaginatedDataList, {
ToolbarDeleteButton,
+ ToolbarAddButton,
} from '@components/PaginatedDataList';
import { getQSConfig, parseQueryString } from '@util/qs';
@@ -38,12 +43,15 @@ class TemplatesList extends Component {
selected: [],
templates: [],
itemCount: 0,
+ isAddOpen: false,
};
+
this.loadTemplates = this.loadTemplates.bind(this);
this.handleSelectAll = this.handleSelectAll.bind(this);
this.handleSelect = this.handleSelect.bind(this);
this.handleTemplateDelete = this.handleTemplateDelete.bind(this);
this.handleDeleteErrorClose = this.handleDeleteErrorClose.bind(this);
+ this.handleAddToggle = this.handleAddToggle.bind(this);
}
componentDidMount() {
@@ -76,8 +84,13 @@ class TemplatesList extends Component {
}
}
+ handleAddToggle() {
+ const { isAddOpen } = this.state;
+ this.setState({ isAddOpen: !isAddOpen });
+ }
+
async handleTemplateDelete() {
- const { selected } = this.state;
+ const { selected, itemCount } = this.state;
this.setState({ hasContentLoading: true, hasDeletionError: false });
try {
@@ -92,6 +105,7 @@ class TemplatesList extends Component {
return deletePromise;
})
);
+ this.setState({ itemCount: itemCount - selected.length });
} catch (err) {
this.setState({ hasDeletionError: true });
} finally {
@@ -101,14 +115,35 @@ class TemplatesList extends Component {
async loadTemplates() {
const { location } = this.props;
+ const { actions: cachedActions } = this.state;
const params = parseQueryString(QS_CONFIG, location.search);
+ let optionsPromise;
+ if (cachedActions) {
+ optionsPromise = Promise.resolve({ data: { actions: cachedActions } });
+ } else {
+ optionsPromise = JobTemplatesAPI.readOptions();
+ }
+
+ const promises = Promise.all([
+ JobTemplatesAPI.read(params),
+ optionsPromise,
+ ]);
+
this.setState({ contentError: null, hasContentLoading: true });
+
try {
- const {
- data: { count, results },
- } = await UnifiedJobTemplatesAPI.read(params);
+ const [
+ {
+ data: { count, results },
+ },
+ {
+ data: { actions },
+ },
+ ] = await promises;
+
this.setState({
+ actions,
itemCount: count,
templates: results,
selected: [],
@@ -128,8 +163,12 @@ class TemplatesList extends Component {
templates,
itemCount,
selected,
+ isAddOpen,
+ actions,
} = this.state;
const { match, i18n } = this.props;
+ const canAdd =
+ actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
const isAllSelected = selected.length === templates.length;
const { medium } = PageSectionVariants;
return (
@@ -176,6 +215,34 @@ class TemplatesList extends Component {
itemsToDelete={selected}
itemName={i18n._(t`Template`)}
/>,
+ canAdd && (
+
+ }
+ dropdownItems={[
+
+ {i18n._(t`Job Template`)}
+ ,
+
+ {i18n._(t`Workflow Template`)}
+ ,
+ ]}
+ />
+ ),
]}
/>
)}
@@ -189,6 +256,34 @@ class TemplatesList extends Component {
isSelected={selected.some(row => row.id === template.id)}
/>
)}
+ emptyStateControls={
+ canAdd && (
+ }
+ dropdownItems={[
+
+ {i18n._(t`Job Template`)}
+ ,
+
+ {i18n._(t`Workflow Template`)}
+ ,
+ ]}
+ />
+ )
+ }
/>
', () => {
results: mockTemplates,
},
});
+
+ UnifiedJobTemplatesAPI.readOptions.mockResolvedValue({
+ data: {
+ actions: [],
+ },
+ });
});
afterEach(() => {
@@ -116,7 +122,7 @@ describe('', () => {
'TemplatesList',
el => el.state('hasContentLoading') === false
);
- wrapper
+ await wrapper
.find('DataListCheck#select-jobTemplate-1')
.props()
.onChange();