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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +1 -0
  3. data/app/controllers/api/v2/job_invocations_controller.rb +16 -1
  4. data/app/controllers/ui_job_wizard_controller.rb +16 -4
  5. data/app/graphql/mutations/job_invocations/create.rb +43 -0
  6. data/app/graphql/types/job_invocation_input.rb +13 -0
  7. data/app/graphql/types/recurrence_input.rb +8 -0
  8. data/app/graphql/types/scheduling_input.rb +6 -0
  9. data/app/graphql/types/targeting_enum.rb +7 -0
  10. data/app/helpers/concerns/foreman_remote_execution/hosts_helper_extensions.rb +20 -9
  11. data/app/helpers/remote_execution_helper.rb +1 -1
  12. data/app/lib/actions/remote_execution/run_host_job.rb +6 -1
  13. data/app/lib/actions/remote_execution/run_hosts_job.rb +57 -3
  14. data/app/mailers/rex_job_mailer.rb +15 -0
  15. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +12 -0
  16. data/app/models/job_invocation.rb +4 -0
  17. data/app/models/job_invocation_composer.rb +21 -13
  18. data/app/models/remote_execution_provider.rb +18 -2
  19. data/app/models/rex_mail_notification.rb +13 -0
  20. data/app/models/targeting.rb +3 -3
  21. data/app/services/ui_notifications/remote_execution_jobs/base_job_finish.rb +2 -1
  22. data/app/views/dashboard/_latest-jobs.html.erb +21 -0
  23. data/app/views/job_invocations/_preview_hosts_list.html.erb +1 -1
  24. data/app/views/job_invocations/refresh.js.erb +1 -0
  25. data/app/views/rex_job_mailer/job_finished.html.erb +24 -0
  26. data/app/views/rex_job_mailer/job_finished.text.erb +9 -0
  27. data/app/views/template_invocations/show.html.erb +3 -2
  28. data/config/routes.rb +1 -0
  29. data/db/migrate/20210816100932_rex_setting_category_to_dsl.rb +5 -0
  30. data/db/seeds.d/50-notification_blueprints.rb +14 -0
  31. data/db/seeds.d/95-mail_notifications.rb +24 -0
  32. data/foreman_remote_execution.gemspec +1 -1
  33. data/lib/foreman_remote_execution/engine.rb +116 -7
  34. data/lib/foreman_remote_execution/version.rb +1 -1
  35. data/package.json +9 -7
  36. data/test/functional/api/v2/job_invocations_controller_test.rb +20 -0
  37. data/test/functional/cockpit_controller_test.rb +0 -1
  38. data/test/graphql/mutations/job_invocations/create.rb +58 -0
  39. data/test/helpers/remote_execution_helper_test.rb +0 -1
  40. data/test/unit/actions/run_host_job_test.rb +21 -0
  41. data/test/unit/actions/run_hosts_job_test.rb +99 -4
  42. data/test/unit/concerns/host_extensions_test.rb +36 -3
  43. data/test/unit/job_invocation_composer_test.rb +3 -5
  44. data/test/unit/job_invocation_report_template_test.rb +16 -13
  45. data/test/unit/job_template_effective_user_test.rb +0 -4
  46. data/test/unit/remote_execution_provider_test.rb +46 -4
  47. data/test/unit/targeting_test.rb +68 -1
  48. data/webpack/JobWizard/JobWizard.js +142 -28
  49. data/webpack/JobWizard/JobWizard.scss +86 -33
  50. data/webpack/JobWizard/JobWizardConstants.js +44 -0
  51. data/webpack/JobWizard/JobWizardSelectors.js +32 -0
  52. data/webpack/JobWizard/__tests__/fixtures.js +89 -6
  53. data/webpack/JobWizard/__tests__/integration.test.js +29 -22
  54. data/webpack/JobWizard/__tests__/validation.test.js +141 -0
  55. data/webpack/JobWizard/autofill.js +38 -0
  56. data/webpack/JobWizard/index.js +7 -0
  57. data/webpack/JobWizard/steps/AdvancedFields/AdvancedFields.js +23 -9
  58. data/webpack/JobWizard/steps/AdvancedFields/DescriptionField.js +32 -9
  59. data/webpack/JobWizard/steps/AdvancedFields/Fields.js +48 -1
  60. data/webpack/JobWizard/steps/AdvancedFields/__tests__/AdvancedFields.test.js +242 -23
  61. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +82 -0
  62. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.js +5 -2
  63. data/webpack/JobWizard/steps/CategoryAndTemplate/CategoryAndTemplate.test.js +3 -2
  64. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +3 -2
  65. data/webpack/JobWizard/steps/HostsAndInputs/HostPreviewModal.js +62 -0
  66. data/webpack/JobWizard/steps/HostsAndInputs/HostSearch.js +54 -0
  67. data/webpack/JobWizard/steps/HostsAndInputs/SelectAPI.js +33 -0
  68. data/webpack/JobWizard/steps/HostsAndInputs/SelectGQL.js +52 -0
  69. data/webpack/JobWizard/steps/HostsAndInputs/SelectedChips.js +100 -0
  70. data/webpack/JobWizard/steps/HostsAndInputs/TemplateInputs.js +23 -0
  71. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/HostsAndInputs.test.js +151 -0
  72. data/webpack/JobWizard/steps/HostsAndInputs/__tests__/TemplateInputs.test.js +53 -0
  73. data/webpack/JobWizard/steps/HostsAndInputs/buildHostQuery.js +18 -0
  74. data/webpack/JobWizard/steps/HostsAndInputs/hostgroups.gql +8 -0
  75. data/webpack/JobWizard/steps/HostsAndInputs/hosts.gql +8 -0
  76. data/webpack/JobWizard/steps/HostsAndInputs/index.js +214 -0
  77. data/webpack/JobWizard/steps/ReviewDetails/index.js +193 -0
  78. data/webpack/JobWizard/steps/Schedule/PurposeField.js +31 -0
  79. data/webpack/JobWizard/steps/Schedule/QueryType.js +46 -43
  80. data/webpack/JobWizard/steps/Schedule/RepeatCron.js +53 -0
  81. data/webpack/JobWizard/steps/Schedule/RepeatDaily.js +37 -0
  82. data/webpack/JobWizard/steps/Schedule/RepeatHour.js +54 -0
  83. data/webpack/JobWizard/steps/Schedule/RepeatMonth.js +46 -0
  84. data/webpack/JobWizard/steps/Schedule/RepeatOn.js +95 -31
  85. data/webpack/JobWizard/steps/Schedule/RepeatWeek.js +70 -0
  86. data/webpack/JobWizard/steps/Schedule/ScheduleType.js +24 -21
  87. data/webpack/JobWizard/steps/Schedule/StartEndDates.js +78 -23
  88. data/webpack/JobWizard/steps/Schedule/__tests__/Schedule.test.js +402 -0
  89. data/webpack/JobWizard/steps/Schedule/__tests__/StartEndDates.test.js +20 -10
  90. data/webpack/JobWizard/steps/Schedule/index.js +166 -29
  91. data/webpack/JobWizard/steps/form/DateTimePicker.js +126 -0
  92. data/webpack/JobWizard/steps/form/FormHelpers.js +4 -0
  93. data/webpack/JobWizard/steps/form/Formatter.js +49 -17
  94. data/webpack/JobWizard/steps/form/NumberInput.js +5 -2
  95. data/webpack/JobWizard/steps/form/ResourceSelect.js +29 -0
  96. data/webpack/JobWizard/steps/form/SearchSelect.js +121 -0
  97. data/webpack/JobWizard/steps/form/SelectField.js +14 -3
  98. data/webpack/JobWizard/steps/form/WizardTitle.js +14 -0
  99. data/webpack/JobWizard/steps/form/__tests__/SelectSearch.test.js +33 -0
  100. data/webpack/JobWizard/submit.js +120 -0
  101. data/webpack/JobWizard/validation.js +53 -0
  102. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext/index.js +2 -0
  103. data/webpack/__mocks__/foremanReact/common/I18n.js +2 -0
  104. data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteActions.js +1 -0
  105. data/webpack/__mocks__/foremanReact/components/AutoComplete/AutoCompleteConstants.js +1 -0
  106. data/webpack/__mocks__/foremanReact/routes/RouterSelector.js +1 -0
  107. data/webpack/helpers.js +1 -0
  108. data/webpack/react_app/components/RecentJobsCard/RecentJobsCard.js +2 -1
  109. metadata +53 -7
  110. data/app/models/setting/remote_execution.rb +0 -88
  111. data/webpack/JobWizard/steps/AdvancedFields/__tests__/DescriptionField.test.js +0 -23
  112. 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
