This is an automated email from the ASF dual-hosted git repository.

e2corporation pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 72986410 feat(config-ui): added WebHook section to Data Integrations 
(#3017)
72986410 is described below

commit 729864107f2e8024ca711c8da8d27481d061d8a8
Author: 青湛 <[email protected]>
AuthorDate: Wed Sep 21 10:25:32 2022 +0800

    feat(config-ui): added WebHook section to Data Integrations (#3017)
---
 config-ui/package-lock.json                        | 573 ++++++++++++++++++---
 config-ui/package.json                             |   4 +
 config-ui/src/.babelrc                             |   4 +-
 config-ui/src/App.js                               |   4 +
 .../src/components/Sidebar/MenuConfiguration.jsx   |   8 +
 config-ui/src/hooks/useWebhookManager.jsx          | 108 ++++
 config-ui/src/images/icons/copy.svg                |  13 +
 config-ui/src/images/icons/delete.svg              |   7 +
 config-ui/src/images/icons/setting-con.svg         |   6 +
 config-ui/src/images/icons/vector.svg              |   5 +
 config-ui/src/images/integrations/webhook.svg      |   5 +
 .../src/pages/configure/integration/index.jsx      |  26 +-
 .../src/pages/connections/webhook/add-modal.jsx    | 155 ++++++
 .../src/pages/connections/webhook/delete-modal.jsx |  45 ++
 config-ui/src/pages/connections/webhook/index.jsx  | 156 ++++++
 config-ui/src/pages/connections/webhook/styled.js  | 165 ++++++
 .../connections/webhook/view-or-edit-modal.jsx     | 133 +++++
 17 files changed, 1340 insertions(+), 77 deletions(-)

diff --git a/config-ui/package-lock.json b/config-ui/package-lock.json
index 33c39051..bfb2f2d0 100644
--- a/config-ui/package-lock.json
+++ b/config-ui/package-lock.json
@@ -12,6 +12,8 @@
         "@blueprintjs/core": "^3.49.1",
         "@blueprintjs/popover2": "^0.11.4",
         "@blueprintjs/select": "^3.18.10",
+        "@emotion/react": "^11.10.4",
+        "@emotion/styled": "^11.10.4",
         "@fontsource/inter": "^4.5.11",
         "@uiw/react-textarea-code-editor": "^1.4.16",
         "axios": "^0.21.4",
@@ -24,6 +26,7 @@
         "file-saver": "^2.0.5",
         "jetbrains-mono": "^1.0.6",
         "react": "17.0.2",
+        "react-copy-to-clipboard": "^5.1.0",
         "react-dom": "17.0.2",
         "react-router-dom": "^5.3.0",
         "react-transition-group": "^2.9.0",
@@ -34,6 +37,7 @@
         "@babel/preset-env": "^7.12.7",
         "@babel/preset-flow": "^7.14.5",
         "@babel/preset-react": "^7.12.7",
+        "@emotion/babel-plugin": "^11.10.2",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
         "@svgr/webpack": "^5.5.0",
         "@testing-library/jest-dom": "^5.14.1",
@@ -334,11 +338,11 @@
       }
     },
     "node_modules/@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz";,
-      "integrity": 
"sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz";,
+      "integrity": 
"sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
       "dependencies": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.18.6"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -374,10 +378,9 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.14.5",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz";,
-      "integrity": 
"sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
-      "dev": true,
+      "version": "7.19.0",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz";,
+      "integrity": 
"sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==",
       "engines": {
         "node": ">=6.9.0"
       }
@@ -444,10 +447,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.18.10",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz";,
+      "integrity": 
"sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz";,
-      "integrity": 
"sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz";,
+      "integrity": 
"sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
       "engines": {
         "node": ">=6.9.0"
       }
@@ -907,12 +918,11 @@
       }
     },
     "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.16.0",
-      "resolved": 
"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz";,
-      "integrity": 
"sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==",
-      "dev": true,
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz";,
+      "integrity": 
"sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.18.6"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1856,11 +1866,12 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz";,
-      "integrity": 
"sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
+      "version": "7.19.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz";,
+      "integrity": 
"sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-string-parser": "^7.18.10",
+        "@babel/helper-validator-identifier": "^7.18.6",
         "to-fast-properties": "^2.0.0"
       },
       "engines": {
@@ -2086,6 +2097,195 @@
         "node": ">=10.0.0"
       }
     },
