diff --git a/awx/ui_next/src/api/index.js b/awx/ui_next/src/api/index.js
index cddf01e259..3160ebd907 100644
--- a/awx/ui_next/src/api/index.js
+++ b/awx/ui_next/src/api/index.js
@@ -29,6 +29,7 @@ import Root from './models/Root';
import Schedules from './models/Schedules';
import Settings from './models/Settings';
import SystemJobs from './models/SystemJobs';
+import SystemJobTemplates from './models/SystemJobTemplates';
import Teams from './models/Teams';
import Tokens from './models/Tokens';
import UnifiedJobTemplates from './models/UnifiedJobTemplates';
@@ -71,6 +72,7 @@ const RootAPI = new Root();
const SchedulesAPI = new Schedules();
const SettingsAPI = new Settings();
const SystemJobsAPI = new SystemJobs();
+const SystemJobTemplatesAPI = new SystemJobTemplates();
const TeamsAPI = new Teams();
const TokensAPI = new Tokens();
const UnifiedJobTemplatesAPI = new UnifiedJobTemplates();
@@ -114,6 +116,7 @@ export {
SchedulesAPI,
SettingsAPI,
SystemJobsAPI,
+ SystemJobTemplatesAPI,
TeamsAPI,
TokensAPI,
UnifiedJobTemplatesAPI,
diff --git a/awx/ui_next/src/api/models/SystemJobTemplates.js b/awx/ui_next/src/api/models/SystemJobTemplates.js
new file mode 100644
index 0000000000..54712b9abe
--- /dev/null
+++ b/awx/ui_next/src/api/models/SystemJobTemplates.js
@@ -0,0 +1,14 @@
+import Base from '../Base';
+
+class SystemJobTemplates extends Base {
+ constructor(http) {
+ super(http);
+ this.baseUrl = '/api/v2/system_job_templates/';
+ }
+
+ readDetail(id) {
+ return this.http.get(`${this.baseUrl}${id}/`).then(({ data }) => data);
+ }
+}
+
+export default SystemJobTemplates;
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJob.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJob.jsx
new file mode 100644
index 0000000000..a875f95141
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJob.jsx
@@ -0,0 +1,147 @@
+import React, { useEffect, useCallback } from 'react';
+import {
+ Link,
+ Redirect,
+ Route,
+ Switch,
+ useLocation,
+ useParams,
+} from 'react-router-dom';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { CaretLeftIcon } from '@patternfly/react-icons';
+import { Card, PageSection } from '@patternfly/react-core';
+import { useRouteMatch } from 'react-router-dom';
+
+import { SystemJobTemplatesAPI } from '../../api';
+
+import ContentError from '../../components/ContentError';
+import ContentLoading from '../../components/ContentLoading';
+import RoutedTabs from '../../components/RoutedTabs';
+import { useConfig } from '../../contexts/Config';
+import useRequest from '../../util/useRequest';
+
+import ManagementJobDetails from './ManagementJobDetails';
+import ManagementJobEdit from './ManagementJobEdit';
+import ManagementJobNotifications from './ManagementJobNotifications';
+import ManagementJobSchedules from './ManagementJobSchedules';
+
+function ManagementJob({ i18n, setBreadcrumb }) {
+ const segment = '/management_jobs';
+
+ const match = useRouteMatch();
+ const { id } = useParams();
+ const { pathname } = useLocation();
+ const { me, isNotificationAdmin } = useConfig();
+
+ const { isLoading, error, request, result } = useRequest(
+ useCallback(async () => SystemJobTemplatesAPI.readDetail(id), [id])
+ );
+
+ useEffect(() => {
+ request();
+ }, [request, pathname]);
+
+ useEffect(() => {
+ if (!result) return;
+
+ setBreadcrumb(result);
+ }, [result, setBreadcrumb]);
+
+ const tabsArray = [
+ {
+ id: 99,
+ link: segment,
+ name: (
+ <>
+
+ {i18n._(t`Back to management jobs`)}
+ >
+ ),
+ },
+ {
+ id: 0,
+ link: `${segment}/${id}/details`,
+ name: i18n._(t`Details`),
+ },
+ {
+ id: 1,
+ name: i18n._(t`Schedules`),
+ link: `${match.url}/schedules`,
+ },
+ ];
+
+ if (me?.is_system_auditor || isNotificationAdmin) {
+ tabsArray.push({
+ id: 2,
+ name: i18n._(t`Notifications`),
+ link: `${match.url}/notifications`,
+ });
+ }
+
+ const LoadingScreen = (
+
+
+ {pathname.endsWith('edit') ? null : (
+
+ )}
+
+
+
+ );
+
+ const ErrorScreen = (
+
+
+
+ {error?.response?.status === 404 && (
+
+ {i18n._(t`Management job not found.`)}
+ {''}
+ {i18n._(t`View all management jobs`)}
+
+ )}
+
+
+
+ );
+
+ if (error) {
+ return ErrorScreen;
+ }
+
+ if (isLoading) {
+ return LoadingScreen;
+ }
+
+ return (
+
+
+ {pathname.endsWith('edit') ? null : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default withI18n()(ManagementJob);
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/ManagementJobDetails.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/ManagementJobDetails.jsx
new file mode 100644
index 0000000000..2ed939563a
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/ManagementJobDetails.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+
+import { CardBody } from '../../../components/Card';
+
+function ManagementJobDetails() {
+ return Management Job Details;
+}
+
+export default withI18n()(ManagementJobDetails);
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/index.js b/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/index.js
new file mode 100644
index 0000000000..bcd1ccefec
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobDetails/index.js
@@ -0,0 +1 @@
+export { default } from './ManagementJobDetails';
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx
new file mode 100644
index 0000000000..9ddff5539a
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import { CardBody } from '../../../components/Card';
+
+function ManagementJobEdit() {
+ return Management Job Edit;
+}
+
+export default ManagementJobEdit;
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/index.js b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/index.js
new file mode 100644
index 0000000000..2924ff2c14
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/index.js
@@ -0,0 +1 @@
+export { default } from './ManagementJobEdit';
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx
new file mode 100644
index 0000000000..d1fdde1c37
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { withI18n } from '@lingui/react';
+import { Card, PageSection } from '@patternfly/react-core';
+
+function ManagementJobDetails() {
+ return (
+
+ Management Job List
+
+ );
+}
+
+export default withI18n()(ManagementJobDetails);
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/index.js b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/index.js
new file mode 100644
index 0000000000..e55f0f261f
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/index.js
@@ -0,0 +1 @@
+export { default } from './ManagementJobList';
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/ManagementJobNotifications.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/ManagementJobNotifications.jsx
new file mode 100644
index 0000000000..43d8ba78c0
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/ManagementJobNotifications.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import { CardBody } from '../../../components/Card';
+
+function ManagementJobNotifications({ managementJob }) {
+ return Management Job Notifications;
+}
+
+export default ManagementJobNotifications;
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/index.js b/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/index.js
new file mode 100644
index 0000000000..799d7c45fb
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobNotifications/index.js
@@ -0,0 +1 @@
+export { default } from './ManagementJobNotifications';
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/ManagementJobSchedules.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/ManagementJobSchedules.jsx
new file mode 100644
index 0000000000..e3f48f9c57
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/ManagementJobSchedules.jsx
@@ -0,0 +1,10 @@
+import React from 'react';
+
+import { CardBody } from '../../../components/Card';
+import { Schedules } from '../../../components/Schedule';
+
+function ManagementJobSchedules({ managementJob }) {
+ return Management Job Schedules;
+}
+
+export default ManagementJobSchedules;
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/index.js b/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/index.js
new file mode 100644
index 0000000000..983527b91a
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobSchedules/index.js
@@ -0,0 +1 @@
+export { default } from './ManagementJobSchedules';
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx
index 94f5a077c5..8f9ff47304 100644
--- a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.jsx
@@ -1,17 +1,51 @@
-import React, { Fragment } from 'react';
+import React, { useState, useCallback } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
+import { Route, Switch } from 'react-router-dom';
import ScreenHeader from '../../components/ScreenHeader';
+import ManagementJob from './ManagementJob';
+import ManagementJobList from './ManagementJobList';
function ManagementJobs({ i18n }) {
+ const basePath = '/management_jobs';
+
+ const [breadcrumbConfig, setBreadcrumbConfig] = useState({
+ [basePath]: i18n._(t`Management jobs`),
+ });
+
+ const buildBreadcrumbConfig = useCallback(
+ ({ id, name }, nested) => {
+ if (!id) return;
+
+ setBreadcrumbConfig({
+ [basePath]: i18n._(t`Management job`),
+ [`${basePath}/${id}`]: name,
+ [`${basePath}/${id}/details`]: i18n._(t`Details`),
+ [`${basePath}/${id}/edit`]: i18n._(t`Edit details`),
+ [`${basePath}/${id}/notifications`]: i18n._(t`Notifications`),
+ [`${basePath}/schedules`]: i18n._(t`Schedules`),
+ [`${basePath}/schedules/add`]: i18n._(t`Create New Schedule`),
+ [`${basePath}/schedules/${nested?.id}`]: `${nested?.name}`,
+ [`${basePath}/schedules/${nested?.id}/details`]: i18n._(t`Details`),
+ [`${basePath}/schedules/${nested?.id}/edit`]: i18n._(t`Edit Details`),
+ });
+ },
+ [i18n]
+ );
+
return (
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.test.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.test.jsx
index df422fe8ec..04e9ed4894 100644
--- a/awx/ui_next/src/screens/ManagementJob/ManagementJobs.test.jsx
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobs.test.jsx
@@ -10,17 +10,20 @@ jest.mock('react-router-dom', () => ({
describe('', () => {
let pageWrapper;
+ let pageSections;
beforeEach(() => {
pageWrapper = mountWithContexts();
+ pageSections = pageWrapper.find('PageSection');
});
afterEach(() => {
pageWrapper.unmount();
});
- test('initially renders without crashing', () => {
+ test('renders ok', () => {
expect(pageWrapper.length).toBe(1);
expect(pageWrapper.find('ScreenHeader').length).toBe(1);
+ expect(pageSections.length).toBe(1);
});
});