foreman_remote_execution 4.7.0 → 5.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +1 -0
- data/app/controllers/api/v2/job_invocations_controller.rb +16 -1
- data/app/controllers/ui_job_wizard_controller.rb +16 -4
- data/app/graphql/mutations/job_invocations/create.rb +43 -0
- data/app/graphql/types/job_invocation_input.rb +13 -0
- data/app/graphql/types/recurrence_input.rb +8 -0
- data/app/graphql/types/scheduling_input.rb +6 -0
- data/app/graphql/types/targeting_enum.rb +7 -0
- data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +20 -9
- data/app/helpers/remote_execution_helper.rb +1 -1
- data/app/lib/actions/remote_execution/run_host_job.rb +6 -1
- data/app/lib/actions/remote_execution/run_hosts_job.rb +57 -3
- data/app/mailers/rex_job_mailer.rb +15 -0
- data/app/models/concerns/foreman_remote_execution/host_extensions.rb +12 -0
- data/app/models/job_invocation.rb +4 -0
- data/app/models/job_invocation_composer.rb +21 -13
- data/app/models/remote_execution_provider.rb +18 -2
- data/app/models/rex_mail_notification.rb +13 -0
- data/app/models/targeting.rb +3 -3
- data/app/services/ui_notifications/remote_execution_jobs/base_job_finish.rb +2 -1
- data/app/views/dashboard/_latest-jobs.html.erb +21 -0
- data/app/views/job_invocations/_preview_hosts_list.html.erb +1 -1
- data/app/views/job_invocations/refresh.js.erb +1 -0
- data/app/views/rex_job_mailer/job_finished.html.erb +24 -0
- data/app/views/rex_job_mailer/job_finished.text.erb +9 -0
- data/app/views/template_invocations/show.html.erb +3 -2
- data/config/routes.rb +1 -0
- data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +5 -0
- data/db/seeds.d/50-notification_blueprints.rb +14 -0
- data/db/seeds.d/95-mail_notifications.rb +24 -0
- data/foreman_remote_execution.gemspec +1 -1
- data/lib/foreman_remote_execution/engine.rb +116 -7
- data/lib/foreman_remote_execution/version.rb +1 -1
- data/package.json +9 -7
- data/test/functional/api/v2/job_invocations_controller_test.rb +20 -0
- data/test/functional/cockpit_controller_test.rb +0 -1
- data/test/graphql/mutations/job_invocations/create.rb +58 -0
- data/test/helpers/remote_execution_helper_test.rb +0 -1
- data/test/unit/actions/run_host_job_test.rb +21 -0
- data/test/unit/actions/run_hosts_job_test.rb +99 -4
- data/test/unit/concerns/host_extensions_test.rb +36 -3
- data/test/unit/job_invocation_composer_test.rb +3 -5
- data/test/unit/job_invocation_report_template_test.rb +16 -13
- data/test/unit/job_template_effective_user_test.rb +0 -4
- data/test/unit/remote_execution_provider_test.rb +46 -4
- data/test/unit/targeting_test.rb +68 -1
- data/webpack/JobWizard/JobWizard.js +142 -28
- data/webpack/JobWizard/JobWizard.scss +86 -33
- data/webpack/JobWizard/JobWizardConstants.js +44 -0
- data/webpack/JobWizard/JobWizardSelectors.js +32 -0
- data/webpack/JobWizard/__tests__/fixtures.js +89 -6
- data/webpack/JobWizard/__tests__/integration.test.js +29 -22
- data/webpack/JobWizard/__tests__/validation.test.js +141 -0
- data/webpack/JobWizard/autofill.js +38 -0
- data/webpack/JobWizard/index.js +7 -0
- data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +23 -9
- data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +32 -9
- data/webpack/JobWizard/steps/AdvancedFields/Fields.js +48 -1
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +242 -23
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +82 -0
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +5 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +3 -2
- data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -2
- data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +62 -0
- data/webpack/JobWizard/steps/HostsAndInputs/HostSearch.js +54 -0
- data/webpack/JobWizard/steps/HostsAndInputs/SelectAPI.js +33 -0
- data/webpack/JobWizard/steps/HostsAndInputs/SelectGQL.js +52 -0
- data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +100 -0
- data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +23 -0
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +151 -0
- data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +53 -0
- data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +18 -0
- data/webpack/JobWizard/steps/HostsAndInputs/hostgroups.gql +8 -0
- data/webpack/JobWizard/steps/HostsAndInputs/hosts.gql +8 -0
- data/webpack/JobWizard/steps/HostsAndInputs/index.js +214 -0
- data/webpack/JobWizard/steps/ReviewDetails/index.js +193 -0
- data/webpack/JobWizard/steps/Schedule/PurposeField.js +31 -0
- data/webpack/JobWizard/steps/Schedule/QueryType.js +46 -43
- data/webpack/JobWizard/steps/Schedule/RepeatCron.js +53 -0
- data/webpack/JobWizard/steps/Schedule/RepeatDaily.js +37 -0
- data/webpack/JobWizard/steps/Schedule/RepeatHour.js +54 -0
- data/webpack/JobWizard/steps/Schedule/RepeatMonth.js +46 -0
- data/webpack/JobWizard/steps/Schedule/RepeatOn.js +95 -31
- data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +70 -0
- data/webpack/JobWizard/steps/Schedule/ScheduleType.js +24 -21
- data/webpack/JobWizard/steps/Schedule/StartEndDates.js +78 -23
- data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +402 -0
- data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +20 -10
- data/webpack/JobWizard/steps/Schedule/index.js +166 -29
- data/webpack/JobWizard/steps/form/DateTimePicker.js +126 -0
- data/webpack/JobWizard/steps/form/FormHelpers.js +4 -0
- data/webpack/JobWizard/steps/form/Formatter.js +49 -17
- data/webpack/JobWizard/steps/form/NumberInput.js +5 -2
- data/webpack/JobWizard/steps/form/ResourceSelect.js +29 -0
- data/webpack/JobWizard/steps/form/SearchSelect.js +121 -0
- data/webpack/JobWizard/steps/form/SelectField.js +14 -3
- data/webpack/JobWizard/steps/form/WizardTitle.js +14 -0
- data/webpack/JobWizard/steps/form/__tests__/SelectSearch.test.js +33 -0
- data/webpack/JobWizard/submit.js +120 -0
- data/webpack/JobWizard/validation.js +53 -0
- data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +2 -0
- data/webpack/__mocks__/foremanReact/common/I18n.js +2 -0
- data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteActions.js +1 -0
- data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteConstants.js +1 -0
- data/webpack/__mocks__/foremanReact/routes/RouterSelector.js +1 -0
- data/webpack/helpers.js +1 -0
- data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +2 -1
- metadata +53 -7
- data/app/models/setting/remote_execution.rb +0 -88
- data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
- data/webpack/JobWizard/steps/form/__tests__/Formatter.test.js.example +0 -76
@@ -0,0 +1,46 @@
|
|
1
|
+
import React, { useEffect } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { TextInput, FormGroup, ValidatedOptions } from '@patternfly/react-core';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import { RepeatDaily } from './RepeatDaily';
|
6
|
+
import { noop } from '../../../helpers';
|
7
|
+
|
8
|
+
export const RepeatMonth = ({ repeatData, setRepeatData, setValid }) => {
|
9
|
+
const { days, at } = repeatData;
|
10
|
+
useEffect(() => {
|
11
|
+
if (days && at) {
|
12
|
+
setValid(true);
|
13
|
+
} else {
|
14
|
+
setValid(false);
|
15
|
+
}
|
16
|
+
return () => setValid(true);
|
17
|
+
}, [setValid, days, at]);
|
18
|
+
return (
|
19
|
+
<>
|
20
|
+
<FormGroup label={__('Days')} isRequired>
|
21
|
+
<TextInput
|
22
|
+
isRequired
|
23
|
+
validated={days ? ValidatedOptions.noval : ValidatedOptions.error}
|
24
|
+
aria-label="days"
|
25
|
+
placeholder="1,2..."
|
26
|
+
type="text"
|
27
|
+
value={repeatData.days || ''}
|
28
|
+
onChange={newTime => {
|
29
|
+
setRepeatData({ ...repeatData, days: newTime });
|
30
|
+
}}
|
31
|
+
/>
|
32
|
+
</FormGroup>
|
33
|
+
<RepeatDaily
|
34
|
+
repeatData={repeatData}
|
35
|
+
setRepeatData={setRepeatData}
|
36
|
+
setValid={noop}
|
37
|
+
/>
|
38
|
+
</>
|
39
|
+
);
|
40
|
+
};
|
41
|
+
|
42
|
+
RepeatMonth.propTypes = {
|
43
|
+
repeatData: PropTypes.object.isRequired,
|
44
|
+
setRepeatData: PropTypes.func.isRequired,
|
45
|
+
setValid: PropTypes.func.isRequired,
|
46
|
+
};
|
@@ -4,51 +4,112 @@ import { TextInput, Grid, GridItem, FormGroup } from '@patternfly/react-core';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
import { SelectField } from '../form/SelectField';
|
6
6
|
import { repeatTypes } from '../../JobWizardConstants';
|
7
|
+
import { RepeatCron } from './RepeatCron';
|
8
|
+
import { RepeatHour } from './RepeatHour';
|
9
|
+
import { RepeatMonth } from './RepeatMonth';
|
10
|
+
import { RepeatDaily } from './RepeatDaily';
|
11
|
+
import { RepeatWeek } from './RepeatWeek';
|
7
12
|
|
8
13
|
export const RepeatOn = ({
|
9
14
|
repeatType,
|
10
15
|
setRepeatType,
|
11
16
|
repeatAmount,
|
12
17
|
setRepeatAmount,
|
18
|
+
repeatData,
|
19
|
+
setRepeatData,
|
20
|
+
setValid,
|
13
21
|
}) => {
|
14
22
|
const [repeatValidated, setRepeatValidated] = useState('default');
|
15
23
|
const handleRepeatInputChange = newValue => {
|
16
24
|
setRepeatValidated(newValue >= 1 ? 'default' : 'error');
|
17
25
|
setRepeatAmount(newValue);
|
18
26
|
};
|
27
|
+
|
28
|
+
const getRepeatComponent = () => {
|
29
|
+
switch (repeatType) {
|
30
|
+
case repeatTypes.cronline:
|
31
|
+
return (
|
32
|
+
<RepeatCron
|
33
|
+
repeatData={repeatData}
|
34
|
+
setRepeatData={setRepeatData}
|
35
|
+
setValid={setValid}
|
36
|
+
/>
|
37
|
+
);
|
38
|
+
case repeatTypes.monthly:
|
39
|
+
return (
|
40
|
+
<RepeatMonth
|
41
|
+
repeatData={repeatData}
|
42
|
+
setRepeatData={setRepeatData}
|
43
|
+
setValid={setValid}
|
44
|
+
/>
|
45
|
+
);
|
46
|
+
case repeatTypes.weekly:
|
47
|
+
return (
|
48
|
+
<RepeatWeek
|
49
|
+
repeatData={repeatData}
|
50
|
+
setRepeatData={setRepeatData}
|
51
|
+
setValid={setValid}
|
52
|
+
/>
|
53
|
+
);
|
54
|
+
case repeatTypes.daily:
|
55
|
+
return (
|
56
|
+
<RepeatDaily
|
57
|
+
repeatData={repeatData}
|
58
|
+
setRepeatData={setRepeatData}
|
59
|
+
setValid={setValid}
|
60
|
+
/>
|
61
|
+
);
|
62
|
+
case repeatTypes.hourly:
|
63
|
+
return (
|
64
|
+
<RepeatHour
|
65
|
+
repeatData={repeatData}
|
66
|
+
setRepeatData={setRepeatData}
|
67
|
+
setValid={setValid}
|
68
|
+
/>
|
69
|
+
);
|
70
|
+
case repeatTypes.noRepeat:
|
71
|
+
default:
|
72
|
+
return null;
|
73
|
+
}
|
74
|
+
};
|
19
75
|
return (
|
20
|
-
<
|
21
|
-
<
|
22
|
-
<
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
<GridItem span={1} />
|
35
|
-
<GridItem span={5}>
|
36
|
-
<FormGroup
|
37
|
-
isInline
|
38
|
-
helperTextInvalid={__('Repeat amount can only be a positive number')}
|
39
|
-
validated={repeatValidated}
|
40
|
-
>
|
41
|
-
<TextInput
|
42
|
-
isDisabled={repeatType === repeatTypes.noRepeat}
|
43
|
-
id="repeat-amount"
|
44
|
-
value={repeatAmount}
|
45
|
-
type="text"
|
46
|
-
onChange={newValue => handleRepeatInputChange(newValue)}
|
47
|
-
placeholder={__('Repeat N times')}
|
76
|
+
<FormGroup label={__('Repeat On')}>
|
77
|
+
<Grid>
|
78
|
+
<GridItem span={6}>
|
79
|
+
<SelectField
|
80
|
+
isRequired
|
81
|
+
fieldId="repeat-select"
|
82
|
+
options={Object.values(repeatTypes)}
|
83
|
+
setValue={newValue => {
|
84
|
+
setRepeatType(newValue);
|
85
|
+
if (newValue === repeatTypes.noRepeat) {
|
86
|
+
setRepeatValidated('default');
|
87
|
+
}
|
88
|
+
}}
|
89
|
+
value={repeatType}
|
48
90
|
/>
|
49
|
-
</
|
50
|
-
|
51
|
-
|
91
|
+
</GridItem>
|
92
|
+
<GridItem span={1} />
|
93
|
+
<GridItem span={5}>
|
94
|
+
<FormGroup
|
95
|
+
helperTextInvalid={__(
|
96
|
+
'Repeat amount can only be a positive number'
|
97
|
+
)}
|
98
|
+
validated={repeatValidated}
|
99
|
+
>
|
100
|
+
<TextInput
|
101
|
+
isDisabled={repeatType === repeatTypes.noRepeat}
|
102
|
+
id="repeat-amount"
|
103
|
+
value={repeatAmount}
|
104
|
+
type="text"
|
105
|
+
onChange={newValue => handleRepeatInputChange(newValue)}
|
106
|
+
placeholder={__('Repeat N times')}
|
107
|
+
/>
|
108
|
+
</FormGroup>
|
109
|
+
</GridItem>
|
110
|
+
{getRepeatComponent()}
|
111
|
+
</Grid>
|
112
|
+
</FormGroup>
|
52
113
|
);
|
53
114
|
};
|
54
115
|
|
@@ -58,4 +119,7 @@ RepeatOn.propTypes = {
|
|
58
119
|
repeatAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
59
120
|
.isRequired,
|
60
121
|
setRepeatAmount: PropTypes.func.isRequired,
|
122
|
+
repeatData: PropTypes.object.isRequired,
|
123
|
+
setRepeatData: PropTypes.func.isRequired,
|
124
|
+
setValid: PropTypes.func.isRequired,
|
61
125
|
};
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import React, { useEffect } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { FormGroup, Checkbox } from '@patternfly/react-core';
|
4
|
+
import { translate as __, documentLocale } from 'foremanReact/common/I18n';
|
5
|
+
import { RepeatDaily } from './RepeatDaily';
|
6
|
+
import { noop } from '../../../helpers';
|
7
|
+
|
8
|
+
const getWeekDays = () => {
|
9
|
+
const locale = documentLocale().replace(/-/g, '_');
|
10
|
+
const baseDate = new Date(Date.UTC(2017, 0, 2)); // just a Monday
|
11
|
+
const weekDays = [];
|
12
|
+
for (let i = 0; i < 7; i++) {
|
13
|
+
try {
|
14
|
+
weekDays.push(baseDate.toLocaleDateString(locale, { weekday: 'short' }));
|
15
|
+
} catch {
|
16
|
+
weekDays.push(baseDate.toLocaleDateString('en', { weekday: 'short' }));
|
17
|
+
}
|
18
|
+
baseDate.setDate(baseDate.getDate() + 1);
|
19
|
+
}
|
20
|
+
return weekDays;
|
21
|
+
};
|
22
|
+
|
23
|
+
export const RepeatWeek = ({ repeatData, setRepeatData, setValid }) => {
|
24
|
+
const { daysOfWeek, at } = repeatData;
|
25
|
+
useEffect(() => {
|
26
|
+
if (daysOfWeek && Object.values(daysOfWeek).includes(true) && at) {
|
27
|
+
setValid(true);
|
28
|
+
} else {
|
29
|
+
setValid(false);
|
30
|
+
}
|
31
|
+
return () => setValid(true);
|
32
|
+
}, [setValid, daysOfWeek, at]);
|
33
|
+
const days = getWeekDays();
|
34
|
+
const handleChangeDays = (checked, { target: { name } }) => {
|
35
|
+
setRepeatData({
|
36
|
+
...repeatData,
|
37
|
+
daysOfWeek: { ...repeatData.daysOfWeek, [name]: checked },
|
38
|
+
});
|
39
|
+
};
|
40
|
+
return (
|
41
|
+
<>
|
42
|
+
<FormGroup label={__('Days of week')} isRequired>
|
43
|
+
<div id="repeat-on-weekly">
|
44
|
+
{days.map((day, index) => (
|
45
|
+
<Checkbox
|
46
|
+
aria-label={`${day} checkbox`}
|
47
|
+
key={index}
|
48
|
+
isChecked={daysOfWeek?.[index]}
|
49
|
+
name={index}
|
50
|
+
id={`repeat-on-day-${index}`}
|
51
|
+
onChange={handleChangeDays}
|
52
|
+
label={day}
|
53
|
+
/>
|
54
|
+
))}
|
55
|
+
</div>
|
56
|
+
</FormGroup>
|
57
|
+
|
58
|
+
<RepeatDaily
|
59
|
+
repeatData={repeatData}
|
60
|
+
setRepeatData={setRepeatData}
|
61
|
+
setValid={noop}
|
62
|
+
/>
|
63
|
+
</>
|
64
|
+
);
|
65
|
+
};
|
66
|
+
RepeatWeek.propTypes = {
|
67
|
+
repeatData: PropTypes.object.isRequired,
|
68
|
+
setRepeatData: PropTypes.func.isRequired,
|
69
|
+
setValid: PropTypes.func.isRequired,
|
70
|
+
};
|
@@ -1,25 +1,28 @@
|
|
1
|
-
import React
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
2
3
|
import { FormGroup, Radio } from '@patternfly/react-core';
|
3
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
4
5
|
|
5
|
-
export const ScheduleType = () =>
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
6
|
+
export const ScheduleType = ({ isFuture, setIsFuture }) => (
|
7
|
+
<FormGroup label={__('Schedule type')} fieldId="schedule-type">
|
8
|
+
<Radio
|
9
|
+
isChecked={!isFuture}
|
10
|
+
name="schedule-type"
|
11
|
+
onChange={() => setIsFuture(false)}
|
12
|
+
id="schedule-type-now"
|
13
|
+
label={__('Execute now')}
|
14
|
+
/>
|
15
|
+
<Radio
|
16
|
+
isChecked={isFuture}
|
17
|
+
name="schedule-type"
|
18
|
+
onChange={() => setIsFuture(true)}
|
19
|
+
id="schedule-type-future"
|
20
|
+
label={__('Schedule for future execution')}
|
21
|
+
/>
|
22
|
+
</FormGroup>
|
23
|
+
);
|
24
|
+
|
25
|
+
ScheduleType.propTypes = {
|
26
|
+
isFuture: PropTypes.bool.isRequired,
|
27
|
+
setIsFuture: PropTypes.func.isRequired,
|
25
28
|
};
|
@@ -1,35 +1,81 @@
|
|
1
|
-
import React, {
|
1
|
+
import React, { useEffect } from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
|
-
import { FormGroup,
|
3
|
+
import { FormGroup, Checkbox } from '@patternfly/react-core';
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import { DateTimePicker } from '../form/DateTimePicker';
|
6
|
+
import { helpLabel } from '../form/FormHelpers';
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
export const StartEndDates = ({
|
9
|
+
startsAt,
|
10
|
+
setStartsAt,
|
11
|
+
startsBefore,
|
12
|
+
setStartsBefore,
|
13
|
+
ends,
|
14
|
+
setEnds,
|
15
|
+
isNeverEnds,
|
16
|
+
setIsNeverEnds,
|
17
|
+
validEnd,
|
18
|
+
setValidEnd,
|
19
|
+
isFuture,
|
20
|
+
isStartBeforeDisabled,
|
21
|
+
isEndDisabled,
|
22
|
+
}) => {
|
9
23
|
const toggleIsNeverEnds = (checked, event) => {
|
10
24
|
const value = event?.target?.checked;
|
11
25
|
setIsNeverEnds(value);
|
12
|
-
setEnds('');
|
13
26
|
};
|
27
|
+
useEffect(() => {
|
28
|
+
if (isNeverEnds) setValidEnd(true);
|
29
|
+
else if ((!startsAt.length && !startsBefore.length) || !ends)
|
30
|
+
setValidEnd(true);
|
31
|
+
else if (
|
32
|
+
startsAt.length
|
33
|
+
? new Date(startsAt).getTime() <= new Date(ends).getTime()
|
34
|
+
: new Date(startsBefore).getTime() <= new Date(ends).getTime()
|
35
|
+
)
|
36
|
+
setValidEnd(true);
|
37
|
+
else setValidEnd(false);
|
38
|
+
}, [startsAt, startsBefore, ends, isNeverEnds, setValidEnd]);
|
14
39
|
return (
|
15
40
|
<>
|
16
|
-
<FormGroup label={__('Starts')} fieldId="start-date">
|
17
|
-
<
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
placeholder="mm/dd/yy, hh:mm UTC"
|
41
|
+
<FormGroup label={__('Starts at')} fieldId="start-at-date">
|
42
|
+
<DateTimePicker
|
43
|
+
allowEmpty={!isFuture}
|
44
|
+
ariaLabel="starts at"
|
45
|
+
dateTime={startsAt}
|
46
|
+
setDateTime={setStartsAt}
|
23
47
|
/>
|
24
48
|
</FormGroup>
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
49
|
+
|
50
|
+
<FormGroup
|
51
|
+
label={__('Starts before')}
|
52
|
+
fieldId="start-before-date"
|
53
|
+
labelIcon={helpLabel(
|
54
|
+
__(
|
55
|
+
'Indicates that the action should be cancelled if it cannot be started before this time.'
|
56
|
+
),
|
57
|
+
'start-before-date'
|
58
|
+
)}
|
59
|
+
>
|
60
|
+
<DateTimePicker
|
61
|
+
isDisabled={isStartBeforeDisabled}
|
62
|
+
allowEmpty={!isFuture}
|
63
|
+
ariaLabel="starts before"
|
64
|
+
dateTime={startsBefore}
|
65
|
+
setDateTime={setStartsBefore}
|
66
|
+
/>
|
67
|
+
</FormGroup>
|
68
|
+
<FormGroup
|
69
|
+
label={__('Ends')}
|
70
|
+
fieldId="end-date"
|
71
|
+
helperTextInvalid={__('End time needs to be after start time')}
|
72
|
+
validated={validEnd ? 'success' : 'error'}
|
73
|
+
>
|
74
|
+
<DateTimePicker
|
75
|
+
ariaLabel="ends"
|
76
|
+
dateTime={ends}
|
77
|
+
setDateTime={setEnds}
|
78
|
+
isDisabled={isNeverEnds || isEndDisabled}
|
33
79
|
/>
|
34
80
|
<Checkbox
|
35
81
|
label={__('Never ends')}
|
@@ -44,8 +90,17 @@ export const StartEndDates = ({ starts, setStarts, ends, setEnds }) => {
|
|
44
90
|
};
|
45
91
|
|
46
92
|
StartEndDates.propTypes = {
|
47
|
-
|
48
|
-
|
93
|
+
startsAt: PropTypes.string.isRequired,
|
94
|
+
setStartsAt: PropTypes.func.isRequired,
|
95
|
+
startsBefore: PropTypes.string.isRequired,
|
96
|
+
setStartsBefore: PropTypes.func.isRequired,
|
49
97
|
ends: PropTypes.string.isRequired,
|
50
98
|
setEnds: PropTypes.func.isRequired,
|
99
|
+
isNeverEnds: PropTypes.bool.isRequired,
|
100
|
+
setIsNeverEnds: PropTypes.func.isRequired,
|
101
|
+
validEnd: PropTypes.bool.isRequired,
|
102
|
+
setValidEnd: PropTypes.func.isRequired,
|
103
|
+
isFuture: PropTypes.bool.isRequired,
|
104
|
+
isStartBeforeDisabled: PropTypes.bool.isRequired,
|
105
|
+
isEndDisabled: PropTypes.bool.isRequired,
|
51
106
|
};
|