foreman-tasks 1.1.3 → 1.2.0

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/api/tasks_controller.rb +29 -1
  3. data/app/controllers/foreman_tasks/tasks_controller.rb +3 -11
  4. data/app/models/foreman_tasks/task.rb +4 -0
  5. data/app/models/setting/foreman_tasks.rb +1 -1
  6. data/app/services/ui_notifications/tasks/task_bulk_stop.rb +36 -0
  7. data/app/views/foreman_tasks/api/tasks/details.json.rabl +0 -1
  8. data/config/routes.rb +1 -0
  9. data/db/migrate/20200517215015_rename_bookmarks_controller.rb +35 -0
  10. data/db/migrate/20200519093217_drop_dynflow_allow_dangerous_actions_setting.foreman_tasks.rb +5 -0
  11. data/db/seeds.d/30-notification_blueprints.rb +7 -0
  12. data/lib/foreman_tasks/engine.rb +1 -1
  13. data/lib/foreman_tasks/tasks/cleanup.rake +2 -2
  14. data/lib/foreman_tasks/tasks/export_tasks.rake +1 -1
  15. data/lib/foreman_tasks/version.rb +1 -1
  16. data/locale/action_names.rb +1 -1
  17. data/locale/en/foreman_tasks.po +227 -41
  18. data/locale/foreman_tasks.pot +579 -288
  19. data/webpack/ForemanTasks/Components/TaskActions/TaskAction.test.js +60 -0
  20. data/webpack/ForemanTasks/Components/{TasksTable/TasksTableActionHelpers.js → TaskActions/TaskActionHelpers.js} +21 -6
  21. data/webpack/ForemanTasks/Components/{TasksTable/__tests__/TasksTableActionHelpers.test.js → TaskActions/TaskActionHelpers.test.js} +2 -2
  22. data/webpack/ForemanTasks/Components/TaskActions/TaskActionsConstants.js +16 -0
  23. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.js +60 -0
  24. data/webpack/ForemanTasks/Components/TaskActions/UnlockModals.test.js +14 -0
  25. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/TaskAction.test.js.snap +233 -0
  26. data/webpack/ForemanTasks/Components/TaskActions/__snapshots__/UnlockModals.test.js.snap +25 -0
  27. data/webpack/ForemanTasks/Components/TaskActions/index.js +115 -0
  28. data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +130 -152
  29. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +67 -3
  30. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +0 -1
  31. data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +101 -70
  32. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +0 -15
  33. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +0 -5
  34. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +0 -6
  35. data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +0 -9
  36. data/webpack/ForemanTasks/Components/TaskDetails/TasksDetailsHelper.js +6 -1
  37. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +5 -0
  38. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +2 -7
  39. data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +4 -0
  40. data/webpack/ForemanTasks/Components/TaskDetails/index.js +4 -8
  41. data/webpack/ForemanTasks/Components/TasksTable/Components/ActionSelectButton.js +14 -1
  42. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModal.js +83 -0
  43. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalActions.js +106 -0
  44. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalReducer.js +38 -0
  45. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/ConfirmModalSelectors.js +45 -0
  46. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModal.test.js +36 -0
  47. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalActions.test.js +205 -0
  48. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalReducer.test.js +27 -0
  49. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/ConfirmModalSelectors.test.js +54 -0
  50. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModal.test.js.snap +41 -0
  51. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalReducer.test.js.snap +19 -0
  52. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/__test__/__snapshots__/ConfirmModalSelectors.test.js.snap +30 -0
  53. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmModal/index.js +29 -0
  54. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ActionSelectButton.test.js +1 -0
  55. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ActionSelectButton.test.js.snap +11 -0
  56. data/webpack/ForemanTasks/Components/TasksTable/TasksBulkActions.js +113 -30
  57. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +13 -9
  58. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +23 -63
  59. data/webpack/ForemanTasks/Components/TasksTable/TasksTableConstants.js +10 -12
  60. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +24 -94
  61. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSchema.js +2 -2
  62. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +7 -4
  63. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksBulkActions.test.js +35 -0
  64. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTable.fixtures.js +2 -12
  65. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableActions.test.js +22 -26
  66. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/SubTasksPage.test.js.snap +2 -14
  67. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksBulkActions.test.js.snap +107 -0
  68. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksIndexPage.test.js.snap +2 -14
  69. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableActions.test.js.snap +17 -124
  70. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +24 -128
  71. data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -2
  72. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.js +47 -19
  73. data/webpack/ForemanTasks/Components/common/ActionButtons/ActionButton.test.js +61 -14
  74. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ActionButton.test.js.snap +80 -21
  75. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.scss +9 -0
  76. data/webpack/ForemanTasks/Components/common/ClickConfirmation/ClickConfirmation.test.js +44 -0
  77. data/webpack/ForemanTasks/Components/common/ClickConfirmation/__snapshots__/ClickConfirmation.test.js.snap +52 -0
  78. data/webpack/ForemanTasks/Components/common/ClickConfirmation/index.js +59 -66
  79. data/webpack/ForemanTasks/Components/common/{ToastTypesConstants.js → ToastsHelpers/ToastTypesConstants.js} +0 -0
  80. data/webpack/ForemanTasks/Components/common/ToastsHelpers/index.js +15 -0
  81. data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
  82. data/webpack/ForemanTasks/Routes/ForemanTasksRoutes.test.js +2 -1
  83. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +2 -2
  84. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +17 -3
  85. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +3 -0
  86. metadata +32 -22
  87. data/db/migrate/20200611090846_add_task_lock_index_on_resource_type_and_task_id.rb +0 -9
  88. data/lib/foreman_tasks/tasks/dynflow.rake +0 -6
  89. data/webpack/ForemanTasks/Components/TasksTable/Components/CancelConfirm.js +0 -53
  90. data/webpack/ForemanTasks/Components/TasksTable/Components/ConfirmationModals.js +0 -56
  91. data/webpack/ForemanTasks/Components/TasksTable/Components/ResumeConfirm.js +0 -52
  92. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/CancelConfirm.test.js +0 -26
  93. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ConfirmationModals.test.js +0 -24
  94. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/ResumeConfirm.test.js +0 -26
  95. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/CancelConfirm.test.js.snap +0 -65
  96. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ConfirmationModals.test.js.snap +0 -30
  97. data/webpack/ForemanTasks/Components/TasksTable/Components/__test__/__snapshots__/ResumeConfirm.test.js.snap +0 -63
  98. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.js +0 -23
  99. data/webpack/ForemanTasks/Components/common/ActionButtons/CancelButton.test.js +0 -26
  100. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.js +0 -23
  101. data/webpack/ForemanTasks/Components/common/ActionButtons/ResumeButton.test.js +0 -27
  102. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/CancelButton.test.js.snap +0 -15
  103. data/webpack/ForemanTasks/Components/common/ActionButtons/__snapshots__/ResumeButton.test.js.snap +0 -15
