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

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

commit 5175a054638826b06531edd97256289d50dcbf9a
Author: mintsweet <[email protected]>
AuthorDate: Mon May 6 15:27:56 2024 +1200

    feat: add some tips when updating scope config
---
 config-ui/src/api/scope-config/index.ts            |  5 ++
 .../src/api/scope-config/{index.ts => types.ts}    | 25 ++------
 .../src/plugins/components/scope-config/index.tsx  | 75 +++++++++++++++++-----
 .../routes/blueprint/connection-detail/index.tsx   |  3 +-
 config-ui/src/routes/connection/connection.tsx     | 50 +++++++++++++--
 5 files changed, 117 insertions(+), 41 deletions(-)

diff --git a/config-ui/src/api/scope-config/index.ts 
b/config-ui/src/api/scope-config/index.ts
index caaa789d7..41547d9c0 100644
--- a/config-ui/src/api/scope-config/index.ts
+++ b/config-ui/src/api/scope-config/index.ts
@@ -18,6 +18,8 @@
 
 import { request } from '@/utils';
 
+import { ICheck } from './types';
+
 export const list = (plugin: string, connectionId: ID) =>
   request(`/plugins/${plugin}/connections/${connectionId}/scope-configs`);
 
@@ -35,3 +37,6 @@ export const update = (plugin: string, connectionId: ID, id: 
ID, data: any) =>
     method: 'patch',
     data,
   });
+
+export const check = (plugin: string, id: ID): Promise<ICheck> =>
+  request(`/plugins/${plugin}/scope-config/${id}/projects`);
diff --git a/config-ui/src/api/scope-config/index.ts 
b/config-ui/src/api/scope-config/types.ts
similarity index 53%
copy from config-ui/src/api/scope-config/index.ts
copy to config-ui/src/api/scope-config/types.ts
index caaa789d7..4c779c658 100644
--- a/config-ui/src/api/scope-config/index.ts
+++ b/config-ui/src/api/scope-config/types.ts
@@ -16,22 +16,9 @@
  *
  */
 
-import { request } from '@/utils';
-
-export const list = (plugin: string, connectionId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scope-configs`);
-
-export const get = (plugin: string, connectionId: ID, id: ID) =>
-  
request(`/plugins/${plugin}/connections/${connectionId}/scope-configs/${id}`);
-
-export const create = (plugin: string, connectionId: ID, data: any) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scope-configs`, {
-    method: 'post',
-    data,
-  });
-
-export const update = (plugin: string, connectionId: ID, id: ID, data: any) =>
-  
request(`/plugins/${plugin}/connections/${connectionId}/scope-configs/${id}`, {
-    method: 'patch',
-    data,
-  });
+export type ICheck = {
+  count: number;
+  projects: Array<{
+    name: string;
+  }>;
+};
diff --git a/config-ui/src/plugins/components/scope-config/index.tsx 
b/config-ui/src/plugins/components/scope-config/index.tsx
index 7dd5b7e05..0bc83870d 100644
--- a/config-ui/src/plugins/components/scope-config/index.tsx
+++ b/config-ui/src/plugins/components/scope-config/index.tsx
@@ -18,10 +18,11 @@
 
 import { useState } from 'react';
 import { LinkOutlined, EditOutlined } from '@ant-design/icons';
-import { Button, Modal } from 'antd';
+import { theme, Button, Modal, Flex, Space } from 'antd';
 import styled from 'styled-components';
 
 import API from '@/api';
+import { Message } from '@/components';
 import { operator } from '@/utils';
 
 import { PluginName } from '../plugin-name';
@@ -34,16 +35,39 @@ interface Props {
   plugin: string;
   connectionId: ID;
   scopeId: ID;
+  scopeName: string;
   id?: ID;
   name?: string;
-  onSuccess?: () => void;
+  onSuccess?: (id?: ID) => void;
 }
 
