diff --git a/CHANGELOG.md b/CHANGELOG.md index a3737757..28fdd29e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- UI drawer updates for webhooks2 ([#2419](https://github.com/grafana/oncall/pull/2419)) - Removed url from sms notification, changed format ([2317](https://github.com/grafana/oncall/pull/2317)) ## v1.3.4 (2023-07-05) diff --git a/grafana-plugin/src/components/HamburgerMenu/HamburgerMenu.module.scss b/grafana-plugin/src/components/HamburgerMenu/HamburgerMenu.module.scss index a9fbc840..96dc92e0 100644 --- a/grafana-plugin/src/components/HamburgerMenu/HamburgerMenu.module.scss +++ b/grafana-plugin/src/components/HamburgerMenu/HamburgerMenu.module.scss @@ -1,9 +1,6 @@ .hamburgerMenu { cursor: pointer; color: var(--primary-text-color); - border: var(--border-weak); - border-radius: var(--border-radius); - background-color: var(--button-background); display: inline-flex; flex-direction: column; align-items: center; @@ -11,21 +8,15 @@ justify-content: center; padding: 4px; - &:hover { - background-color: var(--button-hover-background); - } - &--withBackground { height: 32px; width: 30px; cursor: pointer; - color: var(--primary-text-color); } &--small { height: 24px; width: 22px; cursor: pointer; - color: var(--primary-text-color); } } diff --git a/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx b/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx index dd36d0ee..a94c2caf 100644 --- a/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx +++ b/grafana-plugin/src/components/ScheduleQualityDetails/ScheduleQualityDetails.tsx @@ -96,7 +96,7 @@ export const ScheduleQualityDetails: FC = ({ qualit )} -
+
diff --git a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx index ebb86236..3f6b5025 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx +++ b/grafana-plugin/src/containers/IntegrationContainers/ExpandedIntegrationRouteDisplay/ExpandedIntegrationRouteDisplay.tsx @@ -42,9 +42,6 @@ import { UserActions } from 'utils/authorization'; const cx = cn.bind(styles); -const ACTIONS_LIST_WIDTH = 200; -const ACTIONS_LIST_BORDER = 2; - interface ExpandedIntegrationRouteDisplayProps { alertReceiveChannelId: AlertReceiveChannel['id']; channelFilterId: ChannelFilter['id']; @@ -370,7 +367,7 @@ export const RouteButtonsDisplay: React.FC = ({
-
+
@@ -388,8 +385,8 @@ export const RouteButtonsDisplay: React.FC = ({ {({ openMenu }) => ( diff --git a/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css b/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css index b0cae583..c335c84b 100644 --- a/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css +++ b/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css @@ -9,3 +9,7 @@ .content { margin: 4px; } + +.tabs__content { + padding-top: 16px; +} diff --git a/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.tsx b/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.tsx index 007794ad..e7baf4df 100644 --- a/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhook2Form/OutgoingWebhook2Form.tsx @@ -1,15 +1,20 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; -import { Button, Drawer, HorizontalGroup } from '@grafana/ui'; +import { Button, ConfirmModal, ConfirmModalProps, Drawer, HorizontalGroup, Tab, TabsBar } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; +import { useHistory } from 'react-router-dom'; import GForm from 'components/GForm/GForm'; import Text from 'components/Text/Text'; +import OutgoingWebhook2Status from 'containers/OutgoingWebhook2Status/OutgoingWebhook2Status'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types'; +import { WebhookFormActionType } from 'pages/outgoing_webhooks_2/OutgoingWebhooks2.types'; import { useStore } from 'state/useStore'; +import { KeyValuePair } from 'utils'; import { UserActions } from 'utils/authorization'; +import { PLUGIN_ROOT } from 'utils/consts'; import { form } from './OutgoingWebhook2Form.config'; @@ -19,65 +24,221 @@ const cx = cn.bind(styles); interface OutgoingWebhook2FormProps { id: OutgoingWebhook2['id'] | 'new'; - action: 'new' | 'update'; + action: WebhookFormActionType; onHide: () => void; onUpdate: () => void; + onDelete: () => void; } +export const WebhookTabs = { + Settings: new KeyValuePair('Settings', 'Settings'), + LastRun: new KeyValuePair('LastRun', 'Last Run'), +}; + const OutgoingWebhook2Form = observer((props: OutgoingWebhook2FormProps) => { - const { id, action, onUpdate, onHide } = props; + const history = useHistory(); + const { id, action, onUpdate, onHide, onDelete } = props; + const [activeTab, setActiveTab] = useState( + action === WebhookFormActionType.EDIT_SETTINGS ? WebhookTabs.Settings.key : WebhookTabs.LastRun.key + ); - const store = useStore(); - - const { outgoingWebhook2Store } = store; - - const data = - id === 'new' - ? { is_webhook_enabled: true, is_legacy: false } - : action === 'new' - ? { ...outgoingWebhook2Store.items[id], is_legacy: false, name: '' } - : outgoingWebhook2Store.items[id]; + const { outgoingWebhook2Store } = useStore(); + const isNew = action === WebhookFormActionType.NEW; + const isNewOrCopy = isNew || action === WebhookFormActionType.COPY; const handleSubmit = useCallback( (data: Partial) => { - (action === 'new' ? outgoingWebhook2Store.create(data) : outgoingWebhook2Store.update(id, data)).then(() => { + (isNewOrCopy ? outgoingWebhook2Store.create(data) : outgoingWebhook2Store.update(id, data)).then(() => { onHide(); - onUpdate(); }); }, [id] ); + if ( + (action === WebhookFormActionType.EDIT_SETTINGS || action === WebhookFormActionType.VIEW_LAST_RUN) && + !outgoingWebhook2Store.items[id] + ) { + return null; + } + + let data: + | OutgoingWebhook2 + | { + is_webhook_enabled: boolean; + is_legacy: boolean; + }; + + if (isNew) { + data = { is_webhook_enabled: true, is_legacy: false }; + } else if (isNewOrCopy) { + data = { ...outgoingWebhook2Store.items[id], is_legacy: false, name: '' }; + } else { + data = outgoingWebhook2Store.items[id]; + } + + if ( + (action === WebhookFormActionType.EDIT_SETTINGS || action === WebhookFormActionType.VIEW_LAST_RUN) && + !outgoingWebhook2Store.items[id] + ) { + // nothing to show if we open invalid ID for edit/last_run + return null; + } + + if (action === WebhookFormActionType.NEW || action === WebhookFormActionType.COPY) { + // show just the creation form, not the tabs + return ( + + {renderWebhookForm()} + + ); + } + return ( - -
- - - - - - - -
- {data.is_legacy ? ( -
- Legacy migrated webhooks are not editable. -
- ) : ( - '' - )} + // show tabbed drawer (edit/live_run) + + + { + setActiveTab(WebhookTabs.Settings.key); + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/edit/${id}`); + }} + active={activeTab === WebhookTabs.Settings.key} + label={WebhookTabs.Settings.value} + /> + + { + setActiveTab(WebhookTabs.LastRun.key); + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/last_run/${id}`); + }} + active={activeTab === WebhookTabs.LastRun.key} + label={WebhookTabs.LastRun.value} + /> + + + ); + + function renderWebhookForm() { + return ( + <> +
+ +
+ + + + + + +
+
+ + ); + } }); +interface WebhookTabsProps { + id: OutgoingWebhook2['id'] | 'new'; + activeTab: string; + action: WebhookFormActionType; + data: + | OutgoingWebhook2 + | { + is_webhook_enabled: boolean; + is_legacy: boolean; + }; + onHide: () => void; + onUpdate: () => void; + onDelete: () => void; + handleSubmit: (data: Partial) => void; +} + +const WebhookTabsContent: React.FC = ({ + id, + action, + activeTab, + data, + handleSubmit, + onHide, + onUpdate, + onDelete, +}) => { + const [confirmationModal, setConfirmationModal] = useState(undefined); + + return ( +
+ {confirmationModal && ( + setConfirmationModal(undefined)} /> + )} + + {activeTab === WebhookTabs.Settings.key && ( + <> +
+ +
+ + + + + + + + + +
+
+ {data.is_legacy ? ( +
+ Legacy migrated webhooks are not editable. +
+ ) : ( + '' + )} + + )} + {activeTab === WebhookTabs.LastRun.key && } +
+ ); +}; + export default OutgoingWebhook2Form; diff --git a/grafana-plugin/src/containers/OutgoingWebhook2Status/OutgoingWebhook2Status.tsx b/grafana-plugin/src/containers/OutgoingWebhook2Status/OutgoingWebhook2Status.tsx index 502893c9..4c122f51 100644 --- a/grafana-plugin/src/containers/OutgoingWebhook2Status/OutgoingWebhook2Status.tsx +++ b/grafana-plugin/src/containers/OutgoingWebhook2Status/OutgoingWebhook2Status.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Drawer, Label, VerticalGroup } from '@grafana/ui'; +import { Label, VerticalGroup } from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; @@ -16,7 +16,6 @@ const cx = cn.bind(styles); interface OutgoingWebhook2StatusProps { id: OutgoingWebhook2['id']; - onHide: () => void; onUpdate: () => void; } @@ -49,7 +48,7 @@ function format_response_field(str) { } const OutgoingWebhook2Status = observer((props: OutgoingWebhook2StatusProps) => { - const { id, onHide } = props; + const { id } = props; const store = useStore(); @@ -58,76 +57,65 @@ const OutgoingWebhook2Status = observer((props: OutgoingWebhook2StatusProps) => const data = outgoingWebhook2Store.items[id]; return ( - - Outgoing Webhook Status - - } - onClose={onHide} - closeOnMaskClick - > -
- - - {data.name} - - {data.id} - - {data.trigger_type_name} +
+ + + {data.name} + + {data.id} + + {data.trigger_type_name} - {data.last_response_log.timestamp ? ( - - - {data.last_response_log.timestamp} + {data.last_response_log.timestamp ? ( + + + {data.last_response_log.timestamp} - {data.last_response_log.url && ( - - )} - {data.last_response_log.status_code && ( - - - {data.last_response_log.status_code} - - )} + {data.last_response_log.url && ( + + )} + {data.last_response_log.status_code && ( + + + {data.last_response_log.status_code} + + )} - {data.last_response_log.content && ( - - - {format_response_field(data.last_response_log.content)} - - )} - {data.last_response_log.request_trigger && ( - - )} - {data.last_response_log.request_headers && ( - - )} - {data.last_response_log.request_data && ( - - )} - - ) : ( - - An event triggering this webhook has not been sent yet! - - )} - -
- + {data.last_response_log.content && ( + + + {format_response_field(data.last_response_log.content)} + + )} + {data.last_response_log.request_trigger && ( + + )} + {data.last_response_log.request_headers && ( + + )} + {data.last_response_log.request_data && ( + + )} +
+ ) : ( + + An event triggering this webhook has not been sent yet! + + )} + +
); }); diff --git a/grafana-plugin/src/pages/integration/Integration.tsx b/grafana-plugin/src/pages/integration/Integration.tsx index 7a71337e..54a2eff8 100644 --- a/grafana-plugin/src/pages/integration/Integration.tsx +++ b/grafana-plugin/src/pages/integration/Integration.tsx @@ -88,8 +88,6 @@ interface IntegrationState extends PageBaseState { openRoutes: string[]; } -const ACTIONS_LIST_WIDTH = 200; -const ACTIONS_LIST_BORDER = 2; const NEW_ROUTE_DEFAULT = ''; @observer @@ -685,7 +683,7 @@ const IntegrationSendDemoPayloadModal: React.FC Cancel - openNotification('CURL copied!')}> + openNotification('CURL has been copied')}>
-
+
@@ -918,14 +916,7 @@ const IntegrationActions: React.FC = ({
)} > - {({ openMenu }) => ( - - )} + {({ openMenu }) => }
diff --git a/grafana-plugin/src/pages/integrations/Integrations.tsx b/grafana-plugin/src/pages/integrations/Integrations.tsx index 5a691838..db376a56 100644 --- a/grafana-plugin/src/pages/integrations/Integrations.tsx +++ b/grafana-plugin/src/pages/integrations/Integrations.tsx @@ -40,8 +40,6 @@ const cx = cn.bind(styles); const FILTERS_DEBOUNCE_MS = 500; const ITEMS_PER_PAGE = 15; const MAX_LINE_LENGTH = 40; -const ACTIONS_LIST_WIDTH = 200; -const ACTIONS_LIST_BORDER = 2; interface IntegrationsState extends PageBaseState { integrationsFilters: Filters; @@ -402,7 +400,7 @@ class Integrations extends React.Component
- openNotification('Integration ID is copied')}> + openNotification('Integration ID has been copied')}>
@@ -412,7 +410,7 @@ class Integrations extends React.Component
-
+
@@ -447,9 +445,7 @@ class Integrations extends React.Component
)} > - {({ openMenu }) => ( - - )} + {({ openMenu }) => } ); }; diff --git a/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.css b/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.css deleted file mode 100644 index 15487545..00000000 --- a/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.css +++ /dev/null @@ -1,15 +0,0 @@ -.header { - display: flex; - align-items: center; - width: 100%; - padding-top: 12px; -} - -.header__title { - display: flex; - align-items: baseline; -} - -.header__desc { - margin-bottom: 12px; -} diff --git a/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.scss b/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.scss new file mode 100644 index 00000000..6b9ed498 --- /dev/null +++ b/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.module.scss @@ -0,0 +1,41 @@ +.header { + display: flex; + align-items: center; + width: 100%; + padding-top: 12px; +} + +.header__title { + display: flex; + align-items: baseline; +} + +.header__desc { + margin-bottom: 12px; +} + +.hamburgerMenu { + display: flex; + flex-direction: column; + width: 225px; + border-radius: 2px; +} + +.hamburgerMenu__item { + padding: 8px; + display: flex; + align-items: center; + flex-direction: row; + flex-shrink: 0; + white-space: nowrap; + border-left: 2px solid transparent; + cursor: pointer; + min-width: 84px; + display: flex; + gap: 8px; + flex-direction: row; + + &:hover { + background: var(--cards-background); + } +} \ No newline at end of file diff --git a/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.tsx b/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.tsx index 7b10792e..0ba16373 100644 --- a/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.tsx +++ b/grafana-plugin/src/pages/outgoing_webhooks_2/OutgoingWebhooks2.tsx @@ -1,6 +1,15 @@ import React from 'react'; -import { Button, HorizontalGroup, Icon, IconButton, VerticalGroup } from '@grafana/ui'; +import { + Button, + ConfirmModal, + ConfirmModalProps, + HorizontalGroup, + Icon, + IconButton, + VerticalGroup, + WithContextMenu, +} from '@grafana/ui'; import cn from 'classnames/bind'; import { observer } from 'mobx-react'; import moment from 'moment-timezone'; @@ -9,6 +18,7 @@ import CopyToClipboard from 'react-copy-to-clipboard'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import GTable from 'components/GTable/GTable'; +import HamburgerMenu from 'components/HamburgerMenu/HamburgerMenu'; import PageErrorHandlingWrapper, { PageBaseState } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper'; import { getWrongTeamResponseInfo, @@ -16,50 +26,45 @@ import { } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers'; import PluginLink from 'components/PluginLink/PluginLink'; import Text from 'components/Text/Text'; -import WithConfirm from 'components/WithConfirm/WithConfirm'; import OutgoingWebhook2Form from 'containers/OutgoingWebhook2Form/OutgoingWebhook2Form'; -import OutgoingWebhook2Status from 'containers/OutgoingWebhook2Status/OutgoingWebhook2Status'; import RemoteFilters from 'containers/RemoteFilters/RemoteFilters'; import TeamName from 'containers/TeamName/TeamName'; import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip'; -import { ActionDTO } from 'models/action'; import { FiltersValues } from 'models/filters/filters.types'; import { OutgoingWebhook } from 'models/outgoing_webhook/outgoing_webhook.types'; import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types'; import { AppFeature } from 'state/features'; import { PageProps, WithStoreProps } from 'state/types'; import { withMobXProviderContext } from 'state/withStore'; +import { openErrorNotification, openNotification } from 'utils'; import { isUserActionAllowed, UserActions } from 'utils/authorization'; import { PLUGIN_ROOT } from 'utils/consts'; -import styles from './OutgoingWebhooks2.module.css'; +import styles from './OutgoingWebhooks2.module.scss'; +import { WebhookFormActionType } from './OutgoingWebhooks2.types'; const cx = cn.bind(styles); -const Action = { - STATUS: 'status', - EDIT: 'edit', - COPY: 'copy', -}; - interface OutgoingWebhooks2Props extends WithStoreProps, PageProps, RouteComponentProps<{ id: string; action: string }> {} interface OutgoingWebhooks2State extends PageBaseState { - outgoingWebhook2Action?: 'new' | 'update'; + outgoingWebhook2Action?: WebhookFormActionType; outgoingWebhook2Id?: OutgoingWebhook2['id']; + confirmationModal: ConfirmModalProps; } @observer class OutgoingWebhooks2 extends React.Component { state: OutgoingWebhooks2State = { errorData: initErrorDataState(), + confirmationModal: undefined, }; componentDidUpdate(prevProps: OutgoingWebhooks2Props) { - if (prevProps.match.params.id !== this.props.match.params.id) { + if (prevProps.match.params.id !== this.props.match.params.id && !this.state.outgoingWebhook2Action) { this.parseQueryParams(); } } @@ -77,37 +82,36 @@ class OutgoingWebhooks2 extends React.Component this.setState({ errorData: { ...getWrongTeamResponseInfo(error) } })); - } - - if (isNewWebhook || (action === Action.COPY && outgoingWebhook2)) { - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: 'new' }); - } else if (action === Action.EDIT && outgoingWebhook2) { - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: 'update' }); - } else if (action === Action.STATUS && outgoingWebhook2) { - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: undefined }); + .catch((error) => + this.setState({ errorData: { ...getWrongTeamResponseInfo(error) }, outgoingWebhook2Action: undefined }) + ); } }; update = () => { const { store } = this.props; - return store.outgoingWebhook2Store.updateItems(); }; render() { - const { store, query } = this.props; - const { outgoingWebhook2Id, outgoingWebhook2Action, errorData } = this.state; + const { + store, + history, + match: { + params: { id }, + }, + } = this.props; + const { outgoingWebhook2Id, outgoingWebhook2Action, errorData, confirmationModal } = this.state; const webhooks = store.outgoingWebhook2Store.getSearchResult(); @@ -151,10 +155,21 @@ class OutgoingWebhooks2 extends React.Component {() => ( <> + {confirmationModal && ( + + this.setState({ + confirmationModal: undefined, + }) + } + /> + )} +
{this.renderOutgoingWebhooksFilters()}
+ {outgoingWebhook2Id && outgoingWebhook2Action && ( - )} - {outgoingWebhook2Id && !outgoingWebhook2Action && ( - { + this.onDeleteClick(outgoingWebhook2Id).then(() => { + this.setState({ outgoingWebhook2Id: undefined, outgoingWebhook2Action: undefined }); + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2`); + }); + }} /> )} @@ -245,53 +260,86 @@ class OutgoingWebhooks2 extends React.Component; } - renderActionButtons = (record: ActionDTO) => { + renderActionButtons = (record: OutgoingWebhook2) => { return ( - - - - ID {record.id} -
- (click to copy ID to clipboard) + ( +
+
this.onLastRunClick(record.id)}> + + View Last Run + +
+ +
this.onEditClick(record.id)}> + + Edit settings + +
+ +
+ this.setState({ + confirmationModal: { + isOpen: true, + confirmText: 'Confirm', + dismissText: 'Cancel', + onConfirm: () => this.onDisableWebhook(record.id, !record.is_webhook_enabled), + title: `Are you sure you want to ${record.is_webhook_enabled ? 'disable' : 'enable'} webhook?`, + } as ConfirmModalProps, + }) + } + > + + {record.is_webhook_enabled ? 'Disable' : 'Enable'} + +
+ +
this.onCopyClick(record.id)}> + + Make a copy + +
+ + openNotification('Webhook ID has been copied')}> +
+ + + UID: {record.id} +
- } - tooltipPlacement="top" - name="info-circle" - /> -
- - this.onStatusClick(record.id)} - /> - - - this.onEditClick(record.id)} /> - - - this.onCopyClick(record.id)} - /> - - - - - - - + + +
+ +
+ this.setState({ + confirmationModal: { + isOpen: true, + confirmText: 'Confirm', + dismissText: 'Cancel', + onConfirm: () => this.onDeleteClick(record.id), + body: 'The action cannot be undone.', + title: `Are you sure you want to delete webhook?`, + } as Partial as ConfirmModalProps, + }) + } + > + + + + Delete Webhook + + +
+
+ )} + > + {({ openMenu }) => } + ); }; @@ -331,36 +379,56 @@ class OutgoingWebhooks2 extends React.Component { + onDeleteClick = (id: OutgoingWebhook2['id']): Promise => { const { store } = this.props; - - return () => { - store.outgoingWebhook2Store.delete(id).then(this.update); - }; + return store.outgoingWebhook2Store + .delete(id) + .then(this.update) + .then(() => openNotification('Webhook has been removed')) + .catch(() => openNotification('Webook could not been removed')) + .finally(() => this.setState({ confirmationModal: undefined })); }; onEditClick = (id: OutgoingWebhook2['id']) => { const { history } = this.props; - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: 'update' }); - - history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/edit/${id}`); + this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: WebhookFormActionType.EDIT_SETTINGS }, () => + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/edit/${id}`) + ); }; onCopyClick = (id: OutgoingWebhook2['id']) => { const { history } = this.props; - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: 'new' }); - - history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/copy/${id}`); + this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: WebhookFormActionType.COPY }, () => + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/copy/${id}`) + ); }; - onStatusClick = (id: OutgoingWebhook2['id']) => { + onDisableWebhook = (id: OutgoingWebhook2['id'], isEnabled: boolean) => { + const { + store: { outgoingWebhook2Store }, + } = this.props; + + const data = { + ...{ ...outgoingWebhook2Store.items[id], is_webhook_enabled: isEnabled }, + is_legacy: false, + }; + + outgoingWebhook2Store + .update(id, data) + .then(() => this.update()) + .then(() => openNotification(`Webhook has been ${isEnabled ? 'enabled' : 'disabled'}`)) + .catch(() => openErrorNotification('Webhook could not been updated')) + .finally(() => this.setState({ confirmationModal: undefined })); + }; + + onLastRunClick = (id: OutgoingWebhook2['id']) => { const { history } = this.props; - this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: undefined }); - - history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/status/${id}`); + this.setState({ outgoingWebhook2Id: id, outgoingWebhook2Action: WebhookFormActionType.VIEW_LAST_RUN }, () => + history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/last_run/${id}`) + ); }; handleOutgoingWebhookFormHide = () => { @@ -372,6 +440,18 @@ class OutgoingWebhooks2 extends React.Component