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

mintsweet 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 f254f25c1 feat(config-ui): missed content about move data scope (#5331)
f254f25c1 is described below

commit f254f25c168f9fb74ab4e5c6b28711d048a49f03
Author: 青湛 <[email protected]>
AuthorDate: Thu Jun 1 11:34:49 2023 +0800

    feat(config-ui): missed content about move data scope (#5331)
---
 .../src/pages/blueprint/connection-detail/api.ts   |   4 +-
 .../pages/blueprint/connection-detail/index.tsx    | 132 ++++++++++++---------
 .../pages/blueprint/connection-detail/styled.ts    |   8 +-
 config-ui/src/pages/blueprint/detail/api.ts        |   3 +-
 .../pages/blueprint/detail/blueprint-detail.tsx    |  19 ++-
 .../components/add-connection-dialog/index.tsx     |  24 +++-
 .../configuration.tsx => configuration-panel.tsx}  |  72 ++++++-----
 .../detail/{panel/status.tsx => status-panel.tsx}  |  12 +-
 config-ui/src/pages/blueprint/detail/types.ts      |  28 -----
 config-ui/src/pages/blueprint/types.ts             |   1 -
 config-ui/src/pages/connection/detail/index.tsx    |   1 -
 .../components/data-scope-select-remote/index.tsx  |   7 +-
 .../plugins/components/data-scope-select/index.tsx |  94 +++++++++------
 .../plugins/components/data-scope-select/styled.ts |  13 +-
 14 files changed, 222 insertions(+), 196 deletions(-)

diff --git a/config-ui/src/pages/blueprint/connection-detail/api.ts 
b/config-ui/src/pages/blueprint/connection-detail/api.ts
index 7686757f3..dac107063 100644
--- a/config-ui/src/pages/blueprint/connection-detail/api.ts
+++ b/config-ui/src/pages/blueprint/connection-detail/api.ts
@@ -26,5 +26,5 @@ export const updateBlueprint = (id: ID, payload: any) =>
 export const getConnection = (plugin: string, connectionId: ID) =>
   request(`/plugins/${plugin}/connections/${connectionId}`);
 
-export const getDataScope = (plugin: string, connectionId: ID, scopeId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`);
+export const getDataScopes = (plugin: string, connectionId: ID) =>
+  request(`/plugins/${plugin}/connections/${connectionId}/scopes`);
diff --git a/config-ui/src/pages/blueprint/connection-detail/index.tsx 
b/config-ui/src/pages/blueprint/connection-detail/index.tsx
index 657fd570c..39dc7cf44 100644
--- a/config-ui/src/pages/blueprint/connection-detail/index.tsx
+++ b/config-ui/src/pages/blueprint/connection-detail/index.tsx
@@ -21,10 +21,10 @@ import { useHistory, useParams } from 'react-router-dom';
 import { Button, Intent, Position } from '@blueprintjs/core';
 import { Popover2 } from '@blueprintjs/popover2';
 
-import { Dialog, PageHeader, PageLoading } from '@/components';
-import { EntitiesLabel } from '@/config';
+import { PageLoading, PageHeader, ExternalLink, Buttons, Table, Dialog } from 
'@/components';
 import { useRefreshData } from '@/hooks';
-import { DataScopeSelect, getPluginConfig } from '@/plugins';
+import { DataScopeSelect, getPluginId } from '@/plugins';
+import { operator } from '@/utils';
 
 import * as API from './api';
 import * as S from './styled';
@@ -33,33 +33,30 @@ export const BlueprintConnectionDetailPage = () => {
   const [version, setVersion] = useState(1);
   const [isOpen, setIsOpen] = useState(false);
 
-  const { pname, bid, unique } = useParams<{ pname?: string; bid: string; 
unique: string }>();
+  const { bid, unique } = useParams<{ bid: string; unique: string }>();
   const history = useHistory();
 
   const { ready, data } = useRefreshData(async () => {
     const [plugin, connectionId] = unique.split('-');
-    const blueprint = await API.getBlueprint(bid);
-    const connection = await API.getConnection(plugin, connectionId);
-    const scope = blueprint.settings.connections.find(
-      (cs: any) => cs.plugin === plugin && cs.connectionId === +connectionId,
-    ).scopes;
-    const config = getPluginConfig(plugin);
-    const origin = await Promise.all(scope.map((sc: any) => 
API.getDataScope(plugin, connectionId, sc.id)));
+    const [blueprint, connection, scopes] = await Promise.all([
+      API.getBlueprint(bid),
+      API.getConnection(plugin, connectionId),
+      API.getDataScopes(plugin, connectionId),
+    ]);
+
+    const scopeIds = blueprint.settings.connections
+      .find((cs: any) => cs.plugin === plugin && cs.connectionId === 
+connectionId)
+      .scopes.map((sc: any) => +sc.id);
 
     return {
       blueprint,
-      bpName: blueprint.name,
-      csName: connection.name,
-      entities: scope[0].entities,
       connection: {
         unique,
         plugin,
-        connectionId: +connectionId,
+        id: +connectionId,
         name: connection.name,
-        icon: config.icon,
-        scope,
-        origin,
       },
+      scopes: scopes.filter((sc: any) => 
scopeIds.includes(sc[getPluginId(plugin)])),
     };
   }, [version]);
 
@@ -67,52 +64,72 @@ export const BlueprintConnectionDetailPage = () => {
     return <PageLoading />;
   }
 
-  const { blueprint, bpName, csName, entities, connection } = data;
+  const { blueprint, connection, scopes } = data;
 
   const handleShowDataScope = () => setIsOpen(true);
   const handleHideDataScope = () => setIsOpen(false);
 
   const handleRemoveConnection = async () => {
-    await API.updateBlueprint(blueprint.id, {
-      ...blueprint,
-      settings: {
-        ...blueprint.settings,
-        connections: blueprint.settings.connections.filter(
-          (cs: any) => !(cs.plugin === connection.plugin && cs.connectionId 
=== connection.connectionId),
-        ),
-      },
-    });
-    history.push(pname ? `/projects/:${pname}` : 
`/blueprints/${blueprint.id}`);
+    const [success] = await operator(() =>
+      API.updateBlueprint(blueprint.id, {
+        ...blueprint,
+        settings: {
+          ...blueprint.settings,
+          connections: blueprint.settings.connections.filter(
+            (cs: any) => !(cs.plugin === connection.plugin && cs.connectionId 
=== connection.id),
+          ),
+        },
+      }),
+    );
+
+    if (success) {
+      history.push(`/blueprints/${blueprint.id}`);
+    }
   };
 
-  const handleChangeDataScope = (scope: any) => {
-    console.log(scope);
-    setVersion((v) => v + 1);
+  const handleChangeDataScope = async (scope: any) => {
+    const [success] = await operator(
+      () =>
+        API.updateBlueprint(blueprint.id, {
+          ...blueprint,
+          settings: {
+            ...blueprint.settings,
+            connections: blueprint.settings.connections.map((cs: any) => {
+              if (cs.plugin === connection.plugin && cs.connectionId === 
connection.id) {
+                return {
+                  ...cs,
+                  scopes: scope.map((sc: any) => ({ id: 
`${sc[getPluginId(connection.plugin)]}` })),
+                };
+              }
+              return cs;
+            }),
+          },
+        }),
+      {
+        formatMessage: () => 'Update data scope successful.',
+      },
+    );
+
+    if (success) {
+      handleHideDataScope();
+      setVersion((v) => v + 1);
+    }
   };
 
   return (
     <PageHeader
       breadcrumbs={[
-        ...(pname
-          ? [
-              {
-                name: 'Projects',
-                path: '/projects',
-              },
-              {
-                name: pname,
-                path: `/projects/${pname}`,
-              },
-            ]
-          : [{ name: bpName, path: `/blueprints/${bid}` }]),
-        { name: `Connection - ${csName}`, path: '' },
+        { name: blueprint.name, path: `/blueprints/${bid}` },
+        { name: `Connection - ${connection.name}`, path: '' },
       ]}
     >
-      <S.Action>
+      <S.Top>
         <span>
-          <Button intent={Intent.PRIMARY} icon="annotation" 
onClick={handleShowDataScope}>
-            Edit Data Scope
-          </Button>
+          If you would like to manage Data Entities and Data Scope of this 
Connection, please{' '}
+          <ExternalLink 
link={`/connections/${connection.plugin}/${connection.id}`}>
+            go to the Connection detail page
+          </ExternalLink>
+          .
         </span>
         <Popover2
           position={Position.BOTTOM}
@@ -129,15 +146,11 @@ export const BlueprintConnectionDetailPage = () => {
             Remove this Connection
           </Button>
         </Popover2>
-      </S.Action>
-      <S.Entities>
-        <h4>Data Entities</h4>
-        <ul>
-          {entities.map((it: string) => (
-            <li key={it}>{EntitiesLabel[it]}</li>
-          ))}
-        </ul>
-      </S.Entities>
+      </S.Top>
+      <Buttons position="top" align="left">
+        <Button intent={Intent.PRIMARY} icon="annotation" text="Manage Data 
Scope" onClick={handleShowDataScope} />
+      </Buttons>
+      <Table columns={[{ title: 'Data Scope', dataIndex: 'name', key: 'name' 
}]} dataSource={scopes} />
       <Dialog
         isOpen={isOpen}
         title="Change Data Scope"
@@ -147,7 +160,8 @@ export const BlueprintConnectionDetailPage = () => {
       >
         <DataScopeSelect
           plugin={connection.plugin}
-          connectionId={connection.connectionId}
+          connectionId={connection.id}
+          initialScope={scopes}
           onCancel={handleHideDataScope}
           onSubmit={handleChangeDataScope}
         />
diff --git a/config-ui/src/pages/blueprint/connection-detail/styled.ts 
b/config-ui/src/pages/blueprint/connection-detail/styled.ts
index 92de69ebe..716316b8c 100644
--- a/config-ui/src/pages/blueprint/connection-detail/styled.ts
+++ b/config-ui/src/pages/blueprint/connection-detail/styled.ts
@@ -18,15 +18,11 @@
 
 import styled from 'styled-components';
 
-export const Action = styled.div`
+export const Top = styled.div`
   display: flex;
   align-items: center;
   justify-content: space-between;
-  margin-bottom: 24px;
-
-  .bp4-button + .bp4-button {
-    margin-left: 8px;
-  }
+  margin-bottom: 36px;
 `;
 
 export const ActionDelete = styled.div`
diff --git a/config-ui/src/pages/blueprint/detail/api.ts 
b/config-ui/src/pages/blueprint/detail/api.ts
index 2fc8424eb..2f260fe5f 100644
--- a/config-ui/src/pages/blueprint/detail/api.ts
+++ b/config-ui/src/pages/blueprint/detail/api.ts
@@ -24,7 +24,8 @@ export const getBlueprint = (id: ID): Promise<BlueprintType> 
=> request(`/bluepr
 
 export const getBlueprintPipelines = (id: ID) => 
request(`/blueprints/${id}/pipelines`);
 
-export const runBlueprint = (id: ID) => request(`/blueprints/${id}/trigger`, { 
method: 'post' });
+export const runBlueprint = (id: ID, skipCollectors: boolean) =>
+  request(`/blueprints/${id}/trigger`, { method: 'post', data: { 
skipCollectors } });
 
 export const updateBlueprint = (id: ID, payload: any) =>
   request(`/blueprints/${id}`, { method: 'patch', data: payload });
diff --git a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx 
b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
index 36cf81e99..f3daee155 100644
--- a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
+++ b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
@@ -25,8 +25,8 @@ import { PageLoading, Dialog } from '@/components';
 import { useRefreshData } from '@/hooks';
 import { operator } from '@/utils';
 
-import { Configuration } from './panel/configuration';
-import { Status } from './panel/status';
+import { ConfigurationPanel } from './configuration-panel';
+import { StatusPanel } from './status-panel';
 import * as API from './api';
 import * as S from './styled';
 
@@ -62,6 +62,7 @@ export const BlueprintDetail = ({ id }: Props) => {
         }),
       {
         setOperating,
+        formatMessage: () => 'Update blueprint successful.',
       },
     );
 
