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

likyh 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 eab029901 feat(config-ui): show conflict list when delete connection 
or scope (#5529)
eab029901 is described below

commit eab0299010efbc3c0c012dfa9e3f01320fc45c44
Author: 青湛 <[email protected]>
AuthorDate: Tue Jun 20 22:04:09 2023 +0800

    feat(config-ui): show conflict list when delete connection or scope (#5529)
    
    * feat(config-ui): adjust the style for component message
    
    * feat(config-ui): show conflict list when delete connection or scope
---
 config-ui/src/components/message/index.tsx      |   1 -
 config-ui/src/pages/connection/detail/index.tsx | 117 +++++++++++++++++-------
 config-ui/src/pages/connection/detail/styled.ts |  12 ++-
 3 files changed, 93 insertions(+), 37 deletions(-)

diff --git a/config-ui/src/components/message/index.tsx 
b/config-ui/src/components/message/index.tsx
index bc803d9d6..5bee8625b 100644
--- a/config-ui/src/components/message/index.tsx
+++ b/config-ui/src/components/message/index.tsx
@@ -22,7 +22,6 @@ import styled from 'styled-components';
 const Wrapper = styled.div`
   display: flex;
   align-items: center;
-  font-size: 12px;
 
   & > .bp4-icon {
     margin-right: 8px;
diff --git a/config-ui/src/pages/connection/detail/index.tsx 
b/config-ui/src/pages/connection/detail/index.tsx
index f9c79bd1c..6a3d55813 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -18,9 +18,9 @@
 
 import { useState, useEffect, useMemo } from 'react';
 import { useParams, useHistory, Link } from 'react-router-dom';
-import { Button, Icon, Intent } from '@blueprintjs/core';
+import { Button, Intent } from '@blueprintjs/core';
 
-import { PageHeader, Buttons, Dialog, IconButton, Table, Message } from 
'@/components';
+import { PageHeader, Buttons, Dialog, IconButton, Table, Message, toast } from 
'@/components';
 import { useTips, useConnections, useRefreshData } from '@/hooks';
 import ClearImg from '@/images/icons/clear.svg';
 import {
@@ -55,12 +55,15 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
     | 'clearDataScope'
     | 'deleteDataScope'
     | 'associateScopeConfig'
+    | 'deleteConnectionFailed'
+    | 'deleteDataScopeFailed'
   >();
   const [operating, setOperating] = useState(false);
   const [version, setVersion] = useState(1);
   const [scopeId, setScopeId] = useState<ID>();
   const [scopeIds, setScopeIds] = useState<ID[]>([]);
   const [scopeConfigId, setScopeConfigId] = useState<ID>();
+  const [conflict, setConflict] = useState<string[]>([]);
 
   const history = useHistory();
   const { onGet, onTest, onRefresh } = useConnections();
@@ -84,14 +87,32 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
   };
 
   const handleDelete = async () => {
-    const [success] = await operator(() => API.deleteConnection(plugin, 
connectionId), {
-      setOperating,
-      formatMessage: () => 'Delete Connection Successful.',
-    });
+    const [, res] = await operator(
+      async () => {
+        try {
+          await API.deleteConnection(plugin, connectionId);
+          return { status: 'success' };
+        } catch (err: any) {
+          const { status, data } = err.response;
+          return { status: status === 409 ? 'conflict' : 'error', conflict: 
[...data.projects, ...data.blueprints] };
+        }
+      },
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
 
-    if (success) {
+    if (res.status === 'success') {
+      toast.success('Delete Connection Successful.');
       onRefresh(plugin);
       history.push('/connections');
+    } else if (res.status === 'conflict') {
+      setType('deleteConnectionFailed');
+      setConflict(res.conflict);
+    } else {
+      toast.error('Operation failed.');
+      handleHideDialog();
     }
   };
 
@@ -126,13 +147,31 @@ const ConnectionDetail = ({ plugin, connectionId }: 
Props) => {
   const handleDeleteDataScope = async (onlyData: boolean) => {
     if (!scopeId) return;
 
-    const [success] = await operator(() => API.deleteDataScope(plugin, 
connectionId, scopeId, onlyData), {
-      setOperating,
-      formatMessage: () => (onlyData ? 'Clear historical data successful.' : 
'Delete Data Scope successful.'),
-    });
+    const [, res] = await operator(
+      async () => {
+        try {
+          await API.deleteDataScope(plugin, connectionId, scopeId, onlyData);
+          return { status: 'success' };
+        } catch (err: any) {
+          const { status, data } = err.response;
+          return { status: status === 409 ? 'conflict' : 'error', conflict: 
[...data.projects, ...data.blueprints] };
+        }
+      },
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
 
-    if (success) {
+    if (res.status === 'success') {
       setVersion((v) => v + 1);
+      toast.success(onlyData ? 'Clear historical data successful.' : 'Delete 
Data Scope successful.');
+      handleHideDialog();
+    } else if (res.status === 'conflict') {
+      setType('deleteDataScopeFailed');
+      setConflict(res.conflict);
+    } else {
+      toast.error('Operation failed.');
       handleHideDialog();
     }
   };
@@ -219,7 +258,7 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
                     <ul>
                       {blueprints.map((bp: any, i: number) =>
                         bp.projectName ? (
-                          <li>
+                          <li key={bp.projectName}>
                             <Link 
to={`/projects/${bp.projectName}`}>{bp.projectName}</Link>
                           </li>
                         ) : null,
@@ -296,13 +335,10 @@ const ConnectionDetail = ({ plugin, connectionId }: 
Props) => {
           onCancel={handleHideDialog}
           onOk={handleDelete}
         >
-          <S.DialogBody>
-            <Icon icon="warning-sign" />
-            <span>
-              This operation cannot be undone. Deleting a Data Connection will 
delete all data that have been collected
-              in this Connection.
-            </span>
-          </S.DialogBody>
+          <Message
+            content=" This operation cannot be undone. Deleting a Data 
Connection will delete all data that have been collected
+              in this Connection."
+          />
         </Dialog>
       )}
       {type === 'updateConnection' && (
@@ -353,10 +389,7 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
           onCancel={handleHideDialog}
           onOk={() => handleDeleteDataScope(true)}
         >
-          <S.DialogBody>
-            <Icon icon="warning-sign" />
-            <span>This operation cannot be undone.</span>
-          </S.DialogBody>
+          <Message content="This operation cannot be undone." />
         </Dialog>
       )}
       {type === 'deleteDataScope' && (
@@ -369,13 +402,10 @@ const ConnectionDetail = ({ plugin, connectionId }: 
Props) => {
           onCancel={handleHideDialog}
           onOk={() => handleDeleteDataScope(false)}
         >
-          <S.DialogBody>
-            <Icon icon="warning-sign" />
-            <span>
-              This operation cannot be undone. Deleting Data Scope will delete 
all data that have been collected in the
-              past.
-            </span>
-          </S.DialogBody>
+          <Message
+            content="This operation cannot be undone. Deleting Data Scope will 
delete all data that have been collected in the
+              past."
+          />
         </Dialog>
       )}
       {type === 'associateScopeConfig' && (
@@ -400,6 +430,31 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
           )}
         </Dialog>
       )}
+      {(type === 'deleteConnectionFailed' || type === 'deleteDataScopeFailed') 
&& (
+        <Dialog
+          isOpen
+          style={{ width: 820 }}
+          footer={null}
+          title={`This Data ${type === 'deleteConnectionFailed' ? 'Connection' 
: 'Scope'} can not be deleted.`}
+          onCancel={handleHideDialog}
+        >
+          <S.DialogBody>
+            <Message
+              content={`This Data ${
+                type === 'deleteConnectionFailed' ? 'Connection' : 'Scope'
+              } can not be deleted because it has been used in the following 
projects/blueprints:`}
+            />
+            <ul>
+              {conflict.map((it) => (
+                <li>{it}</li>
+              ))}
+            </ul>
+            <Buttons position="bottom" align="right">
+              <Button intent={Intent.PRIMARY} text="OK" 
onClick={handleHideDialog} />
+            </Buttons>
+          </S.DialogBody>
+        </Dialog>
+      )}
     </PageHeader>
   );
 };
diff --git a/config-ui/src/pages/connection/detail/styled.ts 
b/config-ui/src/pages/connection/detail/styled.ts
index 8d79884eb..bd925519b 100644
--- a/config-ui/src/pages/connection/detail/styled.ts
+++ b/config-ui/src/pages/connection/detail/styled.ts
@@ -44,11 +44,13 @@ export const DialogTitle = styled.div`
 `;
 
 export const DialogBody = styled.div`
-  display: flex;
-  align-items: center;
+  ul {
+    margin-top: 16px;
+    padding-left: 36px;
 
-  .bp4-icon {
-    margin-right: 8px;
-    color: #f4be55;
+    li {
+      margin-bottom: 4px;
+      color: #7497f7;
+    }
   }
 `;

Reply via email to