foreman_webhooks 5.0.3 → 5.1.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -15
  3. data/app/models/webhook.rb +1 -1
  4. data/lib/foreman_webhooks/version.rb +1 -1
  5. data/webpack/ForemanWebhooks/Routes/ForemanWebhooksRoutes.js +1 -1
  6. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/actionCellFormatter.test.js +109 -0
  7. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/enabledCellFormatter.test.js +38 -0
  8. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/__tests__/nameToEditFormatter.test.js +82 -0
  9. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/actionCellFormatter.js +23 -0
  10. data/webpack/ForemanWebhooks/Routes/Webhooks/{WebhooksIndexPage/Components/WebhooksTable/Components/EnabledCell.js → Components/WebhookTable/Components/Formatters/enabledCellFormatter.js} +6 -1
  11. data/webpack/ForemanWebhooks/Routes/Webhooks/Components/WebhookTable/Components/Formatters/nameToEditFormatter.js +33 -0
  12. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookCreateModal.js +1 -0
  13. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookDeleteModal.js +5 -1
  14. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhookEditModal.js +1 -0
  15. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/WebhooksIndexPage.js +120 -16
  16. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/WebhooksIndexPage.test.js +130 -22
  17. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/integration.test.js +10 -3
  18. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/mocks/MockForemanTableIndexPage.js +37 -0
  19. data/webpack/ForemanWebhooks/Routes/Webhooks/constants.js +3 -3
  20. data/webpack/test_setup.js +3 -0
  21. metadata +18 -93
  22. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/EmptyWebhooksTable/index.js +0 -29
  23. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/__snapshots__/enabledCellFormatter.test.js.snap +0 -7
  24. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/__tests__/enabledCellFormatter.test.js +0 -7
  25. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/actionCellFormatter.js +0 -20
  26. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/enabledCellFormatter.js +0 -6
  27. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/index.js +0 -3
  28. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/Formatters/nameToEditFormatter.js +0 -19
  29. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/NameToEditCell.js +0 -42
  30. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/__tests__/EnabledCell.test.js +0 -14
  31. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/Components/__tests__/__snapshots__/EnabledCell.test.js.snap +0 -5
  32. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTable.js +0 -121
  33. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/WebhooksTableSchema.js +0 -48
  34. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/WebhooksTable.test.js +0 -64
  35. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/__tests__/__snapshots__/WebhooksTable.test.js.snap +0 -250
  36. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/Components/WebhooksTable/index.js +0 -76
  37. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/__tests__/__snapshots__/WebhooksIndexPage.test.js.snap +0 -17
  38. data/webpack/ForemanWebhooks/Routes/Webhooks/WebhooksIndexPage/index.js +0 -3
  39. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +0 -1
  40. data/webpack/__mocks__/foremanReact/common/HOC.js +0 -30
  41. data/webpack/__mocks__/foremanReact/common/I18n.js +0 -7
  42. data/webpack/__mocks__/foremanReact/common/helpers.js +0 -7
  43. data/webpack/__mocks__/foremanReact/common/urlHelpers.js +0 -8
  44. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalActions.js +0 -2
  45. data/webpack/__mocks__/foremanReact/components/ForemanModal/ForemanModalHooks.js +0 -10
  46. data/webpack/__mocks__/foremanReact/components/ForemanModal/index.js +0 -23
  47. data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +0 -2
  48. data/webpack/__mocks__/foremanReact/components/Loading/Loading.js +0 -2
  49. data/webpack/__mocks__/foremanReact/components/Loading/index.js +0 -3
  50. data/webpack/__mocks__/foremanReact/components/PF4/TableIndexPage/TableIndexPage.js +0 -10
  51. data/webpack/__mocks__/foremanReact/components/Pagination/index.js +0 -4
  52. data/webpack/__mocks__/foremanReact/components/common/ActionButtons/ActionButtons.js +0 -3
  53. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
  54. data/webpack/__mocks__/foremanReact/components/common/forms/ForemanForm.js +0 -9
  55. data/webpack/__mocks__/foremanReact/components/common/forms/FormField.js +0 -3
  56. data/webpack/__mocks__/foremanReact/components/common/table.js +0 -26
  57. data/webpack/__mocks__/foremanReact/constants.js +0 -24
  58. data/webpack/__mocks__/foremanReact/history.js +0 -3
  59. data/webpack/__mocks__/foremanReact/redux/API/APISelectors.js +0 -6
  60. data/webpack/__mocks__/foremanReact/redux/API/index.js +0 -10
  61. data/webpack/__mocks__/foremanReact/redux/actions/common/forms.js +0 -1
  62. data/webpack/__mocks__/foremanReact/redux/actions/toasts.js +0 -8
  63. data/webpack/__mocks__/foremanReact/routes/common/PageLayout/PageLayout.js +0 -10
  64. /data/webpack/ForemanWebhooks/Routes/Webhooks/{WebhooksIndexPage/Components/WebhooksTable → Components/WebhookTable}/Components/ActionButtons/ActionButton.js +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25c2a83a7c2015b3e3d0b184beb4ce534c27bbe90cbd9fb301d138d7e183e5c5
