From d859dbc3e58baea91445de542571afebd76ac8d0 Mon Sep 17 00:00:00 2001 From: Mark Berger Date: Wed, 12 Oct 2022 17:49:47 +0300 Subject: [PATCH 1/4] Update Notification Settings design - Removed styled-components - Added new behavior 'Test' and 'Submit' buttons --- .../icons}/DocsIcon.tsx | 4 +- .../src/components/icons/PlayIcon.tsx | 12 + airbyte-webapp/src/locales/en.json | 13 +- .../cloud/views/layout/SideBar/SideBar.tsx | 2 +- .../NotificationPage/NotificationPage.tsx | 61 +-- .../components/WebHookForm.module.scss | 168 +++++++++ .../components/WebHookForm.tsx | 347 +++++++++++------- .../NotificationPage/components/help.png | Bin 0 -> 31497 bytes airbyte-webapp/src/utils/links.ts | 2 + .../src/views/layout/SideBar/SideBar.tsx | 2 +- 10 files changed, 413 insertions(+), 198 deletions(-) rename airbyte-webapp/src/{views/layout/SideBar/components => components/icons}/DocsIcon.tsx (82%) create mode 100644 airbyte-webapp/src/components/icons/PlayIcon.tsx create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.module.scss create mode 100644 airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/help.png diff --git a/airbyte-webapp/src/views/layout/SideBar/components/DocsIcon.tsx b/airbyte-webapp/src/components/icons/DocsIcon.tsx similarity index 82% rename from airbyte-webapp/src/views/layout/SideBar/components/DocsIcon.tsx rename to airbyte-webapp/src/components/icons/DocsIcon.tsx index 3aef838cf6ac1..9fcd7481a760b 100644 --- a/airbyte-webapp/src/views/layout/SideBar/components/DocsIcon.tsx +++ b/airbyte-webapp/src/components/icons/DocsIcon.tsx @@ -1,4 +1,4 @@ -const DocsIcon = ({ color = "currentColor" }: { color?: string }): JSX.Element => ( +export const DocsIcon = ({ color = "currentColor" }: { color?: string }): JSX.Element => ( ); - -export default DocsIcon; diff --git a/airbyte-webapp/src/components/icons/PlayIcon.tsx b/airbyte-webapp/src/components/icons/PlayIcon.tsx new file mode 100644 index 0000000000000..0eda66dc6b6ea --- /dev/null +++ b/airbyte-webapp/src/components/icons/PlayIcon.tsx @@ -0,0 +1,12 @@ +interface Props { + color?: string; +} + +export const PlayIcon = ({ color = "currentColor" }: Props): JSX.Element => ( + + + +); diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index 8a5af3249a252..d27f83e15d9e0 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -483,16 +483,19 @@ "admin.customImage": "custom", "settings.accountSettings": "Account Settings", - "settings.changeSaved": "change saved!", "settings.webhook": "Connection status Webhook", "settings.webhookTitle": "Connection status Webhook URL", - "settings.webhookDescriprion": "Get notifications the way you want (for instance, on Slack, or other).", - "settings.webhookTestText": "Testing the Webhook will send a “Hello World”. ", - "settings.sendOnSuccess": "Send notifications when sync succeeds.", - "settings.sendOnFailure": "Send notifications when sync fails.", + "settings.webhookTestText": "Testing the Webhook will send a “Hello World”.", + "settings.syncNotifications.label": "Notify me:", + "settings.sendOnSuccess": "When Sync succeeds", + "settings.sendOnFailure": "When Sync fails", "settings.yourWebhook": "Your Webhook URL", "settings.test": "Test", "settings.notifications": "Notifications", + "settings.notificationGuide.button": "Need help with Webhook URL?", + "settings.notificationGuide.title": "How to get notification the way you want with your webhook URL?", + "settings.notificationGuide.link.configuration": "Configure Sync notifications", + "settings.notificationGuide.link.slackConfiguration": "Configure a Slack Notifications Webhook", "settings.metrics": "Metrics", "settings.notificationSettings": "Notification Settings", "settings.metricsSettings": "Metrics Settings", diff --git a/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx index c3818e7f7165b..6696a666a3ec6 100644 --- a/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx +++ b/airbyte-webapp/src/packages/cloud/views/layout/SideBar/SideBar.tsx @@ -8,6 +8,7 @@ import { NavLink } from "react-router-dom"; import { Link } from "components"; import { CreditsIcon } from "components/icons/CreditsIcon"; +import { DocsIcon } from "components/icons/DocsIcon"; import { Text } from "components/ui/Text"; import { useExperiment } from "hooks/services/Experiment"; @@ -21,7 +22,6 @@ import { links } from "utils/links"; import ChatIcon from "views/layout/SideBar/components/ChatIcon"; import ConnectionsIcon from "views/layout/SideBar/components/ConnectionsIcon"; import DestinationIcon from "views/layout/SideBar/components/DestinationIcon"; -import DocsIcon from "views/layout/SideBar/components/DocsIcon"; import OnboardingIcon from "views/layout/SideBar/components/OnboardingIcon"; import RecipesIcon from "views/layout/SideBar/components/RecipesIcon"; import SettingsIcon from "views/layout/SideBar/components/SettingsIcon"; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx index 10e81d65a9a7f..0ec1f987f47b0 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/NotificationPage.tsx @@ -1,60 +1,17 @@ -import React, { useMemo, useState, useCallback } from "react"; -import { FormattedMessage } from "react-intl"; +import React, { useMemo } from "react"; import HeadTitle from "components/HeadTitle"; import { useTrackPage, PageTrackingCodes } from "hooks/services/Analytics"; -import useWorkspace, { useCurrentWorkspace, WebhookPayload } from "hooks/services/useWorkspace"; +import { useCurrentWorkspace } from "hooks/services/useWorkspace"; -import { Content, SettingsCard } from "../SettingsComponents"; -import WebHookForm from "./components/WebHookForm"; - -function useAsyncWithTimeout(f: (data: K) => Promise) { - const [errorMessage, setErrorMessage] = useState(null); - const [successMessage, setSuccessMessage] = useState(null); - const call = useCallback( - async (data: K) => { - setSuccessMessage(null); - setErrorMessage(null); - try { - await f(data); - setSuccessMessage(); - - setTimeout(() => { - setSuccessMessage(null); - }, 2000); - } catch (e) { - setErrorMessage(); - - setTimeout(() => { - setErrorMessage(null); - }, 2000); - } - }, - [f] - ); - - return { - call, - successMessage, - errorMessage, - }; -} +import { WebHookForm } from "./components/WebHookForm"; const NotificationPage: React.FC = () => { useTrackPage(PageTrackingCodes.SETTINGS_NOTIFICATION); - const { updateWebhook, testWebhook } = useWorkspace(); const workspace = useCurrentWorkspace(); - - const { - call: onSubmitWebhook, - errorMessage, - successMessage, - } = useAsyncWithTimeout(async (data: WebhookPayload) => updateWebhook(data)); - const firstNotification = workspace.notifications?.[0]; - const initialValues = useMemo( () => ({ webhook: firstNotification?.slackConfiguration?.webhook, @@ -67,17 +24,7 @@ const NotificationPage: React.FC = () => { return ( <> - }> - - - - + ); }; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.module.scss b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.module.scss new file mode 100644 index 0000000000000..ee7ed998a5c5e --- /dev/null +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.module.scss @@ -0,0 +1,168 @@ +@use "scss/colors"; +@use "scss/variables"; + +.webhookGuide { + opacity: 0; + visibility: hidden; + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + height: 0; + max-height: 0; + padding: 0 variables.$spacing-xl; + margin-bottom: 0; + background-color: colors.$grey-50; + border-radius: variables.$border-radius-xs; + transition: variables.$transition-out; + + &.active { + opacity: 1; + visibility: visible; + height: 150px; + max-height: 100%; + margin-bottom: variables.$spacing-xl; + } + + .webhookGuideTitle { + display: flex; + justify-content: space-between; + padding-bottom: variables.$spacing-lg; + + .crossIcon { + font-size: 22px; + } + } + + ul { + list-style-type: none; + list-style-position: inside; + margin: 0; + padding: 0; + + li + li { + padding-top: variables.$spacing-md; + } + } + + .webhookGuideLink { + display: inline-flex; + align-items: center; + padding: variables.$spacing-sm 0; + color: colors.$blue; + + .text { + color: colors.$blue; + padding-left: variables.$spacing-md; + } + } + + .webhookGuideImg { + position: absolute; + right: 60px; + bottom: -20px; + width: 106px; + height: 125px; + } +} + +.webhookUrlLabelRow { + position: relative; + margin-bottom: variables.$spacing-md; + + .webhookUrlLabelCell { + flex: auto; + width: 100%; + + label { + padding-bottom: 0; + } + } + + .webhookGuideButtonCell { + position: relative; + flex: auto; + padding: 0; + + .webhookGuideButton { + color: colors.$blue; + text-decoration: underline; + padding-left: 0; + padding-right: 0; + white-space: nowrap; + margin-right: 45px; + + &:hover { + color: colors.$blue; + } + } + + .webhookGuideButtonImg { + position: absolute; + bottom: 6px; + right: 0; + width: 34px; + height: 40px; + } + } +} + +.webhookUrlRow { + margin-bottom: variables.$spacing-xl; + + .webhookUrlInputCell { + position: relative; + flex: auto; + width: 100%; + + .webhookErrorMessage { + position: absolute; + bottom: -16px; + left: 0; + color: colors.$red-600; + } + } + + .testButtonCell { + flex: auto; + padding: 0; + + .testButton { + white-space: nowrap; + } + } +} + +.notificationSettingsLabelRow { + margin-bottom: variables.$spacing-md; + + .notificationSettingsLabelCell { + label { + padding-bottom: 0; + } + } +} + +.notificationSettingsRow { + margin-bottom: variables.$spacing-md; + + .notificationSettingsCell { + position: relative; + display: flex; + + .sendOnFailure { + margin-right: variables.$spacing-xl; + } + } +} + +.tooltip { + transform: translate(0, -#{variables.$spacing-md}); + white-space: nowrap; +} + +.action { + display: flex; + justify-content: flex-end; + margin-top: variables.$spacing-xl; +} diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx index c09f9f97a717f..f4fdc783ce87b 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx @@ -1,51 +1,40 @@ +import { faXmark } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import classNames from "classnames"; import { Field, FieldProps, Form, Formik } from "formik"; -import React from "react"; +import React, { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import styled from "styled-components"; import * as yup from "yup"; import { Label, LabeledSwitch } from "components"; +import { DocsIcon } from "components/icons/DocsIcon"; +import { PlayIcon } from "components/icons/PlayIcon"; import { Row, Cell } from "components/SimpleTableComponents"; import { Button } from "components/ui/Button"; import { Input } from "components/ui/Input"; +import { Text } from "components/ui/Text"; +import { Tooltip } from "components/ui/Tooltip"; -import { WebhookPayload } from "hooks/services/useWorkspace"; -import { equal } from "utils/objects"; +import useWorkspace, { WebhookPayload } from "hooks/services/useWorkspace"; +import { links } from "utils/links"; -const Text = styled.div` - font-style: normal; - font-weight: normal; - font-size: 13px; - line-height: 150%; - padding-bottom: 5px; -`; +import { Content, SettingsCard } from "../../SettingsComponents"; +import help from "./help.png"; +import styles from "./WebHookForm.module.scss"; -const InputRow = styled(Row)` - height: auto; - margin-bottom: 40px; -`; - -const Message = styled(Text)` - margin: -40px 0 21px; - padding: 0; - color: ${({ theme }) => theme.greyColor40}; -`; - -const FeedbackCell = styled(Cell)` - &:last-child { - text-align: left; - } - padding-left: 11px; -`; +const enum WebhookAction { + Test = "test", + Save = "save", +} -const Success = styled.div` - font-size: 13px; - color: ${({ theme }) => theme.successColor}; -`; +interface FormActionType { + [WebhookAction.Test]: boolean; + [WebhookAction.Save]: boolean; +} -const Error = styled(Success)` - color: ${({ theme }) => theme.dangerColor}; -`; +interface WebHookFormProps { + webhook: WebhookPayload; +} const webhookValidationSchema = yup.object().shape({ webhook: yup.string().url("form.url.error"), @@ -53,43 +42,36 @@ const webhookValidationSchema = yup.object().shape({ sendOnFailure: yup.boolean(), }); -interface WebHookFormProps { - webhook: WebhookPayload; - successMessage?: React.ReactNode; - errorMessage?: React.ReactNode; - onSubmit: (data: WebhookPayload) => void; - onTest: (data: WebhookPayload) => void; -} - -const WebHookForm: React.FC = ({ webhook, onSubmit, successMessage, errorMessage, onTest }) => { +export const WebHookForm: React.FC = ({ webhook }) => { + const [webhookViewGuide, setWebhookViewGuide] = useState(false); + const [formAction, setFormAction] = useState({ test: false, save: false }); + const [webhookUrlError, setWebhookUrlError] = useState(false); + const { updateWebhook, testWebhook } = useWorkspace(); const { formatMessage } = useIntl(); - const feedBackBlock = (dirty: boolean, isSubmitting: boolean, webhook?: string) => { - if (successMessage) { - return {successMessage}; + const webhookChange = async (action: WebhookAction, data: WebhookPayload) => { + setFormAction((value) => ({ ...value, [action]: true })); + if (action === WebhookAction.Test) { + await testWebhookAction(data); } - - if (errorMessage) { - return {errorMessage}; + if (action === WebhookAction.Save) { + await updateWebhook(data); } + setFormAction((value) => ({ ...value, [action]: false })); + }; - if (dirty) { - return ( - - ); + const testWebhookAction = async (data: WebhookPayload) => { + if (webhookUrlError) { + setWebhookUrlError(false); } - - if (webhook) { - return ( - - ); + try { + const response = await testWebhook(data); + if (response.status === "failed") { + setWebhookUrlError(true); + } + } catch (e) { + setWebhookUrlError(true); } - - return null; }; return ( @@ -99,76 +81,179 @@ const WebHookForm: React.FC = ({ webhook, onSubmit, successMes validateOnBlur validateOnChange={false} validationSchema={webhookValidationSchema} - onSubmit={(values: WebhookPayload) => { - if (equal(webhook, values)) { - onTest(values); - } else { - onSubmit(values); - } - }} + onSubmit={(values: WebhookPayload) => webhookChange(WebhookAction.Save, values)} > - {({ isSubmitting, initialValues, dirty, errors }) => ( + {({ dirty, errors, values }) => (
- - - - - - - - {({ field, meta }: FieldProps) => ( - - )} - - - {feedBackBlock(dirty, isSubmitting, initialValues.webhook)} - - {initialValues.webhook ? ( - - - - ) : null} - - - - {({ field }: FieldProps) => ( - } - /> - )} - - - - - {({ field }: FieldProps) => ( - } - /> - )} - - - + }> + +
+
+ + + +
+ +
+
+ + {formatMessage({ +
+ + + + + + {!webhookViewGuide && ( + <> + + {formatMessage({ + + )} + + + + + + {({ field, meta }: FieldProps) => ( + + )} + + {webhookUrlError && ( + + + + )} + + + webhookChange(WebhookAction.Test, values)} + > + + + } + > + + + + + + + + + + + + + {({ field }: FieldProps) => ( + } + /> + )} + + + {({ field }: FieldProps) => ( + } + /> + )} + + + +
+
+
+ +
)} ); }; - -export default WebHookForm; diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/help.png b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/help.png new file mode 100644 index 0000000000000000000000000000000000000000..29a435549f155442e5b116d237d43bb316a897f0 GIT binary patch literal 31497 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 Date: Fri, 28 Oct 2022 13:16:48 +0300 Subject: [PATCH 2/4] Update Notification Settings design - Removed styled-components - Added new behavior 'Test' and 'Submit' buttons --- .../components/WebHookForm.tsx | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx index f4fdc783ce87b..602fadd786a41 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx @@ -50,28 +50,20 @@ export const WebHookForm: React.FC = ({ webhook }) => { const { formatMessage } = useIntl(); const webhookChange = async (action: WebhookAction, data: WebhookPayload) => { + setWebhookUrlError(false); setFormAction((value) => ({ ...value, [action]: true })); - if (action === WebhookAction.Test) { - await testWebhookAction(data); - } - if (action === WebhookAction.Save) { - await updateWebhook(data); - } - setFormAction((value) => ({ ...value, [action]: false })); - }; - - const testWebhookAction = async (data: WebhookPayload) => { - if (webhookUrlError) { - setWebhookUrlError(false); - } try { - const response = await testWebhook(data); - if (response.status === "failed") { + const testWebhookResp = await testWebhook(data); + if (testWebhookResp.status === "succeeded" && action === WebhookAction.Save) { + await updateWebhook(data); + } + if (testWebhookResp.status === "failed") { setWebhookUrlError(true); } } catch (e) { setWebhookUrlError(true); } + setFormAction((value) => ({ ...value, [action]: false })); }; return ( @@ -136,12 +128,7 @@ export const WebHookForm: React.FC = ({ webhook }) => { - @@ -180,7 +167,12 @@ export const WebHookForm: React.FC = ({ webhook }) => { /> )} - {webhookUrlError && ( + {!!errors.webhook && ( + + + + )} + {webhookUrlError && !errors.webhook && ( From 6e27675f3fc9f25c735858ebe14d30c6617ccecd Mon Sep 17 00:00:00 2001 From: Mark Berger Date: Sat, 29 Oct 2022 01:56:17 +0300 Subject: [PATCH 3/4] Update Notification Settings design - Removed styled-components - Added new behavior 'Test' and 'Submit' buttons --- .../components/WebHookForm.tsx | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx index 602fadd786a41..a38c64adb2d18 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx @@ -11,6 +11,7 @@ import { DocsIcon } from "components/icons/DocsIcon"; import { PlayIcon } from "components/icons/PlayIcon"; import { Row, Cell } from "components/SimpleTableComponents"; import { Button } from "components/ui/Button"; +import { Heading } from "components/ui/Heading"; import { Input } from "components/ui/Input"; import { Text } from "components/ui/Text"; import { Tooltip } from "components/ui/Tooltip"; @@ -18,6 +19,7 @@ import { Tooltip } from "components/ui/Tooltip"; import useWorkspace, { WebhookPayload } from "hooks/services/useWorkspace"; import { links } from "utils/links"; +import { useNotificationService } from "../../../../../hooks/services/Notification"; import { Content, SettingsCard } from "../../SettingsComponents"; import help from "./help.png"; import styles from "./WebHookForm.module.scss"; @@ -45,27 +47,60 @@ const webhookValidationSchema = yup.object().shape({ export const WebHookForm: React.FC = ({ webhook }) => { const [webhookViewGuide, setWebhookViewGuide] = useState(false); const [formAction, setFormAction] = useState({ test: false, save: false }); - const [webhookUrlError, setWebhookUrlError] = useState(false); + const { registerNotification, unregisterAllNotifications } = useNotificationService(); const { updateWebhook, testWebhook } = useWorkspace(); const { formatMessage } = useIntl(); - const webhookChange = async (action: WebhookAction, data: WebhookPayload) => { - setWebhookUrlError(false); + const webhookAction = async (action: WebhookAction, data: WebhookPayload) => { + unregisterAllNotifications(); setFormAction((value) => ({ ...value, [action]: true })); - try { - const testWebhookResp = await testWebhook(data); - if (testWebhookResp.status === "succeeded" && action === WebhookAction.Save) { - await updateWebhook(data); + if (action === WebhookAction.Test) { + switch (await testWebhookAction(data)) { + case true: { + registerNotification({ + id: "webhook-success", + title: "Webhook URL test successfully passed", + isError: false, + }); + break; + } + case false: { + registerNotification({ + id: "webhook-error", + title: "Webhook URL test is not passed. Please verify your webhook URL", + isError: true, + }); + break; + } } - if (testWebhookResp.status === "failed") { - setWebhookUrlError(true); + } + if (action === WebhookAction.Save) { + switch (await testWebhookAction(data)) { + case true: { + await updateWebhook(data); + break; + } + case false: { + registerNotification({ + id: "save-error", + title: "Settings is not saved. Please verify your webhook URL", + isError: true, + }); + break; + } } - } catch (e) { - setWebhookUrlError(true); } setFormAction((value) => ({ ...value, [action]: false })); }; + const testWebhookAction = async (data: WebhookPayload): Promise => { + try { + return (await testWebhook(data))?.status === "succeeded"; + } catch (e) { + return false; + } + }; + return ( = ({ webhook }) => { validateOnBlur validateOnChange={false} validationSchema={webhookValidationSchema} - onSubmit={(values: WebhookPayload) => webhookChange(WebhookAction.Save, values)} + onSubmit={(values: WebhookPayload) => webhookAction(WebhookAction.Save, values)} > {({ dirty, errors, values }) => (
@@ -81,9 +116,9 @@ export const WebHookForm: React.FC = ({ webhook }) => {
- + - +
From 40fe886458309f044ddf6cf60c88d9d9363ba112 Mon Sep 17 00:00:00 2001 From: Mark Berger Date: Tue, 1 Nov 2022 03:08:57 +0200 Subject: [PATCH 4/4] Update Notification Settings design - Removed styled-components - Added new behavior 'Test' and 'Submit' buttons --- airbyte-webapp/src/locales/en.json | 3 +++ .../NotificationPage/components/WebHookForm.tsx | 16 +++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/airbyte-webapp/src/locales/en.json b/airbyte-webapp/src/locales/en.json index d27f83e15d9e0..e6f728bcd7c18 100644 --- a/airbyte-webapp/src/locales/en.json +++ b/airbyte-webapp/src/locales/en.json @@ -484,6 +484,9 @@ "settings.accountSettings": "Account Settings", "settings.webhook": "Connection status Webhook", + "settings.webhook.test.passed": "Webhook test passed!", + "settings.webhook.test.failed": "Webhook test failed. Please verify that the webhook URL is valid.", + "settings.webhook.save.failed": "Cannot save changes because the webhook test failed. Please verify that the webhook URL is valid.", "settings.webhookTitle": "Connection status Webhook URL", "settings.webhookTestText": "Testing the Webhook will send a “Hello World”.", "settings.syncNotifications.label": "Notify me:", diff --git a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx index a38c64adb2d18..c841292dae981 100644 --- a/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx +++ b/airbyte-webapp/src/pages/SettingsPage/pages/NotificationPage/components/WebHookForm.tsx @@ -58,16 +58,16 @@ export const WebHookForm: React.FC = ({ webhook }) => { switch (await testWebhookAction(data)) { case true: { registerNotification({ - id: "webhook-success", - title: "Webhook URL test successfully passed", + id: "settings.webhook.test.passed", + title: formatMessage({ id: "settings.webhook.test.passed" }), isError: false, }); break; } case false: { registerNotification({ - id: "webhook-error", - title: "Webhook URL test is not passed. Please verify your webhook URL", + id: "settings.webhook.test.failed", + title: formatMessage({ id: "settings.webhook.test.failed" }), isError: true, }); break; @@ -82,8 +82,8 @@ export const WebHookForm: React.FC = ({ webhook }) => { } case false: { registerNotification({ - id: "save-error", - title: "Settings is not saved. Please verify your webhook URL", + id: "settings.webhook.save.failed", + title: formatMessage({ id: "settings.webhook.save.failed" }), isError: true, }); break; @@ -95,7 +95,9 @@ export const WebHookForm: React.FC = ({ webhook }) => { const testWebhookAction = async (data: WebhookPayload): Promise => { try { - return (await testWebhook(data))?.status === "succeeded"; + // TODO: Temporary solution. The current implementation of the back-end requires at least one selected trigger). Should be removed after back-end fixes + const payload = { ...data, sendOnSuccess: true }; + return (await testWebhook(payload))?.status === "succeeded"; } catch (e) { return false; }