@@ -8,6 +8,8 @@ import Pagination from 'foremanReact/components/Pagination/PaginationWrapper';
8
8
  import { getURIQuery } from 'foremanReact/common/helpers';
9
9
  import createTasksTableSchema from './TasksTableSchema';
10
10
  import { updateURlQuery } from './TasksTableHelpers';
11
+ import { RESUME_MODAL, CANCEL_MODAL } from './TasksTableConstants';
12
+ import { FORCE_UNLOCK_MODAL } from '../TaskActions/TaskActionsConstants';
11
13
 
12
14
  const TasksTable = ({
13
15
  getTableItems,
@@ -23,7 +25,7 @@ const TasksTable = ({
23
25
  selectRow,
24
26
  unselectRow,
25
27
  openClickedModal,
26
- modalProps,
28
+ openModal,
27
29
  allRowsSelected,
28
30
  }) => {
29
31
  const { search, pathname } = history.location;
@@ -97,14 +99,21 @@ const TasksTable = ({
97
99
  openClickedModal({
98
100
  taskId,
99
101
  taskName,
100
- setModalOpen: modalProps.cancelModal.setModalOpen,
102
+ setModalOpen: () => openModal(CANCEL_MODAL),
101
103
  });
102
104
  },
103
105
  resumeTask: (taskId, taskName) => {
104
106
  openClickedModal({
105
107
  taskId,
106
108
  taskName,
107
- setModalOpen: modalProps.resumeModal.setModalOpen,
109
+ setModalOpen: () => openModal(RESUME_MODAL),
110
+ });
111
+ },
112
+ forceCancelTask: (taskId, taskName) => {
113
+ openClickedModal({
114
+ taskId,
115
+ taskName,
116
+ setModalOpen: () => openModal(FORCE_UNLOCK_MODAL),
108
117
  });
109
118
  },
110
119
  };
@@ -151,12 +160,7 @@ TasksTable.propTypes = {
151
160
  unselectAllRows: PropTypes.func.isRequired,
152
161
  selectRow: PropTypes.func.isRequired,
153
162
  unselectRow: PropTypes.func.isRequired,
154
- modalProps: PropTypes.shape({
155
- cancelSelectedModal: PropTypes.object,
156
- resumeSelectedModal: PropTypes.object,
157
- cancelModal: PropTypes.object,
158
- resumeModal: PropTypes.object,
159
- }).isRequired,
163
+ openModal: PropTypes.func.isRequired,
160
164
  allRowsSelected: PropTypes.bool,
161
165
  };
162
166
 
@@ -1,8 +1,6 @@
1
1
  import { getURIQuery } from 'foremanReact/common/helpers';
2
2
  import { getTableItemsAction } from 'foremanReact/components/common/table';
3
- import API from 'foremanReact/API';
4
- import { addToast } from 'foremanReact/redux/actions/toasts';
5
- import { sprintf } from 'foremanReact/common/I18n';
3
+
6
4
  import {
7
5
  TASKS_TABLE_ID,
8
6
  SELECT_ROWS,
@@ -11,21 +9,15 @@ import {
11
9
  UNSELECT_ROWS,
12
10
  UPDATE_CLICKED,
13
11
  OPEN_SELECT_ALL,
14
- TASKS_RESUME_REQUEST,
15
- TASKS_RESUME_SUCCESS,
16
- TASKS_RESUME_FAILURE,
17
- TASKS_CANCEL_REQUEST,
18
- TASKS_CANCEL_SUCCESS,
19
- TASKS_CANCEL_FAILURE,
12
+ UPDATE_MODAL,
20
13
  } from './TasksTableConstants';
21
- import { TOAST_TYPES } from '../common/ToastTypesConstants';
22
14
  import { getApiPathname } from './TasksTableHelpers';
23
15
  import { fetchTasksSummary } from '../TasksDashboard/TasksDashboardActions';
24
16
  import {
25
- resumeToastInfo,
26
- cancelToastInfo,
27
- toastDispatch,
28
- } from './TasksTableActionHelpers';
17
+ cancelTaskRequest,
18
+ resumeTaskRequest,
19
+ forceCancelTaskRequest,
20
+ } from '../TaskActions';
29
21
 
30
22
  export const getTableItems = url =>
31
23
  getTableItemsAction(TASKS_TABLE_ID, getURIQuery(url), getApiPathname(url));
@@ -45,34 +37,6 @@ export const cancelTask = ({
45
37
  reloadPage(url, parentTaskID, dispatch);
46
38
  };
47
39
 
48
- export const cancelTaskRequest = (id, name) => async dispatch => {
49
- dispatch(
50
- addToast({
51
- type: TOAST_TYPES.INFO,
52
- message: sprintf('Trying to cancel %s task', name),
53
- })
54
- );
55
- dispatch({ type: TASKS_CANCEL_REQUEST });
56
- try {
57
- await API.post(`/foreman_tasks/tasks/${id}/cancel`);
58
- dispatch({ type: TASKS_CANCEL_SUCCESS });
59
- toastDispatch({
60
- type: 'cancelled',
61
- name,
62
- toastInfo: cancelToastInfo,
63
- dispatch,
64
- });
65
- } catch ({ response }) {
66
- dispatch({ type: TASKS_CANCEL_FAILURE, payload: response });
67
- toastDispatch({
68
- type: 'skipped',
69
- name,
70
- toastInfo: cancelToastInfo,
71
- dispatch,
72
- });
73
- }
74
- };
75
-
76
40
  export const resumeTask = ({
77
41
  taskId,
78
42
  taskName,
@@ -83,27 +47,15 @@ export const resumeTask = ({
83
47
  reloadPage(url, parentTaskID, dispatch);
84
48
  };
85
49
 
86
- export const resumeTaskRequest = (id, name) => async dispatch => {
87
- dispatch({ type: TASKS_RESUME_REQUEST });
88
- try {
89
- await API.post(`/foreman_tasks/tasks/${id}/resume`);
90
-
91
- dispatch({ type: TASKS_RESUME_SUCCESS });
92
- toastDispatch({
93
- type: 'resumed',
94
- name,
95
- toastInfo: resumeToastInfo,
96
- dispatch,
97
- });
98
- } catch ({ response }) {
99
- dispatch({ type: TASKS_RESUME_FAILURE, payload: response });
100
- toastDispatch({
101
- type: 'failed',
102
- name,
103
- toastInfo: resumeToastInfo,
104
- dispatch,
105
- });
106
- }
50
+ export const forceCancelTask = ({
51
+ taskId,
52
+ taskName,
53
+ url,
54
+ parentTaskID,
55
+ }) => async dispatch => {
56
+ await dispatch(forceCancelTaskRequest(taskId, taskName));
57
+ dispatch(getTableItems(url));
58
+ dispatch(fetchTasksSummary(getURIQuery(url).time, parentTaskID));
107
59
  };
108
60
 
109
61
  export const selectPage = results => dispatch => {
@@ -141,3 +93,11 @@ export const openClickedModal = ({ taskId, taskName, setModalOpen }) => {
141
93
  payload: { clicked: { taskId, taskName } },
142
94
  };
143
95
  };
96
+
97
+ export const openModalAction = (modalID, setModalOpen) => {
98
+ setModalOpen();
99
+ return {
100
+ type: UPDATE_MODAL,
101
+ payload: { modalID },
102
+ };
103
+ };
@@ -8,23 +8,21 @@ export const UNSELECT_ALL_ROWS = 'UNSELECT_ALL_ROWS';
8
8
  export const SELECT_ALL_ROWS = 'SELECT_ALL_ROWS';
9
9
  export const OPEN_SELECT_ALL = 'OPEN_SELECT_ALL';
10
10
 
11
- export const TASKS_RESUME_REQUEST = 'TASKS_RESUME_REQUEST';
12
- export const TASKS_RESUME_SUCCESS = 'TASKS_RESUME_SUCCESS';
13
- export const TASKS_RESUME_FAILURE = 'TASKS_RESUME_FAILURE';
14
- export const TASKS_CANCEL_REQUEST = 'TASKS_CANCEL_REQUEST';
15
- export const TASKS_CANCEL_SUCCESS = 'TASKS_CANCEL_SUCCESS';
16
- export const TASKS_CANCEL_FAILURE = 'TASKS_CANCEL_FAILURE';
17
-
18
11
  export const BULK_CANCEL_PATH = 'bulk_cancel';
19
12
  export const BULK_RESUME_PATH = 'bulk_resume';
13
+ export const BULK_FORCE_CANCEL_PATH = 'bulk_stop';
14
+
15
+ export const CANCEL_MODAL = 'cancelConfirmModal';
16
+ export const RESUME_MODAL = 'resumeConfirmModal';
17
+ export const CANCEL_SELECTED_MODAL = 'cancelSelectedConfirmModal';
18
+ export const RESUME_SELECTED_MODAL = 'resumeSelectedConfirmModal';
19
+ export const CONFIRM_MODAL = 'ConfirmModal';
20
+ export const FORCE_UNLOCK_SELECTED_MODAL = 'forceUnlockSelectedConfirmModal';
20
21
 
21
- export const CANCEL_CONFIRM_MODAL_ID = 'cancelConfirmModal';
22
- export const RESUME_CONFIRM_MODAL_ID = 'resumeConfirmModal';
23
- export const CANCEL_SELECTED_CONFIRM_MODAL_ID = 'cancelSelectedConfirmModal';
24
- export const RESUME_SELECTED_CONFIRM_MODAL_ID = 'resumeSelectedConfirmModal';
25
22
  export const UPDATE_CLICKED = 'UPDATE_CLICKED';
23
+ export const UPDATE_MODAL = 'UPDATE_MODAL';
26
24
 
27
25
  export const TASKS_SEARCH_PROPS = {
28
26
  ...getControllerSearchProps('tasks'),
29
- controller: 'foreman_tasks/tasks',
27
+ controller: 'foreman_tasks_tasks',
30
28
  };
@@ -11,13 +11,13 @@ import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanMod
11
11
  import TasksDashboard from '../TasksDashboard';
12
12
  import TasksTable from './TasksTable';
13
13
  import { resolveSearchQuery, getCSVurl } from './TasksTableHelpers';
14
- import { ConfirmationModals } from './Components/ConfirmationModals';
14
+ import ConfirmModal from './Components/ConfirmModal/';
15
15
  import {
16
16
  TASKS_SEARCH_PROPS,
17
- CANCEL_SELECTED_CONFIRM_MODAL_ID,
18
- RESUME_SELECTED_CONFIRM_MODAL_ID,
19
- RESUME_CONFIRM_MODAL_ID,
20
- CANCEL_CONFIRM_MODAL_ID,
17
+ CANCEL_SELECTED_MODAL,
18
+ RESUME_SELECTED_MODAL,
19
+ FORCE_UNLOCK_SELECTED_MODAL,
20
+ CONFIRM_MODAL,
21
21
  } from './TasksTableConstants';
22
22
  import { ActionSelectButton } from './Components/ActionSelectButton';
23
23
  import './TasksTablePage.scss';
@@ -26,10 +26,11 @@ import { SelectAllAlert } from './Components/SelectAllAlert';
26
26
  const TasksTablePage = ({
27
27
  getBreadcrumbs,
28
28
  history,
29
- clicked,
30
29
  createHeader,
31
30
  selectAllRows,
32
31
  showSelectAll,
32
+ modalID,
33
+ openModalAction,
33
34
  ...props
34
35
  }) => {
35
36
  const url = history.location.pathname + history.location.search;
@@ -38,83 +39,20 @@ const TasksTablePage = ({
38
39
  resolveSearchQuery(searchQuery, history);
39
40
  };
40
41
 
41
- const getSelectedTasks = () => {
42
- const selectedIDs = props.results.filter(item =>
43
- props.selectedRows.includes(item.id)
44
- );
45
- return selectedIDs.map(item => ({
46
- name: item.action,
47
- id: item.id,
48
- isCancellable: item.availableActions.cancellable,
49
- isResumable: item.availableActions.resumable,
50
- }));
51
- };
52
-
53
- const modalProps = {
54
- cancelSelectedModal: useForemanModal({
55
- id: CANCEL_SELECTED_CONFIRM_MODAL_ID,
56
- }),
57
- resumeSelectedModal: useForemanModal({
58
- id: RESUME_SELECTED_CONFIRM_MODAL_ID,
59
- }),
60
- cancelModal: useForemanModal({ id: CANCEL_CONFIRM_MODAL_ID }),
61
- resumeModal: useForemanModal({ id: RESUME_CONFIRM_MODAL_ID }),
62
- };
42
+ const { setModalOpen, setModalClosed } = useForemanModal({
43
+ id: CONFIRM_MODAL,
44
+ });
63
45
 
64
- const {
65
- bulkCancelById,
66
- bulkCancelBySearch,
67
- bulkResumeById,
68
- bulkResumeBySearch,
69
- cancelTask,
70
- resumeTask,
71
- parentTaskID,
72
- } = props;
73
- const tasksActions = {
74
- cancelSelectedTasks: () => {
75
- props.allRowsSelected
76
- ? bulkCancelBySearch({ query: uriQuery, parentTaskID })
77
- : bulkCancelById({
78
- selected: getSelectedTasks(),
79
- url,
80
- parentTaskID,
81
- });
82
- },
83
- cancelTask: () => {
84
- cancelTask({
85
- taskId: clicked.taskId,
86
- taskName: clicked.taskName,
87
- url,
88
- parentTaskID,
89
- });
90
- },
91
- resumeSelectedTasks: () => {
92
- props.allRowsSelected
93
- ? bulkResumeBySearch({ query: uriQuery, parentTaskID })
94
- : bulkResumeById({
95
- selected: getSelectedTasks(),
96
- url,
97
- parentTaskID,
98
- });
99
- },
100
- resumeTask: () => {
101
- resumeTask({
102
- taskId: clicked.taskId,
103
- taskName: clicked.taskName,
104
- url,
105
- parentTaskID,
106
- });
107
- },
108
- };
46
+ const openModal = id => openModalAction(id, setModalOpen);
109
47
 
110
48
  return (
111
49
  <div className="tasks-table-wrapper">
112
- <ConfirmationModals
113
- tasksActions={tasksActions}
114
- selectedRowsLen={
115
- props.allRowsSelected ? props.itemCount : props.selectedRows.length
116
- }
117
- modalProps={modalProps}
50
+ <ConfirmModal
51
+ id={CONFIRM_MODAL}
52
+ url={url}
53
+ parentTaskID={props.parentTaskID}
54
+ uriQuery={uriQuery}
55
+ setModalClosed={setModalClosed}
118
56
  />
119
57
  <PageLayout
120
58
  searchable
@@ -132,8 +70,9 @@ const TasksTablePage = ({
132
70
  />
133
71
  <ActionSelectButton
134
72
  disabled={!(props.selectedRows.length || props.allRowsSelected)}
135
- onCancel={modalProps.cancelSelectedModal.setModalOpen}
136
- onResume={modalProps.resumeSelectedModal.setModalOpen}
73
+ onCancel={() => openModal(CANCEL_SELECTED_MODAL)}
74
+ onResume={() => openModal(RESUME_SELECTED_MODAL)}
75
+ onForceCancel={() => openModal(FORCE_UNLOCK_SELECTED_MODAL)}
137
76
  />
138
77
  </React.Fragment>
139
78
  }
@@ -152,7 +91,7 @@ const TasksTablePage = ({
152
91
  allRowsSelected={props.allRowsSelected}
153
92
  />
154
93
  )}
155
- <TasksTable history={history} {...props} modalProps={modalProps} />
94
+ <TasksTable history={history} {...props} openModal={openModal} />
156
95
  </React.Fragment>
157
96
  </PageLayout>
158
97
  </div>
@@ -172,20 +111,11 @@ TasksTablePage.propTypes = {
172
111
  actionName: PropTypes.string,
173
112
  status: PropTypes.oneOf(Object.keys(STATUS)),
174
113
  history: PropTypes.object.isRequired,
175
- cancelTask: PropTypes.func.isRequired,
176
- resumeTask: PropTypes.func.isRequired,
177
- bulkCancelById: PropTypes.func.isRequired,
178
- bulkCancelBySearch: PropTypes.func.isRequired,
179
- bulkResumeById: PropTypes.func.isRequired,
180
- bulkResumeBySearch: PropTypes.func.isRequired,
181
114
  selectedRows: PropTypes.arrayOf(PropTypes.string),
182
115
  parentTaskID: PropTypes.string,
183
116
  createHeader: PropTypes.func,
184
- clicked: PropTypes.shape({
185
- taskId: PropTypes.string,
186
- taskName: PropTypes.string,
187
- parentTaskID: PropTypes.string,
188
- }),
117
+ modalID: PropTypes.string,
118
+ openModalAction: PropTypes.func.isRequired,
189
119
  showSelectAll: PropTypes.bool,
190
120
  unselectAllRows: PropTypes.func.isRequired,
191
121
  };
@@ -200,9 +130,9 @@ TasksTablePage.defaultProps = {
200
130
  status: STATUS.PENDING,
201
131
  selectedRows: [],
202
132
  parentTaskID: null,
203
- clicked: {},
204
133
  createHeader: () => __('Tasks'),
205
134
  showSelectAll: false,
135
+ modalID: '',
206
136
  };
207
137
 
208
138
  export default TasksTablePage;
@@ -67,7 +67,7 @@ const createTasksTableSchema = (
67
67
  sortableColumn('started_at', __('Started at'), 3, sortController, [
68
68
  dateCellFormmatter,
69
69
  ]),
70
- sortableColumn('duration', __('Duration'), 3, sortController, [
70
+ sortableColumn('duration', __('Duration'), 2, sortController, [
71
71
  durationCellFormmatter,
72
72
  ]),
73
73
  column(
@@ -76,7 +76,7 @@ const createTasksTableSchema = (
76
76
  headFormat,
77
77
  [actionCellFormatter(taskActions)],
78
78
  {
79
- className: 'col-md-1',
79
+ className: 'col-md-2',
80
80
  }
81
81
  ),
82
82
  ];
@@ -24,9 +24,6 @@ export const selectActionName = state =>
24
24
  export const selectSelectedRows = state =>
25
25
  selectTasksTableQuery(state).selectedRows || [];
26
26
 
27
- export const selectClicked = state =>
28
- selectTasksTableQuery(state).clicked || {};
29
-
30
27
  export const selectResults = createSelector(
31
28
  selectTasksTableContent,
32
29
  ({ results }) =>
@@ -38,7 +35,10 @@ export const selectResults = createSelector(
38
35
  username: result.username || '',
39
36
  state: result.state + (result.frozen ? ` ${__('Disabled')}` : ''),
40
37
  duration: getDuration(result.started_at, result.ended_at),
41
- availableActions: result.available_actions,
38
+ availableActions: {
39
+ ...result.available_actions,
40
+ stoppable: result.state !== 'stopped',
41
+ },
42
42
  }))
43
43
  );
44
44
 
@@ -54,3 +54,6 @@ export const selectAllRowsSelected = state =>
54
54
 
55
55
  export const selectShowSelectAll = state =>
56
56
  selectTasksTableQuery(state).showSelectAll;
57
+
58
+ export const selectModalID = state =>
59
+ selectTasksTableQuery(state).modalID || '';
@@ -5,6 +5,8 @@ import {
5
5
  bulkCancelBySearch,
6
6
  bulkResumeById,
7
7
  bulkResumeBySearch,
8
+ bulkForceCancelById,
9
+ bulkForceCancelBySearch,
8
10
  } from '../TasksBulkActions';
9
11
 
10
12
  jest.mock('foremanReact/components/common/table', () => ({
@@ -68,6 +70,28 @@ const fixtures = {
68
70
  }));
69
71
  return bulkCancelById({ selected, url: 'some-url' });
70
72
  },
73
+
74
+ 'handles bulkForceCancelById requests': () => {
75
+ const selected = [{ ...task, isCancellable: true }];
76
+
77
+ API.post.mockImplementation(() => ({
78
+ data: {
79
+ stopped_length: 2,
80
+ skipped_length: 4,
81
+ },
82
+ }));
83
+ return bulkForceCancelById({ selected, url: 'some-url' });
84
+ },
85
+
86
+ 'handles bulkForceCancelById requests that fail': () => {
87
+ const selected = [{ ...task, isResumable: true }];
88
+
89
+ API.post.mockImplementation(() =>
90
+ Promise.reject(new Error('Network Error'))
91
+ );
92
+ return bulkForceCancelById({ selected, url: 'some-url' });
93
+ },
94
+
71
95
  'handles bulkCancelById requests that are not cancellable': () => {
72
96
  const selected = [{ ...task, isCancellable: false }];
73
97
  return bulkCancelById({ selected, url: 'some-url' });
@@ -77,6 +101,11 @@ const fixtures = {
77
101
  return bulkResumeById({ selected, url: 'some-url' });
78
102
  },
79
103
 
104
+ 'handles bulkForceCancelById requests that are stopped': () => {
105
+ const selected = [{ ...task, isResumable: false, state: 'stopped' }];
106
+ return bulkForceCancelById({ selected, url: 'some-url' });
107
+ },
108
+
80
109
  'handles bulkCancelBySearch requests': () => {
81
110
  API.post.mockImplementation(() => ({
82
111
  data: {
@@ -105,6 +134,12 @@ const fixtures = {
105
134
  parentTaskID: 'parent',
106
135
  });
107
136
  },
137
+ 'handles bulkForceCancelBySearch requests': () =>
138
+ bulkForceCancelBySearch({
139
+ query: { search: {} },
140
+ url: 'some-url',
141
+ parentTaskID: 'parent',
142
+ }),
108
143
  };
109
144
 
110
145
  describe('TasksTable bulk actions', () => {