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();