From 1f2edd81abbdcc7f316e6d46d26308c15564169a Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 4 Mar 2020 16:41:21 -0500 Subject: [PATCH 1/6] Adds generic schedule detail component and applies it to JT schedules --- awx/ui_next/package-lock.json | 22 ++ awx/ui_next/package.json | 1 + awx/ui_next/src/api/models/Schedules.js | 8 + .../MultiButtonToggle/MultiButtonToggle.jsx | 1 + .../components/Schedule/ScheduleDetail.jsx | 203 ++++++++++++++ .../Schedule/ScheduleDetail.test.jsx | 265 ++++++++++++++++++ .../ScheduleList.jsx | 2 +- .../ScheduleList.test.jsx | 2 +- .../ScheduleListItem.jsx | 2 +- .../ScheduleListItem.test.jsx | 2 +- .../Schedule/ScheduleOccurrences.jsx | 72 +++++ .../Schedule/ScheduleOccurrences.test.jsx | 61 ++++ .../ScheduleToggle.jsx | 0 .../ScheduleToggle.test.jsx | 0 .../data.schedules.json | 0 awx/ui_next/src/components/Schedule/index.js | 5 + .../src/components/ScheduleList/index.js | 1 - awx/ui_next/src/screens/Project/Project.jsx | 2 +- .../src/screens/Schedule/Schedules.jsx | 2 +- .../JobTemplateSchedule.jsx | 157 +++++++++++ .../JobTemplateSchedule.test.jsx | 111 ++++++++ .../Template/JobTemplateSchedule/index.js | 1 + .../JobTemplateSchedules.jsx | 26 ++ .../JobTemplateSchedules.test.jsx | 31 ++ .../Template/JobTemplateSchedules/index.js | 1 + awx/ui_next/src/screens/Template/Template.jsx | 26 +- .../src/screens/Template/Template.test.jsx | 2 +- .../src/screens/Template/Templates.jsx | 9 +- .../screens/Template/WorkflowJobTemplate.jsx | 2 +- awx/ui_next/src/types.js | 2 +- awx/ui_next/src/util/dates.jsx | 4 + 31 files changed, 1009 insertions(+), 14 deletions(-) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleDetail.jsx create mode 100644 awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleList.jsx (98%) rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleList.test.jsx (99%) rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleListItem.jsx (98%) rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleListItem.test.jsx (98%) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx create mode 100644 awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleToggle.jsx (100%) rename awx/ui_next/src/components/{ScheduleList => Schedule}/ScheduleToggle.test.jsx (100%) rename awx/ui_next/src/components/{ScheduleList => Schedule}/data.schedules.json (100%) create mode 100644 awx/ui_next/src/components/Schedule/index.js delete mode 100644 awx/ui_next/src/components/ScheduleList/index.js create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx create mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js diff --git a/awx/ui_next/package-lock.json b/awx/ui_next/package-lock.json index 7c8b645522..75b693e4d8 100644 --- a/awx/ui_next/package-lock.json +++ b/awx/ui_next/package-lock.json @@ -13202,6 +13202,12 @@ "yallist": "^2.1.2" } }, + "luxon": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.22.0.tgz", + "integrity": "sha512-3sLvlfbFo+AxVEY3IqxymbumtnlgBwjDExxK60W3d+trrUzErNAz/PfvPT+mva+vEUrdIodeCOs7fB6zHtRSrw==", + "optional": true + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -16217,6 +16223,22 @@ "inherits": "^2.0.1" } }, + "rrule": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.4.tgz", + "integrity": "sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA==", + "requires": { + "luxon": "^1.21.3", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + } + } + }, "rst-selector-parser": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json index cdd79cd2d7..2d290c7c15 100644 --- a/awx/ui_next/package.json +++ b/awx/ui_next/package.json @@ -77,6 +77,7 @@ "react-dom": "^16.13.0", "react-router-dom": "^5.1.2", "react-virtualized": "^9.21.1", + "rrule": "^2.6.4", "styled-components": "^4.2.0" } } diff --git a/awx/ui_next/src/api/models/Schedules.js b/awx/ui_next/src/api/models/Schedules.js index e5581d2875..01bff4891e 100644 --- a/awx/ui_next/src/api/models/Schedules.js +++ b/awx/ui_next/src/api/models/Schedules.js @@ -5,6 +5,14 @@ class Schedules extends Base { super(http); this.baseUrl = '/api/v2/schedules/'; } + + createPreview(data) { + return this.http.post(`${this.baseUrl}preview/`, data); + } + + readCredentials(resourceId, params) { + return this.http.get(`${this.baseUrl}${resourceId}/credentials/`, params); + } } export default Schedules; diff --git a/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx b/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx index 9c72cfbab3..b76c4c6a61 100644 --- a/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx +++ b/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx @@ -21,6 +21,7 @@ function MultiButtonToggle({ buttons, value, onChange }) { {buttons && buttons.map(([buttonValue, buttonLabel]) => ( setValue(buttonValue)} variant={buttonValue === value ? 'primary' : 'secondary'} diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx new file mode 100644 index 0000000000..a8aad4daf1 --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx @@ -0,0 +1,203 @@ +import React, { useCallback, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { rrulestr } from 'rrule'; +import styled from 'styled-components'; +import { shape } from 'prop-types'; +import { withI18n } from '@lingui/react'; +import { t } from '@lingui/macro'; +import { Schedule } from '@types'; +import { Chip, ChipGroup, Title } from '@patternfly/react-core'; +import { CardBody } from '@components/Card'; +import ContentError from '@components/ContentError'; +import ContentLoading from '@components/ContentLoading'; +import CredentialChip from '@components/CredentialChip'; +import { DetailList, Detail, UserDateDetail } from '@components/DetailList'; +import { ScheduleOccurrences, ScheduleToggle } from '@components/Schedule'; +import { formatDateString } from '@util/dates'; +import useRequest from '@util/useRequest'; +import { SchedulesAPI } from '@api'; + +const PromptTitle = styled(Title)` + --pf-c-title--m-md--FontWeight: 700; +`; + +function ScheduleDetail({ preview, schedule, i18n }) { + const { + id, + created, + description, + diff_mode, + dtend, + dtstart, + job_tags, + job_type, + inventory, + limit, + modified, + name, + next_run, + rrule, + scm_branch, + skip_tags, + summary_fields, + timezone, + } = schedule; + + const { + result: credentials, + isLoading, + error, + request: fetchCredentials, + } = useRequest( + useCallback(async () => { + const { data } = await SchedulesAPI.readCredentials(id); + return data.results; + }, [id]), + [] + ); + + useEffect(() => { + fetchCredentials(); + }, [fetchCredentials]); + + const rule = rrulestr(rrule); + const repeatFrequency = + rule.options.freq === 3 && dtstart === dtend + ? i18n._(t`None (Run Once)`) + : rule.toText().replace(/^\w/, c => c.toUpperCase()); + const showPromptedFields = + (credentials && credentials.length > 0) || + job_type || + (inventory && summary_fields.inventory) || + scm_branch || + limit || + typeof diff_mode === 'boolean' || + (job_tags && job_tags.length > 0) || + (skip_tags && skip_tags.length > 0); + + if (isLoading) { + return ; + } + + if (error) { + return ; + } + + return ( + + + + + + + + + + + + + + {showPromptedFields && ( + <> + + {i18n._(t`Prompted Fields`)} + + + {inventory && summary_fields.inventory && ( + + {summary_fields.inventory.name} + + } + /> + )} + + + {typeof diff_mode === 'boolean' && ( + + )} + {credentials && credentials.length > 0 && ( + + {credentials.map(c => ( + + ))} + + } + /> + )} + {job_tags && job_tags.length > 0 && ( + + {job_tags.split(',').map(jobTag => ( + + {jobTag} + + ))} + + } + /> + )} + {skip_tags && skip_tags.length > 0 && ( + + {skip_tags.split(',').map(skipTag => ( + + {skipTag} + + ))} + + } + /> + )} + + )} + + + ); +} + +ScheduleDetail.propTypes = { + preview: shape(), + schedule: Schedule.isRequired, +}; + +ScheduleDetail.defaultProps = { + preview: { local: [], utc: [] }, +}; + +export default withI18n()(ScheduleDetail); diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx new file mode 100644 index 0000000000..0dbe6ff6bb --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx @@ -0,0 +1,265 @@ +import React from 'react'; +import { SchedulesAPI } from '@api'; +import { Route } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import { act } from 'react-dom/test-utils'; +import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; +import ScheduleDetail from './ScheduleDetail'; + +jest.mock('@api/models/Schedules'); + +const schedule = { + url: '/api/v2/schedules/1', + rrule: + 'DTSTART;TZID=America/New_York:20200220T000000 RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1', + id: 1, + summary_fields: { + unified_job_template: { + id: 1, + name: 'Mock JT', + description: '', + unified_job_type: 'job', + }, + user_capabilities: { + edit: true, + delete: true, + }, + created_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + modified_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + inventory: { + id: 1, + name: 'Test Inventory', + }, + }, + created: '2020-03-03T20:38:54.210306Z', + modified: '2020-03-03T20:38:54.210336Z', + name: 'Mock JT Schedule', + enabled: false, + description: 'A good schedule', + timezone: 'America/New_York', + dtstart: '2020-03-16T04:00:00Z', + dtend: '2020-07-06T04:00:00Z', + next_run: '2020-03-16T04:00:00Z', +}; + +const preview = { + local: [], + utc: [], +}; + +describe('', () => { + let wrapper; + const history = createMemoryHistory({ + initialEntries: ['/templates/job_template/1/schedules/1/details'], + }); + afterEach(() => { + wrapper.unmount(); + }); + test('details should render with the proper values without prompts', async () => { + SchedulesAPI.readCredentials.mockResolvedValueOnce({ + data: { + count: 0, + results: [], + }, + }); + await act(async () => { + wrapper = mountWithContexts( + ( + + )} + />, + { + context: { + router: { + history, + route: { + location: history.location, + match: { params: { id: 1 } }, + }, + }, + }, + } + ); + }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + expect( + wrapper + .find('Detail[label="Name"]') + .find('dd') + .text() + ).toBe('Mock JT Schedule'); + expect( + wrapper + .find('Detail[label="Description"]') + .find('dd') + .text() + ).toBe('A good schedule'); + expect(wrapper.find('Detail[label="First Run"]').length).toBe(1); + expect(wrapper.find('Detail[label="Next Run"]').length).toBe(1); + expect(wrapper.find('Detail[label="Last Run"]').length).toBe(1); + expect( + wrapper + .find('Detail[label="Local Time Zone"]') + .find('dd') + .text() + ).toBe('America/New_York'); + expect(wrapper.find('Detail[label="Repeat Frequency"]').length).toBe(1); + expect(wrapper.find('Detail[label="Created"]').length).toBe(1); + expect(wrapper.find('Detail[label="Last Modified"]').length).toBe(1); + expect(wrapper.find('Title[children="Prompted Fields"]').length).toBe(0); + expect(wrapper.find('Detail[label="Job Type"]').length).toBe(0); + expect(wrapper.find('Detail[label="Inventory"]').length).toBe(0); + expect(wrapper.find('Detail[label="SCM Branch"]').length).toBe(0); + expect(wrapper.find('Detail[label="Limit"]').length).toBe(0); + expect(wrapper.find('Detail[label="Show Changes"]').length).toBe(0); + expect(wrapper.find('Detail[label="Credentials"]').length).toBe(0); + expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0); + expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0); + }); + test('details should render with the proper values with prompts', async () => { + SchedulesAPI.readCredentials.mockResolvedValueOnce({ + data: { + count: 2, + results: [ + { + id: 1, + name: 'Cred 1', + }, + { + id: 2, + name: 'Cred 2', + }, + ], + }, + }); + const scheduleWithPrompts = { + ...schedule, + job_type: 'run', + inventory: 1, + job_tags: 'tag1', + skip_tags: 'tag2', + scm_branch: 'foo/branch', + limit: 'localhost', + diff_mode: true, + verbosity: 1, + }; + await act(async () => { + wrapper = mountWithContexts( + ( + + )} + />, + { + context: { + router: { + history, + route: { + location: history.location, + match: { params: { id: 1 } }, + }, + }, + }, + } + ); + }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + expect( + wrapper + .find('Detail[label="Name"]') + .find('dd') + .text() + ).toBe('Mock JT Schedule'); + expect( + wrapper + .find('Detail[label="Description"]') + .find('dd') + .text() + ).toBe('A good schedule'); + expect(wrapper.find('Detail[label="First Run"]').length).toBe(1); + expect(wrapper.find('Detail[label="Next Run"]').length).toBe(1); + expect(wrapper.find('Detail[label="Last Run"]').length).toBe(1); + expect( + wrapper + .find('Detail[label="Local Time Zone"]') + .find('dd') + .text() + ).toBe('America/New_York'); + expect(wrapper.find('Detail[label="Repeat Frequency"]').length).toBe(1); + expect(wrapper.find('Detail[label="Created"]').length).toBe(1); + expect(wrapper.find('Detail[label="Last Modified"]').length).toBe(1); + expect(wrapper.find('Title[children="Prompted Fields"]').length).toBe(1); + expect( + wrapper + .find('Detail[label="Job Type"]') + .find('dd') + .text() + ).toBe('run'); + expect(wrapper.find('Detail[label="Inventory"]').length).toBe(1); + expect( + wrapper + .find('Detail[label="SCM Branch"]') + .find('dd') + .text() + ).toBe('foo/branch'); + expect( + wrapper + .find('Detail[label="Limit"]') + .find('dd') + .text() + ).toBe('localhost'); + expect(wrapper.find('Detail[label="Show Changes"]').length).toBe(1); + expect(wrapper.find('Detail[label="Credentials"]').length).toBe(1); + expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(1); + expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(1); + }); + test('error shown when error encountered fetching credentials', async () => { + SchedulesAPI.readCredentials.mockRejectedValueOnce( + new Error({ + response: { + config: { + method: 'get', + url: '/api/v2/job_templates/1/schedules/1/credentials', + }, + data: 'An error occurred', + status: 500, + }, + }) + ); + await act(async () => { + wrapper = mountWithContexts( + ( + + )} + />, + { + context: { + router: { + history, + route: { + location: history.location, + match: { params: { id: 1 } }, + }, + }, + }, + } + ); + }); + await waitForElement(wrapper, 'ContentError', el => el.length === 1); + }); +}); diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleList.jsx b/awx/ui_next/src/components/Schedule/ScheduleList.jsx similarity index 98% rename from awx/ui_next/src/components/ScheduleList/ScheduleList.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList.jsx index 999f28a6b9..71f1e2d3f4 100644 --- a/awx/ui_next/src/components/ScheduleList/ScheduleList.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleList.jsx @@ -7,13 +7,13 @@ import { SchedulesAPI } from '@api'; import AlertModal from '@components/AlertModal'; import ErrorDetail from '@components/ErrorDetail'; import DataListToolbar from '@components/DataListToolbar'; +import { ScheduleListItem } from '@components/Schedule'; import PaginatedDataList, { ToolbarAddButton, ToolbarDeleteButton, } from '@components/PaginatedDataList'; import useRequest, { useDeleteItems } from '@util/useRequest'; import { getQSConfig, parseQueryString } from '@util/qs'; -import ScheduleListItem from './ScheduleListItem'; const QS_CONFIG = getQSConfig('schedule', { page: 1, diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleList.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleList.test.jsx similarity index 99% rename from awx/ui_next/src/components/ScheduleList/ScheduleList.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList.test.jsx index 25101677c8..c210837aea 100644 --- a/awx/ui_next/src/components/ScheduleList/ScheduleList.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleList.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { SchedulesAPI } from '@api'; -import ScheduleList from './ScheduleList'; +import { ScheduleList } from '@components/Schedule'; import mockSchedules from './data.schedules.json'; jest.mock('@api/models/Schedules'); diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleListItem.jsx b/awx/ui_next/src/components/Schedule/ScheduleListItem.jsx similarity index 98% rename from awx/ui_next/src/components/ScheduleList/ScheduleListItem.jsx rename to awx/ui_next/src/components/Schedule/ScheduleListItem.jsx index 79ff96f6e1..ff2b3d05b5 100644 --- a/awx/ui_next/src/components/ScheduleList/ScheduleListItem.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleListItem.jsx @@ -15,10 +15,10 @@ import { } from '@patternfly/react-core'; import { PencilAltIcon } from '@patternfly/react-icons'; import { DetailList, Detail } from '@components/DetailList'; +import { ScheduleToggle } from '@components/Schedule'; import styled from 'styled-components'; import { Schedule } from '@types'; import { formatDateString } from '@util/dates'; -import ScheduleToggle from './ScheduleToggle'; const DataListAction = styled(_DataListAction)` align-items: center; diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleListItem.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx similarity index 98% rename from awx/ui_next/src/components/ScheduleList/ScheduleListItem.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx index 8e64b651a2..2fe7bd4b53 100644 --- a/awx/ui_next/src/components/ScheduleList/ScheduleListItem.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import ScheduleListItem from './ScheduleListItem'; +import { ScheduleListItem } from '@components/Schedule'; const mockSchedule = { rrule: diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx b/awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx new file mode 100644 index 0000000000..fd200a4195 --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx @@ -0,0 +1,72 @@ +import React, { useState } from 'react'; +import { shape } from 'prop-types'; +import { withI18n } from '@lingui/react'; +import { t } from '@lingui/macro'; +import { formatDateString, formatDateStringUTC } from '@util/dates'; +import { Split, SplitItem, TextListItemVariants } from '@patternfly/react-core'; +import { DetailName, DetailValue } from '@components/DetailList'; +import MultiButtonToggle from '@components/MultiButtonToggle'; + +function ScheduleOccurrences({ preview = { local: [], utc: [] }, i18n }) { + const [mode, setMode] = useState('local'); + + if (preview.local.length < 2) { + return null; + } + + return ( + <> + + + +
+ + {i18n._(t`Occurrences`)} + + + {i18n._(t`(Limited to first 10)`)} + +
+
+ + setMode(newMode)} + /> + +
+
+ + {preview[mode].map(dateStr => ( +
+ {mode === 'local' + ? formatDateString(dateStr) + : formatDateStringUTC(dateStr)} +
+ ))} +
+ + ); +} + +ScheduleOccurrences.propTypes = { + preview: shape(), +}; + +ScheduleOccurrences.defaultProps = { + preview: { local: [], utc: [] }, +}; + +export default withI18n()(ScheduleOccurrences); diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx new file mode 100644 index 0000000000..46cc795995 --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { mountWithContexts } from '@testUtils/enzymeHelpers'; +import ScheduleOccurrences from './ScheduleOccurrences'; + +describe('', () => { + let wrapper; + describe('At least two dates passed in', () => { + beforeAll(() => { + wrapper = mountWithContexts( + + ); + }); + afterAll(() => { + wrapper.unmount(); + }); + test('Local option initially set', async () => { + expect(wrapper.find('MultiButtonToggle').props().value).toBe('local'); + }); + test('It renders the correct number of dates', async () => { + expect(wrapper.find('dd').children().length).toBe(2); + }); + test('Clicking UTC button toggles the dates to utc', async () => { + wrapper.find('button[aria-label="UTC"]').simulate('click'); + expect(wrapper.find('MultiButtonToggle').props().value).toBe('utc'); + expect(wrapper.find('dd').children().length).toBe(2); + expect( + wrapper + .find('dd') + .children() + .at(0) + .text() + ).toBe('3/16/2020, 4:00:00 AM'); + expect( + wrapper + .find('dd') + .children() + .at(1) + .text() + ).toBe('3/30/2020, 4:00:00 AM'); + }); + }); + describe('Only one date passed in', () => { + test('Component should not render chldren', async () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('ScheduleOccurrences').children().length).toBe(0); + wrapper.unmount(); + }); + }); +}); diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleToggle.jsx b/awx/ui_next/src/components/Schedule/ScheduleToggle.jsx similarity index 100% rename from awx/ui_next/src/components/ScheduleList/ScheduleToggle.jsx rename to awx/ui_next/src/components/Schedule/ScheduleToggle.jsx diff --git a/awx/ui_next/src/components/ScheduleList/ScheduleToggle.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleToggle.test.jsx similarity index 100% rename from awx/ui_next/src/components/ScheduleList/ScheduleToggle.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleToggle.test.jsx diff --git a/awx/ui_next/src/components/ScheduleList/data.schedules.json b/awx/ui_next/src/components/Schedule/data.schedules.json similarity index 100% rename from awx/ui_next/src/components/ScheduleList/data.schedules.json rename to awx/ui_next/src/components/Schedule/data.schedules.json diff --git a/awx/ui_next/src/components/Schedule/index.js b/awx/ui_next/src/components/Schedule/index.js new file mode 100644 index 0000000000..bc17abadfc --- /dev/null +++ b/awx/ui_next/src/components/Schedule/index.js @@ -0,0 +1,5 @@ +export { default as ScheduleList } from './ScheduleList'; +export { default as ScheduleListItem } from './ScheduleListItem'; +export { default as ScheduleOccurrences } from './ScheduleOccurrences'; +export { default as ScheduleToggle } from './ScheduleToggle'; +export { default as ScheduleDetail } from './ScheduleDetail'; diff --git a/awx/ui_next/src/components/ScheduleList/index.js b/awx/ui_next/src/components/ScheduleList/index.js deleted file mode 100644 index 35e4093cba..0000000000 --- a/awx/ui_next/src/components/ScheduleList/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ScheduleList'; diff --git a/awx/ui_next/src/screens/Project/Project.jsx b/awx/ui_next/src/screens/Project/Project.jsx index de6b609bdb..4a758d79a2 100644 --- a/awx/ui_next/src/screens/Project/Project.jsx +++ b/awx/ui_next/src/screens/Project/Project.jsx @@ -9,7 +9,7 @@ import RoutedTabs from '@components/RoutedTabs'; import ContentError from '@components/ContentError'; import NotificationList from '@components/NotificationList'; import { ResourceAccessList } from '@components/ResourceAccessList'; -import ScheduleList from '@components/ScheduleList'; +import { ScheduleList } from '@components/Schedule'; import ProjectDetail from './ProjectDetail'; import ProjectEdit from './ProjectEdit'; import ProjectJobTemplatesList from './ProjectJobTemplatesList'; diff --git a/awx/ui_next/src/screens/Schedule/Schedules.jsx b/awx/ui_next/src/screens/Schedule/Schedules.jsx index 514f4b4392..3322e10d1b 100644 --- a/awx/ui_next/src/screens/Schedule/Schedules.jsx +++ b/awx/ui_next/src/screens/Schedule/Schedules.jsx @@ -4,7 +4,7 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import Breadcrumbs from '@components/Breadcrumbs'; -import ScheduleList from '@components/ScheduleList'; +import { ScheduleList } from '@components/Schedule'; import { SchedulesAPI } from '@api'; import { PageSection, Card } from '@patternfly/react-core'; diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx new file mode 100644 index 0000000000..134ee37ffb --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx @@ -0,0 +1,157 @@ +import React, { useEffect, useState } from 'react'; +import { t } from '@lingui/macro'; +import { withI18n } from '@lingui/react'; + +import { + Switch, + Route, + Link, + Redirect, + useLocation, + useParams, +} from 'react-router-dom'; +import { CardActions } from '@patternfly/react-core'; +import { CaretLeftIcon } from '@patternfly/react-icons'; +import CardCloseButton from '@components/CardCloseButton'; +import RoutedTabs from '@components/RoutedTabs'; +import ContentError from '@components/ContentError'; +import ContentLoading from '@components/ContentLoading'; +import { TabbedCardHeader } from '@components/Card'; +import { ScheduleDetail } from '@components/Schedule'; +import { SchedulesAPI } from '@api'; + +function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { + const [jobTemplateSchedule, setJobTemplateSchedule] = useState(null); + const [schedulePreview, setSchedulePreview] = useState(null); + const [contentLoading, setContentLoading] = useState(true); + const [contentError, setContentError] = useState(null); + const { id: jobTemplateId, scheduleId } = useParams(); + const location = useLocation(); + + useEffect(() => { + const loadData = async () => { + try { + const { data } = await SchedulesAPI.readDetail(scheduleId); + const { data: preview } = await SchedulesAPI.createPreview({ + rrule: data.rrule, + }); + + setJobTemplateSchedule(data); + setSchedulePreview(preview); + setBreadcrumb(jobTemplate, data); + } catch (err) { + setContentError(err); + } finally { + setContentLoading(false); + } + }; + + loadData(); + }, [location.pathname, scheduleId, jobTemplate, setBreadcrumb]); + + const tabsArray = [ + { + name: ( + <> + + {i18n._(t`Back to Schedules`)} + + ), + link: `/templates/job_template/${jobTemplate.id}/schedules`, + id: 99, + }, + { + name: i18n._(t`Details`), + link: `/templates/job_template/${ + jobTemplate.id + }/schedules/${jobTemplateSchedule && jobTemplateSchedule.id}/details`, + id: 0, + }, + ]; + + if (contentLoading) { + return ; + } + + if ( + jobTemplateSchedule.summary_fields.unified_job_template.id !== + parseInt(jobTemplateId, 10) + ) { + return ( + + {jobTemplateSchedule && ( + + {i18n._(t`View Template Schedules`)} + + )} + + ); + } + + if (contentError) { + return ; + } + + let cardHeader = null; + if ( + location.pathname.includes('schedules/') && + !location.pathname.endsWith('edit') + ) { + cardHeader = ( + + + + + + + ); + } + return ( + <> + {cardHeader} + + + {jobTemplateSchedule && [ + { + return ( + + ); + }} + />, + ]} + { + return ( + + {jobTemplate && ( + + {i18n._(t`View Template Details`)} + + )} + + ); + }} + /> + + + ); +} + +export { JobTemplateSchedule as _JobTemplateSchedule }; +export default withI18n()(JobTemplateSchedule); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx new file mode 100644 index 0000000000..edbf0ead28 --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { SchedulesAPI } from '@api'; +import { Route } from 'react-router-dom'; +import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; +import { act } from 'react-dom/test-utils'; +import { createMemoryHistory } from 'history'; + +import JobTemplateSchedule from './JobTemplateSchedule'; + +jest.mock('@api/models/Schedules'); + +SchedulesAPI.readDetail.mockResolvedValue({ + data: { + url: '/api/v2/schedules/1', + rrule: + 'DTSTART;TZID=America/New_York:20200220T000000 RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1', + id: 1, + summary_fields: { + unified_job_template: { + id: 1, + name: 'Mock JT', + description: '', + unified_job_type: 'job', + }, + user_capabilities: { + edit: true, + delete: true, + }, + created_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + modified_by: { + id: 1, + username: 'admin', + first_name: '', + last_name: '', + }, + }, + created: '2020-03-03T20:38:54.210306Z', + modified: '2020-03-03T20:38:54.210336Z', + name: 'Mock JT Schedule', + next_run: '2020-02-20T05:00:00Z', + }, +}); + +SchedulesAPI.createPreview.mockResolvedValue({ + data: { + local: [], + utc: [], + }, +}); + +SchedulesAPI.readCredentials.mockResolvedValue({ + data: { + count: 0, + results: [], + }, +}); + +describe('', () => { + let wrapper; + let history; + const jobTemplate = { id: 1, name: 'Mock JT' }; + beforeAll(async () => { + history = createMemoryHistory({ + initialEntries: ['/templates/job_template/1/schedules/1/details'], + }); + await act(async () => { + wrapper = mountWithContexts( + ( + {}} + jobTemplate={jobTemplate} + /> + )} + />, + { + context: { + router: { + history, + route: { + location: history.location, + match: { + params: { id: 1 }, + }, + }, + }, + }, + } + ); + }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + }); + afterAll(() => { + wrapper.unmount(); + }); + test('renders successfully', async () => { + expect(wrapper.length).toBe(1); + }); + test('expect all tabs to exist, including Back to Schedules', async () => { + expect( + wrapper.find('button[link="/templates/job_template/1/schedules"]').length + ).toBe(1); + expect(wrapper.find('button[aria-label="Details"]').length).toBe(1); + }); +}); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js b/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js new file mode 100644 index 0000000000..553eeb129d --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js @@ -0,0 +1 @@ +export { default } from './JobTemplateSchedule'; diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx new file mode 100644 index 0000000000..e16f9629a1 --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { withI18n } from '@lingui/react'; + +import { Switch, Route, withRouter } from 'react-router-dom'; + +import JobTemplateSchedule from '../JobTemplateSchedule/JobTemplateSchedule'; + +function JobTemplateSchedules({ setBreadcrumb, jobTemplate }) { + return ( + + ( + + )} + /> + + ); +} + +export { JobTemplateSchedules as _JobTemplateSchedules }; +export default withI18n()(withRouter(JobTemplateSchedules)); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx new file mode 100644 index 0000000000..3a577e5557 --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { mountWithContexts } from '@testUtils/enzymeHelpers'; +import { act } from 'react-dom/test-utils'; +import { createMemoryHistory } from 'history'; +import JobTemplateSchedules from './JobTemplateSchedules'; + +describe('', () => { + test('initially renders successfully', async () => { + let wrapper; + const history = createMemoryHistory({ + initialEntries: ['/templates/job_template/1/schedules'], + }); + const jobTemplate = { id: 1, name: 'Mock JT' }; + + await act(async () => { + wrapper = mountWithContexts( + {}} + jobTemplate={jobTemplate} + />, + + { + context: { + router: { history, route: { location: history.location } }, + }, + } + ); + }); + expect(wrapper.length).toBe(1); + }); +}); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js b/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js new file mode 100644 index 0000000000..25b0df327b --- /dev/null +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js @@ -0,0 +1 @@ +export { default } from './JobTemplateSchedules'; diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index 9de406abaf..b867c41bee 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -10,10 +10,11 @@ import ContentError from '@components/ContentError'; import JobList from '@components/JobList'; import NotificationList from '@components/NotificationList'; import RoutedTabs from '@components/RoutedTabs'; -import ScheduleList from '@components/ScheduleList'; +import { ScheduleList } from '@components/Schedule'; import { ResourceAccessList } from '@components/ResourceAccessList'; import JobTemplateDetail from './JobTemplateDetail'; import JobTemplateEdit from './JobTemplateEdit'; +import JobTemplateSchedules from './JobTemplateSchedules'; import { JobTemplatesAPI, OrganizationsAPI } from '@api'; import SurveyList from './shared/SurveyList'; @@ -96,7 +97,7 @@ class Template extends Component { } render() { - const { i18n, location, match, me } = this.props; + const { i18n, location, match, me, setBreadcrumb } = this.props; const { contentError, hasContentLoading, @@ -126,6 +127,10 @@ class Template extends Component { } tabsArray.push( + { + name: i18n._(t`Schedules`), + link: `${match.url}/schedules`, + }, { name: i18n._(t`Completed Jobs`), link: `${match.url}/completed_jobs`, @@ -149,7 +154,10 @@ class Template extends Component { ); - if (location.pathname.endsWith('edit')) { + if ( + location.pathname.endsWith('edit') || + location.pathname.includes('schedules/') + ) { cardHeader = null; } @@ -211,6 +219,18 @@ class Template extends Component { )} /> )} + {template && ( + ( + + )} + /> + )} {canSeeNotificationsTab && ( ', () => { const tabs = await waitForElement( wrapper, '.pf-c-tabs__item', - el => el.length === 6 + el => el.length === 7 ); expect(tabs.at(2).text()).toEqual('Notifications'); done(); diff --git a/awx/ui_next/src/screens/Template/Templates.jsx b/awx/ui_next/src/screens/Template/Templates.jsx index 9bc33d0a0e..760ef2d1e9 100644 --- a/awx/ui_next/src/screens/Template/Templates.jsx +++ b/awx/ui_next/src/screens/Template/Templates.jsx @@ -27,7 +27,7 @@ class Templates extends Component { }; } - setBreadCrumbConfig = template => { + setBreadCrumbConfig = (template, nestedResource) => { const { i18n } = this.props; if (!template) { return; @@ -53,6 +53,13 @@ class Templates extends Component { t`Completed Jobs` ), [`/templates/${template.type}/${template.id}/survey`]: i18n._(t`Survey`), + [`/templates/${template.type}/${template.id}/schedules`]: i18n._( + t`Schedules` + ), + [`/templates/${template.type}/${template.id}/schedules/${nestedResource && + nestedResource.id}`]: `${nestedResource && nestedResource.name}`, + [`/templates/${template.type}/${template.id}/schedules/${nestedResource && + nestedResource.id}/details`]: i18n._(t`Schedule Details`), }; this.setState({ breadcrumbConfig }); }; diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx index 22d1dcd187..6407ec4e96 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplate.jsx @@ -10,7 +10,7 @@ import ContentError from '@components/ContentError'; import FullPage from '@components/FullPage'; import JobList from '@components/JobList'; import RoutedTabs from '@components/RoutedTabs'; -import ScheduleList from '@components/ScheduleList'; +import { ScheduleList } from '@components/Schedule'; import ContentLoading from '@components/ContentLoading'; import { WorkflowJobTemplatesAPI, CredentialsAPI } from '@api'; import WorkflowJobTemplateDetail from './WorkflowJobTemplateDetail'; diff --git a/awx/ui_next/src/types.js b/awx/ui_next/src/types.js index b4270c707d..c1b592cb5a 100644 --- a/awx/ui_next/src/types.js +++ b/awx/ui_next/src/types.js @@ -291,7 +291,7 @@ export const Schedule = shape({ skip_tags: string, limit: string, diff_mode: bool, - verbosity: string, + verbosity: number, unified_job_template: number, enabled: bool, dtstart: string, diff --git a/awx/ui_next/src/util/dates.jsx b/awx/ui_next/src/util/dates.jsx index 87c362414a..ca897142e4 100644 --- a/awx/ui_next/src/util/dates.jsx +++ b/awx/ui_next/src/util/dates.jsx @@ -5,6 +5,10 @@ export function formatDateString(dateString, lang = getLanguage(navigator)) { return new Date(dateString).toLocaleString(lang); } +export function formatDateStringUTC(dateString, lang = getLanguage(navigator)) { + return new Date(dateString).toLocaleString(lang, { timeZone: 'UTC' }); +} + export function secondsToHHMMSS(seconds) { return new Date(seconds * 1000).toISOString().substr(11, 8); } From 01fe89e43c57f78134b2a940e140b04c28f0600f Mon Sep 17 00:00:00 2001 From: mabashian Date: Wed, 4 Mar 2020 17:09:30 -0500 Subject: [PATCH 2/6] Moves request to generate preview down into the ScheduleDetail component --- .../components/Schedule/ScheduleDetail.jsx | 27 +++++++++---------- .../Schedule/ScheduleDetail.test.jsx | 22 +++++++-------- .../JobTemplateSchedule.jsx | 13 +-------- 3 files changed, 23 insertions(+), 39 deletions(-) diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx index a8aad4daf1..c6ae0b97c4 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { rrulestr } from 'rrule'; import styled from 'styled-components'; -import { shape } from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Schedule } from '@types'; @@ -21,7 +20,7 @@ const PromptTitle = styled(Title)` --pf-c-title--m-md--FontWeight: 700; `; -function ScheduleDetail({ preview, schedule, i18n }) { +function ScheduleDetail({ schedule, i18n }) { const { id, created, @@ -44,21 +43,26 @@ function ScheduleDetail({ preview, schedule, i18n }) { } = schedule; const { - result: credentials, + result: [credentials, preview], isLoading, error, - request: fetchCredentials, + request: fetchCredentialsAndPreview, } = useRequest( useCallback(async () => { - const { data } = await SchedulesAPI.readCredentials(id); - return data.results; - }, [id]), + const [{ data }, { data: schedulePreview }] = await Promise.all([ + SchedulesAPI.readCredentials(id), + SchedulesAPI.createPreview({ + rrule, + }), + ]); + return [data.results, schedulePreview]; + }, [id, rrule]), [] ); useEffect(() => { - fetchCredentials(); - }, [fetchCredentials]); + fetchCredentialsAndPreview(); + }, [fetchCredentialsAndPreview]); const rule = rrulestr(rrule); const repeatFrequency = @@ -192,12 +196,7 @@ function ScheduleDetail({ preview, schedule, i18n }) { } ScheduleDetail.propTypes = { - preview: shape(), schedule: Schedule.isRequired, }; -ScheduleDetail.defaultProps = { - preview: { local: [], utc: [] }, -}; - export default withI18n()(ScheduleDetail); diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx index 0dbe6ff6bb..d67ff21fc2 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx @@ -52,10 +52,12 @@ const schedule = { next_run: '2020-03-16T04:00:00Z', }; -const preview = { - local: [], - utc: [], -}; +SchedulesAPI.createPreview.mockResolvedValue({ + data: { + local: [], + utc: [], + }, +}); describe('', () => { let wrapper; @@ -76,9 +78,7 @@ describe('', () => { wrapper = mountWithContexts( ( - - )} + component={() => } />, { context: { @@ -159,9 +159,7 @@ describe('', () => { wrapper = mountWithContexts( ( - - )} + component={() => } />, { context: { @@ -243,9 +241,7 @@ describe('', () => { wrapper = mountWithContexts( ( - - )} + component={() => } />, { context: { diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx index 134ee37ffb..9a70207bc8 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx @@ -22,7 +22,6 @@ import { SchedulesAPI } from '@api'; function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { const [jobTemplateSchedule, setJobTemplateSchedule] = useState(null); - const [schedulePreview, setSchedulePreview] = useState(null); const [contentLoading, setContentLoading] = useState(true); const [contentError, setContentError] = useState(null); const { id: jobTemplateId, scheduleId } = useParams(); @@ -32,12 +31,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { const loadData = async () => { try { const { data } = await SchedulesAPI.readDetail(scheduleId); - const { data: preview } = await SchedulesAPI.createPreview({ - rrule: data.rrule, - }); - setJobTemplateSchedule(data); - setSchedulePreview(preview); setBreadcrumb(jobTemplate, data); } catch (err) { setContentError(err); @@ -122,12 +116,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { key="details" path="/templates/job_template/:id/schedules/:scheduleId/details" render={() => { - return ( - - ); + return ; }} />, ]} From 90e047821d52c8e321bc206e9b93b12f85e0ab1a Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 5 Mar 2020 17:46:23 -0500 Subject: [PATCH 3/6] Makes entire hierarchy of schedule components generic --- .../Schedule/Schedule.jsx} | 56 +++++++++---------- .../Schedule/Schedule.test.jsx} | 11 ++-- .../{ => ScheduleDetail}/ScheduleDetail.jsx | 0 .../ScheduleDetail.test.jsx | 0 .../Schedule/ScheduleDetail/index.js | 1 + .../{ => ScheduleList}/ScheduleList.jsx | 2 +- .../{ => ScheduleList}/ScheduleList.test.jsx | 4 +- .../{ => ScheduleList}/ScheduleListItem.jsx | 0 .../ScheduleListItem.test.jsx | 2 +- .../components/Schedule/ScheduleList/index.js | 1 + .../ScheduleOccurrences.jsx | 0 .../ScheduleOccurrences.test.jsx | 0 .../Schedule/ScheduleOccurrences/index.js | 1 + .../{ => ScheduleToggle}/ScheduleToggle.jsx | 0 .../ScheduleToggle.test.jsx | 0 .../Schedule/ScheduleToggle/index.js | 1 + .../src/components/Schedule/Schedules.jsx | 33 +++++++++++ .../Schedule/Schedules.test.jsx} | 9 +-- awx/ui_next/src/components/Schedule/index.js | 3 +- awx/ui_next/src/screens/Project/Project.jsx | 13 +++-- .../{Schedules.jsx => AllSchedules.jsx} | 4 +- ...hedules.test.jsx => AllSchedules.test.jsx} | 8 +-- awx/ui_next/src/screens/Schedule/index.js | 2 +- .../Template/JobTemplateSchedule/index.js | 1 - .../JobTemplateSchedules.jsx | 26 --------- .../Template/JobTemplateSchedules/index.js | 1 - awx/ui_next/src/screens/Template/Template.jsx | 22 ++------ .../src/screens/Template/Template.test.jsx | 3 +- .../screens/Template/WorkflowJobTemplate.jsx | 15 +++-- 29 files changed, 110 insertions(+), 109 deletions(-) rename awx/ui_next/src/{screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx => components/Schedule/Schedule.jsx} (60%) rename awx/ui_next/src/{screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx => components/Schedule/Schedule.test.jsx} (91%) rename awx/ui_next/src/components/Schedule/{ => ScheduleDetail}/ScheduleDetail.jsx (100%) rename awx/ui_next/src/components/Schedule/{ => ScheduleDetail}/ScheduleDetail.test.jsx (100%) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleDetail/index.js rename awx/ui_next/src/components/Schedule/{ => ScheduleList}/ScheduleList.jsx (98%) rename awx/ui_next/src/components/Schedule/{ => ScheduleList}/ScheduleList.test.jsx (98%) rename awx/ui_next/src/components/Schedule/{ => ScheduleList}/ScheduleListItem.jsx (100%) rename awx/ui_next/src/components/Schedule/{ => ScheduleList}/ScheduleListItem.test.jsx (98%) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleList/index.js rename awx/ui_next/src/components/Schedule/{ => ScheduleOccurrences}/ScheduleOccurrences.jsx (100%) rename awx/ui_next/src/components/Schedule/{ => ScheduleOccurrences}/ScheduleOccurrences.test.jsx (100%) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleOccurrences/index.js rename awx/ui_next/src/components/Schedule/{ => ScheduleToggle}/ScheduleToggle.jsx (100%) rename awx/ui_next/src/components/Schedule/{ => ScheduleToggle}/ScheduleToggle.test.jsx (100%) create mode 100644 awx/ui_next/src/components/Schedule/ScheduleToggle/index.js create mode 100644 awx/ui_next/src/components/Schedule/Schedules.jsx rename awx/ui_next/src/{screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx => components/Schedule/Schedules.test.jsx} (75%) rename awx/ui_next/src/screens/Schedule/{Schedules.jsx => AllSchedules.jsx} (93%) rename awx/ui_next/src/screens/Schedule/{Schedules.test.jsx => AllSchedules.test.jsx} (79%) delete mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js delete mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx delete mode 100644 awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx b/awx/ui_next/src/components/Schedule/Schedule.jsx similarity index 60% rename from awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx rename to awx/ui_next/src/components/Schedule/Schedule.jsx index 9a70207bc8..852f811b40 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.jsx +++ b/awx/ui_next/src/components/Schedule/Schedule.jsx @@ -20,19 +20,21 @@ import { TabbedCardHeader } from '@components/Card'; import { ScheduleDetail } from '@components/Schedule'; import { SchedulesAPI } from '@api'; -function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { - const [jobTemplateSchedule, setJobTemplateSchedule] = useState(null); +function Schedule({ i18n, setBreadcrumb, unifiedJobTemplate }) { + const [schedule, setSchedule] = useState(null); const [contentLoading, setContentLoading] = useState(true); const [contentError, setContentError] = useState(null); - const { id: jobTemplateId, scheduleId } = useParams(); + const { scheduleId } = useParams(); const location = useLocation(); + const { pathname } = location; + const pathRoot = pathname.substr(0, pathname.indexOf('schedules')); useEffect(() => { const loadData = async () => { try { const { data } = await SchedulesAPI.readDetail(scheduleId); - setJobTemplateSchedule(data); - setBreadcrumb(jobTemplate, data); + setSchedule(data); + setBreadcrumb(unifiedJobTemplate, data); } catch (err) { setContentError(err); } finally { @@ -41,7 +43,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { }; loadData(); - }, [location.pathname, scheduleId, jobTemplate, setBreadcrumb]); + }, [location.pathname, scheduleId, unifiedJobTemplate, setBreadcrumb]); const tabsArray = [ { @@ -51,14 +53,12 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { {i18n._(t`Back to Schedules`)} ), - link: `/templates/job_template/${jobTemplate.id}/schedules`, + link: `${pathRoot}schedules`, id: 99, }, { name: i18n._(t`Details`), - link: `/templates/job_template/${ - jobTemplate.id - }/schedules/${jobTemplateSchedule && jobTemplateSchedule.id}/details`, + link: `${pathRoot}schedules/${schedule && schedule.id}/details`, id: 0, }, ]; @@ -68,15 +68,13 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { } if ( - jobTemplateSchedule.summary_fields.unified_job_template.id !== - parseInt(jobTemplateId, 10) + schedule.summary_fields.unified_job_template.id !== + parseInt(unifiedJobTemplate.id, 10) ) { return ( - {jobTemplateSchedule && ( - - {i18n._(t`View Template Schedules`)} - + {schedule && ( + {i18n._(t`View Schedules`)} )} ); @@ -95,9 +93,7 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { - + ); @@ -107,16 +103,16 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { {cardHeader} - {jobTemplateSchedule && [ + {schedule && [ { - return ; + return ; }} />, ]} @@ -126,11 +122,9 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { render={() => { return ( - {jobTemplate && ( - - {i18n._(t`View Template Details`)} + {unifiedJobTemplate && ( + + {i18n._(t`View Details`)} )} @@ -142,5 +136,5 @@ function JobTemplateSchedule({ i18n, setBreadcrumb, jobTemplate }) { ); } -export { JobTemplateSchedule as _JobTemplateSchedule }; -export default withI18n()(JobTemplateSchedule); +export { Schedule as _Schedule }; +export default withI18n()(Schedule); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx b/awx/ui_next/src/components/Schedule/Schedule.test.jsx similarity index 91% rename from awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx rename to awx/ui_next/src/components/Schedule/Schedule.test.jsx index edbf0ead28..3ed3c5b025 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedule/JobTemplateSchedule.test.jsx +++ b/awx/ui_next/src/components/Schedule/Schedule.test.jsx @@ -4,8 +4,7 @@ import { Route } from 'react-router-dom'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { act } from 'react-dom/test-utils'; import { createMemoryHistory } from 'history'; - -import JobTemplateSchedule from './JobTemplateSchedule'; +import Schedule from './Schedule'; jest.mock('@api/models/Schedules'); @@ -60,10 +59,10 @@ SchedulesAPI.readCredentials.mockResolvedValue({ }, }); -describe('', () => { +describe('', () => { let wrapper; let history; - const jobTemplate = { id: 1, name: 'Mock JT' }; + const unifiedJobTemplate = { id: 1, name: 'Mock JT' }; beforeAll(async () => { history = createMemoryHistory({ initialEntries: ['/templates/job_template/1/schedules/1/details'], @@ -73,9 +72,9 @@ describe('', () => { ( - {}} - jobTemplate={jobTemplate} + unifiedJobTemplate={unifiedJobTemplate} /> )} />, diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleDetail.jsx rename to awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleDetail.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleDetail/index.js b/awx/ui_next/src/components/Schedule/ScheduleDetail/index.js new file mode 100644 index 0000000000..dc7a5b7477 --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleDetail/index.js @@ -0,0 +1 @@ +export { default } from './ScheduleDetail'; diff --git a/awx/ui_next/src/components/Schedule/ScheduleList.jsx b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx similarity index 98% rename from awx/ui_next/src/components/Schedule/ScheduleList.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx index 71f1e2d3f4..999f28a6b9 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleList.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.jsx @@ -7,13 +7,13 @@ import { SchedulesAPI } from '@api'; import AlertModal from '@components/AlertModal'; import ErrorDetail from '@components/ErrorDetail'; import DataListToolbar from '@components/DataListToolbar'; -import { ScheduleListItem } from '@components/Schedule'; import PaginatedDataList, { ToolbarAddButton, ToolbarDeleteButton, } from '@components/PaginatedDataList'; import useRequest, { useDeleteItems } from '@util/useRequest'; import { getQSConfig, parseQueryString } from '@util/qs'; +import ScheduleListItem from './ScheduleListItem'; const QS_CONFIG = getQSConfig('schedule', { page: 1, diff --git a/awx/ui_next/src/components/Schedule/ScheduleList.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.test.jsx similarity index 98% rename from awx/ui_next/src/components/Schedule/ScheduleList.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.test.jsx index c210837aea..85ceffb759 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleList.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleList.test.jsx @@ -2,8 +2,8 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { SchedulesAPI } from '@api'; -import { ScheduleList } from '@components/Schedule'; -import mockSchedules from './data.schedules.json'; +import ScheduleList from './ScheduleList'; +import mockSchedules from '../data.schedules.json'; jest.mock('@api/models/Schedules'); diff --git a/awx/ui_next/src/components/Schedule/ScheduleListItem.jsx b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleListItem.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleListItem.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList/ScheduleListItem.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleListItem.test.jsx similarity index 98% rename from awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleList/ScheduleListItem.test.jsx index 2fe7bd4b53..8e64b651a2 100644 --- a/awx/ui_next/src/components/Schedule/ScheduleListItem.test.jsx +++ b/awx/ui_next/src/components/Schedule/ScheduleList/ScheduleListItem.test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import { ScheduleListItem } from '@components/Schedule'; +import ScheduleListItem from './ScheduleListItem'; const mockSchedule = { rrule: diff --git a/awx/ui_next/src/components/Schedule/ScheduleList/index.js b/awx/ui_next/src/components/Schedule/ScheduleList/index.js new file mode 100644 index 0000000000..35e4093cba --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleList/index.js @@ -0,0 +1 @@ +export { default } from './ScheduleList'; diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleOccurrences.jsx rename to awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.test.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleOccurrences.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleOccurrences/ScheduleOccurrences.test.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleOccurrences/index.js b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/index.js new file mode 100644 index 0000000000..2b21bebcce --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleOccurrences/index.js @@ -0,0 +1 @@ +export { default } from './ScheduleOccurrences'; diff --git a/awx/ui_next/src/components/Schedule/ScheduleToggle.jsx b/awx/ui_next/src/components/Schedule/ScheduleToggle/ScheduleToggle.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleToggle.jsx rename to awx/ui_next/src/components/Schedule/ScheduleToggle/ScheduleToggle.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleToggle.test.jsx b/awx/ui_next/src/components/Schedule/ScheduleToggle/ScheduleToggle.test.jsx similarity index 100% rename from awx/ui_next/src/components/Schedule/ScheduleToggle.test.jsx rename to awx/ui_next/src/components/Schedule/ScheduleToggle/ScheduleToggle.test.jsx diff --git a/awx/ui_next/src/components/Schedule/ScheduleToggle/index.js b/awx/ui_next/src/components/Schedule/ScheduleToggle/index.js new file mode 100644 index 0000000000..65573a4fde --- /dev/null +++ b/awx/ui_next/src/components/Schedule/ScheduleToggle/index.js @@ -0,0 +1 @@ +export { default } from './ScheduleToggle'; diff --git a/awx/ui_next/src/components/Schedule/Schedules.jsx b/awx/ui_next/src/components/Schedule/Schedules.jsx new file mode 100644 index 0000000000..6330debbe8 --- /dev/null +++ b/awx/ui_next/src/components/Schedule/Schedules.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { withI18n } from '@lingui/react'; +import { Switch, Route, useRouteMatch } from 'react-router-dom'; +import { Schedule, ScheduleList } from '@components/Schedule'; + +function Schedules({ setBreadcrumb, unifiedJobTemplate, loadSchedules }) { + const match = useRouteMatch(); + + return ( + + ( + + )} + /> + { + return ; + }} + /> + + ); +} + +export { Schedules as _Schedules }; +export default withI18n()(Schedules); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx b/awx/ui_next/src/components/Schedule/Schedules.test.jsx similarity index 75% rename from awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx rename to awx/ui_next/src/components/Schedule/Schedules.test.jsx index 3a577e5557..5273c040e6 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.test.jsx +++ b/awx/ui_next/src/components/Schedule/Schedules.test.jsx @@ -2,9 +2,9 @@ import React from 'react'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { act } from 'react-dom/test-utils'; import { createMemoryHistory } from 'history'; -import JobTemplateSchedules from './JobTemplateSchedules'; +import Schedules from './Schedules'; -describe('', () => { +describe('', () => { test('initially renders successfully', async () => { let wrapper; const history = createMemoryHistory({ @@ -14,10 +14,7 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts( - {}} - jobTemplate={jobTemplate} - />, + {}} jobTemplate={jobTemplate} />, { context: { diff --git a/awx/ui_next/src/components/Schedule/index.js b/awx/ui_next/src/components/Schedule/index.js index bc17abadfc..f734f868d2 100644 --- a/awx/ui_next/src/components/Schedule/index.js +++ b/awx/ui_next/src/components/Schedule/index.js @@ -1,5 +1,6 @@ +export { default as Schedule } from './Schedule'; +export { default as Schedules } from './Schedules'; export { default as ScheduleList } from './ScheduleList'; -export { default as ScheduleListItem } from './ScheduleListItem'; export { default as ScheduleOccurrences } from './ScheduleOccurrences'; export { default as ScheduleToggle } from './ScheduleToggle'; export { default as ScheduleDetail } from './ScheduleDetail'; diff --git a/awx/ui_next/src/screens/Project/Project.jsx b/awx/ui_next/src/screens/Project/Project.jsx index 4a758d79a2..0016acdc39 100644 --- a/awx/ui_next/src/screens/Project/Project.jsx +++ b/awx/ui_next/src/screens/Project/Project.jsx @@ -9,7 +9,7 @@ import RoutedTabs from '@components/RoutedTabs'; import ContentError from '@components/ContentError'; import NotificationList from '@components/NotificationList'; import { ResourceAccessList } from '@components/ResourceAccessList'; -import { ScheduleList } from '@components/Schedule'; +import { Schedules } from '@components/Schedule'; import ProjectDetail from './ProjectDetail'; import ProjectEdit from './ProjectEdit'; import ProjectJobTemplatesList from './ProjectJobTemplatesList'; @@ -116,7 +116,7 @@ class Project extends Component { } render() { - const { location, match, me, i18n } = this.props; + const { location, match, me, i18n, setBreadcrumb } = this.props; const { project, @@ -175,7 +175,10 @@ class Project extends Component { cardHeader = null; } - if (location.pathname.endsWith('edit')) { + if ( + location.pathname.endsWith('edit') || + location.pathname.includes('schedules/') + ) { cardHeader = null; } @@ -247,7 +250,9 @@ class Project extends Component { ( - diff --git a/awx/ui_next/src/screens/Schedule/Schedules.jsx b/awx/ui_next/src/screens/Schedule/AllSchedules.jsx similarity index 93% rename from awx/ui_next/src/screens/Schedule/Schedules.jsx rename to awx/ui_next/src/screens/Schedule/AllSchedules.jsx index 3322e10d1b..1f7ffa3188 100644 --- a/awx/ui_next/src/screens/Schedule/Schedules.jsx +++ b/awx/ui_next/src/screens/Schedule/AllSchedules.jsx @@ -8,7 +8,7 @@ import { ScheduleList } from '@components/Schedule'; import { SchedulesAPI } from '@api'; import { PageSection, Card } from '@patternfly/react-core'; -function Schedules({ i18n }) { +function AllSchedules({ i18n }) { const loadScheduleOptions = () => { return SchedulesAPI.readOptions(); }; @@ -41,4 +41,4 @@ function Schedules({ i18n }) { ); } -export default withI18n()(Schedules); +export default withI18n()(AllSchedules); diff --git a/awx/ui_next/src/screens/Schedule/Schedules.test.jsx b/awx/ui_next/src/screens/Schedule/AllSchedules.test.jsx similarity index 79% rename from awx/ui_next/src/screens/Schedule/Schedules.test.jsx rename to awx/ui_next/src/screens/Schedule/AllSchedules.test.jsx index ebdc67a046..50e4b76f66 100644 --- a/awx/ui_next/src/screens/Schedule/Schedules.test.jsx +++ b/awx/ui_next/src/screens/Schedule/AllSchedules.test.jsx @@ -1,9 +1,9 @@ import React from 'react'; import { mountWithContexts } from '@testUtils/enzymeHelpers'; import { createMemoryHistory } from 'history'; -import Schedules from './Schedules'; +import AllSchedules from './AllSchedules'; -describe('', () => { +describe('', () => { let wrapper; afterEach(() => { @@ -11,7 +11,7 @@ describe('', () => { }); test('initially renders succesfully', () => { - wrapper = mountWithContexts(); + wrapper = mountWithContexts(); }); test('should display schedule list breadcrumb heading', () => { @@ -19,7 +19,7 @@ describe('', () => { initialEntries: ['/schedules'], }); - wrapper = mountWithContexts(, { + wrapper = mountWithContexts(, { context: { router: { history, diff --git a/awx/ui_next/src/screens/Schedule/index.js b/awx/ui_next/src/screens/Schedule/index.js index 64f2dedc84..3e38a47a40 100644 --- a/awx/ui_next/src/screens/Schedule/index.js +++ b/awx/ui_next/src/screens/Schedule/index.js @@ -1 +1 @@ -export { default } from './Schedules'; +export { default } from './AllSchedules'; diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js b/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js deleted file mode 100644 index 553eeb129d..0000000000 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedule/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './JobTemplateSchedule'; diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx b/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx deleted file mode 100644 index e16f9629a1..0000000000 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedules/JobTemplateSchedules.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { withI18n } from '@lingui/react'; - -import { Switch, Route, withRouter } from 'react-router-dom'; - -import JobTemplateSchedule from '../JobTemplateSchedule/JobTemplateSchedule'; - -function JobTemplateSchedules({ setBreadcrumb, jobTemplate }) { - return ( - - ( - - )} - /> - - ); -} - -export { JobTemplateSchedules as _JobTemplateSchedules }; -export default withI18n()(withRouter(JobTemplateSchedules)); diff --git a/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js b/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js deleted file mode 100644 index 25b0df327b..0000000000 --- a/awx/ui_next/src/screens/Template/JobTemplateSchedules/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './JobTemplateSchedules'; diff --git a/awx/ui_next/src/screens/Template/Template.jsx b/awx/ui_next/src/screens/Template/Template.jsx index b867c41bee..7cdd8ce032 100644 --- a/awx/ui_next/src/screens/Template/Template.jsx +++ b/awx/ui_next/src/screens/Template/Template.jsx @@ -10,11 +10,10 @@ import ContentError from '@components/ContentError'; import JobList from '@components/JobList'; import NotificationList from '@components/NotificationList'; import RoutedTabs from '@components/RoutedTabs'; -import { ScheduleList } from '@components/Schedule'; +import { Schedules } from '@components/Schedule'; import { ResourceAccessList } from '@components/ResourceAccessList'; import JobTemplateDetail from './JobTemplateDetail'; import JobTemplateEdit from './JobTemplateEdit'; -import JobTemplateSchedules from './JobTemplateSchedules'; import { JobTemplatesAPI, OrganizationsAPI } from '@api'; import SurveyList from './shared/SurveyList'; @@ -221,12 +220,14 @@ class Template extends Component { )} {template && ( ( - )} /> @@ -248,17 +249,6 @@ class Template extends Component { )} - {template && ( - ( - - )} - /> - )} {template && ( ', () => { const wrapper = mountWithContexts(