foreman_remote_execution 16.2.1 → 16.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +10 -10
  3. data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +9 -9
  4. data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +10 -10
  5. data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +28 -28
  6. data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +32 -32
  7. data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +10 -10
  8. data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +27 -27
  9. data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +9 -9
  10. data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +9 -9
  11. data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +27 -27
  12. data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +9 -9
  13. data/lib/foreman_remote_execution/version.rb +1 -1
  14. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  15. data/locale/de/foreman_remote_execution.po +10 -10
  16. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  17. data/locale/en_GB/foreman_remote_execution.po +9 -9
  18. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  19. data/locale/es/foreman_remote_execution.po +10 -10
  20. data/locale/foreman_remote_execution.pot +66 -66
  21. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  22. data/locale/fr/foreman_remote_execution.po +28 -28
  23. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  24. data/locale/ja/foreman_remote_execution.po +32 -32
  25. data/locale/ka/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  26. data/locale/ka/foreman_remote_execution.po +10 -10
  27. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  28. data/locale/ko/foreman_remote_execution.po +27 -27
  29. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  30. data/locale/pt_BR/foreman_remote_execution.po +9 -9
  31. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  32. data/locale/ru/foreman_remote_execution.po +9 -9
  33. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  34. data/locale/zh_CN/foreman_remote_execution.po +27 -27
  35. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  36. data/locale/zh_TW/foreman_remote_execution.po +9 -9
  37. data/webpack/JobInvocationDetail/CheckboxesActions.js +39 -30
  38. data/webpack/JobInvocationDetail/JobInvocationConstants.js +0 -1
  39. data/webpack/JobInvocationDetail/JobInvocationHostTable.js +156 -170
  40. data/webpack/JobInvocationDetail/OpenAllInvocationsModal.js +27 -32
  41. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +10 -2
  42. data/webpack/JobInvocationDetail/__tests__/TableToolbarActions.test.js +46 -30
  43. data/webpack/JobInvocationDetail/index.js +13 -2
  44. data/webpack/react_app/components/RegistrationExtension/RexInterface.js +5 -19
  45. data/webpack/react_app/components/RegistrationExtension/__tests__/RexInterface.test.js +19 -3
  46. metadata +2 -3
  47. data/webpack/react_app/components/RegistrationExtension/__tests__/__snapshots__/RexInterface.test.js.snap +0 -36
@@ -14,23 +14,21 @@ import {
14
14
  } from '@patternfly/react-icons';
15
15
  import axios from 'axios';
16
16
  import { foremanUrl } from 'foremanReact/common/helpers';
17
- import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
18
17
  import { translate as __, sprintf } from 'foremanReact/common/I18n';
19
18
  import { addToast } from 'foremanReact/components/ToastsList';
20
19
  import PropTypes from 'prop-types';
21
- import React, { useState } from 'react';
20
+ import React, { useEffect, useState } from 'react';
22
21
  import { useDispatch, useSelector } from 'react-redux';
23
22
 
24
23
  import {
25
24
  DIRECT_OPEN_HOST_LIMIT,
26
- MAX_HOSTS_API_SIZE,
27
25
  templateInvocationPageUrl,
28
26
  } from './JobInvocationConstants';
29
27
  import {
30
28
  selectHasPermission,
31
29
  selectTaskCancelable,
32
30
  } from './JobInvocationSelectors';
33
- import OpenAllInvocationsModal, { PopupAlert } from './OpenAllInvocationsModal';
31
+ import OpenAllInvocationsModal from './OpenAllInvocationsModal';
34
32
 
35
33
  /* eslint-disable camelcase */
