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;
+ }
}
`;