-export const ScopeConfig = ({ plugin, connectionId, scopeId, id, name, 
onSuccess }: Props) => {
-  const [type, setType] = useState<'associate' | 'update'>();
+export const ScopeConfig = ({ plugin, connectionId, scopeId, scopeName, id, 
name, onSuccess }: Props) => {
+  const [type, setType] = useState<'associate' | 'update' | 
'relatedProjects'>();
+  const [relatedProjects, setRelatedProjects] = useState<Array<{ name: string; 
scopes: string[] }>>([]);
+
+  const {
+    token: { colorPrimary },
+  } = theme.useToken();
 
   const handleHideDialog = () => setType(undefined);
 
+  const handleCheckScopeConfig = async () => {
+    if (!id) return;
+
+    const [success, res] = await operator(() => API.scopeConfig.check(plugin, 
id), { hideToast: true });
+
+    if (success) {
+      const projects = res.projects.map((it: any) => ({ name: it.name, scopes: 
[] }));
+
+      if (projects.length !== 1) {
+        setRelatedProjects(projects);
+        setType('relatedProjects');
+      } else {
+        setType('update');
+      }
+    }
+  };
+
   const handleAssociate = async (trId: ID) => {
     const [success] = await operator(
       () => API.scope.update(plugin, connectionId, scopeId, { scopeConfigId: 
trId !== 'None' ? +trId : null }),
@@ -54,13 +78,13 @@ export const ScopeConfig = ({ plugin, connectionId, 
scopeId, id, name, onSuccess
 
     if (success) {
       handleHideDialog();
-      onSuccess?.();
+      onSuccess?.(id);
     }
   };
 
   const handleUpdate = (trId: ID) => {
     handleHideDialog();
-    onSuccess?.();
+    onSuccess?.(id);
   };
 
   return (
@@ -74,17 +98,7 @@ export const ScopeConfig = ({ plugin, connectionId, scopeId, 
id, name, onSuccess
           setType('associate');
         }}
       />
-      {id && (
-        <Button
-          size="small"
-          type="link"
-          icon={<EditOutlined />}
-          onClick={() => {
-            // TO-DO: check if the scope config is associated with any scope
-            setType('update');
-          }}
-        />
-      )}
+      {id && <Button size="small" type="link" icon={<EditOutlined />} 
onClick={handleCheckScopeConfig} />}
       {type === 'associate' && (
         <Modal
           open
@@ -127,6 +141,33 @@ export const ScopeConfig = ({ plugin, connectionId, 
scopeId, id, name, onSuccess
           />
         </Modal>
       )}
+      {type === 'relatedProjects' && (
+        <Modal
+          open
+          width={830}
+          centered
+          footer={null}
+          title={`Edit '${name}' for '${scopeName}'`}
+          onCancel={handleHideDialog}
+        >
+          <Message content="The change will apply to all following projects:" 
/>
+          <ul style={{ marginTop: 15, marginLeft: 30 }}>
+            {relatedProjects.map((it) => (
+              <li style={{ color: colorPrimary }}>
+                {it.name}:{it.scopes.join(',')}
+              </li>
+            ))}
+          </ul>
+          <Flex justify="end">
+            <Space>
+              <Button onClick={handleHideDialog}>Cancel</Button>
+              <Button type="primary" onClick={() => setType('update')}>
+                Continue
+              </Button>
+            </Space>
+          </Flex>
+        </Modal>
+      )}
     </Wrapper>
   );
 };
diff --git a/config-ui/src/routes/blueprint/connection-detail/index.tsx 
b/config-ui/src/routes/blueprint/connection-detail/index.tsx
index b686d9e81..08c8d8a06 100644
--- a/config-ui/src/routes/blueprint/connection-detail/index.tsx
+++ b/config-ui/src/routes/blueprint/connection-detail/index.tsx
@@ -263,11 +263,12 @@ export const BlueprintConnectionDetailPage = () => {
             {
               title: 'Scope Config',
               key: 'scopeConfig',
-              render: (_, { id, scopeConfigId, scopeConfigName }) => (
+              render: (_, { id, name, scopeConfigId, scopeConfigName }) => (
                 <ScopeConfig
                   plugin={plugin}
                   connectionId={connectionId}
                   scopeId={id}
+                  scopeName={name}
                   id={scopeConfigId}
                   name={scopeConfigName}
                   onSuccess={handleChangeScopeConfig}
diff --git a/config-ui/src/routes/connection/connection.tsx 
b/config-ui/src/routes/connection/connection.tsx
index 8b4913903..ba6d5d66a 100644
--- a/config-ui/src/routes/connection/connection.tsx
+++ b/config-ui/src/routes/connection/connection.tsx
@@ -69,10 +69,13 @@ export const Connection = () => {
     token: { colorPrimary },
   } = theme.useToken();
 
+  const [modal, contextHolder] = Modal.useModal();
+
   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],
@@ -227,9 +230,46 @@ export const Connection = () => {
     }
   };
 
-  const handleScopeConfigChange = () => {
-    // TO-DO: check scope config change will effect the scope config
-    setVersion(version + 1);
+  const handleScopeConfigChange = async (scopeConfigId?: ID) => {
+    if (!scopeConfigId) {
+      return;
+    }
+
+    const [success, res] = await operator(() => API.scopeConfig.check(plugin, 
scopeConfigId), { hideToast: true });
+
+    if (success) {
+      modal.success({
+        closable: true,
+        centered: true,
+        width: 830,
+        title: 'Scope Config Saved',
+        content: (
+          <>
+            <div style={{ marginBottom: 16 }}>
+              The listed projects are impacted. Please re-transform the data 
to apply the updated scope config.
+            </div>
+            <ul>
+              {res.projects.map((it: any) => (
+                <li style={{ marginBottom: 10 }}>
+                  <Space>
+                    <span>{it.name}</span>
+                    <Button
+                      size="small"
+                      type="link"
+                      onClick={() => navigate(PATHS.PROJECT(it.name, { tab: 
'status' }))}
+                    >
+                      Re-transform Data
+                    </Button>
+                  </Space>
+                </li>
+              ))}
+            </ul>
+          </>
+        ),
+        footer: null,
+        onCancel: () => setVersion(version + 1),
+      });
+    }
   };
 
   return (
@@ -305,11 +345,12 @@ export const Connection = () => {
               title: 'Scope Config',
               key: 'scopeConfig',
               width: 400,
-              render: (_, { id, configId, configName }) => (
+              render: (_, { id, name, configId, configName }) => (
                 <ScopeConfig
                   plugin={plugin}
                   connectionId={connectionId}
                   scopeId={id}
+                  scopeName={name}
                   id={configId}
                   name={configName}
                   onSuccess={handleScopeConfigChange}
@@ -515,6 +556,7 @@ export const Connection = () => {
           )}
         </Modal>
       )}
+      {contextHolder}
     </PageHeader>
   );
 };

Reply via email to