4
- data.tar.gz: b0a16fa70ffde8610190a3c0718046d4c0dedf25633a21bfb5ba341efde778ce
3
+ metadata.gz: c90b385f92db59657a6f026239c2ccfda1b574c852563755fd4dab6522cbf84c
4
+ data.tar.gz: 05324c9d1589fed4e35b0bf477d0edc7380b5e1abe391872d27a18457355f6f9
5
5
  SHA512:
6
- metadata.gz: ce79fa03fd4dccce1a495510bbeded83546b3330dde622fa5b4d03ef61d8eb1f0d752c72257ab36f6fcc28cf7b1c8164fb500c764914d6f06d0712ce756c38cb
7
- data.tar.gz: 5a949f01600d04a667e710107e2275f58e104ced6a5317c90a21285851cb7a9e39a85770cdd34b9c6e4bed029fe9d705b4bfd31e1cd9680b1eceac1456bb6488
6
+ metadata.gz: d8956d6c344db760a44ee50c9699d701078a5cae604dc561a40d55ecb25717d03129dceed5bb770c337cd9b1bc28455b0fff5aa80431e2b6834a368476f358ad
7
+ data.tar.gz: 5d47beee070a29ac44960ea8c8bbe81e19179be5d806f062809a804457fc2929ea63dab6c6569b89d8ebf0d22a0af7c8a14dee2e1c53b15f1a3e6889eb485755
data/Rakefile CHANGED
@@ -6,21 +6,6 @@ begin
6
6
  rescue LoadError
7
7
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
8
8
  end
9
- begin
10
- require 'rdoc/task'
11
- rescue LoadError
12
- require 'rdoc/rdoc'
13
- require 'rake/rdoctask'
14
- RDoc::Task = Rake::RDocTask
15
- end
16
-
17
- RDoc::Task.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'ForemanWebhooks'
20
- rdoc.options << '--line-numbers'
21
- rdoc.rdoc_files.include('README.rdoc')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
23
- end
24
9
 
25
10
  APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
26
11
 
@@ -11,7 +11,7 @@ class Webhook < ApplicationRecord
11
11
 
12
12
  graphql_type 'ForemanWebhooks::Types::Webhook'
13
13
 
14
- EVENT_POSTFIX = ".#{Foreman::Observable::DEFAULT_NAMESPACE}"
14
+ EVENT_POSTFIX = ".#{Foreman::Observable::DEFAULT_NAMESPACE}".freeze
15
15
 
16
16
  DEFAULT_PAYLOAD_TEMPLATE = 'Webhook Template - Payload Default'
