mirror of
https://github.com/ansible/awx.git
synced 2026-03-26 13:25:02 -02:30
Merge pull request #6115 from jlmitch5/scheduleListInDetailViews
add schedule list to detail views Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -61,6 +61,10 @@ class JobTemplates extends InstanceGroupsMixin(NotificationsMixin(Base)) {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readScheduleList(id, params) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/schedules/`, { params });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default JobTemplates;
|
export default JobTemplates;
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class Projects extends LaunchUpdateMixin(NotificationsMixin(Base)) {
|
|||||||
return this.http.get(`${this.baseUrl}${id}/playbooks/`);
|
return this.http.get(`${this.baseUrl}${id}/playbooks/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readScheduleList(id, params) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/schedules/`, { params });
|
||||||
|
}
|
||||||
|
|
||||||
readSync(id) {
|
readSync(id) {
|
||||||
return this.http.get(`${this.baseUrl}${id}/update/`);
|
return this.http.get(`${this.baseUrl}${id}/update/`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ class WorkflowJobTemplates extends Base {
|
|||||||
createNode(id, data) {
|
createNode(id, data) {
|
||||||
return this.http.post(`${this.baseUrl}${id}/workflow_nodes/`, data);
|
return this.http.post(`${this.baseUrl}${id}/workflow_nodes/`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readScheduleList(id, params) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/schedules/`, { params });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WorkflowJobTemplates;
|
export default WorkflowJobTemplates;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { useLocation } from 'react-router-dom';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { SchedulesAPI } from '@api';
|
import { SchedulesAPI } from '@api';
|
||||||
import { Card, PageSection } from '@patternfly/react-core';
|
|
||||||
import AlertModal from '@components/AlertModal';
|
import AlertModal from '@components/AlertModal';
|
||||||
import ErrorDetail from '@components/ErrorDetail';
|
import ErrorDetail from '@components/ErrorDetail';
|
||||||
import DataListToolbar from '@components/DataListToolbar';
|
import DataListToolbar from '@components/DataListToolbar';
|
||||||
@@ -12,7 +11,7 @@ import PaginatedDataList, {
|
|||||||
} from '@components/PaginatedDataList';
|
} from '@components/PaginatedDataList';
|
||||||
import useRequest, { useDeleteItems } from '@util/useRequest';
|
import useRequest, { useDeleteItems } from '@util/useRequest';
|
||||||
import { getQSConfig, parseQueryString } from '@util/qs';
|
import { getQSConfig, parseQueryString } from '@util/qs';
|
||||||
import { ScheduleListItem } from '.';
|
import ScheduleListItem from './ScheduleListItem';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('schedule', {
|
const QS_CONFIG = getQSConfig('schedule', {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -20,7 +19,7 @@ const QS_CONFIG = getQSConfig('schedule', {
|
|||||||
order_by: 'unified_job_template__polymorphic_ctype__model',
|
order_by: 'unified_job_template__polymorphic_ctype__model',
|
||||||
});
|
});
|
||||||
|
|
||||||
function ScheduleList({ i18n }) {
|
function ScheduleList({ i18n, loadSchedules }) {
|
||||||
const [selected, setSelected] = useState([]);
|
const [selected, setSelected] = useState([]);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -33,14 +32,12 @@ function ScheduleList({ i18n }) {
|
|||||||
} = useRequest(
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
const params = parseQueryString(QS_CONFIG, location.search);
|
const params = parseQueryString(QS_CONFIG, location.search);
|
||||||
|
const response = loadSchedules(params);
|
||||||
const {
|
const {
|
||||||
data: { count, results },
|
data: { count, results },
|
||||||
} = await SchedulesAPI.read(params);
|
} = await response;
|
||||||
return {
|
return { itemCount: count, schedules: results };
|
||||||
itemCount: count,
|
}, [location, loadSchedules]),
|
||||||
schedules: results,
|
|
||||||
};
|
|
||||||
}, [location]),
|
|
||||||
{
|
{
|
||||||
schedules: [],
|
schedules: [],
|
||||||
itemCount: 0,
|
itemCount: 0,
|
||||||
@@ -88,63 +85,61 @@ function ScheduleList({ i18n }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<>
|
||||||
<Card>
|
<PaginatedDataList
|
||||||
<PaginatedDataList
|
contentError={contentError}
|
||||||
contentError={contentError}
|
hasContentLoading={isLoading || isDeleteLoading}
|
||||||
hasContentLoading={isLoading || isDeleteLoading}
|
items={schedules}
|
||||||
items={schedules}
|
itemCount={itemCount}
|
||||||
itemCount={itemCount}
|
qsConfig={QS_CONFIG}
|
||||||
qsConfig={QS_CONFIG}
|
onRowClick={handleSelect}
|
||||||
onRowClick={handleSelect}
|
renderItem={item => (
|
||||||
renderItem={item => (
|
<ScheduleListItem
|
||||||
<ScheduleListItem
|
isSelected={selected.some(row => row.id === item.id)}
|
||||||
isSelected={selected.some(row => row.id === item.id)}
|
key={item.id}
|
||||||
key={item.id}
|
onSelect={() => handleSelect(item)}
|
||||||
onSelect={() => handleSelect(item)}
|
schedule={item}
|
||||||
schedule={item}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
toolbarSearchColumns={[
|
||||||
toolbarSearchColumns={[
|
{
|
||||||
{
|
name: i18n._(t`Name`),
|
||||||
name: i18n._(t`Name`),
|
key: 'name',
|
||||||
key: 'name',
|
isDefault: true,
|
||||||
isDefault: true,
|
},
|
||||||
},
|
]}
|
||||||
]}
|
toolbarSortColumns={[
|
||||||
toolbarSortColumns={[
|
{
|
||||||
{
|
name: i18n._(t`Name`),
|
||||||
name: i18n._(t`Name`),
|
key: 'name',
|
||||||
key: 'name',
|
},
|
||||||
},
|
{
|
||||||
{
|
name: i18n._(t`Next Run`),
|
||||||
name: i18n._(t`Next Run`),
|
key: 'next_run',
|
||||||
key: 'next_run',
|
},
|
||||||
},
|
{
|
||||||
{
|
name: i18n._(t`Type`),
|
||||||
name: i18n._(t`Type`),
|
key: 'unified_job_template__polymorphic_ctype__model',
|
||||||
key: 'unified_job_template__polymorphic_ctype__model',
|
},
|
||||||
},
|
]}
|
||||||
]}
|
renderToolbar={props => (
|
||||||
renderToolbar={props => (
|
<DataListToolbar
|
||||||
<DataListToolbar
|
{...props}
|
||||||
{...props}
|
showSelectAll
|
||||||
showSelectAll
|
isAllSelected={isAllSelected}
|
||||||
isAllSelected={isAllSelected}
|
onSelectAll={handleSelectAll}
|
||||||
onSelectAll={handleSelectAll}
|
qsConfig={QS_CONFIG}
|
||||||
qsConfig={QS_CONFIG}
|
additionalControls={[
|
||||||
additionalControls={[
|
<ToolbarDeleteButton
|
||||||
<ToolbarDeleteButton
|
key="delete"
|
||||||
key="delete"
|
onDelete={handleDelete}
|
||||||
onDelete={handleDelete}
|
itemsToDelete={selected}
|
||||||
itemsToDelete={selected}
|
pluralizedItemName={i18n._(t`Schedules`)}
|
||||||
pluralizedItemName={i18n._(t`Schedules`)}
|
/>,
|
||||||
/>,
|
]}
|
||||||
]}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
{deletionError && (
|
{deletionError && (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
isOpen={deletionError}
|
isOpen={deletionError}
|
||||||
@@ -156,7 +151,7 @@ function ScheduleList({ i18n }) {
|
|||||||
<ErrorDetail error={deletionError} />
|
<ErrorDetail error={deletionError} />
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
)}
|
)}
|
||||||
</PageSection>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
import { SchedulesAPI } from '@api';
|
import { SchedulesAPI } from '@api';
|
||||||
import ScheduleList from './ScheduleList';
|
import ScheduleList from './ScheduleList';
|
||||||
import mockSchedules from '../data.schedules.json';
|
import mockSchedules from './data.schedules.json';
|
||||||
|
|
||||||
jest.mock('@api/models/Schedules');
|
jest.mock('@api/models/Schedules');
|
||||||
|
|
||||||
@@ -22,8 +22,11 @@ describe('ScheduleList', () => {
|
|||||||
describe('read call successful', () => {
|
describe('read call successful', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
SchedulesAPI.read.mockResolvedValue({ data: mockSchedules });
|
SchedulesAPI.read.mockResolvedValue({ data: mockSchedules });
|
||||||
|
const loadSchedules = params => SchedulesAPI.read(params);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(<ScheduleList />);
|
wrapper = mountWithContexts(
|
||||||
|
<ScheduleList loadSchedules={loadSchedules} />
|
||||||
|
);
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
});
|
});
|
||||||
@@ -18,7 +18,7 @@ import { DetailList, Detail } from '@components/DetailList';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Schedule } from '@types';
|
import { Schedule } from '@types';
|
||||||
import { formatDateString } from '@util/dates';
|
import { formatDateString } from '@util/dates';
|
||||||
import ScheduleToggle from '../shared/ScheduleToggle';
|
import ScheduleToggle from './ScheduleToggle';
|
||||||
|
|
||||||
const DataListAction = styled(_DataListAction)`
|
const DataListAction = styled(_DataListAction)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
1
awx/ui_next/src/components/ScheduleList/index.js
Normal file
1
awx/ui_next/src/components/ScheduleList/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './ScheduleList';
|
||||||
@@ -9,10 +9,10 @@ import RoutedTabs from '@components/RoutedTabs';
|
|||||||
import ContentError from '@components/ContentError';
|
import ContentError from '@components/ContentError';
|
||||||
import NotificationList from '@components/NotificationList';
|
import NotificationList from '@components/NotificationList';
|
||||||
import { ResourceAccessList } from '@components/ResourceAccessList';
|
import { ResourceAccessList } from '@components/ResourceAccessList';
|
||||||
|
import ScheduleList from '@components/ScheduleList';
|
||||||
import ProjectDetail from './ProjectDetail';
|
import ProjectDetail from './ProjectDetail';
|
||||||
import ProjectEdit from './ProjectEdit';
|
import ProjectEdit from './ProjectEdit';
|
||||||
import ProjectJobTemplatesList from './ProjectJobTemplatesList';
|
import ProjectJobTemplatesList from './ProjectJobTemplatesList';
|
||||||
import ProjectSchedules from './ProjectSchedules';
|
|
||||||
import { OrganizationsAPI, ProjectsAPI } from '@api';
|
import { OrganizationsAPI, ProjectsAPI } from '@api';
|
||||||
|
|
||||||
class Project extends Component {
|
class Project extends Component {
|
||||||
@@ -30,6 +30,7 @@ class Project extends Component {
|
|||||||
};
|
};
|
||||||
this.loadProject = this.loadProject.bind(this);
|
this.loadProject = this.loadProject.bind(this);
|
||||||
this.loadProjectAndRoles = this.loadProjectAndRoles.bind(this);
|
this.loadProjectAndRoles = this.loadProjectAndRoles.bind(this);
|
||||||
|
this.loadSchedules = this.loadSchedules.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -103,6 +104,11 @@ class Project extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadSchedules(params) {
|
||||||
|
const { project } = this.state;
|
||||||
|
return ProjectsAPI.readScheduleList(project.id, params);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { location, match, me, i18n } = this.props;
|
const { location, match, me, i18n } = this.props;
|
||||||
|
|
||||||
@@ -134,16 +140,17 @@ class Project extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tabsArray.push(
|
tabsArray.push({
|
||||||
{
|
name: i18n._(t`Job Templates`),
|
||||||
name: i18n._(t`Job Templates`),
|
link: `${match.url}/job_templates`,
|
||||||
link: `${match.url}/job_templates`,
|
});
|
||||||
},
|
|
||||||
{
|
if (project?.scm_type) {
|
||||||
|
tabsArray.push({
|
||||||
name: i18n._(t`Schedules`),
|
name: i18n._(t`Schedules`),
|
||||||
link: `${match.url}/schedules`,
|
link: `${match.url}/schedules`,
|
||||||
}
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
tabsArray.forEach((tab, n) => {
|
tabsArray.forEach((tab, n) => {
|
||||||
tab.id = n;
|
tab.id = n;
|
||||||
@@ -230,10 +237,14 @@ class Project extends Component {
|
|||||||
<ProjectJobTemplatesList id={Number(match.params.id)} />
|
<ProjectJobTemplatesList id={Number(match.params.id)} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Route
|
{project?.scm_type && project.scm_type !== '' && (
|
||||||
path="/projects/:id/schedules"
|
<Route
|
||||||
render={() => <ProjectSchedules id={Number(match.params.id)} />}
|
path="/projects/:id/schedules"
|
||||||
/>
|
render={() => (
|
||||||
|
<ScheduleList loadSchedules={this.loadSchedules} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Route
|
<Route
|
||||||
key="not-found"
|
key="not-found"
|
||||||
path="*"
|
path="*"
|
||||||
|
|||||||
@@ -68,6 +68,49 @@ describe('<Project />', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('schedules tab shown for scm based projects.', async done => {
|
||||||
|
ProjectsAPI.readDetail.mockResolvedValue({ data: mockDetails });
|
||||||
|
OrganizationsAPI.read.mockResolvedValue({
|
||||||
|
count: 0,
|
||||||
|
next: null,
|
||||||
|
previous: null,
|
||||||
|
data: { results: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<Project setBreadcrumb={() => {}} me={mockMe} />
|
||||||
|
);
|
||||||
|
const tabs = await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'.pf-c-tabs__item',
|
||||||
|
el => el.length === 4
|
||||||
|
);
|
||||||
|
expect(tabs.at(3).text()).toEqual('Schedules');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('schedules tab hidden for manual projects.', async done => {
|
||||||
|
const manualDetails = Object.assign(mockDetails, { scm_type: '' });
|
||||||
|
ProjectsAPI.readDetail.mockResolvedValue({ data: manualDetails });
|
||||||
|
OrganizationsAPI.read.mockResolvedValue({
|
||||||
|
count: 0,
|
||||||
|
next: null,
|
||||||
|
previous: null,
|
||||||
|
data: { results: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<Project setBreadcrumb={() => {}} me={mockMe} />
|
||||||
|
);
|
||||||
|
const tabs = await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'.pf-c-tabs__item',
|
||||||
|
el => el.length === 3
|
||||||
|
);
|
||||||
|
tabs.forEach(tab => expect(tab.text()).not.toEqual('Schedules'));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
||||||
const history = createMemoryHistory({
|
const history = createMemoryHistory({
|
||||||
initialEntries: ['/projects/1/foobar'],
|
initialEntries: ['/projects/1/foobar'],
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default as ScheduleList } from './ScheduleList';
|
|
||||||
export { default as ScheduleListItem } from './ScheduleListItem';
|
|
||||||
@@ -4,9 +4,15 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import Breadcrumbs from '@components/Breadcrumbs';
|
import Breadcrumbs from '@components/Breadcrumbs';
|
||||||
import { ScheduleList } from './ScheduleList';
|
import ScheduleList from '@components/ScheduleList';
|
||||||
|
import { SchedulesAPI } from '@api';
|
||||||
|
import { PageSection, Card } from '@patternfly/react-core';
|
||||||
|
|
||||||
function Schedules({ i18n }) {
|
function Schedules({ i18n }) {
|
||||||
|
const loadSchedules = params => {
|
||||||
|
return SchedulesAPI.read(params);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Breadcrumbs
|
<Breadcrumbs
|
||||||
@@ -16,7 +22,11 @@ function Schedules({ i18n }) {
|
|||||||
/>
|
/>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/schedules">
|
<Route path="/schedules">
|
||||||
<ScheduleList />
|
<PageSection>
|
||||||
|
<Card>
|
||||||
|
<ScheduleList loadSchedules={loadSchedules} />
|
||||||
|
</Card>
|
||||||
|
</PageSection>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import ContentError from '@components/ContentError';
|
|||||||
import JobList from '@components/JobList';
|
import JobList from '@components/JobList';
|
||||||
import NotificationList from '@components/NotificationList';
|
import NotificationList from '@components/NotificationList';
|
||||||
import RoutedTabs from '@components/RoutedTabs';
|
import RoutedTabs from '@components/RoutedTabs';
|
||||||
|
import ScheduleList from '@components/ScheduleList';
|
||||||
import { ResourceAccessList } from '@components/ResourceAccessList';
|
import { ResourceAccessList } from '@components/ResourceAccessList';
|
||||||
import JobTemplateDetail from './JobTemplateDetail';
|
import JobTemplateDetail from './JobTemplateDetail';
|
||||||
import JobTemplateEdit from './JobTemplateEdit';
|
import JobTemplateEdit from './JobTemplateEdit';
|
||||||
@@ -27,6 +28,7 @@ class Template extends Component {
|
|||||||
};
|
};
|
||||||
this.loadTemplate = this.loadTemplate.bind(this);
|
this.loadTemplate = this.loadTemplate.bind(this);
|
||||||
this.loadTemplateAndRoles = this.loadTemplateAndRoles.bind(this);
|
this.loadTemplateAndRoles = this.loadTemplateAndRoles.bind(this);
|
||||||
|
this.loadSchedules = this.loadSchedules.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -81,6 +83,11 @@ class Template extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadSchedules(params) {
|
||||||
|
const { template } = this.state;
|
||||||
|
return JobTemplatesAPI.readScheduleList(template.id, params);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { i18n, location, match, me } = this.props;
|
const { i18n, location, match, me } = this.props;
|
||||||
const {
|
const {
|
||||||
@@ -105,10 +112,6 @@ class Template extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tabsArray.push(
|
tabsArray.push(
|
||||||
{
|
|
||||||
name: i18n._(t`Schedules`),
|
|
||||||
link: '/home',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: i18n._(t`Completed Jobs`),
|
name: i18n._(t`Completed Jobs`),
|
||||||
link: `${match.url}/completed_jobs`,
|
link: `${match.url}/completed_jobs`,
|
||||||
@@ -119,6 +122,13 @@ class Template extends Component {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
tabsArray.push({
|
||||||
|
name: i18n._(t`Schedules`),
|
||||||
|
link: `${match.url}/schedules`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
tabsArray.forEach((tab, n) => {
|
tabsArray.forEach((tab, n) => {
|
||||||
tab.id = n;
|
tab.id = n;
|
||||||
});
|
});
|
||||||
@@ -210,6 +220,12 @@ class Template extends Component {
|
|||||||
<JobList defaultParams={{ job__job_template: template.id }} />
|
<JobList defaultParams={{ job__job_template: template.id }} />
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
|
{template && (
|
||||||
|
<Route
|
||||||
|
path="/templates/:templateType/:id/schedules"
|
||||||
|
render={() => <ScheduleList loadSchedules={this.loadSchedules} />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Route
|
<Route
|
||||||
key="not-found"
|
key="not-found"
|
||||||
path="*"
|
path="*"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import ContentError from '@components/ContentError';
|
|||||||
import FullPage from '@components/FullPage';
|
import FullPage from '@components/FullPage';
|
||||||
import JobList from '@components/JobList';
|
import JobList from '@components/JobList';
|
||||||
import RoutedTabs from '@components/RoutedTabs';
|
import RoutedTabs from '@components/RoutedTabs';
|
||||||
|
import ScheduleList from '@components/ScheduleList';
|
||||||
import { WorkflowJobTemplatesAPI, CredentialsAPI } from '@api';
|
import { WorkflowJobTemplatesAPI, CredentialsAPI } from '@api';
|
||||||
import WorkflowJobTemplateDetail from './WorkflowJobTemplateDetail';
|
import WorkflowJobTemplateDetail from './WorkflowJobTemplateDetail';
|
||||||
import { Visualizer } from './WorkflowJobTemplateVisualizer';
|
import { Visualizer } from './WorkflowJobTemplateVisualizer';
|
||||||
@@ -24,6 +25,7 @@ class WorkflowJobTemplate extends Component {
|
|||||||
template: null,
|
template: null,
|
||||||
};
|
};
|
||||||
this.loadTemplate = this.loadTemplate.bind(this);
|
this.loadTemplate = this.loadTemplate.bind(this);
|
||||||
|
this.loadSchedules = this.loadSchedules.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -70,6 +72,11 @@ class WorkflowJobTemplate extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadSchedules(params) {
|
||||||
|
const { template } = this.state;
|
||||||
|
return WorkflowJobTemplatesAPI.readScheduleList(template.id, params);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { i18n, location, match } = this.props;
|
const { i18n, location, match } = this.props;
|
||||||
const {
|
const {
|
||||||
@@ -85,6 +92,13 @@ class WorkflowJobTemplate extends Component {
|
|||||||
{ name: i18n._(t`Completed Jobs`), link: `${match.url}/completed_jobs` },
|
{ name: i18n._(t`Completed Jobs`), link: `${match.url}/completed_jobs` },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
tabsArray.push({
|
||||||
|
name: i18n._(t`Schedules`),
|
||||||
|
link: `${match.url}/schedules`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
tabsArray.forEach((tab, n) => {
|
tabsArray.forEach((tab, n) => {
|
||||||
tab.id = n;
|
tab.id = n;
|
||||||
});
|
});
|
||||||
@@ -162,6 +176,12 @@ class WorkflowJobTemplate extends Component {
|
|||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
)}
|
||||||
|
{template && (
|
||||||
|
<Route
|
||||||
|
path="/templates/:templateType/:id/schedules"
|
||||||
|
render={() => <ScheduleList loadSchedules={this.loadSchedules} />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Route
|
<Route
|
||||||
key="not-found"
|
key="not-found"
|
||||||
path="*"
|
path="*"
|
||||||
|
|||||||
Reference in New Issue
Block a user