foreman-tasks 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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', () => {