@@ -71,9 +72,10 @@ export const BlueprintDetail = ({ id }: Props) => {
     }
   };
 
-  const handleRun = async () => {
-    const [success] = await operator(() => API.runBlueprint(id), {
+  const handleRun = async (skipCollectors: boolean) => {
+    const [success] = await operator(() => API.runBlueprint(id, 
skipCollectors), {
       setOperating,
+      formatMessage: () => 'Trigger blueprint successful.',
     });
 
     if (success) {
@@ -107,13 +109,18 @@ export const BlueprintDetail = ({ id }: Props) => {
           id="status"
           title="Status"
           panel={
-            <Status blueprint={blueprint} pipelineId={pipelines?.[0]?.id} 
operating={operating} onRun={handleRun} />
+            <StatusPanel
+              blueprint={blueprint}
+              pipelineId={pipelines?.[0]?.id}
+              operating={operating}
+              onRun={handleRun}
+            />
           }
         />
         <Tab
           id="configuration"
           title="Configuration"
-          panel={<Configuration blueprint={blueprint} operating={operating} 
onUpdate={handleUpdate} />}
+          panel={<ConfigurationPanel blueprint={blueprint} 
operating={operating} onUpdate={handleUpdate} />}
         />
         <Tabs.Expander />
         <Switch
diff --git 
a/config-ui/src/pages/blueprint/detail/components/add-connection-dialog/index.tsx
 
b/config-ui/src/pages/blueprint/detail/components/add-connection-dialog/index.tsx
index 95982ba94..cf06480c3 100644
--- 
a/config-ui/src/pages/blueprint/detail/components/add-connection-dialog/index.tsx
+++ 
b/config-ui/src/pages/blueprint/detail/components/add-connection-dialog/index.tsx
@@ -16,29 +16,42 @@
  *
  */
 
-import { useState } from 'react';
+import { useState, useMemo } from 'react';
 import { Button, Intent } from '@blueprintjs/core';
 
 import { Dialog, FormItem, Selector, Buttons } from '@/components';
 import { useConnections } from '@/hooks';
-import { DataScopeSelect } from '@/plugins';
+import { DataScopeSelect, getPluginId } from '@/plugins';
 import type { ConnectionItemType } from '@/store';
 
 interface Props {
+  disabled: string[];
   onCancel: () => void;
   onSubmit: (value: any) => void;
 }
 
-export const AddConnectionDialog = ({ onCancel, onSubmit }: Props) => {
+export const AddConnectionDialog = ({ disabled = [], onCancel, onSubmit }: 
Props) => {
   const [step, setStep] = useState(1);
   const [selectedConnection, setSelectedConnection] = 
useState<ConnectionItemType>();
 
   const { connections } = useConnections();
 
-  const handleSubmit = (scope: any) => console.log(scope);
+  const disabledItems = useMemo(
+    () => connections.filter((cs) => (disabled.length ? 
disabled.includes(cs.unique) : false)),
+    [disabled],
+  );
+
+  const handleSubmit = (scope: any) => {
+    if (!selectedConnection) return;
+    onSubmit({
+      plugin: selectedConnection.plugin,
+      connectionId: selectedConnection.id,
+      scopes: scope.map((sc: any) => ({ id: 
`${sc[getPluginId(selectedConnection.plugin)]}` })),
+    });
+  };
 
   return (
-    <Dialog style={{ width: 820 }} isOpen title={`Add a Connection - Step 
${step}`} footer={null}>
+    <Dialog style={{ width: 820 }} isOpen title={`Add a Connection - Step 
${step}`} footer={null} onCancel={onCancel}>
       {step === 1 && (
         <FormItem
           label="Data Connections"
@@ -47,6 +60,7 @@ export const AddConnectionDialog = ({ onCancel, onSubmit }: 
Props) => {
         >
           <Selector
             items={connections}
+            disabledItems={disabledItems}
             getKey={(it) => it.unique}
             getName={(it) => it.name}
             selectedItem={selectedConnection}
diff --git a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx 
b/config-ui/src/pages/blueprint/detail/configuration-panel.tsx
similarity index 75%
rename from config-ui/src/pages/blueprint/detail/panel/configuration.tsx
rename to config-ui/src/pages/blueprint/detail/configuration-panel.tsx
index 8d7b88220..a76f95655 100644
--- a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
+++ b/config-ui/src/pages/blueprint/detail/configuration-panel.tsx
@@ -20,16 +20,16 @@ import { useState, useEffect, useMemo } from 'react';
 import { Link } from 'react-router-dom';
 import { Button, Intent } from '@blueprintjs/core';
 
-import { IconButton, Table, NoData } from '@/components';
+import { IconButton, Table, NoData, Buttons } from '@/components';
 import { useConnections } from '@/hooks';
 import { getPluginConfig } from '@/plugins';
 
-import type { BlueprintType } from '../../types';
-import { ModeEnum } from '../../types';
-import { validRawPlan } from '../../utils';
+import type { BlueprintType } from '../types';
+import { ModeEnum } from '../types';
+import { validRawPlan } from '../utils';
 
-import { AdvancedEditor, UpdateNameDialog, UpdatePolicyDialog, 
AddConnectionDialog } from '../components';
-import * as S from '../styled';
+import { AdvancedEditor, UpdateNameDialog, UpdatePolicyDialog, 
AddConnectionDialog } from './components';
+import * as S from './styled';
 
 interface Props {
   blueprint: BlueprintType;
@@ -37,7 +37,7 @@ interface Props {
   onUpdate: (payload: any, callback?: () => void) => void;
 }
 
-export const Configuration = ({ blueprint, operating, onUpdate }: Props) => {
+export const ConfigurationPanel = ({ blueprint, operating, onUpdate }: Props) 
=> {
   const [type, setType] = useState<'name' | 'policy' | 'add-connection'>();
   const [rawPlan, setRawPlan] = useState('');
 
@@ -83,10 +83,6 @@ export const Configuration = ({ blueprint, operating, 
onUpdate }: Props) => {
     setType('add-connection');
   };
 
-  const handleAddConnection = (value: any) => {
-    console.log(value);
-  };
-
   return (
     <S.ConfigurationPanel>
       <div className="block">
@@ -149,22 +145,32 @@ export const Configuration = ({ blueprint, operating, 
onUpdate }: Props) => {
               }
             />
           ) : (
-            <S.ConnectionList>
-              {connections.map((cs) => (
-                <S.ConnectionItem key={cs.unique}>
-                  <div className="title">
-                    <img src={cs.icon} alt="" />
-                    <span>{cs.name}</span>
-                  </div>
-                  <div className="count">
-                    <span>{cs.scope.length} data scope</span>
-                  </div>
-                  <div className="link">
-                    <Link to={`${cs.unique}`}>View Detail</Link>
-                  </div>
-                </S.ConnectionItem>
-              ))}
-            </S.ConnectionList>
+            <>
+              <Buttons position="top" align="left">
+                <Button
+                  intent={Intent.PRIMARY}
+                  icon="add"
+                  text="Add a Connection"
+                  onClick={handleShowAddConnectionDialog}
+                />
+              </Buttons>
+              <S.ConnectionList>
+                {connections.map((cs) => (
+                  <S.ConnectionItem key={cs.unique}>
+                    <div className="title">
+                      <img src={cs.icon} alt="" />
+                      <span>{cs.name}</span>
+                    </div>
+                    <div className="count">
+                      <span>{cs.scope.length} data scope</span>
+                    </div>
+                    <div className="link">
+                      <Link 
to={`/blueprints/${blueprint.id}/${cs.unique}`}>Edit Data Scope and Scope 
Config</Link>
+                    </div>
+                  </S.ConnectionItem>
+                ))}
+              </S.ConnectionList>
+            </>
           )}
         </div>
       )}
@@ -205,7 +211,17 @@ export const Configuration = ({ blueprint, operating, 
onUpdate }: Props) => {
           onSubmit={(payload) => onUpdate(payload, handleCancel)}
         />
       )}
-      {type === 'add-connection' && <AddConnectionDialog 
onCancel={handleCancel} onSubmit={handleAddConnection} />}
+      {type === 'add-connection' && (
+        <AddConnectionDialog
+          disabled={connections.map((cs) => cs.unique)}
+          onCancel={handleCancel}
+          onSubmit={(connection) =>
+            onUpdate({
+              settings: { ...blueprint.settings, connections: 
[...blueprint.settings.connections, connection] },
+            })
+          }
+        />
+      )}
     </S.ConfigurationPanel>
   );
 };
diff --git a/config-ui/src/pages/blueprint/detail/panel/status.tsx 
b/config-ui/src/pages/blueprint/detail/status-panel.tsx
similarity index 90%
rename from config-ui/src/pages/blueprint/detail/panel/status.tsx
rename to config-ui/src/pages/blueprint/detail/status-panel.tsx
index 264fa28b3..756e4b3f1 100644
--- a/config-ui/src/pages/blueprint/detail/panel/status.tsx
+++ b/config-ui/src/pages/blueprint/detail/status-panel.tsx
@@ -25,18 +25,18 @@ import { getCron } from '@/config';
 import { PipelineContextProvider, PipelineInfo, PipelineTasks, 
PipelineHistorical } from '@/pages';
 import { formatTime } from '@/utils';
 
-import type { BlueprintType } from '../../types';
+import type { BlueprintType } from '../types';
 
-import * as S from '../styled';
+import * as S from './styled';
 
 interface Props {
   blueprint: BlueprintType;
   pipelineId?: ID;
   operating: boolean;
-  onRun: () => void;
+  onRun: (skipCollectors: boolean) => void;
 }
 
-export const Status = ({ blueprint, pipelineId, operating, onRun }: Props) => {
+export const StatusPanel = ({ blueprint, pipelineId, operating, onRun }: 
Props) => {
   const cron = useMemo(() => getCron(blueprint.isManual, 
blueprint.cronConfig), [blueprint]);
 
   return (
@@ -52,7 +52,7 @@ export const Status = ({ blueprint, pipelineId, operating, 
onRun }: Props) => {
             loading={operating}
             intent={Intent.PRIMARY}
             text="Re-transform Data"
-            onClick={onRun}
+            onClick={() => onRun(true)}
           />
         </Tooltip2>
         <Button
@@ -60,7 +60,7 @@ export const Status = ({ blueprint, pipelineId, operating, 
onRun }: Props) => {
           loading={operating}
           intent={Intent.PRIMARY}
           text="Collect All Data"
-          onClick={onRun}
+          onClick={() => onRun(false)}
         />
       </div>
       <PipelineContextProvider>
diff --git a/config-ui/src/pages/blueprint/detail/types.ts 
b/config-ui/src/pages/blueprint/detail/types.ts
deleted file mode 100644
index 19e2a2ab8..000000000
--- a/config-ui/src/pages/blueprint/detail/types.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-export type ConfigConnectionItemType = {
-  icon: string;
-  name: string;
-  connectionId: ID;
-  plugin: string;
-  entities: string[];
-  selectedEntites: string[];
-  scope: Array<{ id: string; entities: string[] }>;
-  scopeIds: string[];
-};
diff --git a/config-ui/src/pages/blueprint/types.ts 
b/config-ui/src/pages/blueprint/types.ts
index eec9164d6..ca87e5d6a 100644
--- a/config-ui/src/pages/blueprint/types.ts
+++ b/config-ui/src/pages/blueprint/types.ts
@@ -38,7 +38,6 @@ export type BlueprintType = {
       connectionId: ID;
       scopes?: Array<{
         id: string;
-        entities: string[];
       }>;
     }>;
   };
diff --git a/config-ui/src/pages/connection/detail/index.tsx 
b/config-ui/src/pages/connection/detail/index.tsx
index 0d7945497..12be3e8c3 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -109,7 +109,6 @@ const ConnectionDetail = ({ plugin, id }: Props) => {
   const handleCreateDataScope = () => {
     setVersion((v) => v + 1);
     handleShowTips();
-    handleHideDialog();
   };
 
   const handleShowClearDataScopeDialog = (scopeId: ID) => {
diff --git 
a/config-ui/src/plugins/components/data-scope-select-remote/index.tsx 
b/config-ui/src/plugins/components/data-scope-select-remote/index.tsx
index b9954f010..582be6462 100644
--- a/config-ui/src/plugins/components/data-scope-select-remote/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-select-remote/index.tsx
@@ -39,8 +39,8 @@ interface Props {
   plugin: string;
   connectionId: ID;
   disabledScope?: any[];
-  onCancel?: () => void;
-  onSubmit?: (origin: any) => void;
+  onCancel: () => void;
+  onSubmit: (origin: any) => void;
 }
 
 export const DataScopeSelectRemote = ({ plugin, connectionId, disabledScope, 
onSubmit, onCancel }: Props) => {
@@ -79,9 +79,10 @@ export const DataScopeSelectRemote = ({ plugin, 
connectionId, disabledScope, onS
               data,
             });
 
-      onSubmit?.(res);
+      onSubmit(res);
     } finally {
       setOperating(false);
+      onCancel();
     }
   };
 
diff --git a/config-ui/src/plugins/components/data-scope-select/index.tsx 
b/config-ui/src/plugins/components/data-scope-select/index.tsx
index 93cf8b353..4a3730972 100644
--- a/config-ui/src/plugins/components/data-scope-select/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-select/index.tsx
@@ -17,9 +17,9 @@
  */
 
 import { useState, useEffect } from 'react';
-import { Button, InputGroup, Intent } from '@blueprintjs/core';
+import { Button, Intent } from '@blueprintjs/core';
 
-import { FormItem, ExternalLink, Table, Buttons } from '@/components';
+import { PageLoading, FormItem, ExternalLink, Buttons, Table } from 
'@/components';
 import { useRefreshData } from '@/hooks';
 import { getPluginId } from '@/plugins';
 
@@ -48,49 +48,67 @@ export const DataScopeSelect = ({ plugin, connectionId, 
initialScope, onSubmit,
     onSubmit?.(scope);
   };
 
+  if (!ready || !data) {
+    return <PageLoading />;
+  }
+
   return (
     <FormItem
       label="Select Data Scope"
       subLabel={
-        <>
-          {' '}
-          Select the data scope in this Connection that you wish to associate 
with this Project. If you wish to add more
-          Data Scope to this Connection, please{' '}
-          <ExternalLink link={`/connections/${plugin}/${connectionId}`}>go to 
the Connection page</ExternalLink>.
-        </>
+        data.length ? (
+          <>
+            Select the data scope in this Connection that you wish to 
associate with this Project. If you wish to add
+            more Data Scope to this Connection, please{' '}
+            <ExternalLink link={`/connections/${plugin}/${connectionId}`}>go 
to the Connection page</ExternalLink>.
+          </>
+        ) : (
+          <>
+            There is no Data Scope in this connection yet, please{' '}
+            <ExternalLink link={`/connections/${plugin}/${connectionId}`}>
+              add Data Scope and manage their Scope Configs
+            </ExternalLink>{' '}
+            first.
+          </>
+        )
       }
       required
     >
-      <S.Wrapper>
-        <div className="action">
-          <Button intent={Intent.PRIMARY} icon="refresh" text="Refresh Data 
Scope" />
-        </div>
-        <div className="search">
-          <InputGroup placeholder="Search for Data Scopes" />
-        </div>
-        <Table
-          noShadow
-          loading={!ready}
-          columns={[
-            {
-              title: 'Data Scope',
-              dataIndex: 'name',
-              key: 'name',
-            },
-          ]}
-          dataSource={data}
-          rowSelection={{
-            rowKey: getPluginId(plugin),
-            type: 'checkbox',
-            selectedRowKeys: scopeIds as string[],
-            onChange: (selectedRowKeys) => setScopeIds(selectedRowKeys),
-          }}
-        />
-        <Buttons>
-          <Button outlined intent={Intent.PRIMARY} text="Cancel" 
onClick={onCancel} />
-          <Button intent={Intent.PRIMARY} text="Save" onClick={handleSubmit} />
-        </Buttons>
-      </S.Wrapper>
+      {data.length ? (
+        <S.Wrapper>
+          <Buttons position="top" align="left">
+            <Button intent={Intent.PRIMARY} icon="refresh" text="Refresh Data 
Scope" />
+          </Buttons>
+          <Table
+            noShadow
+            loading={!ready}
+            columns={[
+              {
+                title: 'Data Scope',
+                dataIndex: 'name',
+                key: 'name',
+              },
+            ]}
+            dataSource={data}
+            rowSelection={{
+              rowKey: getPluginId(plugin),
+              type: 'checkbox',
+              selectedRowKeys: scopeIds as string[],
+              onChange: (selectedRowKeys) => setScopeIds(selectedRowKeys),
+            }}
+          />
+          <Buttons>
+            <Button outlined intent={Intent.PRIMARY} text="Cancel" 
onClick={onCancel} />
+            <Button disabled={!scopeIds.length} intent={Intent.PRIMARY} 
text="Save" onClick={handleSubmit} />
+          </Buttons>
+        </S.Wrapper>
+      ) : (
+        <S.Wrapper>
+          <ExternalLink link={`/connections/${plugin}/${connectionId}`}>
+            <Button intent={Intent.PRIMARY} icon="add" text="Add Data Scope" />
+          </ExternalLink>
+        </S.Wrapper>
+      )}
     </FormItem>
   );
 };
diff --git a/config-ui/src/plugins/components/data-scope-select/styled.ts 
b/config-ui/src/plugins/components/data-scope-select/styled.ts
index 719705ec3..d61c686ae 100644
--- a/config-ui/src/plugins/components/data-scope-select/styled.ts
+++ b/config-ui/src/plugins/components/data-scope-select/styled.ts
@@ -19,16 +19,5 @@
 import styled from 'styled-components';
 
 export const Wrapper = styled.div`
-  .action {
-    margin-top: 24px;
-  }
-
-  .search {
-    margin-top: 24px;
-    margin-bottom: 24px;
-  }
-
-  .btns {
-    display: flex;
-  }
+  margin-top: 24px;
 `;

Reply via email to