+    "node_modules/@emotion/babel-plugin": {
+      "version": "11.10.2",
+      "resolved": 
"https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz";,
+      "integrity": 
"sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/plugin-syntax-jsx": "^7.17.12",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.0",
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/serialize": "^1.1.0",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.0.13"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/@babel/runtime": {
+      "version": "7.19.0",
+      "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+      "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+      "dependencies": {
+        "regenerator-runtime": "^0.13.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz";,
+      "integrity": 
"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/@emotion/cache": {
+      "version": "11.10.3",
+      "resolved": 
"https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz";,
+      "integrity": 
"sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==",
+      "dependencies": {
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/sheet": "^1.2.0",
+        "@emotion/utils": "^1.2.0",
+        "@emotion/weak-memoize": "^0.3.0",
+        "stylis": "4.0.13"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz";,
+      "integrity": 
"sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
+    },
+    "node_modules/@emotion/is-prop-valid": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz";,
+      "integrity": 
"sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+      "dependencies": {
+        "@emotion/memoize": "^0.8.0"
+      }
+    },
+    "node_modules/@emotion/memoize": {
+      "version": "0.8.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz";,
+      "integrity": 
"sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+    },
+    "node_modules/@emotion/react": {
+      "version": "11.10.4",
+      "resolved": 
"https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz";,
+      "integrity": 
"sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.10.0",
+        "@emotion/cache": "^11.10.0",
+        "@emotion/serialize": "^1.1.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
+        "@emotion/utils": "^1.2.0",
+        "@emotion/weak-memoize": "^0.3.0",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0",
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/react/node_modules/@babel/runtime": {
+      "version": "7.19.0",
+      "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+      "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+      "dependencies": {
+        "regenerator-runtime": "^0.13.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@emotion/serialize": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz";,
+      "integrity": 
"sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==",
+      "dependencies": {
+        "@emotion/hash": "^0.9.0",
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/unitless": "^0.8.0",
+        "@emotion/utils": "^1.2.0",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@emotion/sheet": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz";,
+      "integrity": 
"sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w=="
+    },
+    "node_modules/@emotion/styled": {
+      "version": "11.10.4",
+      "resolved": 
"https://registry.npmjs.org/@emotion/styled/-/styled-11.10.4.tgz";,
+      "integrity": 
"sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.10.0",
+        "@emotion/is-prop-valid": "^1.2.0",
+        "@emotion/serialize": "^1.1.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
+        "@emotion/utils": "^1.2.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0",
+        "@emotion/react": "^11.0.0-rc.0",
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/styled/node_modules/@babel/runtime": {
+      "version": "7.19.0",
+      "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+      "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+      "dependencies": {
+        "regenerator-runtime": "^0.13.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.8.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz";,
+      "integrity": 
"sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
+    },
+    "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz";,
+      "integrity": 
"sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@emotion/utils": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz";,
+      "integrity": 
"sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
+    },
+    "node_modules/@emotion/weak-memoize": {
+      "version": "0.3.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz";,
+      "integrity": 
"sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
+    },
     "node_modules/@eslint/eslintrc": {
       "version": "0.4.3",
       "resolved": 
"https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz";,
@@ -3761,8 +3961,7 @@
     "node_modules/@types/parse-json": {
       "version": "4.0.0",
       "resolved": 
"https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz";,
-      "integrity": 
"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
-      "dev": true
+      "integrity": 
"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
     },
     "node_modules/@types/parse5": {
       "version": "6.0.3",
@@ -5007,6 +5206,20 @@
         "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
       }
     },
+    "node_modules/babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": 
"https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz";,
+      "integrity": 
"sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      },
+      "engines": {
+        "node": ">=10",
+        "npm": ">=6"
+      }
+    },
     "node_modules/babel-plugin-module-resolver": {
       "version": "4.1.0",
       "resolved": 
"https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz";,
@@ -6807,6 +7020,14 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/copy-to-clipboard": {
+      "version": "3.3.2",
+      "resolved": 
"https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz";,
+      "integrity": 
"sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==",
+      "dependencies": {
+        "toggle-selection": "^1.0.6"
+      }
+    },
     "node_modules/copy-webpack-plugin": {
       "version": "6.4.1",
       "resolved": 
"https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz";,
@@ -6879,7 +7100,6 @@
       "version": "7.0.1",
       "resolved": 
"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz";,
       "integrity": 
"sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
-      "dev": true,
       "dependencies": {
         "@types/parse-json": "^4.0.0",
         "import-fresh": "^3.2.1",
@@ -7462,6 +7682,11 @@
       "integrity": 
"sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
       "dev": true
     },
+    "node_modules/csstype": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz";,
+      "integrity": 
"sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+    },
     "node_modules/cyclist": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz";,
@@ -8804,7 +9029,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz";,
       "integrity": 
"sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
       "dependencies": {
         "is-arrayish": "^0.2.1"
       }
@@ -10730,6 +10954,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz";,
+      "integrity": 
"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz";,
@@ -12768,8 +12997,7 @@
     "node_modules/is-arrayish": {
       "version": "0.2.1",
       "resolved": 
"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz";,
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
     },
     "node_modules/is-bigint": {
       "version": "1.0.4",
@@ -15246,8 +15474,7 @@
     "node_modules/json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": 
"https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz";,
-      "integrity": 
"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": 
"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "node_modules/json-schema": {
       "version": "0.2.3",
@@ -16147,8 +16374,7 @@
     "node_modules/lines-and-columns": {
       "version": "1.1.6",
       "resolved": 
"https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz";,
-      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
-      "dev": true
+      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
     },
     "node_modules/listify": {
       "version": "1.0.3",
@@ -18406,7 +18632,6 @@
       "version": "5.2.0",
       "resolved": 
"https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz";,
       "integrity": 
"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.0.0",
         "error-ex": "^1.3.1",
@@ -18523,7 +18748,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz";,
       "integrity": 
"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -19821,6 +20045,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-copy-to-clipboard": {
+      "version": "5.1.0",
+      "resolved": 
"https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz";,
+      "integrity": 
"sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+      "dependencies": {
+        "copy-to-clipboard": "^3.3.1",
+        "prop-types": "^15.8.1"
+      },
+      "peerDependencies": {
+        "react": "^15.3.0 || 16 || 17 || 18"
+      }
+    },
     "node_modules/react-dom": {
       "version": "17.0.2",
       "resolved": 
"https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz";,
@@ -22390,6 +22626,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/stylis": {
+      "version": "4.0.13",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz";,
+      "integrity": 
"sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+    },
     "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": 
"https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz";,
@@ -22947,6 +23188,11 @@
         "ret": "~0.1.10"
       }
     },
+    "node_modules/toggle-selection": {
+      "version": "1.0.6",
+      "resolved": 
"https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz";,
+      "integrity": 
"sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+    },
     "node_modules/toidentifier": {
       "version": "1.0.0",
       "resolved": 
"https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz";,
@@ -25852,7 +26098,6 @@
       "version": "1.10.2",
       "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz";,
       "integrity": 
"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
-      "dev": true,
       "engines": {
         "node": ">= 6"
       }
@@ -26166,11 +26411,11 @@
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz";,
-      "integrity": 
"sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz";,
+      "integrity": 
"sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-module-transforms": {
@@ -26197,10 +26442,9 @@
       }
     },
     "@babel/helper-plugin-utils": {
-      "version": "7.14.5",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz";,
-      "integrity": 
"sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
-      "dev": true
+      "version": "7.19.0",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz";,
+      "integrity": 
"sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw=="
     },
     "@babel/helper-remap-async-to-generator": {
       "version": "7.16.0",
@@ -26249,10 +26493,15 @@
         "@babel/types": "^7.16.0"
       }
     },
+    "@babel/helper-string-parser": {
+      "version": "7.18.10",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz";,
+      "integrity": 
"sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw=="
+    },
     "@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz";,
-      "integrity": 
"sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w=="
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz";,
+      "integrity": 
"sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g=="
     },
     "@babel/helper-validator-option": {
       "version": "7.14.5",
@@ -26556,12 +26805,11 @@
       }
     },
     "@babel/plugin-syntax-jsx": {
-      "version": "7.16.0",
-      "resolved": 
"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz";,
-      "integrity": 
"sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==",
-      "dev": true,
+      "version": "7.18.6",
+      "resolved": 
"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz";,
+      "integrity": 
"sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-logical-assignment-operators": {
@@ -27196,11 +27444,12 @@
       }
     },
     "@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz";,
-      "integrity": 
"sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
+      "version": "7.19.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz";,
+      "integrity": 
"sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
       "requires": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-string-parser": "^7.18.10",
+        "@babel/helper-validator-identifier": "^7.18.6",
         "to-fast-properties": "^2.0.0"
       }
     },
@@ -27389,6 +27638,156 @@
       "integrity": 
"sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==",
       "dev": true
     },