36
34
  const ActionsKebab = ({
@@ -73,7 +71,7 @@ const ActionsKebab = ({
73
71
  onClick={() => handleOpenHosts('failed')}
74
72
  isDisabled={failedCount === 0}
75
73
  >
76
- {sprintf(__('Open all failed runs (%s)'), failedCount)}
74
+ {sprintf(__('Open all failed runs on this page (%s)'), failedCount)}
77
75
  </DropdownItem>,
78
76
  ];
79
77
 
@@ -104,17 +102,18 @@ const ActionsKebab = ({
104
102
 
105
103
  export const CheckboxesActions = ({
106
104
  selectedIds,
107
- failedCount,
105
+ allJobs,
108
106
  jobID,
109
107
  filter,
110
108
  bulkParams,
109
+ setShowAlert,
111
110
  }) => {
112
111
  const [isModalOpen, setIsModalOpen] = useState(false);
113
- const [showAlert, setShowAlert] = useState(false);
114
112
  const [isOpenFailed, setIsOpenFailed] = useState(false);
115
113
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
116
114
  const isTaskCancelable = useSelector(selectTaskCancelable);
117
115
  const dispatch = useDispatch();
116
+ const [toBeOpened, setToBeOpened] = useState([]);
118
117
 
119
118
  const hasCreatePermission = useSelector(
120
119
  selectHasPermission('create_job_invocations')
@@ -129,19 +128,14 @@ export const CheckboxesActions = ({
129
128
  : '';
130
129
  const combinedQuery = `${bulkParams}${filterQuery}`;
131
130
 
132
- const { response: failedHostsData } = useAPI(
133
- 'get',
134
- foremanUrl(`/api/job_invocations/${jobID}/hosts`),
135
- {
136
- params: {
137
- per_page: MAX_HOSTS_API_SIZE,
138
- search: `job_invocation.result = failed`,
139
- },
140
- skip: failedCount === 0,
141
- }
142
- );
131
+ const [failedHosts, setFailedHosts] = useState([]);
143
132
 
144
- const failedHosts = failedHostsData?.results || [];
133
+ useEffect(() => {
134
+ const failed = allJobs.filter(i => i.job_status === 'error');
135
+ setFailedHosts(failed);
136
+ }, [allJobs]);
137
+
138
+ const failedCount = failedHosts.length;
145
139
 
146
140
  const openLink = url => {
147
141
  const newWin = window.open(url);
@@ -151,24 +145,35 @@ export const CheckboxesActions = ({
151
145
  }
152
146
  };
153
147
 
148
+ const openTabs = tabs => {
149
+ tabs.forEach(open => {
150
+ const openId = open.id ?? open;
151
+ openLink(templateInvocationPageUrl(openId, jobID));
152
+ });
153
+ };
154
+
154
155
  const handleOpenHosts = async (type = 'all') => {
155
156
  if (type === 'failed') {
156
157
  if (failedCount <= DIRECT_OPEN_HOST_LIMIT) {
157
- failedHosts.forEach(host =>
158
- openLink(templateInvocationPageUrl(host.id, jobID))
159
- );
158
+ openTabs(failedHosts);
160
159
  return;
161
160
  }
161
+ setToBeOpened(failedHosts);
162
162
  setIsOpenFailed(true);
163
163
  setIsModalOpen(true);
164
164
  return;
165
165
  }
166
166
 
167
+ if (selectedIds.length === 0) {
168
+ selectedIds = allJobs;
169
+ }
170
+
167
171
  if (selectedIds.length <= DIRECT_OPEN_HOST_LIMIT) {
168
- selectedIds.forEach(id => openLink(templateInvocationPageUrl(id, jobID)));
172
+ openTabs(selectedIds);
169
173
  return;
170
174
  }
171
175
 
176
+ setToBeOpened(selectedIds);
172
177
  setIsOpenFailed(false);
173
178
  setIsModalOpen(true);
174
179
  };
@@ -237,13 +242,19 @@ export const CheckboxesActions = ({
237
242
  <Button
238
243
  aria-label="open all template invocations in new tab"
239
244
  className="open-all-button"
240
- isDisabled={selectedIds.length === 0}
245
+ isDisabled={allJobs.length === 0}
241
246
  isInline
242
247
  onClick={() => handleOpenHosts('all')}
243
248
  ouiaId="template-invocation-new-tab-button"
244
249
  variant="link"
245
250
  >
246
- <Tooltip content={__('Open selected in new tab')}>
251
+ <Tooltip
252
+ content={
253
+ selectedIds.length === 0
254
+ ? __('Open all rows of the table in new tabs')
255
+ : __('Open selected in new tab')
256
+ }
257
+ >
247
258
  <OutlinedWindowRestoreIcon />
248
259
  </Tooltip>
249
260
  </Button>
@@ -281,16 +292,13 @@ export const CheckboxesActions = ({
281
292
  isDropdownOpen={isDropdownOpen}
282
293
  setIsDropdownOpen={setIsDropdownOpen}
283
294
  />
284
- {showAlert && <PopupAlert setShowAlert={setShowAlert} />}
285
295
  <OpenAllInvocationsModal
286
296
  isOpen={isModalOpen}
287
297
  onClose={() => setIsModalOpen(false)}
288
298
  failedCount={failedCount}
289
- failedHosts={failedHosts}
290
- jobID={jobID}
291
299
  isOpenFailed={isOpenFailed}
292
- setShowAlert={setShowAlert}
293
300
  selectedIds={selectedIds}
301
+ confirmCallback={() => openTabs(toBeOpened)}
294
302
  />
295
303
  </>
296
304
  );
@@ -314,10 +322,11 @@ ActionsKebab.defaultProps = {
314
322
 
315
323
  CheckboxesActions.propTypes = {
316
324
  selectedIds: PropTypes.array.isRequired,
317
- failedCount: PropTypes.number.isRequired,
325
+ allJobs: PropTypes.array.isRequired,
318
326
  jobID: PropTypes.string.isRequired,
319
327
  bulkParams: PropTypes.string,
320
328
  filter: PropTypes.string,
329
+ setShowAlert: PropTypes.func.isRequired,
321
330
  };
322
331
 
323
332
  CheckboxesActions.defaultProps = {
@@ -17,7 +17,6 @@ export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
17
17
  export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
18
18
  export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
19
19
  export const GET_TEMPLATE_INVOCATION = 'GET_TEMPLATE_INVOCATION';
20
- export const MAX_HOSTS_API_SIZE = 100;
21
20
  export const DIRECT_OPEN_HOST_LIMIT = 3;
22
21
  export const ALL_JOB_HOSTS = 'ALL_JOB_HOSTS';
23
22
  export const currentPermissionsUrl = foremanUrl(
@@ -8,9 +8,10 @@ import {
8
8
  ToolbarItem,
9
9
  } from '@patternfly/react-core';
10
10
  import { ExpandableRowContent, Tbody, Td, Tr } from '@patternfly/react-table';
11
+ import { useDispatch } from 'react-redux';
12
+ import { APIActions } from 'foremanReact/redux/API';
11
13
  import { translate as __ } from 'foremanReact/common/I18n';
12
14
  import { foremanUrl } from 'foremanReact/common/helpers';
13
- import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
14
15
  import { RowSelectTd } from 'foremanReact/components/HostsIndex/RowSelectTd';
15
16
  import SelectAllCheckbox from 'foremanReact/components/PF4/TableIndexPage/Table/SelectAllCheckbox';
16
17
  import { Table } from 'foremanReact/components/PF4/TableIndexPage/Table/Table';
@@ -26,22 +27,20 @@ import PropTypes from 'prop-types';
26
27
  import React, { useEffect, useMemo, useState, useRef } from 'react';
27
28
  import { FormattedMessage } from 'react-intl';
28
29
  import { useHistory } from 'react-router-dom';
29
- import URI from 'urijs';
30
+ import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
30
31
  import { CheckboxesActions } from './CheckboxesActions';
31
32
  import DropdownFilter from './DropdownFilter';
32
33
  import Columns, {
33
34
  JOB_INVOCATION_HOSTS,
34
- MAX_HOSTS_API_SIZE,
35
- STATUS_UPPERCASE,
36
35
  LIST_TEMPLATE_INVOCATIONS,
36
+ STATUS_UPPERCASE,
37
37
  ALL_JOB_HOSTS,
38
38
  } from './JobInvocationConstants';
39
- import { PopupAlert } from './OpenAllInvocationsModal';
40
39
  import { TemplateInvocation } from './TemplateInvocation';
41
40
  import { RowActions } from './TemplateInvocationComponents/TemplateActionButtons';
41
+ import { PopupAlert } from './OpenAllInvocationsModal';
42
42
 
43
43
  const JobInvocationHostTable = ({
44
- failedCount,
45
44
  id,
46
45
  initialFilter,
47
46
  onFilterUpdate,
@@ -50,19 +49,29 @@ const JobInvocationHostTable = ({
50
49
  }) => {
51
50
  const columns = Columns();
52
51
  const columnNamesKeys = Object.keys(columns);
53
- const apiOptions = { key: JOB_INVOCATION_HOSTS };
52
+
54
53
  const history = useHistory();
55
- const [selectedFilter, setSelectedFilter] = useState(initialFilter);
54
+ const dispatch = useDispatch();
55
+
56
+ const [showAlert, setShowAlert] = useState(false);
57
+
58
+ const [apiResponse, setApiResponse] = useState([]);
59
+ const [status, setStatus] = useState(STATUS_UPPERCASE.PENDING);
60
+ const [allHostsIds, setAllHostsIds] = useState([]);
61
+
62
+ // Expansive items
56
63
  const [expandedHost, setExpandedHost] = useState([]);
57
64
  const prevStatusLabel = useRef(statusLabel);
58
65
 
59
- useEffect(() => {
60
- if (initialFilter !== selectedFilter) {
61
- wrapSetSelectedFilter(initialFilter);
62
- }
63
- // eslint-disable-next-line react-hooks/exhaustive-deps
64
- }, [initialFilter]);
66
+ const isHostExpanded = host => expandedHost.includes(host);
67
+ const setHostExpanded = (host, isExpanding = true) =>
68
+ setExpandedHost(prevExpanded => {
69
+ const otherExpandedHosts = prevExpanded.filter(h => h !== host);
70
+ return isExpanding ? [...otherExpandedHosts, host] : otherExpandedHosts;
71
+ });
65
72
 
73
+ // Page table params
74
+ // Parse URL
66
75
  const {
67
76
  searchParam: urlSearchQuery = '',
68
77
  page: urlPage,
@@ -70,10 +79,27 @@ const JobInvocationHostTable = ({
70
79
  order: urlOrder,
71
80
  } = useUrlParams();
72
81
 
73
- const constructFilter = (
74
- filter = selectedFilter,
75
- search = urlSearchQuery
76
- ) => {
82
+ const { perPage: foremanPerPage } = useForemanSettings();
83
+
84
+ // default
85
+ const defaultParams = useMemo(
86
+ () => ({
87
+ page: urlPage ? Number(urlPage) : 1,
88
+ per_page: urlPerPage || Number(urlPerPage) || foremanPerPage,
89
+ order: urlOrder || '',
90
+ }),
91
+ [urlPage, urlPerPage, foremanPerPage, urlOrder]
92
+ );
93
+
94
+ // Page row for table
95
+ const { pageRowCount } = getPageStats({
96
+ total: apiResponse?.total || 0,
97
+ page: apiResponse?.page || urlPage || 1,
98
+ perPage: apiResponse?.per_page || urlPerPage || 0,
99
+ });
100
+
101
+ // Search filter
102
+ const constructFilter = (filter = initialFilter, search = urlSearchQuery) => {
77
103
  const dropdownFilterClause =
78
104
  filter && filter !== 'all_statuses'
79
105
  ? `job_invocation.result = ${filter}`
@@ -85,68 +111,93 @@ const JobInvocationHostTable = ({
85
111
  .join(' AND ');
86
112
  };
87
113
 
88
- const defaultParams = useMemo(
89
- () => ({
90
- ...(urlPage ? { page: Number(urlPage) } : {}),
91
- ...(urlPerPage ? { per_page: Number(urlPerPage) } : {}),
92
- ...(urlOrder ? { order: urlOrder } : {}),
93
- }),
94
- [urlPage, urlPerPage, urlOrder]
95
- );
114
+ const handleResponse = (data, key) => {
115
+ if (key === JOB_INVOCATION_HOSTS) {
116
+ const ids = data.data.results.map(i => i.id);
96
117
 
97
- useAPI('get', `/job_invocations/${id}/hosts`, {
98
- params: {
99
- search: defaultParams.search,
100
- },
101
- key: LIST_TEMPLATE_INVOCATIONS,
102
- });
103
- const { response, status, setAPIOptions } = useAPI(
104
- 'get',
105
- `/api/job_invocations/${id}/hosts`,
106
- {
107
- params: defaultParams,
118
+ setApiResponse(data.data);
119
+ setAllHostsIds(ids);
108
120
  }
109
- );
110
121
 
111
- const [allPagesResponse, setAllPagesResponse] = useState([]);
112
- const apiAllParams = {
113
- page: 1,
114
- per_page: Math.min(response?.subtotal || 1, MAX_HOSTS_API_SIZE),
115
- search: constructFilter(selectedFilter, urlSearchQuery),
122
+ setStatus(STATUS_UPPERCASE.RESOLVED);
123
+ };
124
+
125
+ // Call hosts data with params
126
+ const makeApiCall = (requestParams, callParams = {}) => {
127
+ dispatch(
128
+ APIActions.get({
129
+ key: callParams.key ?? ALL_JOB_HOSTS,
130
+ url: callParams.url ?? `/api/job_invocations/${id}/hosts`,
131
+ params: requestParams,
132
+ handleSuccess: data => handleResponse(data, callParams.key),
133
+ handleError: () => setStatus(STATUS_UPPERCASE.ERROR),
134
+ errorToast: ({ response }) =>
135
+ response?.data?.error?.full_messages?.[0] || response,
136
+ })
137
+ );
116
138
  };
117
139
 
118
- const { response: allResponse, setAPIOptions: setAllAPIOptions } = useAPI(
119
- 'get',
120
- `/api/job_invocations/${id}/hosts`,
121
- {
122
- params: apiAllParams,
123
- key: ALL_JOB_HOSTS,
140
+ const filterApiCall = newAPIOptions => {
141
+ const newParams = newAPIOptions?.params ?? newAPIOptions ?? {};
142
+
143
+ const filterSearch = constructFilter(
144
+ initialFilter,
145
+ newParams.search ?? urlSearchQuery
146
+ );
147
+
148
+ const finalParams = {
149
+ ...defaultParams,
150
+ ...newParams,
151
+ };
152
+
153
+ if (filterSearch !== '') {
154
+ finalParams.search = filterSearch;
124
155
  }
125
- );
126
156
 
157
+ makeApiCall(finalParams, { key: JOB_INVOCATION_HOSTS });
158
+
159
+ const urlSearchParams = new URLSearchParams(window.location.search);
160
+
161
+ ['page', 'per_page', 'order'].forEach(key => {
162
+ if (finalParams[key]) urlSearchParams.set(key, finalParams[key]);
163
+ });
164
+
165
+ history.push({ search: urlSearchParams.toString() });
166
+ };
167
+
168
+ // Filter change
169
+ const handleFilterChange = newFilter => {
170
+ onFilterUpdate(newFilter);
171
+ };
172
+
173
+ // Effects
174
+ // run after mount
127
175
  useEffect(() => {
128
- if (response?.subtotal) {
129
- setAllAPIOptions(prevOptions => ({
130
- ...prevOptions,
131
- params: apiAllParams,
132
- }));
176
+ // Job Invo template load
177
+ makeApiCall(
178
+ {},
179
+ {
180
+ url: `/job_invocations/${id}/hosts`,
181
+ key: LIST_TEMPLATE_INVOCATIONS,
182
+ }
183
+ );
184
+
185
+ if (initialFilter === '') {
186
+ onFilterUpdate('all_statuses');
133
187
  }
188
+
134
189
  // eslint-disable-next-line react-hooks/exhaustive-deps
135
- }, [response?.subtotal, selectedFilter, urlSearchQuery]);
190
+ }, []);
136
191
 
137
192
  useEffect(() => {
138
- if (allResponse?.results) {
139
- setAllPagesResponse(allResponse.results);
140
- }
141
- }, [allResponse]);
193
+ if (initialFilter !== '') filterApiCall();
142
194
 
143
- useEffect(() => {
144
195
  if (statusLabel !== prevStatusLabel.current) {
145
- setAPIOptions(prevOptions => ({ ...prevOptions }));
146
196
  prevStatusLabel.current = statusLabel;
197
+ filterApiCall();
147
198
  }
148
199
  // eslint-disable-next-line react-hooks/exhaustive-deps
149
- }, [statusLabel]);
200
+ }, [initialFilter, statusLabel, id]);
150
201
 
151
202
  const {
152
203
  updateSearchQuery: updateSearchQueryBulk,
@@ -155,11 +206,11 @@ const JobInvocationHostTable = ({
155
206
  exclusionSet,
156
207
  ...selectAllOptions
157
208
  } = useBulkSelect({
158
- results: response?.results,
209
+ results: apiResponse?.results,
159
210
  metadata: {
160
- total: response?.total,
161
- page: response?.page,
162
- selectable: response?.subtotal,
211
+ total: apiResponse?.total,
212
+ page: apiResponse?.page,
213
+ selectable: apiResponse?.subtotal,
163
214
  },
164
215
  initialSearchQuery: urlSearchQuery,
165
216
  });
@@ -175,35 +226,11 @@ const JobInvocationHostTable = ({
175
226
  isSelected,
176
227
  } = selectAllOptions;
177
228
 
178
- const allHostIds = allPagesResponse?.map(item => item.id) || [];
179
229
  const selectedIds =
180
230
  areAllRowsSelected() || exclusionSet.size > 0
181
- ? allHostIds.filter(hostId => !exclusionSet.has(hostId))
231
+ ? allHostsIds.filter(hostId => !exclusionSet.has(hostId))
182
232
  : Array.from(inclusionSet);
183
233
 
184
- const { pageRowCount } = getPageStats({
185
- total: response?.total || 0,
186
- page: response?.page || urlPage || 1,
187
- perPage: response?.per_page || urlPerPage || 0,
188
- });
189
-
190
- const selectionToolbar = (
191
- <ToolbarItem key="selectAll">
192
- <SelectAllCheckbox
193
- {...{
194
- selectAll,
195
- selectPage,
196
- selectNone,
197
- selectedCount,
198
- pageRowCount,
199
- }}
200
- totalCount={response?.total}
201
- areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
202
- areAllRowsSelected={areAllRowsSelected()}
203
- />
204
- </ToolbarItem>
205
- );
206
-
207
234
  const controller = 'hosts';
208
235
  const memoDefaultSearchProps = useMemo(
209
236
  () => getControllerSearchProps(controller),
@@ -213,70 +240,40 @@ const JobInvocationHostTable = ({
213
240
  `/${controller}/auto_complete_search`
214
241
  );
215
242
 
216
- const wrapSetSelectedFilter = newFilter => {
217
- setSelectedFilter(newFilter);
218
- onFilterUpdate(newFilter);
219
-
220
- const filterSearch = constructFilter(newFilter, urlSearchQuery);
221
-
222
- const newParams = {
223
- ...defaultParams,
224
- page: 1,
225
- };
226
-
227
- if (filterSearch !== '') {
228
- newParams.search = filterSearch;
229
- }
230
-
231
- setAPIOptions(prev => ({ ...prev, params: newParams }));
232
-
233
- const urlSearchParams = new URLSearchParams(window.location.search);
234
- urlSearchParams.set('page', '1');
235
- history.push({ search: urlSearchParams.toString() });
236
- };
237
-
238
- const wrapSetAPIOptions = newAPIOptions => {
239
- const newParams = newAPIOptions?.params ?? newAPIOptions ?? {};
240
-
241
- const filterSearch = constructFilter(
242
- selectedFilter,
243
- newParams.search ?? urlSearchQuery
244
- );
245
-
246
- const mergedParams = {
247
- ...defaultParams,
248
- ...newParams,
249
- };
250
-
251
- if (filterSearch !== '') {
252
- mergedParams.search = filterSearch;
253
- } else if ('search' in mergedParams) {
254
- delete mergedParams.search;
255
- }
256
-
257
- setAPIOptions(prev => ({ ...prev, params: mergedParams }));
258
-
259
- const { search: _search, ...paramsForUrl } = mergedParams;
260
- const uri = new URI();
261
- uri.setSearch(paramsForUrl);
262
- history.push({ search: uri.search() });
263
- };
264
-
265
243
  const combinedResponse = {
266
244
  response: {
267
245
  search: urlSearchQuery,
268
246
  can_create: false,
269
- results: response?.results || [],
270
- total: response?.total || 0,
247
+ results: apiResponse?.results || [],
248
+ total: apiResponse?.total || 0,
271
249
  per_page: defaultParams?.perPage,
272
250
  page: defaultParams?.page,
273
- subtotal: response?.subtotal || 0,
274
- message: response?.message || 'error',
251
+ subtotal: apiResponse?.subtotal || 0,
252
+ message: apiResponse?.message || 'error',
275
253
  },
276
254
  status,
277
- setAPIOptions: wrapSetAPIOptions,
255
+ setAPIOptions: filterApiCall,
278
256
  };
279
257
 
258
+ const results = apiResponse.results ?? [];
259
+
260
+ const selectionToolbar = (
261
+ <ToolbarItem key="selectAll">
262
+ <SelectAllCheckbox
263
+ {...{
264
+ selectAll,
265
+ selectPage,
266
+ selectNone,
267
+ selectedCount,
268
+ pageRowCount,
269
+ }}
270
+ totalCount={apiResponse?.total}
271
+ areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
272
+ areAllRowsSelected={areAllRowsSelected()}
273
+ />
274
+ </ToolbarItem>
275
+ );
276
+
280
277
  const customEmptyState = (
281
278
  <Tr ouiaId="table-empty">
282
279
  <Td colSpan={100}>
@@ -314,22 +311,11 @@ const JobInvocationHostTable = ({
314
311
  </Tr>
315
312
  );
316
313
 
317
- const { results = [] } = response;
318
-
319
- const isHostExpanded = host => expandedHost.includes(host);
320
- const setHostExpanded = (host, isExpanding = true) =>
321
- setExpandedHost(prevExpanded => {
322
- const otherExpandedHosts = prevExpanded.filter(h => h !== host);
323
- return isExpanding ? [...otherExpandedHosts, host] : otherExpandedHosts;
324
- });
325
- const [showAlert, setShowAlert] = useState(false);
326
-
327
314
  return (
328
315
  <>
329
316
  {showAlert && <PopupAlert setShowAlert={setShowAlert} />}
330
317
  <TableIndexPage
331
318
  apiUrl=""
332
- apiOptions={apiOptions}
333
319
  customSearchProps={memoDefaultSearchProps}
334
320
  controller="hosts"
335
321
  creatable={false}
@@ -338,16 +324,17 @@ const JobInvocationHostTable = ({
338
324
  customToolbarItems={[
339
325
  <DropdownFilter
340
326
  key="dropdown-filter"
341
- dropdownFilter={selectedFilter}
342
- setDropdownFilter={wrapSetSelectedFilter}
327
+ dropdownFilter={initialFilter}
328
+ setDropdownFilter={handleFilterChange}
343
329
  />,
344
330
  <CheckboxesActions
345
331
  bulkParams={selectedCount > 0 ? fetchBulkParams() : null}
346
332
  selectedIds={selectedIds}
347
- failedCount={failedCount}
333
+ allJobs={results}
348
334
  jobID={id}
349
335
  key="checkboxes-actions"
350
- filter={selectedFilter}
336
+ filter={initialFilter}
337
+ setShowAlert={setShowAlert}
351
338
  />,
352
339
  ]}
353
340
  selectionToolbar={selectionToolbar}
@@ -361,21 +348,21 @@ const JobInvocationHostTable = ({
361
348
  : null
362
349
  }
363
350
  params={{
364
- page: response?.page || Number(urlPage),
365
- per_page: response?.per_page || Number(urlPerPage),
351
+ page: defaultParams.page || Number(urlPage),
352
+ per_page: defaultParams.per_page || Number(urlPerPage),
366
353
  order: urlOrder,
367
354
  }}
368
- page={response?.page || Number(urlPage)}
369
- perPage={response?.per_page || Number(urlPerPage)}
370
- setParams={wrapSetAPIOptions}
371
- itemCount={response?.subtotal}
355
+ page={defaultParams.page || Number(urlPage)}
356
+ perPage={defaultParams.per_page || Number(urlPerPage)}
357
+ setParams={filterApiCall}
358
+ itemCount={apiResponse?.subtotal}
372
359
  results={results}
373
360
  url=""
374
361
  showCheckboxes
375
362
  refreshData={() => {}}
376
363
  errorMessage={
377
- status === STATUS_UPPERCASE.ERROR && response?.message
378
- ? response.message
364
+ status === STATUS_UPPERCASE.ERROR && apiResponse?.message
365
+ ? apiResponse.message
379
366
  : null
380
367
  }
381
368
  isPending={status === STATUS_UPPERCASE.PENDING}
@@ -439,7 +426,6 @@ const JobInvocationHostTable = ({
439
426
  JobInvocationHostTable.propTypes = {
440
427
  id: PropTypes.string.isRequired,
441
428
  targeting: PropTypes.object.isRequired,
442
- failedCount: PropTypes.number.isRequired,
443
429
  initialFilter: PropTypes.string.isRequired,
444
430
  statusLabel: PropTypes.string,
445
431
  onFilterUpdate: PropTypes.func,