- <Grid>
21
- <GridItem span={6}>
22
- <SelectField
23
- fieldId="repeat-select"
24
- options={Object.values(repeatTypes)}
25
- setValue={newValue => {
26
- setRepeatType(newValue);
27
- if (newValue === repeatTypes.noRepeat) {
28
- setRepeatValidated('default');
29
- }
30
- }}
31
- value={repeatType}
32
- />
33
- </GridItem>
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
- </FormGroup>
50
- </GridItem>
51
- </Grid>
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, { useState } from '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
- const [isFuture, setIsFuture] = useState(false);
7
- return (
8
- <FormGroup label={__('Schedule type')} fieldId="schedule-type">
9
- <Radio
10
- isChecked={!isFuture}
11
- name="schedule-type"
12
- onChange={() => setIsFuture(false)}
13
- id="schedule-type-now"
14
- label={__('Execute now')}
15
- />
16
- <Radio
17
- isChecked={isFuture}
18
- name="schedule-type"
19
- onChange={() => setIsFuture(true)}
20
- id="schedule-type-future"
21
- label={__('Schedule for future execution')}
22
- />
23
- </FormGroup>
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, { useState } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { FormGroup, TextInput, Checkbox } from '@patternfly/react-core';
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
- // TODO: change to datepicker
7
- export const StartEndDates = ({ starts, setStarts, ends, setEnds }) => {
8
- const [isNeverEnds, setIsNeverEnds] = useState(false);
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
- <TextInput
18
- id="start-date"
19
- value={starts}
20
- type="text"
21
- onChange={newValue => setStarts(newValue)}
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
- <FormGroup label={__('Ends')} fieldId="end-date">
26
- <TextInput
27
- isDisabled={isNeverEnds}
28
- id="end-date"
29
- value={ends}
30
- type="text"
31
- onChange={newValue => setEnds(newValue)}
32
- placeholder="mm/dd/yy, hh:mm UTC"
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
- starts: PropTypes.string.isRequired,
48
- setStarts: PropTypes.func.isRequired,
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
  };