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

mintsweet pushed a commit to branch feat-connection-delete
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git

commit 57d272d101921263ac31b14ec78d4ede3238be18
Author: mintsweet <[email protected]>
AuthorDate: Mon Aug 19 19:33:46 2024 +1200

    feat: move connection delete action to connection list
---
 .../plugins/components/connection-list/index.tsx   | 159 +++++++++++++++++----
 .../plugins/components/connection-status/index.tsx |   2 +-
 config-ui/src/routes/connection/connection.tsx     | 112 +--------------
 3 files changed, 133 insertions(+), 140 deletions(-)

diff --git a/config-ui/src/plugins/components/connection-list/index.tsx 
b/config-ui/src/plugins/components/connection-list/index.tsx
index e0985b1d1..981705833 100644
--- a/config-ui/src/plugins/components/connection-list/index.tsx
+++ b/config-ui/src/plugins/components/connection-list/index.tsx
@@ -18,15 +18,17 @@
 
 import { useState, useMemo } from 'react';
 import { useNavigate } from 'react-router-dom';
-import { EyeOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
-import { theme, Table, Button, Modal } from 'antd';
+import { EyeOutlined, EditOutlined, DeleteOutlined, PlusOutlined } from 
'@ant-design/icons';
+import { theme, Table, Button, Modal, message } from 'antd';
 import styled from 'styled-components';
 
-import { selectConnections } from '@/features/connections';
+import { selectConnections, removeConnection } from '@/features/connections';
+import { Message } from '@/components';
 import { PATHS } from '@/config';
-import { useAppSelector } from '@/hooks';
+import { useAppDispatch, useAppSelector } from '@/hooks';
 import { getPluginConfig, ConnectionStatus, ConnectionForm } from '@/plugins';
 import { WebHookConnection } from '@/plugins/register/webhook';
+import { operator } from '@/utils';
 
 const ModalTitle = styled.div`
   display: flex;
@@ -50,8 +52,11 @@ interface Props {
 }
 
 export const ConnectionList = ({ plugin, onCreate }: Props) => {
-  const [open, setOpen] = useState(false);
+  const [modalType, setModalType] = useState<'update' | 'delete' | 
'deleteFailed'>();
   const [connectionId, setConnectionId] = useState<ID>();
+  const [operating, setOperating] = useState(false);
+  const [conflict, setConflict] = useState<string[]>([]);
+  const [errorMsg, setErrorMsg] = useState('');
 
   const pluginConfig = useMemo(() => getPluginConfig(plugin), [plugin]);
 
@@ -59,24 +64,59 @@ export const ConnectionList = ({ plugin, onCreate }: Props) 
=> {
     token: { colorPrimary },
   } = theme.useToken();
 
+  const dispatch = useAppDispatch();
   const connections = useAppSelector((state) => selectConnections(state, 
plugin));
 
   const navigate = useNavigate();
 
-  if (plugin === 'webhook') {
-    return <WebHookConnection />;
-  }
-
-  const handleShowForm = (id: ID) => {
-    setOpen(true);
+  const handleShowModal = (type: 'update' | 'delete', id: ID) => {
+    setModalType(type);
     setConnectionId(id);
   };
 
-  const hanldeHideForm = () => {
-    setOpen(false);
+  const hanldeHideModal = () => {
+    setModalType(undefined);
     setConnectionId(undefined);
   };
 
+  const handleDelete = async () => {
+    const [, res] = await operator(
+      async () => {
+        try {
+          await dispatch(removeConnection({ plugin, connectionId })).unwrap();
+          return { status: 'success' };
+        } catch (err: any) {
+          const { status, data, message } = err;
+          return {
+            status: status === 409 ? 'conflict' : 'error',
+            conflict: data ? [...data.projects, ...data.blueprints] : [],
+            message,
+          };
+        }
+      },
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
+
+    if (res.status === 'success') {
+      message.success('Delete Connection Successful.');
+      hanldeHideModal();
+    } else if (res.status === 'conflict') {
+      setModalType('deleteFailed');
+      setConflict(res.conflict);
+      setErrorMsg(res.message);
+    } else {
+      message.error('Operation failed.');
+      hanldeHideModal();
+    }
+  };
+
+  if (plugin === 'webhook') {
+    return <WebHookConnection />;
+  }
+
   return (
     <>
       <Table
@@ -97,15 +137,18 @@ export const ConnectionList = ({ plugin, onCreate }: 
Props) => {
           {
             title: '',
             key: 'link',
-            width: 200,
+            width: 300,
             render: (_, { plugin, id }) => (
               <>
                 <Button type="link" icon={<EyeOutlined />} onClick={() => 
navigate(PATHS.CONNECTION(plugin, id))}>
                   Details
                 </Button>
-                <Button type="link" icon={<EditOutlined />} onClick={() => 
handleShowForm(id)}>
+                <Button type="link" icon={<EditOutlined />} onClick={() => 
handleShowModal('update', id)}>
                   Edit
                 </Button>
+                <Button type="link" danger icon={<DeleteOutlined />} 
onClick={() => handleShowModal('delete', id)}>
+                  Delete
+                </Button>
               </>
             ),
           },
@@ -116,22 +159,76 @@ export const ConnectionList = ({ plugin, onCreate }: 
Props) => {
       <Button style={{ marginTop: 16 }} type="primary" icon={<PlusOutlined />} 
onClick={onCreate}>
         Create a New Connection
       </Button>
-      <Modal
-        destroyOnClose
-        open={open}
-        width={820}
-        centered
-        title={
-          <ModalTitle>
-            <span className="icon">{pluginConfig.icon({ color: colorPrimary 
})}</span>
-            <span className="name">Manage Connections: 
{pluginConfig.name}</span>
-          </ModalTitle>
-        }
-        footer={null}
-        onCancel={hanldeHideForm}
-      >
-        <ConnectionForm plugin={plugin} connectionId={connectionId} 
onSuccess={hanldeHideForm} />
-      </Modal>
+      {modalType === 'update' && (
+        <Modal
+          destroyOnClose
+          open
+          width={820}
+          centered
+          title={
+            <ModalTitle>
+              <span className="icon">{pluginConfig.icon({ color: colorPrimary 
})}</span>
+              <span className="name">Manage Connections: 
{pluginConfig.name}</span>
+            </ModalTitle>
+          }
+          footer={null}
+          onCancel={hanldeHideModal}
+        >
+          <ConnectionForm plugin={plugin} connectionId={connectionId} 
onSuccess={hanldeHideModal} />
+        </Modal>
+      )}
+      {modalType === 'delete' && (
+        <Modal
+          open
+          width={820}
+          centered
+          title="Would you like to delete this Data Connection?"
+          okText="Confirm"
+          okButtonProps={{
+            loading: operating,
+          }}
+          onCancel={hanldeHideModal}
+          onOk={handleDelete}
+        >
+          <Message
+            content=" This operation cannot be undone. Deleting a Data 
Connection will delete all data that have been collected
+              in this Connection."
+          />
+        </Modal>
+      )}
+      {modalType === 'deleteFailed' && (
+        <Modal
+          open
+          width={820}
+          centered
+          style={{ width: 820 }}
+          title="This Data Connection can not be deleted."
+          cancelButtonProps={{
+            style: {
+              display: 'none',
+            },
+          }}
+          onCancel={hanldeHideModal}
+          onOk={hanldeHideModal}
+        >
+          {!conflict.length ? (
+            <Message content={errorMsg} />
+          ) : (
+            <>
+              <Message
+                content={`This Data Connection can not be deleted because it 
has been used in the following projects/blueprints:`}
+              />
+              <ul style={{ paddingLeft: 36 }}>
+                {conflict.map((it) => (
+                  <li key={it} style={{ color: colorPrimary }}>
+                    {it}
+                  </li>
+                ))}
+              </ul>
+            </>
+          )}
+        </Modal>
+      )}
     </>
   );
 };
diff --git a/config-ui/src/plugins/components/connection-status/index.tsx 
b/config-ui/src/plugins/components/connection-status/index.tsx
index eb4c2e424..4c8aa3b49 100644
--- a/config-ui/src/plugins/components/connection-status/index.tsx
+++ b/config-ui/src/plugins/components/connection-status/index.tsx
@@ -54,7 +54,7 @@ export const ConnectionStatus = ({ connection }: Props) => {
 
   const dispatch = useAppDispatch();
 
-  const handleTest = () => operator(() => 
dispatch(testConnection(connection)).unwrap());
+  const handleTest = () => operator(() => 
dispatch(testConnection(connection)).unwrap(), { hideToast: true });
 
   return (
     <Wrapper>
diff --git a/config-ui/src/routes/connection/connection.tsx 
b/config-ui/src/routes/connection/connection.tsx
index 3652c26b3..ef7431914 100644
--- a/config-ui/src/routes/connection/connection.tsx
+++ b/config-ui/src/routes/connection/connection.tsx
@@ -17,7 +17,7 @@
  */
 
 import { useState, useMemo } from 'react';
-import { useParams, useNavigate, Link } from 'react-router-dom';
+import { useParams, Link } from 'react-router-dom';
 import { Helmet } from 'react-helmet';
 import { DeleteOutlined, PlusOutlined, LinkOutlined, ClearOutlined } from 
'@ant-design/icons';
 import { theme, Space, Table, Button, Modal, message } from 'antd';
@@ -25,8 +25,8 @@ import { theme, Space, Table, Button, Modal, message } from 
'antd';
 import API from '@/api';
 import { PageHeader, Message, IconButton } from '@/components';
 import { PATHS } from '@/config';
-import { useAppDispatch, useAppSelector } from '@/hooks';
-import { selectConnection, removeConnection } from '@/features';
+import { useAppSelector } from '@/hooks';
+import { selectConnection } from '@/features';
 import { useRefreshData } from '@/hooks';
 import {
   ConnectionStatus,
@@ -45,13 +45,7 @@ const brandName = import.meta.env.DEVLAKE_BRAND_NAME ?? 
'DevLake';
 
 export const Connection = () => {
   const [type, setType] = useState<
-    | 'deleteConnection'
-    | 'createDataScope'
-    | 'clearDataScope'
-    | 'deleteDataScope'
-    | 'associateScopeConfig'
-    | 'deleteConnectionFailed'
-    | 'deleteDataScopeFailed'
+    'createDataScope' | 'clearDataScope' | 'deleteDataScope' | 
'associateScopeConfig' | 'deleteDataScopeFailed'
   >();
   const [operating, setOperating] = useState(false);
   const [version, setVersion] = useState(1);
@@ -69,11 +63,8 @@ export const Connection = () => {
     token: { colorPrimary },
   } = theme.useToken();
 
-  const dispatch = useAppDispatch();
   const connection = useAppSelector((state) => selectConnection(state, 
`${plugin}-${connectionId}`)) as IConnection;
 
-  const navigate = useNavigate();
-
   const { ready, data } = useRefreshData(
     () => API.scope.list(plugin, connectionId, { page, pageSize, blueprints: 
true }),
     [version, page, pageSize],
@@ -101,44 +92,6 @@ export const Connection = () => {
     setType(undefined);
   };
 
-  const handleShowDeleteDialog = () => {
-    setType('deleteConnection');
-  };
-
-  const handleDelete = async () => {
-    const [, res] = await operator(
-      async () => {
-        try {
-          await dispatch(removeConnection({ plugin, connectionId })).unwrap();
-          return { status: 'success' };
-        } catch (err: any) {
-          const { status, data, message } = err;
-          return {
-            status: status === 409 ? 'conflict' : 'error',
-            conflict: data ? [...data.projects, ...data.blueprints] : [],
-            message,
-          };
-        }
-      },
-      {
-        setOperating,
-        hideToast: true,
-      },
-    );
-
-    if (res.status === 'success') {
-      message.success('Delete Connection Successful.');
-      navigate(PATHS.CONNECTIONS());
-    } else if (res.status === 'conflict') {
-      setType('deleteConnectionFailed');
-      setConflict(res.conflict);
-      setErrorMsg(res.message);
-    } else {
-      message.error('Operation failed.');
-      handleHideDialog();
-    }
-  };
-
   const handleShowCreateDataScopeDialog = () => {
     setType('createDataScope');
   };
@@ -238,11 +191,6 @@ export const Connection = () => {
         { name: 'Connections', path: PATHS.CONNECTIONS() },
         { name, path: '' },
       ]}
-      extra={
-        <Button type="primary" danger icon={<DeleteOutlined />} 
onClick={handleShowDeleteDialog}>
-          Delete Connection
-        </Button>
-      }
     >
       <Helmet>
         <title>
@@ -358,25 +306,6 @@ export const Connection = () => {
           }}
         />
       </Space>
-      {type === 'deleteConnection' && (
-        <Modal
-          open
-          width={820}
-          centered
-          title="Would you like to delete this Data Connection?"
-          okText="Confirm"
-          okButtonProps={{
-            loading: operating,
-          }}
-          onCancel={handleHideDialog}
-          onOk={handleDelete}
-        >
-          <Message
-            content=" This operation cannot be undone. Deleting a Data 
Connection will delete all data that have been collected
-              in this Connection."
-          />
-        </Modal>
-      )}
       {type === 'createDataScope' && (
         <Modal
           getContainer={false}
@@ -459,39 +388,6 @@ export const Connection = () => {
           />
         </Modal>
       )}
-      {type === 'deleteConnectionFailed' && (
-        <Modal
-          open
-          width={820}
-          centered
-          style={{ width: 820 }}
-          title="This Data Connection can not be deleted."
-          cancelButtonProps={{
-            style: {
-              display: 'none',
-            },
-          }}
-          onCancel={handleHideDialog}
-          onOk={handleHideDialog}
-        >
-          {!conflict.length ? (
-            <Message content={errorMsg} />
-          ) : (
-            <>
-              <Message
-                content={`This Data Connection can not be deleted because it 
has been used in the following projects/blueprints:`}
-              />
-              <ul style={{ paddingLeft: 36 }}>
-                {conflict.map((it) => (
-                  <li key={it} style={{ color: colorPrimary }}>
-                    {it}
-                  </li>
-                ))}
-              </ul>
-            </>
-          )}
-        </Modal>
-      )}
       {type === 'deleteDataScopeFailed' && (
         <Modal
           open

Reply via email to