17
17
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanWebhooks
4
- VERSION = "5.0.3"
4
+ VERSION = "5.1.0"
5
5
  end
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import ConnectedWebhooksIndexPage from './Webhooks/WebhooksIndexPage';
2
+ import ConnectedWebhooksIndexPage from './Webhooks/WebhooksIndexPage/WebhooksIndexPage';
3
3
 
4
4
  const ForemanWebhooksRoutes = [
5
5
  {
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+
5
+ import actionCellFormatter from '../actionCellFormatter';
6
+
7
+ jest.mock(
8
+ 'foremanReact/components/common/table',
9
+ () => ({
10
+ cellFormatter: content => (
11
+ <div data-testid="cell-formatter-mock">
12
+ {content === false ? null : content}
13
+ </div>
14
+ ),
15
+ }),
16
+ { virtual: true }
17
+ );
18
+
19
+ jest.mock('../../ActionButtons/ActionButton', () => {
20
+ // eslint-disable-next-line global-require
21
+ const PropTypes = require('prop-types');
22
+
23
+ const ActionButton = ({ id, name, canDelete }) => (
24
+ <span
25
+ data-testid="action-button"
26
+ data-id={id}
27
+ data-name={name}
28
+ data-can-delete={String(canDelete)}
29
+ />
30
+ );
31
+ ActionButton.propTypes = {
32
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
33
+ name: PropTypes.string.isRequired,
34
+ canDelete: PropTypes.bool,
35
+ };
36
+ ActionButton.defaultProps = {
37
+ canDelete: false,
38
+ };
39
+
40
+ return { ActionButton };
41
+ });
42
+
43
+ describe('actionCellFormatter', () => {
44
+ const webhookActions = {
45
+ deleteWebhook: jest.fn(),
46
+ testWebhook: jest.fn(),
47
+ };
48
+
49
+ beforeEach(() => {
50
+ jest.clearAllMocks();
51
+ });
52
+
53
+ it('wraps the action control in cellFormatter when rowData is present and the row is editable', () => {
54
+ const formatter = actionCellFormatter(webhookActions);
55
+ const row = { id: 10, name: 'Wh', can_edit: true, can_delete: true };
56
+
57
+ render(formatter(null, { rowData: row }));
58
+
59
+ expect(screen.getByTestId('cell-formatter-mock')).toBeInTheDocument();
60
+ const action = screen.getByTestId('action-button');
61
+ expect(action).toHaveAttribute('data-id', '10');
62
+ expect(action).toHaveAttribute('data-name', 'Wh');
63
+ expect(action).toHaveAttribute('data-can-delete', 'true');
64
+ });
65
+
66
+ it('passes canDelete from can_delete when rowData is present', () => {
67
+ const formatter = actionCellFormatter(webhookActions);
68
+ const row = { id: 2, name: 'A', can_edit: true, can_delete: false };
69
+
70
+ render(formatter(null, { rowData: row }));
71
+
72
+ expect(screen.getByTestId('action-button')).toHaveAttribute(
73
+ 'data-can-delete',
74
+ 'false'
75
+ );
76
+ });
77
+
78
+ it('renders falsy content through cellFormatter when rowData is present but the row is not editable', () => {
79
+ const formatter = actionCellFormatter(webhookActions);
80
+ const row = { id: 5, name: 'B', can_edit: false };
81
+
82
+ render(formatter(null, { rowData: row }));
83
+
84
+ expect(screen.getByTestId('cell-formatter-mock')).toBeInTheDocument();
85
+ expect(screen.queryByTestId('action-button')).not.toBeInTheDocument();
86
+ });
87
+
88
+ it('returns the action control without cellFormatter when rowData is omitted', () => {
89
+ const formatter = actionCellFormatter(webhookActions);
90
+ const row = { id: 20, name: 'Direct', can_edit: true, canDelete: true };
91
+
92
+ render(formatter(row));
93
+
94
+ expect(screen.queryByTestId('cell-formatter-mock')).not.toBeInTheDocument();
95
+ expect(screen.getByTestId('action-button')).toHaveAttribute(
96
+ 'data-id',
97
+ '20'
98
+ );
99
+ });
100
+
101
+ it('returns nothing when rowData is omitted and the row is not editable', () => {
102
+ const formatter = actionCellFormatter(webhookActions);
103
+ const row = { id: 1, name: 'X', can_edit: false };
104
+
105
+ const { container } = render(formatter(row));
106
+
107
+ expect(container).toBeEmptyDOMElement();
108
+ });
109
+ });
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+
5
+ import enabledCellFormatter from '../enabledCellFormatter';
6
+
7
+ jest.mock('@patternfly/react-icons', () => ({
8
+ CheckIcon: () => <span data-testid="check-icon" />,
9
+ BanIcon: () => <span data-testid="ban-icon" />,
10
+ }));
11
+
12
+ const renderCell = (value, extra) => {
13
+ const formatter = enabledCellFormatter();
14
+ const element = formatter(value, extra);
15
+ return render(element);
16
+ };
17
+
18
+ describe('enabledCellFormatter', () => {
19
+ it('uses the cell value when row context includes rowData and value is true', () => {
20
+ renderCell(true, { rowData: { id: 1, enabled: false } });
21
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
22
+ });
23
+
24
+ it('uses the cell value when row context includes rowData and value is false', () => {
25
+ renderCell(false, { rowData: { id: 1, enabled: true } });
26
+ expect(screen.getByTestId('ban-icon')).toBeInTheDocument();
27
+ });
28
+
29
+ it('uses value.enabled when rowData is not present and enabled is true', () => {
30
+ renderCell({ enabled: true });
31
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
32
+ });
33
+
34
+ it('uses value.enabled when rowData is not present and enabled is false', () => {
35
+ renderCell({ enabled: false });
36
+ expect(screen.getByTestId('ban-icon')).toBeInTheDocument();
37
+ });
38
+ });
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+
5
+ import nameToEditFormatter from '../nameToEditFormatter';
6
+
7
+ jest.mock('@patternfly/react-core', () => {
8
+ // eslint-disable-next-line global-require
9
+ const PropTypes = require('prop-types');
10
+
11
+ const Button = ({ children, onClick, isDisabled }) => (
12
+ <button type="button" onClick={onClick} disabled={isDisabled}>
13
+ {children}
14
+ </button>
15
+ );
16
+ Button.propTypes = {
17
+ children: PropTypes.node,
18
+ onClick: PropTypes.func,
19
+ isDisabled: PropTypes.bool,
20
+ };
21
+ Button.defaultProps = {
22
+ children: null,
23
+ onClick: () => {},
24
+ isDisabled: false,
25
+ };
26
+
27
+ return { Button };
28
+ });
29
+
30
+ describe('nameToEditFormatter', () => {
31
+ it('renders the cell value as the label and calls the handler with the row id when can_edit is true', () => {
32
+ const onEdit = jest.fn();
33
+ const formatter = nameToEditFormatter(onEdit);
34
+ const row = { id: 42, name: 'Other', can_edit: true };
35
+
36
+ render(formatter('Shown name', { rowData: row }));
37
+
38
+ expect(screen.getByText('Shown name')).toBeInTheDocument();
39
+ fireEvent.click(screen.getByText('Shown name'));
40
+ expect(onEdit).toHaveBeenCalledTimes(1);
41
+ expect(onEdit).toHaveBeenCalledWith(42);
42
+ });
43
+
44
+ it('prefers canEdit over can_edit when both are set', () => {
45
+ const onEdit = jest.fn();
46
+ const formatter = nameToEditFormatter(onEdit);
47
+
48
+ render(
49
+ formatter('Label', {
50
+ rowData: { id: 1, name: 'N', canEdit: true, can_edit: false },
51
+ })
52
+ );
53
+
54
+ fireEvent.click(screen.getByText('Label'));
55
+ expect(onEdit).toHaveBeenCalledWith(1);
56
+ });
57
+
58
+ it('does not call the handler when the row cannot be edited', () => {
59
+ const onEdit = jest.fn();
60
+ const formatter = nameToEditFormatter(onEdit);
61
+
62
+ render(
63
+ formatter('Read only', {
64
+ rowData: { id: 7, name: 'X', can_edit: false },
65
+ })
66
+ );
67
+
68
+ const control = screen.getByText('Read only');
69
+ expect(control.closest('button')).toBeDisabled();
70
+ fireEvent.click(control);
71
+ expect(onEdit).not.toHaveBeenCalled();
72
+ });
73
+
74
+ it('uses row.name as the label when rowData is not passed and value is the row object', () => {
75
+ const formatter = nameToEditFormatter(jest.fn());
76
+ const row = { id: 3, name: 'From row', can_edit: false };
77
+
78
+ render(formatter(row));
79
+
80
+ expect(screen.getByText('From row')).toBeInTheDocument();
81
+ });
82
+ });
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { cellFormatter } from 'foremanReact/components/common/table';
3
+ import { ActionButton } from '../ActionButtons/ActionButton';
4
+
5
+ const actionCellFormatter = webhookActions => (value, extra) => {
6
+ const row = extra?.rowData ?? value;
7
+ const canEdit = row.canEdit ?? row.can_edit;
8
+ const canDelete = row.canDelete ?? row.can_delete;
9
+ const { id, name } = row;
10
+
11
+ const content = canEdit && (
12
+ <ActionButton
13
+ canDelete={canDelete}
14
+ id={id}
15
+ name={name}
16
+ webhookActions={webhookActions}
17
+ />
18
+ );
19
+
20
+ return extra?.rowData != null ? cellFormatter(content) : content;
21
+ };
22
+
23
+ export default actionCellFormatter;
@@ -13,4 +13,9 @@ EnabledCell.defaultProps = {
13
13
  condition: false,
14
14
  };
15
15
 
16
- export default EnabledCell;
16
+ const enabledCellFormatter = () => (value, extra) => {
17
+ const condition = extra?.rowData != null ? value : value?.enabled;
18
+ return <EnabledCell condition={Boolean(condition)} />;
19
+ };
20
+
21
+ export default enabledCellFormatter;
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { Button } from '@patternfly/react-core';
3
+
4
+ const nameToEditFormatter = onClick => (value, extra) => {
5
+ const row = extra?.rowData ?? value;
6
+ const canEdit = row.canEdit ?? row.can_edit;
7
+ const { id } = row;
8
+ const label = extra?.rowData != null ? value : row.name;
9
+
10
+ return canEdit ? (
11
+ <Button
12
+ ouiaId="name-edit-active-button"
13
+ variant="link"
14
+ isInline
15
+ component="span"
16
+ onClick={() => onClick(id)}
17
+ >
18
+ {label}
19
+ </Button>
20
+ ) : (
21
+ <Button
22
+ ouiaId="name-edit-disabled-button"
23
+ variant="link"
24
+ isInline
25
+ isDisabled
26
+ component="span"
27
+ >
28
+ {label}
29
+ </Button>
30
+ );
31
+ };
32
+
33
+ export default nameToEditFormatter;
@@ -42,6 +42,7 @@ const WebhookCreateModal = ({ onSuccess, onCancel, isOpen }) => {
42
42
  params: { ...values, controller: 'webhooks' },
43
43
  successToast: () => __('Webhook was successfully created.'),
44
44
  handleSuccess: () => {
45
+ setIsSubmitting(false);
45
46
  onSuccess();
46
47
  },
47
48
  handleError: () => setIsSubmitting(false),
@@ -14,6 +14,7 @@ const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
14
14
 
15
15
  const [isSubmitting, setIsSubmitting] = useState(false);
16
16
  const dispatch = useDispatch();
17
+
17
18
  const handleSubmit = () => {
18
19
  setIsSubmitting(true);
19
20
  dispatch(
@@ -25,7 +26,10 @@ const WebhookDeleteModal = ({ toDelete, onSuccess, modalState }) => {
25
26
  errorToast: response =>
26
27
  // eslint-disable-next-line camelcase
27
28
  response?.response?.data?.error?.full_messages?.[0] || response,
28
- handleSuccess: onSuccess,
29
+ handleSuccess: () => {
30
+ setIsSubmitting(false);
31
+ onSuccess();
32
+ },
29
33
  handleError: () => setIsSubmitting(false),
30
34
  })
31
35
  );
@@ -69,6 +69,7 @@ const WebhookEditModal = ({ toEdit, onSuccess, modalState }) => {
69
69
  params: { ...values, controller: 'webhooks' },
70
70
  successToast: () => __('Webhook was successfully updated.'),
71
71
  handleSuccess: () => {
72
+ setIsSubmitting(false);
72
73
  onSuccess();
73
74
  },
74
75
  handleError: () => setIsSubmitting(false),
@@ -4,12 +4,25 @@ import { useSelector, useDispatch } from 'react-redux';
4
4
  import TableIndexPage from 'foremanReact/components/PF4/TableIndexPage/TableIndexPage';
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
6
 
7
- import { WEBHOOKS_API_PATH, WEBHOOKS_API_REQUEST_KEY } from '../constants';
7
+ import {
8
+ WEBHOOKS_API_REQUEST_KEY,
9
+ WEBHOOKS_API_PLAIN_PATH,
10
+ } from '../constants';
8
11
 
9
- import { selectSearch } from '../WebhooksPageSelectors';
12
+ import {
13
+ selectPage,
14
+ selectPerPage,
15
+ selectSearch,
16
+ selectSubtotal,
17
+ } from '../WebhooksPageSelectors';
10
18
 
11
- import WebhooksTable from './Components/WebhooksTable';
12
19
  import WebhookCreateModal from './Components/WebhookCreateModal';
20
+ import WebhookDeleteModal from './Components/WebhookDeleteModal';
21
+ import WebhookEditModal from './Components/WebhookEditModal';
22
+ import WebhookTestModal from './Components/WebhookTestModal';
23
+ import nameToEditFormatter from '../Components/WebhookTable/Components/Formatters/nameToEditFormatter';
24
+ import enabledCellFormatter from '../Components/WebhookTable/Components/Formatters/enabledCellFormatter';
25
+ import actionCellFormatter from '../Components/WebhookTable/Components/Formatters/actionCellFormatter';
13
26
 
14
27
  import { reloadWithSearch, fetchAndPush } from '../WebhooksPageActions';
15
28
 
@@ -17,16 +30,100 @@ const WebhooksIndexPage = () => {
17
30
  const dispatch = useDispatch();
18
31
 
19
32
  const search = useSelector(selectSearch);
33
+ const page = useSelector(selectPage);
34
+ const perPage = useSelector(selectPerPage);
35
+ const itemCount = useSelector(selectSubtotal);
20
36
 
21
37
  const [toDelete, setToDelete] = useState({});
22
38
  const [toTest, setToTest] = useState({});
23
39
  const [toEdit, setToEdit] = useState(0);
24
40
  const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
41
+ const [isTestModalOpen, setIsTestModalOpen] = useState(false);
42
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
43
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false);
25
44
 
26
45
  const openModal = () => {
27
46
  setIsCreateModalOpen(true);
28
47
  };
29
48
 
49
+ const onEditClick = rowData => {
50
+ setToEdit(rowData);
51
+ setIsEditModalOpen(true);
52
+ };
53
+
54
+ const onTestClick = rowData => {
55
+ setToTest(rowData);
56
+ setIsTestModalOpen(true);
57
+ };
58
+
59
+ const onDeleteClick = rowData => {
60
+ setToDelete(rowData);
61
+ setIsDeleteModalOpen(true);
62
+ };
63
+
64
+ const webhookActions = {
65
+ deleteWebhook: (id, name) => {
66
+ onDeleteClick({ id, name });
67
+ },
68
+ testWebhook: (id, name) => {
69
+ onTestClick({ id, name });
70
+ },
71
+ };
72
+
73
+ const modalsStates = {
74
+ testModal: {
75
+ isOpen: isTestModalOpen,
76
+ closeModal: () => {
77
+ setToTest({});
78
+ setIsTestModalOpen(false);
79
+ },
80
+ },
81
+ deleteModal: {
82
+ isOpen: isDeleteModalOpen,
83
+ closeModal: () => {
84
+ setToDelete({});
85
+ setIsDeleteModalOpen(false);
86
+ },
87
+ },
88
+ editModal: {
89
+ isOpen: isEditModalOpen,
90
+ closeModal: () => {
91
+ setToEdit(0);
92
+ setIsEditModalOpen(false);
93
+ },
94
+ },
95
+ };
96
+
97
+ const columns = {
98
+ name: {
99
+ title: __('Name'),
100
+ wrapper: nameToEditFormatter(onEditClick),
101
+ isSorted: true,
102
+ },
103
+ target_url: {
104
+ title: __('Target URL'),
105
+ isSorted: true,
106
+ },
107
+ enabled: {
108
+ title: __('Enabled'),
109
+ wrapper: enabledCellFormatter(),
110
+ isSorted: true,
111
+ },
112
+ actions: {
113
+ title: __('Actions'),
114
+ wrapper: actionCellFormatter(webhookActions),
115
+ },
116
+ };
117
+
118
+ const onDeleteSuccess = () => {
119
+ modalsStates.deleteModal.closeModal();
120
+ const currentPage = page;
121
+ const maxPage = Math.ceil((itemCount - 1) / perPage);
122
+ dispatch(
123
+ fetchAndPush({ page: maxPage < currentPage ? maxPage : currentPage })
124
+ );
125
+ };
126
+
30
127
  return (
31
128
  <>
32
129
  <WebhookCreateModal
@@ -37,24 +134,31 @@ const WebhooksIndexPage = () => {
37
134
  }}
38
135
  onCancel={() => setIsCreateModalOpen(false)}
39
136
  />
137
+ <WebhookDeleteModal
138
+ toDelete={toDelete}
139
+ onSuccess={onDeleteSuccess}
140
+ modalState={modalsStates.deleteModal}
141
+ />
142
+ <WebhookEditModal
143
+ toEdit={toEdit}
144
+ onSuccess={() => {
145
+ modalsStates.editModal.closeModal();
146
+ dispatch(reloadWithSearch(search));
147
+ }}
148
+ modalState={modalsStates.editModal}
149
+ />
150
+ <WebhookTestModal toTest={toTest} modalState={modalsStates.testModal} />
151
+
40
152
  <TableIndexPage
41
153
  header={__('Webhooks')}
42
154
  controller="webhooks"
43
- apiUrl={WEBHOOKS_API_PATH}
155
+ apiUrl={WEBHOOKS_API_PLAIN_PATH}
44
156
  apiOptions={{ key: WEBHOOKS_API_REQUEST_KEY }}
45
157
  customCreateAction={() => openModal}
46
- >
47
- <WebhooksTable
48
- fetchAndPush={params => dispatch(fetchAndPush(params))}
49
- toDelete={toDelete}
50
- setToDelete={setToDelete}
51
- toEdit={toEdit}
52
- setToEdit={setToEdit}
53
- toTest={toTest}
54
- setToTest={setToTest}
55
- reloadWithSearch={query => dispatch(reloadWithSearch(query))}
56
- />
57
- </TableIndexPage>
158
+ columns={columns}
159
+ id="webhooks-table"
160
+ key="webhooks-table"
161
+ />
58
162
  </>
59
163
  );
60
164
  };