+    "@emotion/babel-plugin": {
+      "version": "11.10.2",
+      "resolved": 
"https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz";,
+      "integrity": 
"sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==",
+      "requires": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/plugin-syntax-jsx": "^7.17.12",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.0",
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/serialize": "^1.1.0",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.0.13"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.19.0",
+          "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+          "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": 
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz";,
+          "integrity": 
"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+        }
+      }
+    },
+    "@emotion/cache": {
+      "version": "11.10.3",
+      "resolved": 
"https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz";,
+      "integrity": 
"sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==",
+      "requires": {
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/sheet": "^1.2.0",
+        "@emotion/utils": "^1.2.0",
+        "@emotion/weak-memoize": "^0.3.0",
+        "stylis": "4.0.13"
+      }
+    },
+    "@emotion/hash": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz";,
+      "integrity": 
"sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ=="
+    },
+    "@emotion/is-prop-valid": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz";,
+      "integrity": 
"sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+      "requires": {
+        "@emotion/memoize": "^0.8.0"
+      }
+    },
+    "@emotion/memoize": {
+      "version": "0.8.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz";,
+      "integrity": 
"sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+    },
+    "@emotion/react": {
+      "version": "11.10.4",
+      "resolved": 
"https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz";,
+      "integrity": 
"sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.10.0",
+        "@emotion/cache": "^11.10.0",
+        "@emotion/serialize": "^1.1.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
+        "@emotion/utils": "^1.2.0",
+        "@emotion/weak-memoize": "^0.3.0",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.19.0",
+          "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+          "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
+        }
+      }
+    },
+    "@emotion/serialize": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz";,
+      "integrity": 
"sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==",
+      "requires": {
+        "@emotion/hash": "^0.9.0",
+        "@emotion/memoize": "^0.8.0",
+        "@emotion/unitless": "^0.8.0",
+        "@emotion/utils": "^1.2.0",
+        "csstype": "^3.0.2"
+      }
+    },
+    "@emotion/sheet": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz";,
+      "integrity": 
"sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w=="
+    },
+    "@emotion/styled": {
+      "version": "11.10.4",
+      "resolved": 
"https://registry.npmjs.org/@emotion/styled/-/styled-11.10.4.tgz";,
+      "integrity": 
"sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ==",
+      "requires": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.10.0",
+        "@emotion/is-prop-valid": "^1.2.0",
+        "@emotion/serialize": "^1.1.0",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0",
+        "@emotion/utils": "^1.2.0"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.19.0",
+          "resolved": 
"https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz";,
+          "integrity": 
"sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==",
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
+        }
+      }
+    },
+    "@emotion/unitless": {
+      "version": "0.8.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz";,
+      "integrity": 
"sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
+    },
+    "@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz";,
+      "integrity": 
"sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
+      "requires": {}
+    },
+    "@emotion/utils": {
+      "version": "1.2.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz";,
+      "integrity": 
"sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw=="
+    },
+    "@emotion/weak-memoize": {
+      "version": "0.3.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz";,
+      "integrity": 
"sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg=="
+    },
     "@eslint/eslintrc": {
       "version": "0.4.3",
       "resolved": 
"https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz";,
@@ -28631,8 +29030,7 @@
     "@types/parse-json": {
       "version": "4.0.0",
       "resolved": 
"https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz";,
-      "integrity": 
"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
-      "dev": true
+      "integrity": 
"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
     },
     "@types/parse5": {
       "version": "6.0.3",
@@ -29646,6 +30044,16 @@
         "@types/babel__traverse": "^7.0.6"
       }
     },
+    "babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": 
"https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz";,
+      "integrity": 
"sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      }
+    },
     "babel-plugin-module-resolver": {
       "version": "4.1.0",
       "resolved": 
"https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz";,
@@ -31087,6 +31495,14 @@
       "resolved": 
"https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz";,
       "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
     },
+    "copy-to-clipboard": {
+      "version": "3.3.2",
+      "resolved": 
"https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz";,
+      "integrity": 
"sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==",
+      "requires": {
+        "toggle-selection": "^1.0.6"
+      }
+    },
     "copy-webpack-plugin": {
       "version": "6.4.1",
       "resolved": 
"https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz";,
@@ -31139,7 +31555,6 @@
       "version": "7.0.1",
       "resolved": 
"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz";,
       "integrity": 
"sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
-      "dev": true,
       "requires": {
         "@types/parse-json": "^4.0.0",
         "import-fresh": "^3.2.1",
@@ -31605,6 +32020,11 @@
         }
       }
     },
+    "csstype": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz";,
+      "integrity": 
"sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+    },
     "cyclist": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz";,
@@ -32650,7 +33070,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz";,
       "integrity": 
"sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
       "requires": {
         "is-arrayish": "^0.2.1"
       }
@@ -34071,6 +34490,11 @@
         }
       }
     },
+    "find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz";,
+      "integrity": 
"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+    },
     "find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz";,
@@ -35608,8 +36032,7 @@
     "is-arrayish": {
       "version": "0.2.1",
       "resolved": 
"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz";,
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
     },
     "is-bigint": {
       "version": "1.0.4",
@@ -37448,8 +37871,7 @@
     "json-parse-even-better-errors": {
       "version": "2.3.1",
       "resolved": 
"https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz";,
-      "integrity": 
"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
-      "dev": true
+      "integrity": 
"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "json-schema": {
       "version": "0.2.3",
@@ -38155,8 +38577,7 @@
     "lines-and-columns": {
       "version": "1.1.6",
       "resolved": 
"https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz";,
-      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
-      "dev": true
+      "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
     },
     "listify": {
       "version": "1.0.3",
@@ -39908,7 +40329,6 @@
       "version": "5.2.0",
       "resolved": 
"https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz";,
       "integrity": 
"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
-      "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
         "error-ex": "^1.3.1",
@@ -40002,8 +40422,7 @@
     "path-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz";,
-      "integrity": 
"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
-      "dev": true
+      "integrity": 
"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
     },
     "pbkdf2": {
       "version": "3.1.2",
@@ -41068,6 +41487,15 @@
         "object-assign": "^4.1.1"
       }
     },
+    "react-copy-to-clipboard": {
+      "version": "5.1.0",
+      "resolved": 
"https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz";,
+      "integrity": 
"sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+      "requires": {
+        "copy-to-clipboard": "^3.3.1",
+        "prop-types": "^15.8.1"
+      }
+    },
     "react-dom": {
       "version": "17.0.2",
       "resolved": 
"https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz";,
@@ -43125,6 +43553,11 @@
         }
       }
     },
+    "stylis": {
+      "version": "4.0.13",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz";,
+      "integrity": 
"sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+    },
     "supports-color": {
       "version": "5.5.0",
       "resolved": 
"https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz";,
@@ -43574,6 +44007,11 @@
         "is-number": "^7.0.0"
       }
     },
