diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index c0d738207b..82af53b8b6 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -3359,6 +3359,13 @@ class SystemJobTemplateSerializer(UnifiedJobTemplateSerializer):
del result['default_days']
return result
+ def __init__(self, *args, **kwargs):
+ super(SystemJobTemplateSerializer, self).__init__(*args, **kwargs)
+ for field_name, field_instance in self.fields.items():
+ if field_name != 'default_days':
+ field_instance.read_only = True
+
+
class SystemJobSerializer(UnifiedJobSerializer):
diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py
index 29b26c120d..f532bab606 100644
--- a/awx/api/views/__init__.py
+++ b/awx/api/views/__init__.py
@@ -3423,7 +3423,7 @@ class SystemJobTemplateList(ListAPIView):
return super(SystemJobTemplateList, self).get(request, *args, **kwargs)
-class SystemJobTemplateDetail(RetrieveAPIView):
+class SystemJobTemplateDetail(RetrieveUpdateAPIView):
model = models.SystemJobTemplate
serializer_class = serializers.SystemJobTemplateSerializer
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx
index 9ddff5539a..17908d9894 100644
--- a/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobEdit/ManagementJobEdit.jsx
@@ -1,9 +1,71 @@
-import React from 'react';
+import React, { useState } from 'react';
+import { t } from '@lingui/macro';
+import { withI18n } from '@lingui/react';
+import { Formik } from 'formik';
+import { Form } from '@patternfly/react-core';
+import { useHistory } from 'react-router-dom';
+import { SystemJobTemplatesAPI } from '../../../api';
+import FormField, { FormSubmitError } from '../../../components/FormField';
+import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
+import { FormColumnLayout } from '../../../components/FormLayout';
+import { minMaxValue } from '../../../util/validators';
import { CardBody } from '../../../components/Card';
-function ManagementJobEdit() {
- return Management Job Edit;
+function ManagementJobEdit({ i18n, managementJob }) {
+ const history = useHistory();
+ const [formError, setFormError] = useState(null);
+
+ const handleCancel = () => {
+ history.push(`/management_jobs/${managementJob?.id}/details`);
+ };
+
+ const handleSubmit = async values => {
+ try {
+ await SystemJobTemplatesAPI.update(managementJob?.id, {
+ default_days: values.dataRetention,
+ });
+ history.push(`/management_jobs/${managementJob?.id}/details`);
+ } catch (error) {
+ setFormError(error);
+ }
+ };
+
+ return (
+
+ {managementJob?.default_days ? (
+
+ {formik => (
+
+ )}
+
+ ) : null}
+
+ );
}
-export default ManagementJobEdit;
+export default withI18n()(ManagementJobEdit);
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/LaunchManagementPrompt.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/LaunchManagementPrompt.jsx
new file mode 100644
index 0000000000..ad010be710
--- /dev/null
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/LaunchManagementPrompt.jsx
@@ -0,0 +1,83 @@
+import React, { useState } from 'react';
+import { withI18n } from '@lingui/react';
+import { t } from '@lingui/macro';
+import { Button, TextInput, Tooltip } from '@patternfly/react-core';
+import { RocketIcon } from '@patternfly/react-icons';
+
+import AlertModal from '../../../components/AlertModal';
+
+const clamp = (val, min, max) => {
+ if (val < min) {
+ return min;
+ }
+ if (val > max) {
+ return max;
+ }
+ return val;
+};
+
+function LaunchManagementPrompt({
+ i18n,
+ isOpen,
+ isLoading,
+ onClick,
+ onClose,
+ onConfirm,
+ defaultDays,
+}) {
+ const [dataRetention, setDataRetention] = useState(defaultDays);
+ return (
+ <>
+
+
+
+ onConfirm(dataRetention)}
+ >
+ {i18n._(t`Launch`)}
+ ,
+ ,
+ ]}
+ >
+ {i18n._(t`Set how many days of data should be retained.`)}
+
+ setDataRetention(clamp(value, 0, Number.MAX_SAFE_INTEGER))
+ }
+ aria-label={i18n._(t`Launch`)}
+ />
+
+ >
+ );
+}
+
+export default withI18n()(LaunchManagementPrompt);
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx
index e7a0bfccc1..1a54713c60 100644
--- a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobList.jsx
@@ -91,6 +91,7 @@ function ManagementJobList({ i18n }) {
name,
description,
has_configurable_retention,
+ default_days,
}) => (
)}
diff --git a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobListItem.jsx b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobListItem.jsx
index 5969397952..79c0e25ea1 100644
--- a/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobListItem.jsx
+++ b/awx/ui_next/src/screens/ManagementJob/ManagementJobList/ManagementJobListItem.jsx
@@ -11,10 +11,13 @@ import {
DataListItemCells,
Tooltip,
} from '@patternfly/react-core';
-import { PencilAltIcon, RocketIcon } from '@patternfly/react-icons';
+import { RocketIcon, PencilAltIcon } from '@patternfly/react-icons';
import styled from 'styled-components';
import { SystemJobTemplatesAPI } from '../../../api';
+import AlertModal from '../../../components/AlertModal';
+import ErrorDetail from '../../../components/ErrorDetail';
+import LaunchManagementPrompt from './LaunchManagementPrompt';
const DataListAction = styled(_DataListAction)`
align-items: center;
@@ -31,6 +34,7 @@ function ManagementJobListItem({
id,
name,
description,
+ defaultDays,
}) {
const detailsUrl = `/management_jobs/${id}/details`;
const editUrl = `/management_jobs/${id}/edit`;
@@ -39,6 +43,28 @@ function ManagementJobListItem({
const history = useHistory();
const [isLaunchLoading, setIsLaunchLoading] = useState(false);
+ const [isManagementPromptOpen, setIsManagementPromptOpen] = useState(false);
+ const [isManagementPromptLoading, setIsManagementPromptLoading] = useState(
+ false
+ );
+ const [managementPromptError, setManagementPromptError] = useState(null);
+ const handleManagementPromptClick = () => setIsManagementPromptOpen(true);
+ const handleManagementPromptClose = () => setIsManagementPromptOpen(false);
+
+ const handleManagementPromptConfirm = async days => {
+ setIsManagementPromptLoading(true);
+ try {
+ const { data } = await SystemJobTemplatesAPI.launch(id, {
+ extra_vars: { days },
+ });
+ history.push(`/jobs/management/${data.id}/output`);
+ } catch (error) {
+ setManagementPromptError(error);
+ } finally {
+ setIsManagementPromptLoading(false);
+ }
+ };
+
const handleLaunch = async () => {
setIsLaunchLoading(true);
try {
@@ -52,67 +78,89 @@ function ManagementJobListItem({
};
return (
-
-
-
-
- {name}
-
- ,
-
- {i18n._(t`Description:`)} {description}
- ,
- ]}
- />
-
- {isSuperUser ? (
- <>
-
+
+
+
-
-
- {isConfigurable ? (
-
-
-
- ) : null}
- >
- ) : null}
-
-
-
+
+
+
+
+ )}{' '}
+ >
+ ) : null}
+
+
+
+ {managementPromptError && (
+ <>
+ setManagementPromptError(null)}
+ title={i18n._(t`Management job launch error`)}
+ label={i18n._(t`Management job launch error`)}
+ >
+
+
+ >
+ )}
+ >
);
}