diff --git a/webpack/JobWizard/JobWizard.js b/webpack/JobWizard/JobWizard.js
index 9f329ecd3..42ea09445 100644
--- a/webpack/JobWizard/JobWizard.js
+++ b/webpack/JobWizard/JobWizard.js
@@ -36,7 +36,10 @@ import { useValidation } from './validation';
import { useAutoFill } from './autofill';
import { submit } from './submit';
import { generateDefaultDescription } from './JobWizardHelpers';
-import { StartsBeforeErrorAlert } from './StartsBeforeErrorAlert';
+import {
+ StartsBeforeErrorAlert,
+ StartsAtErrorAlert,
+} from './StartsBeforeErrorAlert';
import { Footer } from './Footer';
import './JobWizard.scss';
@@ -203,22 +206,37 @@ export const JobWizard = ({ rerunData }) => {
}, [rerunData, jobTemplateID, dispatch]);
const [isStartsBeforeError, setIsStartsBeforeError] = useState(false);
+ const [isStartsAtError, setIsStartsAtError] = useState(false);
useEffect(() => {
- const updateStartsBeforeError = () => {
- setIsStartsBeforeError(
- scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE &&
- new Date().getTime() >= new Date(scheduleValue.startsBefore).getTime()
- );
+ const updateStartsError = () => {
+ if (scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE) {
+ setIsStartsAtError(
+ !!scheduleValue?.startsAt?.length &&
+ new Date().getTime() >= new Date(scheduleValue.startsAt).getTime()
+ );
+ setIsStartsBeforeError(
+ !!scheduleValue?.startsBefore?.length &&
+ new Date().getTime() >=
+ new Date(scheduleValue.startsBefore).getTime()
+ );
+ } else if (scheduleValue.scheduleType === SCHEDULE_TYPES.RECURRING) {
+ setIsStartsAtError(
+ !!scheduleValue?.startsAt?.length &&
+ new Date().getTime() >= new Date(scheduleValue.startsAt).getTime()
+ );
+ setIsStartsBeforeError(false);
+ } else {
+ setIsStartsAtError(false);
+ setIsStartsBeforeError(false);
+ }
};
- let interval;
- if (scheduleValue.scheduleType === SCHEDULE_TYPES.FUTURE) {
- updateStartsBeforeError();
- interval = setInterval(updateStartsBeforeError, 5000);
- }
+ updateStartsError();
+ const interval = setInterval(updateStartsError, 5000);
+
return () => {
interval && clearInterval(interval);
};
- }, [scheduleValue.scheduleType, scheduleValue.startsBefore]);
+ }, [scheduleValue]);
const [valid, setValid] = useValidation({
advancedValues,
@@ -432,7 +450,8 @@ export const JobWizard = ({ rerunData }) => {
valid.advanced &&
valid.schedule &&
!isSubmitting &&
- !isStartsBeforeError,
+ !isStartsBeforeError &&
+ !isStartsAtError,
},
];
const location = useForemanLocation();
@@ -456,6 +475,7 @@ export const JobWizard = ({ rerunData }) => {
return (
<>
{isStartsBeforeError && }
+ {isStartsAtError && }
history.goBack()}
navAriaLabel="Run Job steps"
diff --git a/webpack/JobWizard/StartsBeforeErrorAlert.js b/webpack/JobWizard/StartsBeforeErrorAlert.js
index 117417553..1efbd8e7a 100644
--- a/webpack/JobWizard/StartsBeforeErrorAlert.js
+++ b/webpack/JobWizard/StartsBeforeErrorAlert.js
@@ -16,3 +16,18 @@ export const StartsBeforeErrorAlert = () => (
>
);
+
+export const StartsAtErrorAlert = () => (
+ <>
+
+ {__(
+ 'Please go back to "Schedule" - "Future execution" or "Recurring execution" step to fix the error'
+ )}
+
+
+ >
+);
diff --git a/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js b/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js
index 850b6e617..3bcbd4338 100644
--- a/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js
+++ b/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js
@@ -399,7 +399,7 @@ describe('AdvancedFields', () => {
target: { value: 'some search' },
});
- jest.runAllTimers();
+ jest.advanceTimersByTime(10000);
});
expect(newStore.getActions()).toMatchSnapshot('resource search');
});
diff --git a/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js b/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js
index 0b88ecba3..db1cf982a 100644
--- a/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js
+++ b/webpack/JobWizard/steps/Schedule/ScheduleRecurring.js
@@ -51,7 +51,7 @@ export const ScheduleRecurring = ({
if (isNeverEnds) setValidEnd(true);
else if (!ends) setValidEnd(true);
else if (
- !startsAt.length &&
+ !startsAt?.length &&
new Date().getTime() <= new Date(ends).getTime()
)
setValidEnd(true);
@@ -63,7 +63,7 @@ export const ScheduleRecurring = ({
if (!validEnd || !repeatValid) {
wrappedSetValid(false);
- } else if (isFuture && startsAt.length) {
+ } else if (isFuture && startsAt?.length) {
wrappedSetValid(true);
} else if (!isFuture) {
wrappedSetValid(true);
@@ -111,7 +111,9 @@ export const ScheduleRecurring = ({
onChange={() =>
setScheduleValue(current => ({
...current,
- startsAt: new Date().toISOString(),
+ startsAt: new Date(
+ new Date().getTime() + 60000
+ ).toISOString(), // 1 minute in the future
isFuture: true,
}))
}
diff --git a/webpack/JobWizard/steps/Schedule/ScheduleType.js b/webpack/JobWizard/steps/Schedule/ScheduleType.js
index 1fdc55966..77862c1f4 100644
--- a/webpack/JobWizard/steps/Schedule/ScheduleType.js
+++ b/webpack/JobWizard/steps/Schedule/ScheduleType.js
@@ -49,7 +49,7 @@ export const ScheduleType = ({
onChange={() => {
setScheduleValue(current => ({
...current,
- startsAt: new Date().toISOString(),
+ startsAt: new Date(new Date().getTime() + 60000).toISOString(), // 1 minute in the future
scheduleType: SCHEDULE_TYPES.FUTURE,
repeatType: repeatTypes.noRepeat,
}));
diff --git a/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js b/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js
index 96c50976c..d25247093 100644
--- a/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js
+++ b/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js
@@ -329,7 +329,7 @@ describe('Schedule', () => {
fireEvent.click(
screen.getByRole('button', { name: 'Recurring execution' })
);
- jest.runAllTimers();
+ jest.advanceTimersByTime(1000);
});
expect(screen.queryAllByText('Recurring execution')).toHaveLength(3);
diff --git a/webpack/JobWizard/steps/form/DateTimePicker.js b/webpack/JobWizard/steps/form/DateTimePicker.js
index b4dc314e4..bbe362f95 100644
--- a/webpack/JobWizard/steps/form/DateTimePicker.js
+++ b/webpack/JobWizard/steps/form/DateTimePicker.js
@@ -22,6 +22,7 @@ export const DateTimePicker = ({
ariaLabel,
allowEmpty,
includeSeconds,
+ isFutureOnly,
}) => {
const [validated, setValidated] = useState();
const dateFormat = date =>
@@ -87,6 +88,15 @@ export const DateTimePicker = ({
setValidated(ValidatedOptions.error);
}
};
+ const validateFuture = date => {
+ if (
+ isFutureOnly &&
+ date.setHours(1, 0, 0, 0) < new Date().setHours(0, 0, 0, 0)
+ ) {
+ return __('Date must be in the future');
+ }
+ return '';
+ };
return (
<>
{
isRequired={required}
>