From 57d272d101921263ac31b14ec78d4ede3238be18 Mon Sep 17 00:00:00 2001 From: mintsweet <0x1304570@gmail.com> Date: Mon, 19 Aug 2024 19:33:46 +1200 Subject: [PATCH] feat: move connection delete action to connection list --- .../components/connection-list/index.tsx | 159 ++++++++++++++---- .../components/connection-status/index.tsx | 2 +- .../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 e0985b1d14d..9817058337c 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(); + const [operating, setOperating] = useState(false); + const [conflict, setConflict] = useState([]); + 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 ; - } - - 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 ; + } + return ( <> { { title: '', key: 'link', - width: 200, + width: 300, render: (_, { plugin, id }) => ( <> - + ), }, @@ -116,22 +159,76 @@ export const ConnectionList = ({ plugin, onCreate }: Props) => { - - {pluginConfig.icon({ color: colorPrimary })} - Manage Connections: {pluginConfig.name} - - } - footer={null} - onCancel={hanldeHideForm} - > - - + {modalType === 'update' && ( + + {pluginConfig.icon({ color: colorPrimary })} + Manage Connections: {pluginConfig.name} + + } + footer={null} + onCancel={hanldeHideModal} + > + + + )} + {modalType === 'delete' && ( + + + + )} + {modalType === 'deleteFailed' && ( + + {!conflict.length ? ( + + ) : ( + <> + +
    + {conflict.map((it) => ( +
  • + {it} +
  • + ))} +
+ + )} +
+ )} ); }; diff --git a/config-ui/src/plugins/components/connection-status/index.tsx b/config-ui/src/plugins/components/connection-status/index.tsx index eb4c2e424ef..4c8aa3b4935 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 ( diff --git a/config-ui/src/routes/connection/connection.tsx b/config-ui/src/routes/connection/connection.tsx index 3652c26b3d9..ef743191475 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={ - - } > @@ -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