+    "toggle-selection": {
+      "version": "1.0.6",
+      "resolved": 
"https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz";,
+      "integrity": 
"sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+    },
     "toidentifier": {
       "version": "1.0.0",
       "resolved": 
"https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz";,
@@ -45850,8 +46288,7 @@
     "yaml": {
       "version": "1.10.2",
       "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz";,
-      "integrity": 
"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
-      "dev": true
+      "integrity": 
"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
     },
     "yargs": {
       "version": "16.2.0",
diff --git a/config-ui/package.json b/config-ui/package.json
index 2d57941b..51d2736b 100644
--- a/config-ui/package.json
+++ b/config-ui/package.json
@@ -16,6 +16,8 @@
     "@blueprintjs/core": "^3.49.1",
     "@blueprintjs/popover2": "^0.11.4",
     "@blueprintjs/select": "^3.18.10",
+    "@emotion/react": "^11.10.4",
+    "@emotion/styled": "^11.10.4",
     "@fontsource/inter": "^4.5.11",
     "@uiw/react-textarea-code-editor": "^1.4.16",
     "axios": "^0.21.4",
@@ -28,6 +30,7 @@
     "file-saver": "^2.0.5",
     "jetbrains-mono": "^1.0.6",
     "react": "17.0.2",
+    "react-copy-to-clipboard": "^5.1.0",
     "react-dom": "17.0.2",
     "react-router-dom": "^5.3.0",
     "react-transition-group": "^2.9.0",
@@ -38,6 +41,7 @@
     "@babel/preset-env": "^7.12.7",
     "@babel/preset-flow": "^7.14.5",
     "@babel/preset-react": "^7.12.7",
+    "@emotion/babel-plugin": "^11.10.2",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
     "@svgr/webpack": "^5.5.0",
     "@testing-library/jest-dom": "^5.14.1",
diff --git a/config-ui/src/.babelrc b/config-ui/src/.babelrc
index ce0384c9..e26bf167 100644
--- a/config-ui/src/.babelrc
+++ b/config-ui/src/.babelrc
@@ -1,6 +1,4 @@
 {
   "presets": ["@babel/preset-env", "@babel/preset-react"],
-  "plugins": [
-    "@babel/plugin-transform-runtime"
-  ]
+  "plugins": ["@emotion", "@babel/plugin-transform-runtime"]
 }
diff --git a/config-ui/src/App.js b/config-ui/src/App.js
index 5d51f4df..ae50bfed 100644
--- a/config-ui/src/App.js
+++ b/config-ui/src/App.js
@@ -51,6 +51,7 @@ import CreateBlueprint from 
'@/pages/blueprints/create-blueprint'
 import BlueprintDetail from '@/pages/blueprints/blueprint-detail'
 import BlueprintSettings from '@/pages/blueprints/blueprint-settings'
 import Connections from '@/pages/connections/index'
+import { Webhook as WebhookConnection } from '@/pages/connections/webhook'
 import MigrationAlertDialog from '@/components/MigrationAlertDialog'
 
 function App (props) {
@@ -119,6 +120,9 @@ function App (props) {
       <Route exact path='/connections'>
         <Connections />
       </Route>
+      <Route exact path='/connections/webhook'>
+        <WebhookConnection />
+      </Route>
       <Route exact path='/offline'>
         <Offline />
       </Route>
diff --git a/config-ui/src/components/Sidebar/MenuConfiguration.jsx 
b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
index 18645f5b..ea70c611 100644
--- a/config-ui/src/components/Sidebar/MenuConfiguration.jsx
+++ b/config-ui/src/components/Sidebar/MenuConfiguration.jsx
@@ -68,6 +68,14 @@ const MenuConfiguration = (activeRoute) => {
           active: activeRoute.url.endsWith('/integrations/tapd') || 
activeRoute.url.endsWith('/tapd'),
           icon: 'layers',
           classNames: [],
+        },
+        {
+          id: 5,
+          label: 'Webhook',
+          route: '/connections/webhook',
+          active: activeRoute.url.endsWith('/connections/webhook') || 
activeRoute.url.endsWith('/webhook'),
+          icon: 'layers',
+          classNames: [],
         }
       ]
     },
diff --git a/config-ui/src/hooks/useWebhookManager.jsx 
b/config-ui/src/hooks/useWebhookManager.jsx
new file mode 100644
index 00000000..0f1c6ffc
--- /dev/null
+++ b/config-ui/src/hooks/useWebhookManager.jsx
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import { useState, useEffect } from 'react'
+import axios from 'axios'
+
+import { DEVLAKE_ENDPOINT } from '@/utils/config'
+import { ToastNotification } from '@/components/Toast'
+
+export const useWebhookManager = () => {
+  const [loading, setLoading] = useState(false)
+  const [operating, setOperating] = useState(false)
+  const [data, setData] = useState([])
+
+  const fetch = async () => {
+    setLoading(true)
+    try {
+      const res = await 
axios.get(`${DEVLAKE_ENDPOINT}/plugins/webhook/connections`)
+      setData(res.data)
+    } catch (err) {
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  useEffect(() => {
+    ;(async () => {
+      await fetch()
+    })()
+  }, [])
+
+  const onCreate = async (payload) => {
+    setOperating(true)
+    try {
+      const {
+        data: { id },
+      } = await axios.post(`${DEVLAKE_ENDPOINT}/plugins/webhook/connections`, 
payload)
+      const { data } = await 
axios.get(`${DEVLAKE_ENDPOINT}/plugins/webhook/connections/${id}`)
+      fetch()
+      return data
+    } catch (err) {
+    } finally {
+      setOperating(false)
+    }
+  }
+
+  const onUpdate = async (id, payload) => {
+    setOperating(true)
+    ToastNotification.clear()
+    try {
+      await 
axios.patch(`${DEVLAKE_ENDPOINT}/plugins/webhook/connections/${id}`, payload)
+      ToastNotification.show({
+        message: 'Update record succeeded.',
+        intent: 'success',
+        icon: 'small-tick',
+      })
+      fetch()
+    } catch (err) {
+      ToastNotification.show({
+        message: err.response.data.message,
+        intent: 'danger',
+        icon: 'error',
+      })
+    } finally {
+      setOperating(false)
+    }
+  }
+
+  const onDelete = async (id) => {
+    setOperating(true)
+    ToastNotification.clear()
+    try {
+      await 
axios.delete(`${DEVLAKE_ENDPOINT}/plugins/webhook/connections/${id}`)
+      ToastNotification.show({
+        message: 'Delete record succeeded.',
+        intent: 'success',
+        icon: 'small-tick',
+      })
+      fetch()
+    } catch (err) {
+    } finally {
+      setOperating(false)
+    }
+  }
+
+  return {
+    loading,
+    data,
+    operating,
+    onCreate,
+    onUpdate,
+    onDelete,
+  }
+}
diff --git a/config-ui/src/images/icons/copy.svg 
b/config-ui/src/images/icons/copy.svg
new file mode 100644
index 00000000..1982d992
--- /dev/null
+++ b/config-ui/src/images/icons/copy.svg
@@ -0,0 +1,13 @@
+<svg width="100" height="100" viewBox="0 0 16 16" fill="none"
+  xmlns="http://www.w3.org/2000/svg";>
+  <g clip-path="url(#clip0_1432_12373)">
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M7.33325 6.66671C6.96506 
6.66671 6.66659 6.96518 6.66659 7.33337V13.3334C6.66659 13.7016 6.96506 14 
7.33325 14H13.3333C13.7014 14 13.9999 13.7016 13.9999 13.3334V7.33337C13.9999 
6.96518 13.7014 6.66671 13.3333 6.66671H7.33325ZM5.33325 7.33337C5.33325 6.2288 
6.22868 5.33337 7.33325 5.33337H13.3333C14.4378 5.33337 15.3333 6.2288 15.3333 
7.33337V13.3334C15.3333 14.4379 14.4378 15.3334 13.3333 15.3334H7.33325C6.22868 
15.3334 5.33325 14.437 [...]
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M2.66675 1.99996C2.48994 
1.99996 2.32037 2.0702 2.19534 2.19522C2.07032 2.32025 2.00008 2.48981 2.00008 
2.66663V8.66663C2.00008 8.84344 2.07032 9.01301 2.19534 9.13803C2.32037 9.26305 
2.48994 9.33329 2.66675 9.33329H3.33341C3.7016 9.33329 4.00008 9.63177 4.00008 
9.99996C4.00008 10.3681 3.7016 10.6666 3.33341 10.6666H2.66675C2.13631 10.6666 
1.62761 10.4559 1.25253 10.0808C0.877462 9.70577 0.666748 9.19706 0.666748 
8.66663V2.66663C0.6667 [...]
+  </g>
+  <defs>
+    <clipPath id="clip0_1432_12373">
+      <rect width="16" height="16" fill="white"/>
+    </clipPath>
+  </defs>
+</svg>
+  
\ No newline at end of file
diff --git a/config-ui/src/images/icons/delete.svg 
b/config-ui/src/images/icons/delete.svg
new file mode 100644
index 00000000..f21db5c8
--- /dev/null
+++ b/config-ui/src/images/icons/delete.svg
@@ -0,0 +1,7 @@
+<svg width="100" height="100" viewBox="0 0 24 24" fill="none"
+  xmlns="http://www.w3.org/2000/svg";>
+  <path d="M10 10C10.5523 10 11 10.4477 11 11V17C11 17.5523 10.5523 18 10 
18C9.44772 18 9 17.5523 9 17V11C9 10.4477 9.44772 10 10 10Z" fill="#7497F7"/>
+  <path d="M15 17V11C15 10.4477 14.5523 10 14 10C13.4477 10 13 10.4477 13 
11V17C13 17.5523 13.4477 18 14 18C14.5523 18 15 17.5523 15 17Z" fill="#7497F7"/>
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M7 5V4C7 3.20435 7.31607 
2.44129 7.87868 1.87868C8.44129 1.31607 9.20435 1 10 1H14C14.7956 1 15.5587 
1.31607 16.1213 1.87868C16.6839 2.44129 17 3.20435 17 4V5H21C21.5523 5 22 
5.44772 22 6C22 6.55229 21.5523 7 21 7H20V20C20 20.7957 19.6839 21.5587 19.1213 
22.1213C18.5587 22.6839 17.7957 23 17 23H7C6.20435 23 5.44129 22.6839 4.87868 
22.1213C4.31607 21.5587 4 20.7957 4 20V7H3C2.44772 7 2 6.55229 2 6C2 5.44772 
2.44772 5 3 5H7ZM9.29289 3.2928 [...]
+</svg>
+  
\ No newline at end of file
diff --git a/config-ui/src/images/icons/setting-con.svg 
b/config-ui/src/images/icons/setting-con.svg
new file mode 100644
index 00000000..6502fe0f
--- /dev/null
+++ b/config-ui/src/images/icons/setting-con.svg
@@ -0,0 +1,6 @@
+<svg width="100" height="100" viewBox="0 0 24 24" fill="none"
+  xmlns="http://www.w3.org/2000/svg";>
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M12 7.50004C9.51472 7.50004 
7.5 9.51476 7.5 12C7.5 14.4853 9.51472 16.5 12 16.5C14.4853 16.5 16.5 14.4853 
16.5 12C16.5 9.51476 14.4853 7.50004 12 7.50004ZM9.5 12C9.5 10.6193 10.6193 
9.50004 12 9.50004C13.3807 9.50004 14.5 10.6193 14.5 12C14.5 13.3808 13.3807 
14.5 12 14.5C10.6193 14.5 9.5 13.3808 9.5 12Z" fill="#7497F7"/>
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M10.2168 1.93349C10.0009 
1.51031 9.51711 1.29744 9.05927 1.42418C7.3353 1.9014 5.78257 2.78707 4.51135 
3.96909C4.18782 4.26992 4.10003 4.7474 4.29539 5.14364C4.42608 5.40871 4.5 
5.70752 4.5 6.02643C4.5 7.13099 3.60457 8.02643 2.5 8.02643L2.47217 
8.02623C2.0301 8.01992 1.63635 8.30465 1.50385 8.72645C1.17624 9.76945 1 
10.8783 1 12.0264C1 12.7842 1.07677 13.5251 1.22328 14.2413C1.32592 14.7431 
1.79108 15.0862 2.30078 15.0361C2.36594 15.029 [...]
+</svg>
+  
\ No newline at end of file
diff --git a/config-ui/src/images/icons/vector.svg 
b/config-ui/src/images/icons/vector.svg
new file mode 100644
index 00000000..25b9f07e
--- /dev/null
+++ b/config-ui/src/images/icons/vector.svg
@@ -0,0 +1,5 @@
+<svg width="100" height="100" viewBox="0 0 20 20" fill="none"
+  xmlns="http://www.w3.org/2000/svg";>
+  <path d="M10 0C4.47768 0 0 4.47768 0 10C0 15.5223 4.47768 20 10 20C15.5223 
20 20 15.5223 20 10C20 4.47768 15.5223 0 10 0ZM14.3192 6.73438L9.6183 
13.2522C9.5526 13.3439 9.46599 13.4187 9.36564 13.4702C9.26529 13.5218 9.15411 
13.5486 9.0413 13.5486C8.92848 13.5486 8.8173 13.5218 8.71695 13.4702C8.6166 
13.4187 8.52999 13.3439 8.46429 13.2522L5.6808 9.39509C5.59598 9.27679 5.6808 
9.11161 5.82589 9.11161H6.87277C7.10045 9.11161 7.31696 9.22098 7.45089 
9.40848L9.04018 11.6138L12.5491 6.74777 [...]
+</svg>
+  
\ No newline at end of file
diff --git a/config-ui/src/images/integrations/webhook.svg 
b/config-ui/src/images/integrations/webhook.svg
new file mode 100644
index 00000000..54850393
--- /dev/null
+++ b/config-ui/src/images/integrations/webhook.svg
@@ -0,0 +1,5 @@
+<svg width="100" height="100" viewBox="0 0 58 52" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M27.2239 21.9899C24.8672 25.8703 22.6083 29.6289 20.3028 
33.3587C19.7113 34.3151 19.4179 35.0951 19.8914 36.3131C21.1971 39.6749 19.3543 
42.947 15.8922 43.8365C12.6319 44.6743 9.44613 42.5714 8.79873 39.1488C8.2243 
36.1184 10.626 33.1474 14.0369 32.6745C14.3226 32.6334 14.6144 32.6289 15.0942 
32.5939L20.2842 24.0791C17.0239 20.9013 15.0771 17.1851 15.5071 12.5811C15.8176 
9.32719 17.1186 6.51426 19.5064 4.20918C21.7027 2.06888 24.5804 0.728584 
27.6616 0.410795C30.7429 0.0930059 [...]
+  <path d="M33.9556 17.3995C35.6153 20.2687 37.3028 23.1774 38.9733 
26.0664C47.4206 23.5074 53.789 28.0856 56.0727 32.9892C58.8331 38.9116 56.9468 
45.9257 51.527 49.5794C45.9643 53.3305 38.9283 52.6889 33.9991 47.8719L37.8664 
44.7017C42.7351 47.7898 46.9921 47.6438 50.153 43.9886C51.4577 42.4671 52.1615 
40.5375 52.1365 38.5507C52.1114 36.564 51.3591 34.652 50.0164 33.1626C46.8166 
29.6396 42.5286 29.5316 37.3463 32.9147C35.1961 29.1789 33.0086 25.4734 30.9267 
21.7117C30.2249 20.4436 29.45 [...]
+  <path d="M37.9269 40.8548H27.7564C26.7815 44.7807 24.6763 47.951 21.0496 
49.9672C18.3007 51.5212 15.0826 52.0823 11.955 51.5531C5.99955 50.6119 1.13243 
45.3616 0.700828 39.4483C0.216445 32.7505 4.9159 26.7978 11.1818 25.4597C11.615 
26.9985 12.0512 28.5524 12.4844 30.0866C6.7401 32.9588 4.74667 36.5776 6.35507 
41.1042C7.77096 45.0864 11.7889 47.2698 16.1623 46.4259C20.6242 45.5638 22.8722 
41.9328 22.599 36.1048C26.828 36.1048 31.0602 36.0622 35.2892 36.126C36.9411 
36.1519 38.2157 35.984 [...]
+</svg>
\ No newline at end of file
diff --git a/config-ui/src/pages/configure/integration/index.jsx 
b/config-ui/src/pages/configure/integration/index.jsx
index 5e589525..e7cb32c5 100644
--- a/config-ui/src/pages/configure/integration/index.jsx
+++ b/config-ui/src/pages/configure/integration/index.jsx
@@ -22,16 +22,17 @@ import Sidebar from '@/components/Sidebar'
 import AppCrumbs from '@/components/Breadcrumbs'
 import Content from '@/components/Content'
 import { integrationsData } from '@/data/integrations'
+import { ReactComponent as WebHookProviderIcon } from 
'@/images/integrations/webhook.svg'
 
 import '@/styles/integration.scss'
 
-export default function Integration () {
+export default function Integration() {
   const history = useHistory()
 
   const [activeProvider, setActiveProvider] = useState(integrationsData[0])
 
   const handleProviderClick = (providerId) => {
-    const theProvider = integrationsData.find(p => p.id === providerId)
+    const theProvider = integrationsData.find((p) => p.id === providerId)
     if (theProvider) {
       setActiveProvider(theProvider)
       history.push(`/integrations/${theProvider.id}`)
@@ -45,9 +46,7 @@ export default function Integration () {
     console.log(activeProvider)
   }, [activeProvider, history])
 
-  useEffect(() => {
-
-  }, [])
+  useEffect(() => {}, [])
 
   return (
     <>
@@ -59,7 +58,7 @@ export default function Integration () {
             <AppCrumbs
               items={[
                 { href: '/', icon: false, text: 'Dashboard' },
-                { href: '/integrations', icon: false, text: 'Integrations', 
current: true }
+                { href: '/integrations', icon: false, text: 'Integrations', 
current: true },
               ]}
             />
             <div className='headlineContainer'>
@@ -82,6 +81,21 @@ export default function Integration () {
                 </div>
               ))}
             </div>
+            <div className='headlineContainer'>
+              <h1>Webhooks</h1>
+              <p className='page-description'>
+                You can use Webhooks to define Issues and Deployments to be 
used in calculating DORA metrics. Please note: Webhooks cannot
+                be created or managed in Blueprints.
+              </p>
+            </div>
+            <div className='integrationProviders'>
+              <div className='iProvider' style={{ width: 130 }} onClick={() => 
history.push('/connections/webhook')}>
+                <div className='providerIcon'>
+                  <WebHookProviderIcon className='providerIconSvg' width='40' 
height='40' />
+                </div>
+                <div className='providerName'>Issue/Deployment Webhook</div>
+              </div>
+            </div>
           </main>
         </Content>
       </div>
diff --git a/config-ui/src/pages/connections/webhook/add-modal.jsx 
b/config-ui/src/pages/connections/webhook/add-modal.jsx
new file mode 100644
index 00000000..37438a19
--- /dev/null
+++ b/config-ui/src/pages/connections/webhook/add-modal.jsx
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React, { useState } from 'react'
+import { Dialog, Button, Toaster, Position, Intent } from '@blueprintjs/core'
+import { CopyToClipboard } from 'react-copy-to-clipboard'
+
+import { ReactComponent as Vector } from '@/images/icons/vector.svg'
+import { ReactComponent as CopyIcon } from '@/images/icons/copy.svg'
+
+import * as S from './styled'
+
+const CopyToaster = Toaster.create({
+  position: Position.TOP_RIGHT
+})
+
+const postUrlPrefix = `${window.location.origin}/api`
+
+export const AddModal = ({ onSubmit, onCancel }) => {
+  const [step, setStep] = useState(1)
+  const [name, setName] = useState('')
+  const [error, setError] = useState('')
+  const [record, setRecord] = useState({})
+
+  const handleInputChange = (e) => {
+    setName(e.target.value)
+    setError('')
+  }
+
+  const handleSubmit = async () => {
+    if (!name) {
+      setError('Name is required')
+      return
+    }
+
+    const res = await onSubmit({ name })
+
+    setStep(2)
+    setRecord({
+      postIssuesEndpoint: `${postUrlPrefix}${res.postIssuesEndpoint}`,
+      closeIssuesEndpoint: `${postUrlPrefix}${res.closeIssuesEndpoint}`,
+      postPipelineTaskEndpoint: 
`${postUrlPrefix}${res.postPipelineTaskEndpoint}`,
+      closePipelineEndpoint: `${postUrlPrefix}${res.closePipelineEndpoint}`
+    })
+  }
+
+  return (
+    <Dialog isOpen={true} title='Add a New Webhook' style={{ width: 640 }} 
onClose={onCancel}>
+      <S.FormWrapper>
+        {step === 1 && (
+          <>
+            <div className='form'>
+              <h2>Webhook Name *</h2>
+              <p>Give your Webhook a unique name to help you identify it in 
the future.</p>
+              <input
+                type='text'
+                placeholder='Your Webhook Name'
+                className={error ? 'has-error' : ''}
+                value={name || ''}
+                onChange={handleInputChange}
+              />
+              {error && <p className='error'>{error}</p>}
+            </div>
+            <div className='btns'>
+              <Button onClick={onCancel}>Cancel</Button>
+              <Button intent={Intent.PRIMARY} onClick={handleSubmit}>
+                Generate POST URL
+              </Button>
+            </div>
+          </>
+        )}
+        {step === 2 && (
+          <>
+            <div className='tips'>
+              <Vector width={20} height={20} />
+              <span>POST URL Generated!</span>
+            </div>
+            <div className='url'>
+              <h2>POST URL</h2>
+              <p>
+                Copy the following URLs to your issue tracking tool for 
Incidents and CI tool for Deployments by making a POST to DevLake.
+              </p>
+              <h3>Incident</h3>
+              <p>Send incident opened and reopened events</p>
+              <div className='block'>
+                <span>{record.postIssuesEndpoint}</span>
+                <CopyToClipboard
+                  text={record.postIssuesEndpoint}
+                  onCopy={() =>
+                    CopyToaster.show({
+                      message: 'Copy successfully.',
+                      intent: Intent.SUCCESS
+                    })
+                  }
+                >
+                  <CopyIcon width={16} height={16} />
+                </CopyToClipboard>
+              </div>
+              <p>Send incident resolved events</p>
+              <div className='block'>
+                <span>{record.closeIssuesEndpoint}</span>
+                <CopyToClipboard
+                  text={record.closeIssuesEndpoint}
+                  onCopy={() =>
+                    CopyToaster.show({
+                      message: 'Copy successfully.',
+                      intent: Intent.SUCCESS
+                    })
+                  }
+                >
+                  <CopyIcon width={16} height={16} />
+                </CopyToClipboard>
+              </div>
+              <h3>Deployment</h3>
+              <p>Send task started and finished events</p>
+              <div className='block'>
+                <span>{record.postPipelineTaskEndpoint}</span>
+                <CopyToClipboard text={record.postPipelineTaskEndpoint}>
+                  <CopyIcon width={16} height={16} />
+                </CopyToClipboard>
+              </div>
+              <p>Send deployment finished events</p>
+              <div className='block'>
+                <span>{record.closePipelineEndpoint}</span>
+                <CopyToClipboard text={record.closePipelineEndpoint}>
+                  <CopyIcon width={16} height={16} />
+                </CopyToClipboard>
+              </div>
+            </div>
+            <div className='btns'>
+              <Button intent={Intent.PRIMARY} onClick={onCancel}>
+                Done
+              </Button>
+            </div>
+          </>
+        )}
+      </S.FormWrapper>
+    </Dialog>
+  )
+}
diff --git a/config-ui/src/pages/connections/webhook/delete-modal.jsx 
b/config-ui/src/pages/connections/webhook/delete-modal.jsx
new file mode 100644
index 00000000..b713334c
--- /dev/null
+++ b/config-ui/src/pages/connections/webhook/delete-modal.jsx
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React from 'react'
+import { Dialog, Button, Intent } from '@blueprintjs/core'
+
+import * as S from './styled'
+
+export const DeleteModal = ({ record, onSubmit, onCancel }) => {
+  const handleSubmit = () => {
+    onSubmit(record.id)
+    onCancel()
+  }
+
+  return (
+    <Dialog isOpen={true} title='Delete this Webhook?' onClose={onCancel}>
+      <S.FormWrapper>
+        <div className='message'>
+          <p>This Webhook cannot be recovered once it’s deleted.</p>
+        </div>
+        <div className='btns'>
+          <Button onClick={onCancel}>Cancel</Button>
+          <Button intent={Intent.PRIMARY} onClick={handleSubmit}>
+            Confirm
+          </Button>
+        </div>
+      </S.FormWrapper>
+    </Dialog>
+  )
+}
diff --git a/config-ui/src/pages/connections/webhook/index.jsx 
b/config-ui/src/pages/connections/webhook/index.jsx
new file mode 100644
index 00000000..795eaa9b
--- /dev/null
+++ b/config-ui/src/pages/connections/webhook/index.jsx
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React, { useState } from 'react'
+import { Link } from 'react-router-dom'
+import { Icon, Button, Intent } from '@blueprintjs/core'
+
+import Nav from '@/components/Nav'
+import Sidebar from '@/components/Sidebar'
+import Content from '@/components/Content'
+import AppCrumbs from '@/components/Breadcrumbs'
+import { useWebhookManager } from '@/hooks/useWebhookManager'
+import { ReactComponent as WebHookProviderIcon } from 
'@/images/integrations/webhook.svg'
+import { ReactComponent as EditIcon } from '@/images/icons/setting-con.svg'
+import { ReactComponent as DeleteIcon } from '@/images/icons/delete.svg'
+
+import { AddModal } from './add-modal'
+import { ViewOrEditModal } from './view-or-edit-modal'
+import { DeleteModal } from './delete-modal'
+import * as S from './styled'
+
+const postUrlPrefix = `${window.location.origin}/api`
+
+export const Webhook = () => {
+  // defined the form modal is add | edit | delete
+  const [modalType, setModalType] = useState()
+  // defined the edit or delete record
+  const [record, setRecord] = useState()
+
+  const { loading, data, operating, onCreate, onUpdate, onDelete } = 
useWebhookManager()
+
+  const handleShowModal = (mt, r) => {
+    setModalType(mt)
+    setRecord((existingRecord) =>
+      r
+        ? {
+            ...r,
+            postIssuesEndpoint: `${postUrlPrefix}${r.postIssuesEndpoint}`,
+            closeIssuesEndpoint: `${postUrlPrefix}${r.closeIssuesEndpoint}`,
+            postPipelineTaskEndpoint: 
`${postUrlPrefix}${r.postPipelineTaskEndpoint}`,
+            closePipelineEndpoint: `${postUrlPrefix}${r.closePipelineEndpoint}`
+          }
+        : existingRecord
+    )
+  }
+
+  const handleHideModal = () => {
+    setModalType()
+    setRecord()
+  }
+
+  return (
+    <div className='container'>
+      <Nav />
+      <Sidebar />
+      <Content>
+        <div className='main'>
+          <AppCrumbs
+            items={[
+              { href: '/', icon: false, text: 'Dashboard' },
+              // use /connections replace here
+              { href: '/integrations', icon: false, text: 'Integrations' },
+              {
+                href: '/connections/webhook',
+                icon: false,
+                text: 'Webhook',
+                current: true
+              }
+            ]}
+          />
+          <div className='headlineContainer'>
+            <div
+              style={{
+                display: 'flex',
+                alignItems: 'center',
+                justifyContent: 'space-between',
+                marginBottom: 12
+              }}
+            >
+              <div style={{ display: 'flex', alignItems: 'center' }}>
+                <WebHookProviderIcon className='providerIconSvg' width='30' 
height='30' />
+                <h1 style={{ margin: '0 0 0 8px' }}>Webhook</h1>
+              </div>
+              <Link style={{ color: '#777777' }} to='/integrations'>
+                <Icon icon='undo' size={16} /> Go Back
+              </Link>
+            </div>
+            <div className='page-description'>
+              Use Webhooks to define Incidents and Deployments for your CI 
tools if they are not listed in Data Sources.
+            </div>
+          </div>
+          <div className='manageProvider'>
+            <S.Container>
+              <span>
+                <Button intent='primary' text='Add Webhook' 
loading={operating} onClick={() => handleShowModal('add')} />
+              </span>
+              <S.Wrapper>
+                <S.Grid className='title'>
+                  <li>ID</li>
+                  <li>Webhook Name</li>
+                  <li />
+                </S.Grid>
+                {loading ? (
+                  <div>Loading</div>
+                ) : (
+                  data.map((it, i) => (
+                    <S.Grid key={it.id}>
+                      <li>{i + 1}</li>
+                      <li>{it.name}</li>
+                      <li>
+                        <Button
+                          loading={operating}
+                          intent={Intent.PRIMARY}
+                          minimal
+                          small
+                          icon={<EditIcon width={18} height={18} />}
+                          onClick={() => handleShowModal('edit', it)}
+                        />
+                        <Button
+                          loading={operating}
+                          intent={Intent.PRIMARY}
+                          minimal
+                          small
+                          icon={<DeleteIcon width={18} height={18} />}
+                          onClick={() => handleShowModal('delete', it)}
+                        />
+                      </li>
+                    </S.Grid>
+                  ))
+                )}
+              </S.Wrapper>
+            </S.Container>
+          </div>
+        </div>
+      </Content>
+      {modalType === 'add' && <AddModal onSubmit={onCreate} 
onCancel={handleHideModal} />}
+      {modalType === 'edit' && <ViewOrEditModal record={record} 
onSubmit={onUpdate} onCancel={handleHideModal} />}
+      {modalType === 'delete' && <DeleteModal record={record} 
onSubmit={onDelete} onCancel={handleHideModal} />}
+    </div>
+  )
+}
diff --git a/config-ui/src/pages/connections/webhook/styled.js 
b/config-ui/src/pages/connections/webhook/styled.js
new file mode 100644
index 00000000..98ea7408
--- /dev/null
+++ b/config-ui/src/pages/connections/webhook/styled.js
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import styled from '@emotion/styled'
+
+export const Container = styled.div`
+  margin-top: 48px;
+`
+
+export const Wrapper = styled.div`
+  margin-top: 12px;
+  background: #ffffff;
+  box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px rgba(0, 
0, 0, 0.07);
+  border-radius: 4px;
+`
+
+export const Grid = styled.ul`
+  margin: 0;
+  padding: 0;
+  list-style: none;
+  display: flex;
+  align-items: center;
+  padding: 0 16px;
+  width: 100%;
+  height: 48px;
+  font-size: 14px;
+  color: #292b3f;
+  border-top: 1px solid #bdcefb;
+  box-sizing: border-box;
+
+  &.title {
+    font-size: 16px;
+    font-weight: 600;
+  }
+
+  &:first-child {
+    border-top: 0;
+  }
+
+  li {
+    flex: auto;
+
+    &:first-child {
+      flex: 0 0 200px;
+    }
+
+    &:last-child {
+      flex: 0 0 60px;
+      display: flex;
+      justify-content: space-around;
+
+      & > svg {
+        cursor: pointer;
+      }
+    }
+  }
+`
+
+export const FormWrapper = styled.div`
+  padding: 24px;
+  font-size: 14px;
+  color: #292b3f;
+
+  h2 {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 600;
+  }
+
+  h3 {
+    margin: 16px 0 0;
+    font-size: 14px;
+    font-weight: 600;
+  }
+
+  p {
+    margin: 8px 0;
+    font-size: 12px;
+    color: #94959f;
+  }
+
+  .message {
+    margin-bottom: 24px;
+
+    p {
+      font-size: 14px;
+    }
+  }
+
+  .form {
+    input {
+      padding: 7px 12px;
+      width: 100%;
+      height: 32px;
+      background-color: #ffffff;
+      border: 1px solid #dbe4fd;
+      border-radius: 4px;
+      outline: none;
+      box-sizing: border-box;
+
+      &.has-error {
+        border: 1px solid red;
+      }
+    }
+
+    .error {
+      color: red;
+    }
+  }
+
+  .tips {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: 600;
+    font-size: 16px;
+
+    span {
+      margin-left: 4px;
+    }
+  }
+
+  .url {
+    margin-top: 24px;
+
+    .block {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 10px;
+      height: 38px;
+      background-color: #f0f4fe;
+
+      & > svg {
+        cursor: pointer;
+      }
+    }
+  }
+
+  .btns {
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    margin-top: 24px;
+
+    .bp3-button + .bp3-button {
+      margin-left: 8px;
+    }
+  }
+`
diff --git a/config-ui/src/pages/connections/webhook/view-or-edit-modal.jsx 
b/config-ui/src/pages/connections/webhook/view-or-edit-modal.jsx
new file mode 100644
index 00000000..8d6328d9
--- /dev/null
+++ b/config-ui/src/pages/connections/webhook/view-or-edit-modal.jsx
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React, { useState, useEffect } from 'react'
+import { Dialog, Button, Toaster, Position, Intent } from '@blueprintjs/core'
+import { CopyToClipboard } from 'react-copy-to-clipboard'
+
+import { ReactComponent as CopyIcon } from '@/images/icons/copy.svg'
+import * as S from './styled'
+
+const CopyToaster = Toaster.create({
+  position: Position.TOP_RIGHT
+})
+
+export const ViewOrEditModal = ({ record, onSubmit, onCancel }) => {
+  const [name, setName] = useState('')
+  const [error, setError] = useState('')
+
+  useEffect(() => {
+    setName(record?.name)
+  }, [record])
+
+  const handleCancel = () => {
+    setName('')
+    setError('')
+    onCancel()
+  }
+
+  const handleInputChange = (e) => {
+    setName(e.target.value)
+    setError('')
+  }
+
+  const handleSubmit = () => {
+    if (!name) {
+      setError('Name is required')
+      return
+    }
+
+    onSubmit(record.id, { name })
+    handleCancel()
+  }
+
+  return (
+    <Dialog isOpen={true} title='View or Edit Webhook' style={{ width: 640 }} 
onClose={handleCancel}>
+      <S.FormWrapper>
+        <div className='form'>
+          <h2>Webhook Name *</h2>
+          <p>Give your Webhook a unique name to help you identify it in the 
future.</p>
+          <input
+            type='text'
+            placeholder='Your Webhook Name'
+            className={error ? 'has-error' : ''}
+            value={name || ''}
+            onChange={handleInputChange}
+          />
+          {error && <p className='error'>{error}</p>}
+        </div>
+        <div className='url'>
+          <h2>POST URL</h2>
+          <p>Copy the following URLs to your issue tracking tool for Incidents 
and CI tool for Deployments by making a POST to DevLake.</p>
+          <h3>Incident</h3>
+          <p>Send incident opened and reopened events</p>
+          <div className='block'>
+            <span>{record.postIssuesEndpoint}</span>
+            <CopyToClipboard
+              text={record.postIssuesEndpoint}
+              onCopy={() =>
+                CopyToaster.show({
+                  message: 'Copy successfully.',
+                  intent: Intent.SUCCESS
+                })
+              }
+            >
+              <CopyIcon width={16} height={16} />
+            </CopyToClipboard>
+          </div>
+          <p>Send incident resolved events</p>
+          <div className='block'>
+            <span>{record.closeIssuesEndpoint}</span>
+            <CopyToClipboard
+              text={record.closeIssuesEndpoint}
+              onCopy={() =>
+                CopyToaster.show({
+                  message: 'Copy successfully.',
+                  intent: Intent.SUCCESS
+                })
+              }
+            >
+              <CopyIcon width={16} height={16} />
+            </CopyToClipboard>
+          </div>
+          <h3>Deployment</h3>
+          <p>Send task started and finished events</p>
+          <div className='block'>
+            <span>{record.postPipelineTaskEndpoint}</span>
+            <CopyToClipboard text={record.postPipelineTaskEndpoint}>
+              <CopyIcon width={16} height={16} />
+            </CopyToClipboard>
+          </div>
+          <p>Send deployment finished events</p>
+          <div className='block'>
+            <span>{record.closePipelineEndpoint}</span>
+            <CopyToClipboard text={record.closePipelineEndpoint}>
+              <CopyIcon width={16} height={16} />
+            </CopyToClipboard>
+          </div>
+        </div>
+        <div className='btns'>
+          <Button onClick={onCancel}>Cancel</Button>
+          <Button intent={Intent.PRIMARY} onClick={handleSubmit}>
+            Save
+          </Button>
+        </div>
+      </S.FormWrapper>
+    </Dialog>
+  )
+}

Reply via email to