feat(config-ui): about the new design of transformation (#4741)

* feat(config-ui): updated the jsx react to react-jsx

* refactor(config-ui): remove content about transformation

* refactor(config-ui): enhance the feature of the table component

* fix(config-ui): github transformation form missed default value

* fix(config-ui): the datasource judgment error in the table component

* feat(config-ui): add new component workflow

* refactor(config-ui): the transformation in plugins

* feat(config-ui): add plugin component transformation-select

* feat(config-ui): add global type MixConnection

* refactor(config-ui): the plugin transformation rename to transformation-form

* feat(config-ui): new plugin tranformation

* chore(config-ui): remove plugin data scope list

* refactor(config-ui): the plugin data-scope rename to data-scope-form

* feat(config-ui): new plugin data-scope

* refactor(config-ui): the plugin other content

* fix(config-ui): adjust the transformation form

* fix(config-ui): adjust the data scope form

* fix(config-ui): missed initial value in data scope form

* feat(config-ui): add new prop noFooter in transformation

* feat(config-ui): add new page bp-connection-add and bp-connection-detail

* refactor(config-ui): the bp create page

* refactor(config-ui): adjust the bp detail page

* refactor(config-ui): adjust the props for transformation

* refactor(config-ui): the plugin data-scope-form

* refactor(config-ui): adjust the props for data-scope

* refactor(config-ui): adjust the props for dialog

* feat(config-ui): add new page about project connection

* refactor(config-ui): adjust the style for github and gitlab data-scope

* refactor(config-ui): the data-scope-form

* fix(config-ui): the origin error in transformation

* refactor(config-ui): adjust the style for data-scope

* feat(config-ui): add props from in bp connection page

* fix(config-ui): type defined error

* fix(config-ui): some bugs for new transformation and data-scope
diff --git a/config-ui/src/App.tsx b/config-ui/src/App.tsx
index edfa54c..ea9b4e9 100644
--- a/config-ui/src/App.tsx
+++ b/config-ui/src/App.tsx
@@ -16,7 +16,6 @@
  *
  */
 
-import React from 'react';
 import { Switch, Route, Redirect } from 'react-router-dom';
 
 import { BaseLayout } from '@/layouts';
@@ -30,8 +29,8 @@
   BlueprintHomePage,
   BlueprintCreatePage,
   BlueprintDetailPage,
-  TransformationHomePage,
-  TransformationDetailPage,
+  BlueprintConnectioAddPage,
+  BlueprintConnectionDetailPage,
 } from '@/pages';
 
 function App() {
@@ -41,6 +40,8 @@
         <Route path="/" exact component={() => <Redirect to="/projects" />} />
         <Route exact path="/projects" component={() => <ProjectHomePage />} />
         <Route exact path="/projects/:pname" component={() => <ProjectDetailPage />} />
+        <Route exact path="/projects/:pname/:bid/connection-add" component={() => <BlueprintConnectioAddPage />} />
+        <Route exact path="/projects/:pname/:bid/:unique" component={() => <BlueprintConnectionDetailPage />} />
         <Route
           exact
           path="/projects/:pname/create-blueprint"
@@ -53,9 +54,8 @@
         <Route exact path="/blueprints" component={() => <BlueprintHomePage />} />
         <Route exact path="/blueprints/create" component={() => <BlueprintCreatePage from={FromEnum.blueprint} />} />
         <Route exact path="/blueprints/:id" component={() => <BlueprintDetailPage />} />
-        <Route exact path="/transformations" component={() => <TransformationHomePage />} />
-        <Route exact path="/transformations/:plugin/create" component={() => <TransformationDetailPage />} />
-        <Route exact path="/transformations/:plugin/:tid" component={() => <TransformationDetailPage />} />
+        <Route exact path="/blueprints/:bid/connection-add" component={() => <BlueprintConnectioAddPage />} />
+        <Route exact path="/blueprints/:bid/:unique" component={() => <BlueprintConnectionDetailPage />} />
       </Switch>
     </BaseLayout>
   );
diff --git a/config-ui/src/components/dialog/index.tsx b/config-ui/src/components/dialog/index.tsx
index 85d4b89..290b96c 100644
--- a/config-ui/src/components/dialog/index.tsx
+++ b/config-ui/src/components/dialog/index.tsx
@@ -25,7 +25,7 @@
   isOpen: boolean;
   children?: React.ReactNode;
   style?: React.CSSProperties;
-  title?: string;
+  title?: React.ReactNode;
   footer?: React.ReactNode | null;
   cancelText?: string;
   okText?: string;
diff --git a/config-ui/src/components/index.ts b/config-ui/src/components/index.ts
index 3291883..a9948ea 100644
--- a/config-ui/src/components/index.ts
+++ b/config-ui/src/components/index.ts
@@ -16,16 +16,17 @@
  *
  */
 
-export * from './loading';
+export * from './action';
+export * from './card';
+export * from './dialog';
 export * from './divider';
+export * from './inspector';
+export * from './loading';
+export * from './logo';
+export * from './no-data';
 export * from './page-header';
 export * from './selector';
-export * from './dialog';
 export * from './table';
 export * from './toast';
-export * from './logo';
-export * from './card';
-export * from './inspector';
-export * from './action';
 export * from './tooltip';
-export * from './no-data';
+export * from './workflow';
diff --git a/config-ui/src/store/transformations/index.ts b/config-ui/src/components/table/components/index.ts
similarity index 93%
copy from config-ui/src/store/transformations/index.ts
copy to config-ui/src/components/table/components/index.ts
index 34ae5c1..ef68e25 100644
--- a/config-ui/src/store/transformations/index.ts
+++ b/config-ui/src/components/table/components/index.ts
@@ -16,5 +16,5 @@
  *
  */
 
-export * from './types';
-export * from './context';
+export * from './loading';
+export * from './no-data';
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/components/table/components/loading.tsx
similarity index 83%
rename from config-ui/src/store/transformations/types.ts
rename to config-ui/src/components/table/components/loading.tsx
index 48a344e..c0d73a2 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/components/table/components/loading.tsx
@@ -16,8 +16,12 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+import { Card, PageLoading } from '@/components';
+
+export const TableLoading = () => {
+  return (
+    <Card>
+      <PageLoading />
+    </Card>
+  );
 };
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/components/table/components/no-data.tsx
similarity index 61%
copy from config-ui/src/store/transformations/types.ts
copy to config-ui/src/components/table/components/no-data.tsx
index 48a344e..6dbe940 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/components/table/components/no-data.tsx
@@ -16,8 +16,27 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+import { Button, Intent } from '@blueprintjs/core';
+
+import { NoData } from '@/components';
+
+interface Props {
+  text?: React.ReactNode;
+  btnText?: string;
+  onCreate?: () => void;
+}
+
+export const TableNoData = ({ text, btnText, onCreate }: Props) => {
+  return (
+    <NoData
+      text={text}
+      action={
+        onCreate && (
+          <Button intent={Intent.PRIMARY} icon="plus" onClick={onCreate}>
+            {btnText ?? 'Create'}
+          </Button>
+        )
+      }
+    />
+  );
 };
diff --git a/config-ui/src/store/transformations/index.ts b/config-ui/src/components/table/hooks/index.ts
similarity index 93%
rename from config-ui/src/store/transformations/index.ts
rename to config-ui/src/components/table/hooks/index.ts
index 34ae5c1..97fc287 100644
--- a/config-ui/src/store/transformations/index.ts
+++ b/config-ui/src/components/table/hooks/index.ts
@@ -16,5 +16,4 @@
  *
  */
 
-export * from './types';
-export * from './context';
+export * from './use-row-selection';
diff --git a/config-ui/src/components/table/hooks/use-row-selection.ts b/config-ui/src/components/table/hooks/use-row-selection.ts
new file mode 100644
index 0000000..a0a686f
--- /dev/null
+++ b/config-ui/src/components/table/hooks/use-row-selection.ts
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react';
+
+export interface UseRowSelectionProps<T> {
+  dataSource: T[];
+  rowSelection?: {
+    rowKey: string;
+    type?: 'checkbox' | 'radio';
+    selectedRowKeys?: ID[];
+    onChange?: (selectedRowKeys: ID[]) => void;
+  };
+}
+
+export const useRowSelection = <T>({ dataSource, rowSelection }: UseRowSelectionProps<T>) => {
+  const [selectedKeys, setSelectedKeys] = useState<ID[]>([]);
+
+  const {
+    rowKey = 'key',
+    type = 'checkbox',
+    selectedRowKeys,
+    onChange,
+  } = {
+    rowKey: 'key',
+    type: 'checkbox',
+    ...rowSelection,
+  };
+
+  useEffect(() => {
+    setSelectedKeys(selectedRowKeys ?? []);
+  }, [selectedRowKeys]);
+
+  const handleChecked = (data: T) => {
+    const key = (data as any)[rowKey];
+    let result: ID[] = selectedKeys;
+
+    switch (true) {
+      case !selectedKeys.includes(key) && type === 'radio':
+        result = [key];
+        break;
+      case !selectedKeys.includes(key) && type === 'checkbox':
+        result = [...selectedKeys, key];
+        break;
+      case selectedKeys.includes(key) && type === 'checkbox':
+        result = selectedKeys.filter((k) => k !== key);
+        break;
+    }
+
+    onChange ? onChange(result) : setSelectedKeys(result);
+  };
+
+  const handleCheckedAll = () => {
+    let result: ID[] = [];
+
+    if (selectedKeys.length !== dataSource.length) {
+      result = dataSource.map((data: any) => data[rowKey]);
+    }
+
+    onChange ? onChange(result) : setSelectedKeys(result);
+  };
+
+  return useMemo(
+    () => ({
+      canSelection: !!rowSelection,
+      selectionType: type,
+      getCheckedAll: () => dataSource.length === selectedKeys.length,
+      onCheckedAll: handleCheckedAll,
+      getChecked: (data: T) => {
+        return selectedKeys.includes((data as any)[rowKey]);
+      },
+      onChecked: handleChecked,
+    }),
+    [selectedKeys],
+  );
+};
diff --git a/config-ui/src/components/table/styled.ts b/config-ui/src/components/table/styled.ts
index 9ef1169..59a7622 100644
--- a/config-ui/src/components/table/styled.ts
+++ b/config-ui/src/components/table/styled.ts
@@ -18,32 +18,17 @@
 
 import styled from 'styled-components';
 
-export const Container = styled.div`
-  position: relative;
-`;
-
-export const Loading = styled.div`
-  text-align: center;
-`;
-
-export const NoData = styled.div`
-  text-align: center;
-
-  img {
-    display: inline-block;
-  }
-`;
-
 export const Table = styled.table`
   table-layout: fixed;
   width: 100%;
   background-color: #fff;
-  box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px rgba(0, 0, 0, 0.07);
-  border-radius: 8px;
+  border-radius: 4px;
   border-spacing: 0;
 `;
 
-export const THeader = styled.thead``;
+export const THeader = styled.thead`
+  background-color: #f0f4fe;
+`;
 
 export const TBody = styled.tbody``;
 
@@ -57,26 +42,20 @@
 
 export const TH = styled.th`
   padding: 12px 16px;
-  border-bottom: 1px solid #dbe4fd;
+  font-weight: 400;
+  border-bottom: 1px solid #dbdcdf;
+
+  label.bp4-control {
+    margin-bottom: 0;
+  }
 `;
 
 export const TD = styled.td`
   padding: 12px 16px;
-  border-bottom: 1px solid #dbe4fd;
+  border-bottom: 1px solid #dbdcdf;
   word-break: break-word;
-`;
 
-export const TDEllipsis = styled.td`
-  word-break: break-word;
-`;
-
-export const Mask = styled.div`
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
+  label.bp4-control {
+    margin-bottom: 0;
+  }
 `;
diff --git a/config-ui/src/components/table/table.tsx b/config-ui/src/components/table/table.tsx
index 9dfc585..bace2e7 100644
--- a/config-ui/src/components/table/table.tsx
+++ b/config-ui/src/components/table/table.tsx
@@ -17,14 +17,16 @@
  */
 
 import React from 'react';
-import { Button, Intent } from '@blueprintjs/core';
+import { Checkbox, Radio } from '@blueprintjs/core';
 
-import { Loading, Card, NoData, TextTooltip } from '@/components';
+import { TextTooltip } from '@/components';
 
 import { ColumnType } from './types';
+import { TableLoading, TableNoData } from './components';
+import { useRowSelection, UseRowSelectionProps } from './hooks';
 import * as S from './styled';
 
-interface Props<T> {
+interface Props<T> extends UseRowSelectionProps<T> {
   loading?: boolean;
   columns: ColumnType<T>;
   dataSource: T[];
@@ -33,68 +35,77 @@
     btnText?: string;
     onCreate?: () => void;
   };
+  noShadow?: boolean;
 }
 
-export const Table = <T extends Record<string, any>>({ loading, columns, dataSource, noData = {} }: Props<T>) => {
-  const { text, btnText, onCreate } = noData;
+export const Table = <T extends Record<string, any>>({
+  loading,
+  columns,
+  dataSource,
+  noData = {},
+  rowSelection,
+  noShadow = false,
+}: Props<T>) => {
+  const { canSelection, selectionType, getCheckedAll, onCheckedAll, getChecked, onChecked } = useRowSelection<T>({
+    dataSource,
+    rowSelection,
+  });
+
+  if (loading) {
+    return <TableLoading />;
+  }
+
+  if (!dataSource.length) {
+    return <TableNoData {...noData} />;
+  }
 
   return (
-    <S.Container>
-      {loading ? (
-        <Card>
-          <S.Loading>
-            <Loading />
-          </S.Loading>
-        </Card>
-      ) : !dataSource.length ? (
-        <NoData
-          text={text}
-          action={
-            onCreate && (
-              <Button intent={Intent.PRIMARY} icon="plus" onClick={onCreate}>
-                {btnText ?? 'Create'}
-              </Button>
-            )
-          }
-        />
-      ) : (
-        <S.Table>
-          <S.THeader>
-            <S.TR>
-              {columns.map(({ key, width, align = 'left', title }) => (
-                <S.TH key={key} style={{ width, textAlign: align }}>
-                  {title}
-                </S.TH>
-              ))}
-            </S.TR>
-          </S.THeader>
-          <S.TBody>
-            {dataSource.map((data, i) => (
-              <S.TR key={i}>
-                {columns.map(({ key, width, align = 'left', ellipsis, dataIndex, render }) => {
-                  const value = Array.isArray(dataIndex)
-                    ? dataIndex.reduce((acc, cur) => {
-                        acc[cur] = data[cur];
-                        return acc;
-                      }, {} as any)
-                    : data[dataIndex];
-                  return (
-                    <S.TD key={key} style={{ width, textAlign: align }}>
-                      {render ? (
-                        render(value, data)
-                      ) : ellipsis ? (
-                        <TextTooltip content={value}>{value}</TextTooltip>
-                      ) : (
-                        value
-                      )}
-                    </S.TD>
-                  );
-                })}
-              </S.TR>
-            ))}
-          </S.TBody>
-        </S.Table>
-      )}
-    </S.Container>
+    <S.Table
+      style={{
+        boxShadow: noShadow ? 'none' : '0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px rgba(0, 0, 0, 0.07)',
+      }}
+    >
+      <S.THeader>
+        <S.TR>
+          {canSelection && (
+            <S.TH style={{ width: 40, textAlign: 'center' }}>
+              {selectionType === 'checkbox' && <Checkbox checked={getCheckedAll()} onChange={() => onCheckedAll()} />}
+            </S.TH>
+          )}
+          {columns.map(({ key, width, align = 'left', title }) => (
+            <S.TH key={key} style={{ width, textAlign: align }}>
+              {title}
+            </S.TH>
+          ))}
+        </S.TR>
+      </S.THeader>
+      <S.TBody>
+        {dataSource.map((data, i) => (
+          <S.TR key={i}>
+            {canSelection && (
+              <S.TD style={{ width: 40, textAlign: 'center' }}>
+                {selectionType === 'checkbox' && (
+                  <Checkbox checked={getChecked(data)} onChange={() => onChecked(data)} />
+                )}
+                {selectionType === 'radio' && <Radio checked={getChecked(data)} onChange={() => onChecked(data)} />}
+              </S.TD>
+            )}
+            {columns.map(({ key, width, align = 'left', ellipsis, dataIndex, render }) => {
+              const value = Array.isArray(dataIndex)
+                ? dataIndex.reduce((acc, cur) => {
+                    acc[cur] = data[cur];
+                    return acc;
+                  }, {} as any)
+                : data[dataIndex];
+              return (
+                <S.TD key={key} style={{ width, textAlign: align }}>
+                  {render ? render(value, data) : ellipsis ? <TextTooltip content={value}>{value}</TextTooltip> : value}
+                </S.TD>
+              );
+            })}
+          </S.TR>
+        ))}
+      </S.TBody>
+    </S.Table>
   );
 };
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/components/workflow/index.tsx
similarity index 60%
copy from config-ui/src/store/transformations/types.ts
copy to config-ui/src/components/workflow/index.tsx
index 48a344e..f44b45d 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/components/workflow/index.tsx
@@ -16,8 +16,24 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+import { Icon } from '@blueprintjs/core';
+
+import * as S from './styled';
+
+interface Props {
+  steps: string[];
+  activeStep: number;
+}
+
+export const Workflow = ({ steps, activeStep }: Props) => {
+  return (
+    <S.List>
+      {steps.map((step, i) => (
+        <S.Item key={i} actived={i + 1 === activeStep} passed={activeStep > i + 1}>
+          <span className="step">{activeStep > i + 1 ? <Icon icon="tick" /> : i + 1}</span>
+          <span className="name">{step}</span>
+        </S.Item>
+      ))}
+    </S.List>
+  );
 };
diff --git a/config-ui/src/pages/blueprint/create/components/workflow/styled.ts b/config-ui/src/components/workflow/styled.ts
similarity index 88%
rename from config-ui/src/pages/blueprint/create/components/workflow/styled.ts
rename to config-ui/src/components/workflow/styled.ts
index f99272f..1c88a5e 100644
--- a/config-ui/src/pages/blueprint/create/components/workflow/styled.ts
+++ b/config-ui/src/components/workflow/styled.ts
@@ -23,7 +23,7 @@
   align-items: center;
 `;
 
-export const Item = styled.li<{ active?: boolean }>`
+export const Item = styled.li<{ actived: boolean; passed: boolean }>`
   position: relative;
   flex: 1;
   display: flex;
@@ -71,16 +71,21 @@
     background-color: #f0f4fe;
     border-radius: 50%;
 
-    ${({ active }) =>
-      active
+    ${({ actived }) =>
+      actived
         ? `
   color: #fff;
   background-color: #7497f7;
   `
-        : `
-  color: #7497f7;
-  background-color: #f0f4fe;
-      `}
+        : ''}
+
+    ${({ passed }) =>
+      passed
+        ? `
+  color: #fff;
+    background-color: #4DB764;
+    `
+        : ''}
   }
 
   span.name {
diff --git a/config-ui/src/global.d.ts b/config-ui/src/global.d.ts
index 0f3a050..2c326ff 100644
--- a/config-ui/src/global.d.ts
+++ b/config-ui/src/global.d.ts
@@ -18,6 +18,19 @@
 
 type ID = string | number;
 
+type MixConnection = {
+  unique: string;
+  plugin: string;
+  connectionId: ID;
+  name: string;
+  icon: string;
+  scope: Array<{
+    id: string;
+    entities: string[];
+  }>;
+  origin: Array<any>;
+};
+
 declare module '*.svg' {
   const content: any;
   export default content;
diff --git a/config-ui/src/hooks/use-operator.ts b/config-ui/src/hooks/use-operator.ts
index 58e9174..d961d40 100644
--- a/config-ui/src/hooks/use-operator.ts
+++ b/config-ui/src/hooks/use-operator.ts
@@ -23,7 +23,7 @@
 export const useOperator = <T>(
   request: (paylod?: any) => Promise<T>,
   options?: {
-    callback?: () => void;
+    callback?: (res?: any) => void;
     formatReason?: (err: unknown) => string;
     formatMessage?: () => string;
   },
@@ -31,14 +31,14 @@
   const [operating, setOperating] = useState(false);
 
   const handleSubmit = async (paylod?: any) => {
-    const [success] = await operator(() => request(paylod), {
+    const [success, res] = await operator(() => request(paylod), {
       setOperating,
       formatReason: options?.formatReason ? options?.formatMessage : (err) => (err as any).response?.data?.message,
       formatMessage: options?.formatMessage,
     });
 
     if (success) {
-      options?.callback?.();
+      options?.callback?.(res);
     }
   };
 
diff --git a/config-ui/src/layouts/base/use-menu.ts b/config-ui/src/layouts/base/use-menu.ts
index 6e282ca..637cc0c 100644
--- a/config-ui/src/layouts/base/use-menu.ts
+++ b/config-ui/src/layouts/base/use-menu.ts
@@ -59,12 +59,6 @@
           })),
         },
         {
-          key: 'transformation',
-          title: 'Transformation',
-          icon: 'function',
-          path: '/transformations',
-        },
-        {
           key: 'advanced',
           title: 'Advanced',
           icon: 'pulse',
diff --git a/config-ui/src/plugins/components/data-scope-list/api.ts b/config-ui/src/pages/blueprint/connection-add/api.ts
similarity index 80%
copy from config-ui/src/plugins/components/data-scope-list/api.ts
copy to config-ui/src/pages/blueprint/connection-add/api.ts
index 9035ede..0abf652 100644
--- a/config-ui/src/plugins/components/data-scope-list/api.ts
+++ b/config-ui/src/pages/blueprint/connection-add/api.ts
@@ -18,5 +18,7 @@
 
 import { request } from '@/utils';
 
-export const getDataScopeRepo = (plugin: string, connectionId: ID, repoId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${repoId}`);
+export const getBlueprint = (id: ID) => request(`/blueprints/${id}`);
+
+export const updateBlueprint = (id: ID, payload: any) =>
+  request(`/blueprints/${id}`, { method: 'patch', data: payload });
diff --git a/config-ui/src/store/transformations/index.ts b/config-ui/src/pages/blueprint/connection-add/components/index.ts
similarity index 90%
copy from config-ui/src/store/transformations/index.ts
copy to config-ui/src/pages/blueprint/connection-add/components/index.ts
index 34ae5c1..6ffb0bd 100644
--- a/config-ui/src/store/transformations/index.ts
+++ b/config-ui/src/pages/blueprint/connection-add/components/index.ts
@@ -16,5 +16,6 @@
  *
  */
 
-export * from './types';
-export * from './context';
+export * from './step-1';
+export * from './step-2';
+export * from './step-3';
diff --git a/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx b/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
new file mode 100644
index 0000000..1188c44
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-add/components/step-1.tsx
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ */
+
+import { Button, Intent } from '@blueprintjs/core';
+
+import { Table } from '@/components';
+import type { ConnectionItemType } from '@/store';
+import { ConnectionContextProvider, ConnectionContextConsumer } from '@/store';
+
+import { useConnectionAdd } from '../context';
+
+import * as S from './styled';
+
+export const Step1 = () => {
+  const { filter, connection, onChangeConnection, onCancel, onNext } = useConnectionAdd();
+
+  return (
+    <ConnectionContextProvider filterBeta filter={filter}>
+      <ConnectionContextConsumer>
+        {({ connections }) => (
+          <S.Wrapper>
+            <Table
+              columns={[{ title: 'Data Connection', dataIndex: 'name', key: 'name' }]}
+              dataSource={connections}
+              rowSelection={{
+                rowKey: 'unique',
+                type: 'radio',
+                selectedRowKeys: connection?.unique ? [connection?.unique] : [],
+                onChange: (selectedRowKeys) => {
+                  const unique = selectedRowKeys[0];
+                  const connection = connections.find((cs) => cs.unique === unique) as ConnectionItemType;
+                  onChangeConnection({
+                    unique: connection.unique,
+                    plugin: connection.plugin,
+                    connectionId: connection.id,
+                    name: connection.name,
+                    icon: connection.icon,
+                    scope: [],
+                    origin: [],
+                  });
+                },
+              }}
+            />
+            <S.Action>
+              <Button outlined intent={Intent.PRIMARY} onClick={onCancel}>
+                Cancel
+              </Button>
+              <Button intent={Intent.PRIMARY} disabled={!connection} onClick={onNext}>
+                Next Step
+              </Button>
+            </S.Action>
+          </S.Wrapper>
+        )}
+      </ConnectionContextConsumer>
+    </ConnectionContextProvider>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/connection-add/components/step-2.tsx b/config-ui/src/pages/blueprint/connection-add/components/step-2.tsx
new file mode 100644
index 0000000..6675e44
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-add/components/step-2.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ *
+ */
+
+import { DataScope } from '@/plugins';
+
+import { useConnectionAdd } from '../context';
+
+import * as S from './styled';
+
+export const Step2 = () => {
+  const { connection, onChangeConnection, onPrev, onNext } = useConnectionAdd();
+
+  if (!connection) {
+    return null;
+  }
+
+  return (
+    <S.Wrapper>
+      <DataScope
+        connections={[connection]}
+        cancelBtnProps={{ text: 'Previous Step' }}
+        submitBtnProps={{ text: 'Next Step' }}
+        onCancel={onPrev}
+        onSubmit={(connections) => onChangeConnection(connections[0])}
+        onNext={onNext}
+      />
+    </S.Wrapper>
+  );
+};
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/pages/blueprint/connection-add/components/step-3.tsx
similarity index 60%
copy from config-ui/src/store/transformations/types.ts
copy to config-ui/src/pages/blueprint/connection-add/components/step-3.tsx
index 48a344e..45afa12 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/pages/blueprint/connection-add/components/step-3.tsx
@@ -16,8 +16,27 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+import { Transformation } from '@/plugins';
+
+import { useConnectionAdd } from '../context';
+
+import * as S from './styled';
+
+export const Step3 = () => {
+  const { connection, onPrev, operating, onSubmit } = useConnectionAdd();
+
+  if (!connection) {
+    return null;
+  }
+
+  return (
+    <S.Wrapper>
+      <Transformation
+        connections={[connection]}
+        submitBtnProps={{ text: 'Save', loading: operating }}
+        onCancel={onPrev}
+        onNext={() => onSubmit(connection)}
+      />
+    </S.Wrapper>
+  );
 };
diff --git a/config-ui/src/pages/blueprint/create/components/action/styled.ts b/config-ui/src/pages/blueprint/connection-add/components/styled.ts
similarity index 90%
rename from config-ui/src/pages/blueprint/create/components/action/styled.ts
rename to config-ui/src/pages/blueprint/connection-add/components/styled.ts
index 0660328..229370b 100644
--- a/config-ui/src/pages/blueprint/create/components/action/styled.ts
+++ b/config-ui/src/pages/blueprint/connection-add/components/styled.ts
@@ -18,13 +18,13 @@
 
 import styled from 'styled-components';
 
-export const Container = styled.div`
+export const Wrapper = styled.div`
+  margin-top: 36px;
+`;
+
+export const Action = styled.div`
   display: flex;
   align-items: center;
   justify-content: space-between;
   margin-top: 36px;
-
-  .bp4-button + .bp4-button {
-    margin-left: 8px;
-  }
 `;
diff --git a/config-ui/src/pages/blueprint/connection-add/context.tsx b/config-ui/src/pages/blueprint/connection-add/context.tsx
new file mode 100644
index 0000000..1f6e787
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-add/context.tsx
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ *
+ */
+
+import React, { useState, useContext } from 'react';
+import { useHistory } from 'react-router-dom';
+
+import { PageLoading } from '@/components';
+import { useRefreshData, useOperator } from '@/hooks';
+
+import * as API from './api';
+
+type ContextType = {
+  name: string;
+  step: number;
+  filter: string[];
+  connection?: MixConnection;
+  onChangeConnection: (connection: MixConnection) => void;
+
+  onPrev: () => void;
+  onNext: () => void;
+  onCancel: () => void;
+  operating: boolean;
+  onSubmit: (connection: MixConnection) => void;
+};
+
+export const Context = React.createContext<ContextType>({
+  name: '',
+  step: 1,
+  filter: [],
+
+  onChangeConnection: () => {},
+  onPrev: () => {},
+  onNext: () => {},
+  onCancel: () => {},
+  operating: false,
+  onSubmit: () => {},
+});
+
+interface Props {
+  pname?: string;
+  id: string;
+  children: React.ReactNode;
+}
+
+export const ContextProvider = ({ pname, id, children }: Props) => {
+  const [step, setStep] = useState(1);
+  const [connection, setConnection] = useState<MixConnection>();
+
+  const history = useHistory();
+
+  const { ready, data } = useRefreshData(() => API.getBlueprint(id), [id]);
+
+  const { operating, onSubmit } = useOperator(
+    async (connection: MixConnection) => {
+      if (!connection) return;
+      const { plugin, connectionId, scope } = connection;
+
+      const payload = {
+        ...data,
+        settings: {
+          ...data.settings,
+          connections: [
+            ...data.settings.connections,
+            {
+              plugin,
+              connectionId,
+              scopes: scope.map((sc) => ({
+                id: `${sc.id}`,
+                entities: sc.entities,
+              })),
+            },
+          ],
+        },
+      };
+
+      await API.updateBlueprint(data.id, payload);
+    },
+    {
+      callback: () => history.push(pname ? `/projects/${pname}` : `/blueprints/${id}`),
+    },
+  );
+
+  const handlePrev = () => {
+    setStep(step - 1);
+  };
+
+  const handleNext = () => {
+    setStep(step + 1);
+  };
+
+  const handleCancel = () => {
+    history.push(`/blueprints/${id}`);
+  };
+
+  if (!ready || !data) {
+    return <PageLoading />;
+  }
+
+  return (
+    <Context.Provider
+      value={{
+        name: data.name,
+        step,
+        filter: data.settings.connections.map((cs: any) => `${cs.plugin}-${cs.connectionId}`),
+        connection,
+        onChangeConnection: setConnection,
+        onPrev: handlePrev,
+        onNext: handleNext,
+        onCancel: handleCancel,
+        operating,
+        onSubmit,
+      }}
+    >
+      {children}
+    </Context.Provider>
+  );
+};
+
+export const useConnectionAdd = () => useContext(Context);
diff --git a/config-ui/src/pages/blueprint/connection-add/index.tsx b/config-ui/src/pages/blueprint/connection-add/index.tsx
new file mode 100644
index 0000000..1ea7393
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-add/index.tsx
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ *
+ */
+
+import { useParams } from 'react-router-dom';
+
+import { PageHeader, Workflow } from '@/components';
+
+import { Step1, Step2, Step3 } from './components';
+import { ContextProvider, Context } from './context';
+
+export const BlueprintConnectioAddPage = () => {
+  const { pname, bid } = useParams<{ pname?: string; bid: string }>();
+
+  return (
+    <ContextProvider pname={pname} id={bid}>
+      <Context.Consumer>
+        {({ name, step }) => (
+          <PageHeader
+            breadcrumbs={[
+              ...(pname
+                ? [
+                    {
+                      name: 'Projects',
+                      path: '/projects',
+                    },
+                    {
+                      name: pname,
+                      path: `/projects/${pname}`,
+                    },
+                  ]
+                : [{ name: name, path: `/blueprints/${bid}` }]),
+              { name: 'Add a New Connection', path: '' },
+            ]}
+          >
+            <Workflow
+              steps={['Select a Data Connection', 'Set Data Scope', 'Add Transformation (Optional)']}
+              activeStep={step}
+            />
+            {step === 1 && <Step1 />}
+            {step === 2 && <Step2 />}
+            {step === 3 && <Step3 />}
+          </PageHeader>
+        )}
+      </Context.Consumer>
+    </ContextProvider>
+  );
+};
diff --git a/config-ui/src/plugins/components/data-scope-list/api.ts b/config-ui/src/pages/blueprint/connection-detail/api.ts
similarity index 68%
copy from config-ui/src/plugins/components/data-scope-list/api.ts
copy to config-ui/src/pages/blueprint/connection-detail/api.ts
index 9035ede..7686757 100644
--- a/config-ui/src/plugins/components/data-scope-list/api.ts
+++ b/config-ui/src/pages/blueprint/connection-detail/api.ts
@@ -18,5 +18,13 @@
 
 import { request } from '@/utils';
 
-export const getDataScopeRepo = (plugin: string, connectionId: ID, repoId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${repoId}`);
+export const getBlueprint = (id: ID) => request(`/blueprints/${id}`);
+
+export const updateBlueprint = (id: ID, payload: any) =>
+  request(`/blueprints/${id}`, { method: 'patch', data: payload });
+
+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}`);
diff --git a/config-ui/src/pages/blueprint/connection-detail/index.tsx b/config-ui/src/pages/blueprint/connection-detail/index.tsx
new file mode 100644
index 0000000..966706e
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-detail/index.tsx
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ *
+ */
+
+import { useState } from 'react';
+import { useParams, useHistory } from 'react-router-dom';
+import { Button, Intent, Position } from '@blueprintjs/core';
+import { Popover2 } from '@blueprintjs/popover2';
+
+import { PageHeader, PageLoading, Dialog } from '@/components';
+import { EntitiesLabel } from '@/config';
+import { useRefreshData } from '@/hooks';
+import type { PluginConfigType } from '@/plugins';
+import { PluginConfig, DataScope, Transformation } from '@/plugins';
+
+import * as API from './api';
+import * as S from './styled';
+
+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 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 = PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType;
+    const origin = await Promise.all(scope.map((sc: any) => API.getDataScope(plugin, connectionId, sc.id)));
+
+    return {
+      blueprint,
+      bpName: blueprint.name,
+      csName: connection.name,
+      entities: scope[0].entities,
+      connection: {
+        unique,
+        plugin,
+        connectionId: +connectionId,
+        name: connection.name,
+        icon: config.icon,
+        scope,
+        origin,
+      },
+    };
+  }, [version]);
+
+  if (!ready || !data) {
+    return <PageLoading />;
+  }
+
+  const { blueprint, bpName, csName, entities, connection } = data;
+
+  const handleShowDataScope = () => setIsOpen(true);
+  const handleHideDataScope = () => setIsOpen(false);
+
+  const handleChangeDataScope = async () => {
+    await API.updateBlueprint(blueprint.id, {
+      ...blueprint,
+      settings: {
+        ...blueprint.settings,
+        connections: blueprint.settings.connections.map((cs: any) => {
+          if (cs.plugin === connection.plugin && cs.connectionId === connection.connectionId) {
+            return {
+              ...cs,
+              scopes: connection.scope.map((sc: any) => ({
+                id: `${sc.id}`,
+                entities: sc.entities,
+              })),
+            };
+          }
+          return cs;
+        }),
+      },
+    });
+    setVersion((v) => v + 1);
+  };
+
+  const handleChangeTransformation = () => {
+    setVersion((v) => v + 1);
+  };
+
+  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}`);
+  };
+
+  return (
+    <PageHeader
+      breadcrumbs={[
+        ...(pname
+          ? [
+              {
+                name: 'Projects',
+                path: '/projects',
+              },
+              {
+                name: pname,
+                path: `/projects/${pname}`,
+              },
+            ]
+          : [{ name: bpName, path: `/blueprints/${bid}` }]),
+        { name: `Connection - ${csName}`, path: '' },
+      ]}
+    >
+      <S.Action>
+        <span>
+          <Button intent={Intent.PRIMARY} icon="annotation" onClick={handleShowDataScope}>
+            Edit Data Scope
+          </Button>
+        </span>
+        <Popover2
+          position={Position.BOTTOM}
+          content={
+            <S.ActionDelete>
+              <div className="content">Are you sure you want to delete this connection?</div>
+              <div className="btns" onClick={handleRemoveConnection}>
+                <Button intent={Intent.PRIMARY} text="Confirm" />
+              </div>
+            </S.ActionDelete>
+          }
+        >
+          <Button intent={Intent.DANGER} icon="trash">
+            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>
+      <Transformation connections={[connection]} noFooter onSubmit={handleChangeTransformation} />
+      <Dialog
+        isOpen={isOpen}
+        title="Change Data Scope"
+        footer={null}
+        style={{ width: 820 }}
+        onCancel={handleHideDataScope}
+      >
+        <DataScope
+          connections={[connection]}
+          initialScope={connection.origin}
+          onCancel={handleHideDataScope}
+          onSubmit={handleChangeDataScope}
+          onNext={handleHideDataScope}
+        />
+      </Dialog>
+    </PageHeader>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/connection-detail/styled.ts b/config-ui/src/pages/blueprint/connection-detail/styled.ts
new file mode 100644
index 0000000..92de69e
--- /dev/null
+++ b/config-ui/src/pages/blueprint/connection-detail/styled.ts
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ */
+
+import styled from 'styled-components';
+
+export const Action = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 24px;
+
+  .bp4-button + .bp4-button {
+    margin-left: 8px;
+  }
+`;
+
+export const ActionDelete = styled.div`
+  padding: 16px 24px;
+
+  .btns {
+    display: flex;
+    align-items: center;
+    justify-content: end;
+    margin-top: 16px;
+  }
+`;
+
+export const Entities = styled.div`
+  margin-bottom: 24px;
+
+  h4 {
+    margin-bottom: 16px;
+  }
+
+  ul {
+    display: flex;
+    align-items: center;
+
+    li::after {
+      content: ',';
+    }
+
+    li:last-child::after {
+      content: '';
+    }
+
+    li + li {
+      margin-left: 4px;
+    }
+  }
+`;
+
+export const SelectTransformationWrapper = styled.div`
+  .action {
+    margin-bottom: 24px;
+  }
+
+  .btns {
+    display: flex;
+    align-items: center;
+    justify-content: end;
+    margin-top: 24px;
+
+    .bp4-button + .bp4-button {
+      margin-left: 4px;
+    }
+  }
+`;
diff --git a/config-ui/src/pages/blueprint/create/components/action/index.tsx b/config-ui/src/pages/blueprint/create/components/action/index.tsx
deleted file mode 100644
index e2a6eb2..0000000
--- a/config-ui/src/pages/blueprint/create/components/action/index.tsx
+++ /dev/null
@@ -1,77 +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.
- *
- */
-
-import React, { useMemo } from 'react';
-import { ButtonGroup, Button, Icon, Intent, Position, Colors } from '@blueprintjs/core';
-import { Tooltip2 } from '@blueprintjs/popover2';
-
-import { ModeEnum } from '../../../types';
-import { useCreateBP } from '../../bp-context';
-
-import * as S from './styled';
-
-export const Action = () => {
-  const { step, mode, error, showDetail, onChangeStep, onChangeShowInspector, onSave, onSaveAndRun } = useCreateBP();
-
-  const [isFirst, isLast] = useMemo(() => {
-    return [step === 1, (mode === ModeEnum.normal && step === 4) || (mode === ModeEnum.advanced && step === 2)];
-  }, [step, mode]);
-
-  if (showDetail) {
-    return null;
-  }
-
-  return (
-    <S.Container>
-      <ButtonGroup>
-        {!isFirst && (
-          <Button outlined intent={Intent.PRIMARY} text="Previous Step" onClick={() => onChangeStep(step - 1)} />
-        )}
-      </ButtonGroup>
-      <ButtonGroup>
-        <Button
-          minimal
-          intent={Intent.PRIMARY}
-          icon="code"
-          text="Inspect"
-          onClick={() => onChangeShowInspector(true)}
-        />
-        {isLast ? (
-          <>
-            <Button intent={Intent.PRIMARY} text="Save Blueprint" onClick={onSave} />
-            <Button outlined intent={Intent.PRIMARY} text="Save and Run Now" onClick={onSaveAndRun} />
-          </>
-        ) : (
-          <Button
-            intent={Intent.PRIMARY}
-            disabled={!!error}
-            icon={
-              error ? (
-                <Tooltip2 defaultIsOpen placement={Position.TOP} content={error}>
-                  <Icon icon="warning-sign" color={Colors.ORANGE5} style={{ margin: 0 }} />
-                </Tooltip2>
-              ) : null
-            }
-            text="Next Step"
-            onClick={() => onChangeStep(step + 1)}
-          />
-        )}
-      </ButtonGroup>
-    </S.Container>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/components/index.ts b/config-ui/src/pages/blueprint/create/components/index.ts
index b85355f..6e59634 100644
--- a/config-ui/src/pages/blueprint/create/components/index.ts
+++ b/config-ui/src/pages/blueprint/create/components/index.ts
@@ -16,5 +16,7 @@
  *
  */
 
-export * from './workflow';
-export * from './action';
+export * from './step-1';
+export * from './step-2';
+export * from './step-3';
+export * from './step-4';
diff --git a/config-ui/src/pages/blueprint/create/step-one/index.tsx b/config-ui/src/pages/blueprint/create/components/step-1.tsx
similarity index 70%
rename from config-ui/src/pages/blueprint/create/step-one/index.tsx
rename to config-ui/src/pages/blueprint/create/components/step-1.tsx
index 4f5c0da..7273219 100644
--- a/config-ui/src/pages/blueprint/create/step-one/index.tsx
+++ b/config-ui/src/pages/blueprint/create/components/step-1.tsx
@@ -16,16 +16,17 @@
  *
  */
 
-import React, { useMemo } from 'react';
-import { pick } from 'lodash';
-import { InputGroup, Icon } from '@blueprintjs/core';
+import { useMemo } from 'react';
+import { InputGroup, Icon, Button, Intent } from '@blueprintjs/core';
 
 import { useConnection, ConnectionStatusEnum } from '@/store';
 import { Card, Divider, MultiSelector, Loading } from '@/components';
 
 import { ModeEnum, FromEnum } from '../../types';
 import { AdvancedEditor } from '../../components';
-import { useCreateBP } from '../bp-context';
+import { validRawPlan } from '../../utils';
+
+import { useCreate } from '../context';
 
 import * as S from './styled';
 
@@ -33,26 +34,36 @@
   from: FromEnum;
 }
 
-export const StepOne = ({ from }: Props) => {
+export const Step1 = ({ from }: Props) => {
   const { connections, onTest } = useConnection();
-
-  const {
-    mode,
-    name,
-    rawPlan,
-    uniqueList,
-    scopeMap,
-    onChangeMode,
-    onChangeName,
-    onChangeRawPlan,
-    onChangeUniqueList,
-    onChangeScopeMap,
-  } = useCreateBP();
+  const { mode, name, rawPlan, onChangeMode, onChangeName, onChangeConnections, onChangeRawPlan, onNext, ...props } =
+    useCreate();
 
   const fromProject = useMemo(() => from === FromEnum.project, [from]);
+  const uniqueList = useMemo(() => props.connections.map((sc) => sc.unique), [props.connections]);
+
+  const error = useMemo(() => {
+    switch (true) {
+      case !name:
+        return true;
+      case name.length < 3:
+        return true;
+      case mode === ModeEnum.advanced && validRawPlan(rawPlan):
+        return true;
+      case mode === ModeEnum.normal && !uniqueList.length:
+        return true;
+      case mode === ModeEnum.normal &&
+        !connections
+          .filter((cs) => uniqueList.includes(cs.unique))
+          .every((cs) => cs.status === ConnectionStatusEnum.ONLINE):
+        return true;
+      default:
+        return false;
+    }
+  }, [mode, name, connections, props.connections, rawPlan]);
 
   return (
-    <>
+    <S.Wrapper>
       <Card className="card">
         <h2>Blueprint Name</h2>
         <Divider />
@@ -79,9 +90,17 @@
                 if (lastItem) {
                   onTest(lastItem);
                 }
-                const uniqueList = selectedItems.map((sc) => sc.unique);
-                onChangeUniqueList(uniqueList);
-                onChangeScopeMap(pick(scopeMap, uniqueList));
+                onChangeConnections(
+                  selectedItems.map((sc) => ({
+                    unique: sc.unique,
+                    plugin: sc.plugin,
+                    connectionId: sc.id,
+                    name: sc.name,
+                    icon: sc.icon,
+                    scope: [],
+                    origin: [],
+                  })),
+                );
               }}
             />
             <S.ConnectionList>
@@ -128,6 +147,11 @@
           </S.Tips>
         </>
       )}
-    </>
+
+      <S.Btns>
+        <span></span>
+        <Button intent={Intent.PRIMARY} disabled={error} text="Next Step" onClick={onNext} />
+      </S.Btns>
+    </S.Wrapper>
   );
 };
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/pages/blueprint/create/components/step-2.tsx
similarity index 60%
copy from config-ui/src/store/transformations/types.ts
copy to config-ui/src/pages/blueprint/create/components/step-2.tsx
index 48a344e..e3dbff8 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/pages/blueprint/create/components/step-2.tsx
@@ -16,8 +16,25 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+import { DataScope } from '@/plugins';
+
+import { useCreate } from '../context';
+
+import * as S from './styled';
+
+export const Step2 = () => {
+  const { connections, onChangeConnections, onPrev, onNext } = useCreate();
+
+  return (
+    <S.Wrapper>
+      <DataScope
+        connections={connections}
+        cancelBtnProps={{ text: 'Previous Step' }}
+        submitBtnProps={{ text: 'Next Step' }}
+        onCancel={onPrev}
+        onSubmit={onChangeConnections}
+        onNext={onNext}
+      />
+    </S.Wrapper>
+  );
 };
diff --git a/config-ui/src/pages/transformation/detail/styled.ts b/config-ui/src/pages/blueprint/create/components/step-3.tsx
similarity index 67%
copy from config-ui/src/pages/transformation/detail/styled.ts
copy to config-ui/src/pages/blueprint/create/components/step-3.tsx
index ec7ce22..be178fe 100644
--- a/config-ui/src/pages/transformation/detail/styled.ts
+++ b/config-ui/src/pages/blueprint/create/components/step-3.tsx
@@ -16,26 +16,18 @@
  *
  */
 
-import styled from 'styled-components';
+import { Transformation } from '@/plugins';
 
-export const Wrapper = styled.div`
-  .card + .card {
-    margin-top: 32px;
-  }
+import { useCreate } from '../context';
 
-  .name {
-    h3 {
-      margin: 0 0 8px;
-    }
+import * as S from './styled';
 
-    p {
-      margin: 0 0 8px;
-    }
-  }
+export const Step3 = () => {
+  const { connections, onChangeConnections, onPrev, onNext } = useCreate();
 
-  .action {
-    display: flex;
-    justify-content: flex-end;
-    margin-top: 16px;
-  }
-`;
+  return (
+    <S.Wrapper>
+      <Transformation connections={connections} onCancel={onPrev} onSubmit={onChangeConnections} onNext={onNext} />
+    </S.Wrapper>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/create/components/step-4.tsx b/config-ui/src/pages/blueprint/create/components/step-4.tsx
new file mode 100644
index 0000000..6ed6370
--- /dev/null
+++ b/config-ui/src/pages/blueprint/create/components/step-4.tsx
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ */
+
+import { Button, Intent } from '@blueprintjs/core';
+
+import { Card, Divider } from '@/components';
+
+import { ModeEnum } from '../../types';
+import { SyncPolicy } from '../../components';
+
+import { useCreate } from '../context';
+
+import * as S from './styled';
+
+export const Step4 = () => {
+  const {
+    mode,
+    isManual,
+    cronConfig,
+    skipOnFail,
+    timeAfter,
+    onChangeIsManual,
+    onChangeCronConfig,
+    onChangeSkipOnFail,
+    onChangeTimeAfter,
+    onPrev,
+    onSave,
+    onSaveAndRun,
+  } = useCreate();
+
+  return (
+    <S.Wrapper>
+      <Card>
+        <h2>Set Sync Policy</h2>
+        <Divider />
+        <SyncPolicy
+          isManual={isManual}
+          cronConfig={cronConfig}
+          skipOnFail={skipOnFail}
+          showTimeFilter={mode === ModeEnum.normal}
+          timeAfter={timeAfter}
+          onChangeIsManual={onChangeIsManual}
+          onChangeCronConfig={onChangeCronConfig}
+          onChangeSkipOnFail={onChangeSkipOnFail}
+          onChangeTimeAfter={onChangeTimeAfter}
+        />
+      </Card>
+      <S.Btns>
+        <Button intent={Intent.PRIMARY} outlined text="Previous Step" onClick={onPrev} />
+        <div>
+          <Button intent={Intent.PRIMARY} text="Save Blueprint" onClick={onSave} />
+          <Button intent={Intent.PRIMARY} outlined text="Save and Run Now" onClick={onSaveAndRun} />
+        </div>
+      </S.Btns>
+    </S.Wrapper>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/create/step-one/styled.ts b/config-ui/src/pages/blueprint/create/components/styled.ts
similarity index 69%
rename from config-ui/src/pages/blueprint/create/step-one/styled.ts
rename to config-ui/src/pages/blueprint/create/components/styled.ts
index b7219fc..6706609 100644
--- a/config-ui/src/pages/blueprint/create/step-one/styled.ts
+++ b/config-ui/src/pages/blueprint/create/components/styled.ts
@@ -19,6 +19,14 @@
 import styled from 'styled-components';
 import { Colors } from '@blueprintjs/core';
 
+export const Wrapper = styled.div`
+  margin-top: 36px;
+
+  .card + .card {
+    margin-top: 16px;
+  }
+`;
+
 export const ConnectionList = styled.ul`
   padding: 12px;
 
@@ -56,3 +64,38 @@
     cursor: pointer;
   }
 `;
+
+export const ConnectionColumn = styled.div`
+  display: flex;
+  align-items: center;
+
+  img {
+    margin-right: 4px;
+    width: 20px;
+  }
+`;
+
+export const ScopeColumn = styled.ul``;
+
+export const ScopeItem = styled.li`
+  margin-bottom: 4px;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+
+  .bp4-button {
+    margin-left: 6px;
+  }
+`;
+
+export const Btns = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 36px;
+
+  .bp4-button + .bp4-button {
+    margin-left: 8px;
+  }
+`;
diff --git a/config-ui/src/pages/blueprint/create/components/workflow/index.tsx b/config-ui/src/pages/blueprint/create/components/workflow/index.tsx
deleted file mode 100644
index 221bc81..0000000
--- a/config-ui/src/pages/blueprint/create/components/workflow/index.tsx
+++ /dev/null
@@ -1,47 +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.
- *
- */
-
-import React, { useMemo } from 'react';
-
-import { ModeEnum } from '../../../types';
-import { useCreateBP } from '../../bp-context';
-
-import * as S from './styled';
-
-export const WorkFlow = () => {
-  const { step, mode } = useCreateBP();
-
-  const steps = useMemo(
-    () =>
-      mode === ModeEnum.normal
-        ? ['Add Data Connections', 'Set Data Scope', 'Add Transformation (Optional)', 'Set Sync Policy']
-        : ['Create Advanced Configuration', 'Set Sync Policy'],
-    [mode],
-  );
-
-  return (
-    <S.List>
-      {steps.map((it, i) => (
-        <S.Item key={it} active={i + 1 === step}>
-          <span className="step">{i + 1}</span>
-          <span className="name">{it}</span>
-        </S.Item>
-      ))}
-    </S.List>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/bp-context.tsx b/config-ui/src/pages/blueprint/create/context.tsx
similarity index 60%
rename from config-ui/src/pages/blueprint/create/bp-context.tsx
rename to config-ui/src/pages/blueprint/create/context.tsx
index ea5d22b..f2af24d 100644
--- a/config-ui/src/pages/blueprint/create/bp-context.tsx
+++ b/config-ui/src/pages/blueprint/create/context.tsx
@@ -20,49 +20,39 @@
 import { useHistory } from 'react-router-dom';
 import dayjs from 'dayjs';
 
-import type { ConnectionItemType } from '@/store';
-import { useConnection, ConnectionStatusEnum } from '@/store';
 import { operator, formatTime } from '@/utils';
 
 import { ModeEnum, FromEnum } from '../types';
 import { validRawPlan } from '../utils';
 
-import type { BPContextType } from './types';
+import type { ContextType } from './types';
 import * as API from './api';
 
-export const BPContext = React.createContext<BPContextType>({
+export const Context = React.createContext<ContextType>({
   step: 1,
-  error: '',
-  showInspector: false,
-  showDetail: false,
-  payload: {},
 
   name: 'MY BLUEPRINT',
   mode: ModeEnum.normal,
+  connections: [],
   rawPlan: JSON.stringify([[]], null, '  '),
-  uniqueList: [],
-  scopeMap: {},
   cronConfig: '0 0 * * *',
   isManual: false,
   skipOnFail: false,
   timeAfter: null,
 
-  onChangeStep: () => {},
-  onChangeShowInspector: () => {},
-  onChangeShowDetail: () => {},
+  onPrev: () => {},
+  onNext: () => {},
+  onSave: () => {},
+  onSaveAndRun: () => {},
 
   onChangeMode: () => {},
   onChangeName: () => {},
+  onChangeConnections: () => {},
   onChangeRawPlan: () => {},
-  onChangeUniqueList: () => {},
-  onChangeScopeMap: () => {},
   onChangeCronConfig: () => {},
   onChangeIsManual: () => {},
   onChangeSkipOnFail: () => {},
   onChangeTimeAfter: () => {},
-
-  onSave: () => {},
-  onSaveAndRun: () => {},
 });
 
 interface Props {
@@ -71,18 +61,15 @@
   children: React.ReactNode;
 }
 
-export const BPContextProvider = ({ from, projectName, children }: Props) => {
+export const ContextProvider = ({ from, projectName, children }: Props) => {
   const [step, setStep] = useState(1);
-  const [showInspector, setShowInspector] = useState(false);
-  const [showDetail, setShowDetail] = useState(false);
 
   const [name, setName] = useState(
     from === FromEnum.project ? `${window.decodeURIComponent(projectName)}-BLUEPRINT` : 'MY BLUEPRINT',
   );
   const [mode, setMode] = useState<ModeEnum>(ModeEnum.normal);
+  const [connections, setConnections] = useState<MixConnection[]>([]);
   const [rawPlan, setRawPlan] = useState(JSON.stringify([[]], null, '  '));
-  const [uniqueList, setUniqueList] = useState<string[]>([]);
-  const [scopeMap, setScopeMap] = useState<Record<string, any>>({});
   const [cronConfig, setCronConfig] = useState('0 0 * * *');
   const [isManual, setIsManual] = useState(false);
   const [skipOnFail, setSkipOnFail] = useState(true);
@@ -92,30 +79,6 @@
 
   const history = useHistory();
 
-  const { connections } = useConnection();
-
-  const error = useMemo(() => {
-    switch (true) {
-      case !name:
-        return 'Blueprint Name: Enter a valid Name';
-      case name.length < 3:
-        return 'Blueprint Name: Name too short, 3 chars minimum.';
-      case mode === ModeEnum.advanced && validRawPlan(rawPlan):
-        return 'Advanced Mode: Invalid/Empty Configuration';
-      case mode === ModeEnum.normal && !uniqueList.length:
-        return 'Normal Mode: No Data Connections selected.';
-      case mode === ModeEnum.normal &&
-        !connections
-          .filter((cs) => uniqueList.includes(cs.unique))
-          .every((cs) => cs.status === ConnectionStatusEnum.ONLINE):
-        return 'Normal Mode: Has some offline connections';
-      case step === 2 && Object.keys(scopeMap).filter((key) => scopeMap[key].length).length !== uniqueList.length:
-        return 'No Data Scope is Selected';
-      default:
-        return '';
-    }
-  }, [name, mode, rawPlan, uniqueList, connections, step, scopeMap]);
-
   const payload = useMemo(() => {
     const params: any = {
       name,
@@ -131,13 +94,14 @@
       params.settings = {
         version: '2.0.0',
         timeAfter,
-        connections: uniqueList.map((unique) => {
-          const connection = connections.find((cs) => cs.unique === unique) as ConnectionItemType;
-          const scope = scopeMap[unique] ?? [];
+        connections: connections.map((cs) => {
           return {
-            plugin: connection.plugin,
-            connectionId: connection.id,
-            scopes: scope,
+            plugin: cs.plugin,
+            connectionId: cs.connectionId,
+            scopes: cs.scope.map((sc) => ({
+              id: `${sc.id}`,
+              entities: sc.entities,
+            })),
           };
         }),
       };
@@ -149,19 +113,7 @@
     }
 
     return params;
-  }, [
-    name,
-    projectName,
-    mode,
-    cronConfig,
-    isManual,
-    skipOnFail,
-    timeAfter,
-    rawPlan,
-    uniqueList,
-    scopeMap,
-    connections,
-  ]);
+  }, [projectName, name, mode, connections, rawPlan, cronConfig, isManual, skipOnFail, timeAfter]);
 
   const handleSaveAfter = (id: ID) => {
     const path =
@@ -189,48 +141,43 @@
     }
   };
 
+  const handlePrev = () => setStep(step - 1);
+  const handleNext = () => setStep(step + 1);
+
   return (
-    <BPContext.Provider
+    <Context.Provider
       value={{
         step,
-        error,
-        showInspector,
-        showDetail,
-        payload,
 
-        name,
         mode,
+        name,
+        connections,
         rawPlan,
-        uniqueList,
-        scopeMap,
         cronConfig,
         isManual,
         skipOnFail,
         timeAfter,
 
-        onChangeStep: setStep,
-        onChangeShowInspector: setShowInspector,
-        onChangeShowDetail: setShowDetail,
+        onPrev: handlePrev,
+        onNext: handleNext,
+        onSave: handleSave,
+        onSaveAndRun: hanldeSaveAndRun,
 
-        onChangeName: setName,
         onChangeMode: setMode,
+        onChangeName: setName,
+        onChangeConnections: setConnections,
         onChangeRawPlan: setRawPlan,
-        onChangeUniqueList: setUniqueList,
-        onChangeScopeMap: setScopeMap,
         onChangeCronConfig: setCronConfig,
         onChangeIsManual: setIsManual,
         onChangeSkipOnFail: setSkipOnFail,
         onChangeTimeAfter: setTimeAfter,
-
-        onSave: handleSave,
-        onSaveAndRun: hanldeSaveAndRun,
       }}
     >
       {children}
-    </BPContext.Provider>
+    </Context.Provider>
   );
 };
 
-export const useCreateBP = () => {
-  return useContext(BPContext);
+export const useCreate = () => {
+  return useContext(Context);
 };
diff --git a/config-ui/src/pages/blueprint/create/index.tsx b/config-ui/src/pages/blueprint/create/index.tsx
index 6f2b3b8..8cab63e 100644
--- a/config-ui/src/pages/blueprint/create/index.tsx
+++ b/config-ui/src/pages/blueprint/create/index.tsx
@@ -16,21 +16,16 @@
  *
  */
 
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
 import { useParams } from 'react-router-dom';
 
-import { PageHeader, Inspector } from '@/components';
+import { PageHeader, Workflow } from '@/components';
 import { ConnectionContextProvider } from '@/store';
 
 import { ModeEnum, FromEnum } from '../types';
 
-import { BPContext, BPContextProvider } from './bp-context';
-import { WorkFlow, Action } from './components';
-import { StepOne } from './step-one';
-import { StepTwo } from './step-two';
-import { StepThree } from './step-three';
-import { StepFour } from './step-four';
-import * as S from './styled';
+import { ContextProvider, Context } from './context';
+import { Step1, Step2, Step3, Step4 } from './components';
 
 interface Props {
   from: FromEnum;
@@ -59,32 +54,26 @@
 
   return (
     <ConnectionContextProvider filterBeta>
-      <BPContextProvider from={from} projectName={pname}>
-        <BPContext.Consumer>
-          {({ step, mode, name, payload, showInspector, onChangeShowInspector }) => (
+      <ContextProvider from={from} projectName={pname}>
+        <Context.Consumer>
+          {({ step, mode }) => (
             <PageHeader breadcrumbs={breadcrumbs}>
-              <S.Container>
-                <WorkFlow />
-                <S.Content>
-                  {step === 1 && <StepOne from={from} />}
-                  {mode === ModeEnum.normal && step === 2 && <StepTwo />}
-                  {step === 3 && <StepThree />}
-                  {((mode === ModeEnum.normal && step === 4) || (mode === ModeEnum.advanced && step === 2)) && (
-                    <StepFour />
-                  )}
-                </S.Content>
-                <Action />
-                <Inspector
-                  isOpen={showInspector}
-                  title={name}
-                  data={payload}
-                  onClose={() => onChangeShowInspector(false)}
-                />
-              </S.Container>
+              <Workflow
+                steps={
+                  mode === ModeEnum.normal
+                    ? ['Add Data Connections', 'Set Data Scope', 'Add Transformation (Optional)', 'Set Sync Policy']
+                    : ['Create Advanced Configuration', 'Set Sync Policy']
+                }
+                activeStep={step}
+              />
+              {step === 1 && <Step1 from={from} />}
+              {mode === ModeEnum.normal && step === 2 && <Step2 />}
+              {step === 3 && <Step3 />}
+              {((mode === ModeEnum.normal && step === 4) || (mode === ModeEnum.advanced && step === 2)) && <Step4 />}
             </PageHeader>
           )}
-        </BPContext.Consumer>
-      </BPContextProvider>
+        </Context.Consumer>
+      </ContextProvider>
     </ConnectionContextProvider>
   );
 };
diff --git a/config-ui/src/pages/blueprint/create/step-four/index.tsx b/config-ui/src/pages/blueprint/create/step-four/index.tsx
deleted file mode 100644
index 03bcc54..0000000
--- a/config-ui/src/pages/blueprint/create/step-four/index.tsx
+++ /dev/null
@@ -1,57 +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.
- *
- */
-
-import React from 'react';
-
-import { Card, Divider } from '@/components';
-
-import { ModeEnum } from '../../types';
-import { useCreateBP } from '../bp-context';
-import { SyncPolicy } from '../../components';
-
-export const StepFour = () => {
-  const {
-    mode,
-    isManual,
-    cronConfig,
-    skipOnFail,
-    timeAfter,
-    onChangeIsManual,
-    onChangeCronConfig,
-    onChangeSkipOnFail,
-    onChangeTimeAfter,
-  } = useCreateBP();
-
-  return (
-    <Card>
-      <h2>Set Sync Policy</h2>
-      <Divider />
-      <SyncPolicy
-        isManual={isManual}
-        cronConfig={cronConfig}
-        skipOnFail={skipOnFail}
-        showTimeFilter={mode === ModeEnum.normal}
-        timeAfter={timeAfter}
-        onChangeIsManual={onChangeIsManual}
-        onChangeCronConfig={onChangeCronConfig}
-        onChangeSkipOnFail={onChangeSkipOnFail}
-        onChangeTimeAfter={onChangeTimeAfter}
-      />
-    </Card>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/step-three/index.tsx b/config-ui/src/pages/blueprint/create/step-three/index.tsx
deleted file mode 100644
index 5e2396c..0000000
--- a/config-ui/src/pages/blueprint/create/step-three/index.tsx
+++ /dev/null
@@ -1,81 +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.
- *
- */
-
-import React, { useState, useMemo } from 'react';
-import { Icon } from '@blueprintjs/core';
-
-import { Card, Table, Divider } from '@/components';
-import { useConnection } from '@/store';
-import { Transformation } from '@/plugins';
-
-import type { BPConnectionItemType } from '../types';
-import { useCreateBP } from '../bp-context';
-
-import { useColumns } from './use-columns';
-
-export const StepThree = () => {
-  const [connection, setConnection] = useState<BPConnectionItemType>();
-
-  const { connections } = useConnection();
-  const { uniqueList, scopeMap, onChangeShowDetail } = useCreateBP();
-
-  const handleGoDetail = (c: BPConnectionItemType) => {
-    setConnection(c);
-    onChangeShowDetail(true);
-  };
-
-  const handleBack = () => {
-    setConnection(undefined);
-    onChangeShowDetail(false);
-  };
-
-  const columns = useColumns({ onDetail: handleGoDetail });
-  const dataSource = useMemo(
-    () =>
-      uniqueList.map((unique) => {
-        const connection = connections.find((cs) => cs.unique === unique) as BPConnectionItemType;
-        const scope = scopeMap[unique] ?? [];
-        return {
-          ...connection,
-          scopeIds: scope.map((sc: any) => sc.id),
-        };
-      }),
-    [uniqueList, connections, scopeMap],
-  );
-
-  return !connection ? (
-    <Table columns={columns} dataSource={dataSource} />
-  ) : (
-    <Card>
-      <div className="back" onClick={handleBack}>
-        <Icon icon="arrow-left" size={14} />
-        <span>Cancel and Go Back</span>
-      </div>
-      <h2>Add Transformation</h2>
-      <Divider />
-      <Transformation
-        from="create"
-        plugin={connection.plugin}
-        connectionId={connection.id}
-        scopeIds={connection.scopeIds}
-        onCancel={handleBack}
-        onSave={handleBack}
-      />
-    </Card>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/step-three/styled.ts b/config-ui/src/pages/blueprint/create/step-three/styled.ts
deleted file mode 100644
index 5b4d47a..0000000
--- a/config-ui/src/pages/blueprint/create/step-three/styled.ts
+++ /dev/null
@@ -1,29 +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.
- *
- */
-
-import styled from 'styled-components';
-
-export const ConnectionColumn = styled.div`
-  display: flex;
-  align-items: center;
-
-  img {
-    margin-right: 4px;
-    width: 20px;
-  }
-`;
diff --git a/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx b/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx
deleted file mode 100644
index 22bbff8..0000000
--- a/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx
+++ /dev/null
@@ -1,77 +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.
- *
- */
-
-import React, { useMemo } from 'react';
-import { Button, Intent } from '@blueprintjs/core';
-
-import type { ColumnType } from '@/components';
-import { DataScopeList } from '@/plugins';
-
-import type { BPConnectionItemType } from '../types';
-
-import * as S from './styled';
-
-interface Props {
-  onDetail: (connection: BPConnectionItemType) => void;
-}
-
-export const useColumns = ({ onDetail }: Props) => {
-  return useMemo(
-    () =>
-      [
-        {
-          title: 'Data Connections',
-          dataIndex: ['icon', 'name'],
-          key: 'connection',
-          render: ({ icon, name }: Pick<BPConnectionItemType, 'icon' | 'name'>) => (
-            <S.ConnectionColumn>
-              <img src={icon} alt="" />
-              <span>{name}</span>
-            </S.ConnectionColumn>
-          ),
-        },
-        {
-          title: 'Data Scope and Transformation',
-          dataIndex: ['plugin', 'id', 'scopeIds'],
-          key: 'unique',
-          render: ({ plugin, id, scopeIds }: Pick<BPConnectionItemType, 'plugin' | 'id' | 'scopeIds'>) => (
-            <DataScopeList groupByTs plugin={plugin} connectionId={id} scopeIds={scopeIds} />
-          ),
-        },
-        {
-          title: '',
-          key: 'action',
-          align: 'center',
-          render: (_: any, connection: BPConnectionItemType) =>
-            connection.plugin === 'sonarqube' || connection.plugin === 'zentao' ? (
-              'No Transformation Required'
-            ) : (
-              <Button
-                small
-                minimal
-                intent={Intent.PRIMARY}
-                icon="add"
-                text="Add Transformation"
-                onClick={() => onDetail(connection)}
-              />
-            ),
-        },
-      ] as ColumnType<BPConnectionItemType>,
-    [],
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/step-two/index.tsx b/config-ui/src/pages/blueprint/create/step-two/index.tsx
deleted file mode 100644
index 1d0389c..0000000
--- a/config-ui/src/pages/blueprint/create/step-two/index.tsx
+++ /dev/null
@@ -1,96 +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.
- *
- */
-
-import React, { useState, useMemo } from 'react';
-import { Icon } from '@blueprintjs/core';
-
-import { Card, Table, Divider } from '@/components';
-import { useConnection } from '@/store';
-import { DataScope } from '@/plugins';
-
-import type { BPConnectionItemType } from '../types';
-import { useCreateBP } from '../bp-context';
-
-import { useColumns } from './use-columns';
-
-export const StepTwo = () => {
-  const [connection, setConnection] = useState<BPConnectionItemType>();
-
-  const { connections } = useConnection();
-  const { uniqueList, scopeMap, onChangeScopeMap, onChangeShowDetail } = useCreateBP();
-
-  const handleGoDetail = (c: BPConnectionItemType) => {
-    setConnection(c);
-    onChangeShowDetail(true);
-  };
-
-  const handleBack = () => {
-    setConnection(undefined);
-    onChangeShowDetail(false);
-  };
-
-  const handleSave = (scope: any) => {
-    if (!connection) return;
-    onChangeScopeMap({
-      ...scopeMap,
-      [`${connection.unique}`]: scope,
-    });
-    handleBack();
-  };
-
-  const columns = useColumns({ onDetail: handleGoDetail });
-  const dataSource = useMemo(
-    () =>
-      uniqueList.map((unique) => {
-        const connection = connections.find((cs) => cs.unique === unique) as BPConnectionItemType;
-        const scope = scopeMap[unique] ?? [];
-        return {
-          ...connection,
-          scopeIds: scope.map((sc: any) => sc.id),
-        };
-      }),
-    [uniqueList, connections, scopeMap],
-  );
-
-  return !connection ? (
-    <Table columns={columns} dataSource={dataSource} />
-  ) : (
-    <Card>
-      <div className="back" onClick={handleBack}>
-        <Icon icon="arrow-left" size={14} />
-        <span>Cancel and Go Back to the Data Scope List</span>
-      </div>
-      <h2>Set Data Scope</h2>
-      <Divider />
-      <div className="connection">
-        <img src={connection.icon} width={24} alt="" />
-        <span>{connection.name}</span>
-      </div>
-      <DataScope
-        plugin={connection.plugin}
-        connectionId={connection.id}
-        entities={connection.entities}
-        initialValues={{
-          scope: connection.scopeIds.map((id) => ({ id })),
-        }}
-        onCancel={handleBack}
-        onSave={handleSave}
-      />
-    </Card>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/step-two/styled.ts b/config-ui/src/pages/blueprint/create/step-two/styled.ts
deleted file mode 100644
index 5b4d47a..0000000
--- a/config-ui/src/pages/blueprint/create/step-two/styled.ts
+++ /dev/null
@@ -1,29 +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.
- *
- */
-
-import styled from 'styled-components';
-
-export const ConnectionColumn = styled.div`
-  display: flex;
-  align-items: center;
-
-  img {
-    margin-right: 4px;
-    width: 20px;
-  }
-`;
diff --git a/config-ui/src/pages/blueprint/create/step-two/use-columns.tsx b/config-ui/src/pages/blueprint/create/step-two/use-columns.tsx
deleted file mode 100644
index 087fa3b..0000000
--- a/config-ui/src/pages/blueprint/create/step-two/use-columns.tsx
+++ /dev/null
@@ -1,74 +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.
- *
- */
-
-import React, { useMemo } from 'react';
-import { Button, Intent } from '@blueprintjs/core';
-
-import type { ColumnType } from '@/components';
-import { DataScopeList } from '@/plugins';
-
-import type { BPConnectionItemType } from '../types';
-
-import * as S from './styled';
-
-interface Props {
-  onDetail: (connection: BPConnectionItemType) => void;
-}
-
-export const useColumns = ({ onDetail }: Props) => {
-  return useMemo(
-    () =>
-      [
-        {
-          title: 'Data Connections',
-          dataIndex: ['icon', 'name'],
-          key: 'connection',
-          render: ({ icon, name }: Pick<BPConnectionItemType, 'icon' | 'name'>) => (
-            <S.ConnectionColumn>
-              <img src={icon} alt="" />
-              <span>{name}</span>
-            </S.ConnectionColumn>
-          ),
-        },
-        {
-          title: 'Data Scope',
-          dataIndex: ['plugin', 'id', 'scopeIds'],
-          key: 'unique',
-          render: ({ plugin, id, scopeIds }: Pick<BPConnectionItemType, 'plugin' | 'id' | 'scopeIds'>) => (
-            <DataScopeList groupByTs={false} plugin={plugin} connectionId={id} scopeIds={scopeIds} />
-          ),
-        },
-        {
-          title: '',
-          key: 'action',
-          align: 'center',
-          render: (_: any, connection: BPConnectionItemType) => (
-            <Button
-              small
-              minimal
-              intent={Intent.PRIMARY}
-              icon="cog"
-              text="Set Data Scope"
-              onClick={() => onDetail(connection)}
-            />
-          ),
-        },
-      ] as ColumnType<BPConnectionItemType>,
-    [onDetail],
-  );
-};
diff --git a/config-ui/src/pages/blueprint/create/types.ts b/config-ui/src/pages/blueprint/create/types.ts
index a3bd35d..9717371 100644
--- a/config-ui/src/pages/blueprint/create/types.ts
+++ b/config-ui/src/pages/blueprint/create/types.ts
@@ -16,45 +16,32 @@
  *
  */
 
-import type { ConnectionItemType } from '@/store';
-
 import { ModeEnum } from '../types';
 
-export type BPConnectionItemType = ConnectionItemType & {
-  scopeIds: string[];
-};
-
-export type BPContextType = {
+export type ContextType = {
   step: number;
-  error: string;
-  showInspector: boolean;
-  showDetail: boolean;
-  payload: any;
 
-  name: string;
   mode: ModeEnum;
+  name: string;
+  connections: MixConnection[];
   rawPlan: string;
-  uniqueList: string[];
-  scopeMap: Record<string, any>;
   cronConfig: string;
   isManual: boolean;
   skipOnFail: boolean;
   timeAfter: string | null;
 
-  onChangeStep: React.Dispatch<React.SetStateAction<number>>;
-  onChangeShowInspector: React.Dispatch<React.SetStateAction<boolean>>;
-  onChangeShowDetail: React.Dispatch<React.SetStateAction<boolean>>;
+  onPrev: () => void;
+  onNext: () => void;
 
-  onChangeName: React.Dispatch<React.SetStateAction<string>>;
+  onSave: () => void;
+  onSaveAndRun: () => void;
+
   onChangeMode: (mode: ModeEnum) => void;
+  onChangeName: React.Dispatch<React.SetStateAction<string>>;
+  onChangeConnections: React.Dispatch<React.SetStateAction<MixConnection[]>>;
   onChangeRawPlan: React.Dispatch<React.SetStateAction<string>>;
-  onChangeUniqueList: React.Dispatch<React.SetStateAction<string[]>>;
-  onChangeScopeMap: React.Dispatch<React.SetStateAction<Record<string, any>>>;
   onChangeCronConfig: React.Dispatch<React.SetStateAction<string>>;
   onChangeIsManual: React.Dispatch<React.SetStateAction<boolean>>;
   onChangeSkipOnFail: React.Dispatch<React.SetStateAction<boolean>>;
   onChangeTimeAfter: React.Dispatch<React.SetStateAction<string | null>>;
-
-  onSave: () => void;
-  onSaveAndRun: () => void;
 };
diff --git a/config-ui/src/pages/blueprint/detail/blueprint-detail-page.tsx b/config-ui/src/pages/blueprint/detail/blueprint-detail-page.tsx
index bfbde94..52044ac 100644
--- a/config-ui/src/pages/blueprint/detail/blueprint-detail-page.tsx
+++ b/config-ui/src/pages/blueprint/detail/blueprint-detail-page.tsx
@@ -16,11 +16,12 @@
  *
  */
 
-import React from 'react';
 import { useParams } from 'react-router-dom';
 
 import { PageHeader, PageLoading } from '@/components';
 
+import { FromEnum } from '../types';
+
 import { useDetail } from './use-detail';
 import { BlueprintDetail } from './blueprint-detail';
 
@@ -40,7 +41,7 @@
         { name: blueprint.name, path: `/blueprints/${blueprint.id}` },
       ]}
     >
-      <BlueprintDetail id={blueprint.id} />
+      <BlueprintDetail from={FromEnum.blueprint} id={blueprint.id} />
     </PageHeader>
   );
 };
diff --git a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
index 9690500..c3cc981 100644
--- a/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
+++ b/config-ui/src/pages/blueprint/detail/blueprint-detail.tsx
@@ -22,18 +22,31 @@
 
 import { PageLoading } from '@/components';
 
+import { FromEnum } from '../types';
+
 import type { UseDetailProps } from './use-detail';
 import { useDetail } from './use-detail';
 import { Configuration } from './panel/configuration';
 import { Status } from './panel/status';
 import * as S from './styled';
 
-interface Props extends UseDetailProps {}
+interface Props extends UseDetailProps {
+  from?: FromEnum;
+  pname?: string;
+}
 
-export const BlueprintDetail = ({ id }: Props) => {
+export const BlueprintDetail = ({ from = FromEnum.project, pname, id }: Props) => {
   const [activeTab, setActiveTab] = useState<TabId>('status');
 
-  const { loading, blueprint, pipelineId, operating, onRun, onUpdate, onRefresh } = useDetail({
+  const paths = useMemo(
+    () =>
+      from === FromEnum.project
+        ? [`/projects/${pname}/${id}/connection-add`, `/projects/${pname}/${id}/`]
+        : [`/blueprints/${id}/connection-add`, `blueprints/${id}/`],
+    [from, pname],
+  );
+
+  const { loading, blueprint, pipelineId, operating, onRun, onUpdate } = useDetail({
     id,
   });
 
@@ -57,9 +70,7 @@
         <Tab
           id="configuration"
           title="Configuration"
-          panel={
-            <Configuration blueprint={blueprint} operating={operating} onUpdate={onUpdate} onRefresh={onRefresh} />
-          }
+          panel={<Configuration paths={paths} blueprint={blueprint} operating={operating} onUpdate={onUpdate} />}
         />
       </Tabs>
       {showJenkinsTips && (
diff --git a/config-ui/src/pages/blueprint/detail/components/add-scope-dialog/index.tsx b/config-ui/src/pages/blueprint/detail/components/add-scope-dialog/index.tsx
deleted file mode 100644
index fdb3b07..0000000
--- a/config-ui/src/pages/blueprint/detail/components/add-scope-dialog/index.tsx
+++ /dev/null
@@ -1,59 +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.
- *
- */
-
-import React from 'react';
-
-import { Dialog } from '@/components';
-import { DataScope } from '@/plugins';
-
-import type { ConfigConnectionItemType } from '../../types';
-
-interface Props {
-  connection?: ConfigConnectionItemType;
-  onCancel: () => void;
-  onSubmit: (connection: any) => void;
-}
-
-export const AddScopeDialog = ({ connection, onCancel, onSubmit }: Props) => {
-  if (!connection) return null;
-
-  const { plugin, connectionId, entities, scope } = connection;
-
-  const handleSaveScope = (scope: any) => {
-    onSubmit({
-      plugin,
-      connectionId,
-      scopes: scope,
-    });
-  };
-
-  return (
-    <Dialog isOpen title="Change Data Scope" footer={null} style={{ width: 900 }} onCancel={onCancel}>
-      <DataScope
-        plugin={plugin}
-        connectionId={connectionId}
-        entities={entities}
-        initialValues={{
-          scope,
-        }}
-        onCancel={onCancel}
-        onSave={handleSaveScope}
-      />
-    </Dialog>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx b/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
new file mode 100644
index 0000000..2dd24bb
--- /dev/null
+++ b/config-ui/src/pages/blueprint/detail/components/connection-list/index.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ *
+ */
+
+import { useMemo } from 'react';
+import { Link } from 'react-router-dom';
+
+import { PluginConfig } from '@/plugins';
+
+import type { BlueprintType } from '../../../types';
+
+import * as S from './styled';
+
+interface Props {
+  path: string;
+  blueprint: BlueprintType;
+}
+
+export const ConnectionList = ({ path, blueprint }: Props) => {
+  const connections = useMemo(
+    () =>
+      blueprint.settings?.connections
+        .filter((cs) => cs.plugin !== 'webhook')
+        .map((cs: any) => {
+          const plugin = PluginConfig.find((p) => p.plugin === cs.plugin) as any;
+          return {
+            unique: `${cs.plugin}-${cs.connectionId}`,
+            icon: plugin.icon,
+            name: plugin.name,
+            scope: cs.scopes,
+          };
+        })
+        .filter(Boolean),
+    [blueprint],
+  );
+
+  return (
+    <S.List>
+      {connections.map((cs) => (
+        <S.Item 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={`${path}${cs.unique}`}>View Detail</Link>
+          </div>
+        </S.Item>
+      ))}
+    </S.List>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/create/styled.ts b/config-ui/src/pages/blueprint/detail/components/connection-list/styled.ts
similarity index 67%
rename from config-ui/src/pages/blueprint/create/styled.ts
rename to config-ui/src/pages/blueprint/detail/components/connection-list/styled.ts
index 3781099..da2ce28 100644
--- a/config-ui/src/pages/blueprint/create/styled.ts
+++ b/config-ui/src/pages/blueprint/detail/components/connection-list/styled.ts
@@ -18,39 +18,38 @@
 
 import styled from 'styled-components';
 
-export const Container = styled.div``;
+export const List = styled.ul`
+  display: flex;
+  align-items: center;
+`;
 
-export const Content = styled.div`
-  margin-top: 36px;
-  margin-bottom: 24px;
+export const Item = styled.li`
+  margin-right: 24px;
+  padding: 12px 16px;
+  width: 280px;
+  background: #ffffff;
+  box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px rgba(0, 0, 0, 0.07);
+  border-radius: 4px;
 
-  .card + .card {
-    margin-top: 24px;
+  &:last-child {
+    margin-right: 0;
   }
 
-  .back {
+  .title {
     display: flex;
     align-items: center;
-    margin-bottom: 12px;
-    color: #7497f7;
-    cursor: pointer;
 
-    span.bp4-icon {
-      margin-right: 4px;
-      cursor: pointer;
+    img {
+      width: 24px;
+      height: 24px;
     }
-  }
-
-  .connection {
-    display: flex;
-    align-items: center;
-    margin-bottom: 12px;
 
     span {
       margin-left: 8px;
-      font-size: 14px;
-      color: #292b3f;
-      font-weight: 600;
     }
   }
+
+  .count {
+    margin: 24px 0;
+  }
 `;
diff --git a/config-ui/src/pages/blueprint/detail/components/index.ts b/config-ui/src/pages/blueprint/detail/components/index.ts
index 611aa2a..f892b94 100644
--- a/config-ui/src/pages/blueprint/detail/components/index.ts
+++ b/config-ui/src/pages/blueprint/detail/components/index.ts
@@ -18,5 +18,4 @@
 
 export * from './update-name-dialog';
 export * from './update-policy-dialog';
-export * from './add-scope-dialog';
-export * from './update-transformation-dialog';
+export * from './connection-list';
diff --git a/config-ui/src/pages/blueprint/detail/components/update-transformation-dialog/index.tsx b/config-ui/src/pages/blueprint/detail/components/update-transformation-dialog/index.tsx
deleted file mode 100644
index 7eea90f..0000000
--- a/config-ui/src/pages/blueprint/detail/components/update-transformation-dialog/index.tsx
+++ /dev/null
@@ -1,60 +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.
- *
- */
-
-import React from 'react';
-
-import { Dialog } from '@/components';
-import { Transformation } from '@/plugins';
-
-import type { ConfigConnectionItemType } from '../../types';
-
-interface Props {
-  connection?: ConfigConnectionItemType;
-  onCancel: () => void;
-  onRefresh: () => void;
-}
-
-export const UpdateTransformationDialog = ({ connection, onCancel, onRefresh }: Props) => {
-  if (!connection) return null;
-
-  const { plugin, connectionId, scope } = connection;
-
-  const handleSaveAfter = () => {
-    onRefresh();
-    onCancel();
-  };
-
-  return (
-    <Dialog
-      isOpen
-      title="Assign a different transformation by"
-      footer={null}
-      style={{ width: 900 }}
-      onCancel={onCancel}
-    >
-      <Transformation
-        from="update"
-        plugin={plugin}
-        connectionId={connectionId}
-        scopeIds={scope.map((sc) => sc.id)}
-        onCancel={onCancel}
-        onSave={handleSaveAfter}
-      />
-    </Dialog>
-  );
-};
diff --git a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx b/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
index d7b0427..8038ae7 100644
--- a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
+++ b/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
@@ -16,36 +16,32 @@
  *
  */
 
-import React, { useState, useEffect, useMemo } from 'react';
+import { useState, useEffect, useMemo } from 'react';
 import { useHistory } from 'react-router-dom';
 import { Icon, Button, Switch, Colors, Intent } from '@blueprintjs/core';
 import dayjs from 'dayjs';
 
-import { Table, ColumnType } from '@/components';
-import { getCron, transformEntities } from '@/config';
-import { PluginConfig, DataScopeList } from '@/plugins';
+import { getCron } from '@/config';
 
 import type { BlueprintType } from '../../types';
 import { ModeEnum } from '../../types';
 import { validRawPlan } from '../../utils';
 import { AdvancedEditor } from '../../components';
 
-import type { ConfigConnectionItemType } from '../types';
-import { UpdateNameDialog, UpdatePolicyDialog, AddScopeDialog, UpdateTransformationDialog } from '../components';
+import { UpdateNameDialog, UpdatePolicyDialog, ConnectionList } from '../components';
 import * as S from '../styled';
 
 type Type = 'name' | 'frequency' | 'scope' | 'transformation';
 
 interface Props {
+  paths: string[];
   blueprint: BlueprintType;
   operating: boolean;
   onUpdate: (bp: any) => void;
-  onRefresh: () => void;
 }
 
-export const Configuration = ({ blueprint, operating, onUpdate, onRefresh }: Props) => {
+export const Configuration = ({ paths, blueprint, operating, onUpdate }: Props) => {
   const [type, setType] = useState<Type>();
-  const [curConnection, setCurConnection] = useState<ConfigConnectionItemType>();
   const [rawPlan, setRawPlan] = useState('');
 
   const history = useHistory();
@@ -56,27 +52,6 @@
 
   const cron = useMemo(() => getCron(blueprint.isManual, blueprint.cronConfig), [blueprint]);
 
-  const connections = useMemo(
-    () =>
-      blueprint.settings?.connections
-        .filter((cs) => cs.plugin !== 'webhook')
-        .map((cs: any) => {
-          const plugin = PluginConfig.find((p) => p.plugin === cs.plugin) as any;
-          return {
-            icon: plugin.icon,
-            name: plugin.name,
-            connectionId: cs.connectionId,
-            entities: plugin.entities,
-            selectedEntites: cs.scopes?.[0]?.entities ?? [],
-            plugin: cs.plugin,
-            scope: cs.scopes,
-            scopeIds: cs.scopes.map((sc: any) => sc.id),
-          };
-        })
-        .filter(Boolean),
-    [blueprint],
-  );
-
   const handleCancel = () => {
     setType(undefined);
   };
@@ -93,133 +68,47 @@
 
   const handleToggleEnabled = (checked: boolean) => onUpdate({ enable: checked });
 
-  const handleUpdateConnection = (updated: any) =>
-    onUpdate({
-      settings: {
-        ...blueprint.settings,
-        connections: blueprint.settings.connections.map((cs) =>
-          cs.plugin === updated.plugin && cs.connectionId === updated.connectionId ? updated : cs,
-        ),
-      },
-    });
-
   const handleUpdatePlan = () =>
     onUpdate({
       plan: !validRawPlan(rawPlan) ? JSON.parse(rawPlan) : JSON.stringify([[]], null, '  '),
     });
 
-  const columns = useMemo(
-    () =>
-      [
-        {
-          title: 'Data Connections',
-          dataIndex: ['icon', 'name'],
-          key: 'connection',
-          render: ({ icon, name }: Pick<ConfigConnectionItemType, 'icon' | 'name'>) => (
-            <S.ConnectionColumn>
-              <img src={icon} alt="" />
-              <span>{name}</span>
-            </S.ConnectionColumn>
-          ),
-        },
-        {
-          title: 'Data Entities',
-          dataIndex: 'selectedEntites',
-          key: 'selectedEntites',
-          render: (val: string[]) => (
-            <>
-              {transformEntities(val).map(({ label, value }) => (
-                <div key={value}>{label}</div>
-              ))}
-            </>
-          ),
-        },
-        {
-          title: 'Data Scope and Transformation',
-          dataIndex: ['plugin', 'connectionId', 'scopeIds'],
-          key: 'sopce',
-          render: ({
-            plugin,
-            connectionId,
-            scopeIds,
-          }: Pick<ConfigConnectionItemType, 'plugin' | 'connectionId' | 'scopeIds'>) => (
-            <DataScopeList groupByTs plugin={plugin} connectionId={connectionId} scopeIds={scopeIds} />
-          ),
-        },
-        {
-          title: '',
-          key: 'action',
-          align: 'center',
-          render: (_, row: ConfigConnectionItemType) => (
-            <S.ActionColumn>
-              <div
-                className="item"
-                onClick={() => {
-                  setType('scope');
-                  setCurConnection(row);
-                }}
-              >
-                <Icon icon="annotation" color={Colors.BLUE2} />
-                <span>Change Data Scope</span>
-              </div>
-              {row.plugin !== 'sonarqube' && row.plugin !== 'zentao' && (
-                <>
-                  <div
-                    className="item"
-                    onClick={() => {
-                      setType('transformation');
-                      setCurConnection(row);
-                    }}
-                  >
-                    <Icon icon="annotation" color={Colors.BLUE2} />
-                    <span>Re-apply Transformation</span>
-                  </div>
-                  <div className="item" onClick={() => history.push('/transformations')}>
-                    <Icon icon="cog" color={Colors.BLUE2} />
-                    <span>Manage Transformations</span>
-                  </div>
-                </>
-              )}
-            </S.ActionColumn>
-          ),
-        },
-      ] as ColumnType<ConfigConnectionItemType>,
-    [],
-  );
-
   return (
     <S.ConfigurationPanel>
       <div className="top">
-        <div className="block">
-          <h3>Name</h3>
-          <div className="detail">
-            <span>{blueprint.name}</span>
-            <Icon icon="annotation" color={Colors.BLUE2} onClick={() => setType('name')} />
-          </div>
-        </div>
-        <div className="block">
-          <h3>Sync Policy</h3>
-          <div className="detail">
-            <span>
-              {cron.label} {cron.value !== 'manual' ? dayjs(cron.nextTime).format('HH:mm A') : null}
-            </span>
-            <Icon icon="annotation" color={Colors.BLUE2} onClick={() => setType('frequency')} />
-          </div>
-        </div>
-        <div className="block">
-          <h3>Enabled</h3>
-          <div className="detail">
-            <Switch
-              checked={blueprint.enable}
-              onChange={(e) => handleToggleEnabled((e.target as HTMLInputElement).checked)}
-            />
-          </div>
-        </div>
+        <ul>
+          <li>
+            <h3>Name</h3>
+            <div className="detail">
+              <span>{blueprint.name}</span>
+              <Icon icon="annotation" color={Colors.BLUE2} onClick={() => setType('name')} />
+            </div>
+          </li>
+          <li>
+            <h3>Sync Policy</h3>
+            <div className="detail">
+              <span>
+                {cron.label} {cron.value !== 'manual' ? dayjs(cron.nextTime).format('HH:mm A') : null}
+              </span>
+              <Icon icon="annotation" color={Colors.BLUE2} onClick={() => setType('frequency')} />
+            </div>
+          </li>
+        </ul>
+        <Switch
+          label="Blueprint Enabled"
+          checked={blueprint.enable}
+          onChange={(e) => handleToggleEnabled((e.target as HTMLInputElement).checked)}
+        />
       </div>
       {blueprint.mode === ModeEnum.normal && (
         <div className="bottom">
-          <h3>Data Scope and Transformation</h3>
-          <Table columns={columns} dataSource={connections} />
+          <h3>
+            <span>Connections</span>
+            <Button small intent={Intent.PRIMARY} onClick={() => history.push(paths[0])}>
+              Add a Connection
+            </Button>
+          </h3>
+          <ConnectionList path={paths[1]} blueprint={blueprint} />
         </div>
       )}
       {blueprint.mode === ModeEnum.advanced && (
@@ -251,12 +140,6 @@
           onSubmit={handleUpdatePolicy}
         />
       )}
-      {type === 'scope' && (
-        <AddScopeDialog connection={curConnection} onCancel={handleCancel} onSubmit={handleUpdateConnection} />
-      )}
-      {type === 'transformation' && (
-        <UpdateTransformationDialog connection={curConnection} onCancel={handleCancel} onRefresh={onRefresh} />
-      )}
     </S.ConfigurationPanel>
   );
 };
diff --git a/config-ui/src/pages/blueprint/detail/styled.ts b/config-ui/src/pages/blueprint/detail/styled.ts
index 0a772d8..b1a6d84 100644
--- a/config-ui/src/pages/blueprint/detail/styled.ts
+++ b/config-ui/src/pages/blueprint/detail/styled.ts
@@ -26,13 +26,15 @@
   .top {
     display: flex;
     align-items: flex-start;
+    justify-content: space-between;
 
-    .block + .block {
-      margin-left: 32px;
-    }
+    ul {
+      display: flex;
+      align-items: flex-start;
 
-    h3 {
-      margin: 0 0 8px;
+      li + li {
+        margin-left: 48px;
+      }
     }
 
     .detail {
@@ -44,7 +46,13 @@
   }
 
   .bottom {
-    margin-top: 32px;
+    margin-top: 24px;
+
+    h3 {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+    }
 
     .btns {
       margin-top: 16px;
diff --git a/config-ui/src/pages/blueprint/detail/use-detail.ts b/config-ui/src/pages/blueprint/detail/use-detail.ts
index 344ffc5..8c7987e 100644
--- a/config-ui/src/pages/blueprint/detail/use-detail.ts
+++ b/config-ui/src/pages/blueprint/detail/use-detail.ts
@@ -94,7 +94,6 @@
       pipelineId,
       onRun: handleRun,
       onUpdate: handleUpdate,
-      onRefresh: getBlueprint,
     }),
     [loading, operating, blueprint, pipelineId],
   );
diff --git a/config-ui/src/pages/blueprint/index.ts b/config-ui/src/pages/blueprint/index.ts
index 2511a14..cd8a30e 100644
--- a/config-ui/src/pages/blueprint/index.ts
+++ b/config-ui/src/pages/blueprint/index.ts
@@ -20,3 +20,5 @@
 export * from './home';
 export * from './create';
 export * from './detail';
+export * from './connection-add';
+export * from './connection-detail';
diff --git a/config-ui/src/pages/index.ts b/config-ui/src/pages/index.ts
index 3002d23..307e9f4 100644
--- a/config-ui/src/pages/index.ts
+++ b/config-ui/src/pages/index.ts
@@ -20,4 +20,3 @@
 export * from './connection';
 export * from './blueprint';
 export * from './pipeline';
-export * from './transformation';
diff --git a/config-ui/src/pages/project/detail/panel/blueprint.tsx b/config-ui/src/pages/project/detail/panel/blueprint.tsx
index 0c99dcc..d818446 100644
--- a/config-ui/src/pages/project/detail/panel/blueprint.tsx
+++ b/config-ui/src/pages/project/detail/panel/blueprint.tsx
@@ -46,5 +46,5 @@
     );
   }
 
-  return <BlueprintDetail id={project.blueprint.id} />;
+  return <BlueprintDetail pname={project.name} id={project.blueprint.id} />;
 };
diff --git a/config-ui/src/pages/transformation/detail/index.tsx b/config-ui/src/pages/transformation/detail/index.tsx
deleted file mode 100644
index 47d7fb3..0000000
--- a/config-ui/src/pages/transformation/detail/index.tsx
+++ /dev/null
@@ -1,103 +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.
- *
- */
-
-import React from 'react';
-import { useParams, useHistory } from 'react-router-dom';
-import { InputGroup, ButtonGroup, Button, Intent } from '@blueprintjs/core';
-
-import { PageLoading, PageHeader, Card } from '@/components';
-import { GitHubTransformation } from '@/plugins/register/github';
-import { GitLabTransformation } from '@/plugins/register/gitlab';
-import { JenkinsTransformation } from '@/plugins/register/jenkins';
-import { BitbucketTransformation } from '@/plugins/register/bitbucket';
-
-import { useDetail } from './use-detail';
-import * as S from './styled';
-
-export const TransformationDetailPage = () => {
-  const { plugin, tid } = useParams<{ plugin: string; tid?: string }>();
-  const history = useHistory();
-
-  const { loading, operating, name, transformation, onChangeName, onChangeTransformation, onSave } = useDetail({
-    plugin,
-    id: tid,
-  });
-
-  if (loading) {
-    return <PageLoading />;
-  }
-
-  return (
-    <PageHeader
-      breadcrumbs={[
-        { name: 'Transformations', path: '/transformations' },
-        {
-          name: plugin,
-          path: '/transformations',
-        },
-        {
-          name: 'Create',
-          path: `/transformations/${plugin}/${tid ? tid : 'Create'}`,
-        },
-      ]}
-    >
-      <S.Wrapper>
-        <Card className="name card">
-          <h3>Transformation Name *</h3>
-          <p>Give this set of transformation rules a unique name so that you can identify it in the future.</p>
-          <InputGroup
-            placeholder="Enter Transformation Name"
-            value={name}
-            onChange={(e) => onChangeName(e.target.value)}
-          />
-        </Card>
-        <Card className="card">
-          {plugin === 'github' && (
-            <GitHubTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-          )}
-
-          {plugin === 'gitlab' && (
-            <GitLabTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-          )}
-
-          {plugin === 'jenkins' && (
-            <JenkinsTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-          )}
-
-          {plugin === 'bitbucket' && (
-            <BitbucketTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-          )}
-
-          <div className="action">
-            <ButtonGroup>
-              <Button disabled={operating} outlined text="Cancel" onClick={() => history.push('/transformations')} />
-              <Button
-                disabled={!name}
-                loading={operating}
-                outlined
-                intent={Intent.PRIMARY}
-                text="Save"
-                onClick={onSave}
-              />
-            </ButtonGroup>
-          </div>
-        </Card>
-      </S.Wrapper>
-    </PageHeader>
-  );
-};
diff --git a/config-ui/src/pages/transformation/detail/use-detail.ts b/config-ui/src/pages/transformation/detail/use-detail.ts
deleted file mode 100644
index 37705aa..0000000
--- a/config-ui/src/pages/transformation/detail/use-detail.ts
+++ /dev/null
@@ -1,89 +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.
- *
- */
-
-import { useState, useEffect, useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginConfig } from '@/plugins';
-import { operator } from '@/utils';
-
-import * as API from './api';
-
-interface Props {
-  plugin: string;
-  id?: ID;
-}
-
-export const useDetail = ({ plugin, id }: Props) => {
-  const [loading, setLoading] = useState(false);
-  const [operating, setOperating] = useState(false);
-  const [name, setName] = useState('');
-  const [transformation, setTransformation] = useState<any>(
-    (PluginConfig.find((pc) => pc.plugin === plugin) as PluginConfigType)?.transformation,
-  );
-
-  const history = useHistory();
-
-  const getTransformation = async () => {
-    if (!id) return;
-    setLoading(true);
-    try {
-      const res = await API.getTransformation(plugin, id);
-      setName(res.name);
-      setTransformation(res);
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  useEffect(() => {
-    getTransformation();
-  }, []);
-
-  const handleSave = async () => {
-    const payload = {
-      ...transformation,
-      name,
-    };
-
-    const [success] = await operator(
-      () => (id ? API.updateTransformation(plugin, id, payload) : API.createTransformation(plugin, payload)),
-      {
-        setOperating,
-      },
-    );
-
-    if (success) {
-      history.push('/transformations');
-    }
-  };
-
-  return useMemo(
-    () => ({
-      loading,
-      operating,
-      name,
-      transformation,
-      onChangeName: setName,
-      onChangeTransformation: setTransformation,
-      onSave: handleSave,
-    }),
-    [loading, operating, name, transformation],
-  );
-};
diff --git a/config-ui/src/pages/transformation/home/index.tsx b/config-ui/src/pages/transformation/home/index.tsx
deleted file mode 100644
index 35dae32..0000000
--- a/config-ui/src/pages/transformation/home/index.tsx
+++ /dev/null
@@ -1,136 +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.
- *
- */
-
-import React, { useState, useMemo } from 'react';
-import { useHistory } from 'react-router-dom';
-import { ButtonGroup, Button, Icon, Intent } from '@blueprintjs/core';
-
-import { PageHeader, Table, ColumnType, Dialog, Selector, IconButton } from '@/components';
-import type { PluginConfigType } from '@/plugins';
-import { TransformationContextProvider, TransformationContextConsumer, TransformationItemType } from '@/store';
-
-import * as S from './styled';
-
-export const TransformationHomePage = () => {
-  const [active, setActive] = useState('All');
-  const [isOpen, setIsOpen] = useState(false);
-  const [selectedPlugin, setSelectedPlugin] = useState<PluginConfigType>();
-
-  const history = useHistory();
-
-  const handleCreate = () => setIsOpen(true);
-
-  const columns = useMemo(
-    () =>
-      [
-        {
-          title: 'Name',
-          dataIndex: 'name',
-          key: 'name',
-        },
-        {
-          title: 'Data Source',
-          dataIndex: 'plugin',
-          key: 'plugin',
-        },
-        {
-          title: '',
-          key: 'action',
-          width: 100,
-          align: 'center',
-          render: (_, row) =>
-            row.plugin !== 'jira' && (
-              <IconButton
-                icon="cog"
-                tooltip="Detail"
-                onClick={() => history.push(`/transformations/${row.plugin}/${row.id}`)}
-              />
-            ),
-        },
-      ] as ColumnType<TransformationItemType>,
-    [],
-  );
-
-  return (
-    <TransformationContextProvider>
-      <TransformationContextConsumer>
-        {({ plugins, transformations }) => (
-          <PageHeader breadcrumbs={[{ name: 'Transformations', path: '/transformations' }]}>
-            <S.Wrapper>
-              <div className="action">
-                <ButtonGroup>
-                  <Button
-                    intent={active === 'All' ? Intent.PRIMARY : Intent.NONE}
-                    text="All"
-                    onClick={() => setActive('All')}
-                  />
-                  {plugins.map((p) => (
-                    <Button
-                      key={p.plugin}
-                      intent={active === p.plugin ? Intent.PRIMARY : Intent.NONE}
-                      text={p.name}
-                      onClick={() => setActive(p.plugin)}
-                    />
-                  ))}
-                </ButtonGroup>
-                <Button icon="plus" intent={Intent.PRIMARY} text="New Transformation" onClick={handleCreate} />
-              </div>
-              <Table
-                columns={columns}
-                dataSource={transformations.filter((ts) => (active === 'All' ? true : ts.plugin === active))}
-                noData={{
-                  text: 'There is no transformation yet. Please add a new transformation.',
-                  btnText: 'New Transformation',
-                  onCreate: handleCreate,
-                }}
-              />
-              <Dialog
-                isOpen={isOpen}
-                title="Select a Data Source"
-                okText="Continue"
-                okDisabled={!selectedPlugin || selectedPlugin.plugin === 'jira'}
-                onOk={() => history.push(`/transformations/${selectedPlugin?.plugin}/create`)}
-                onCancel={() => setIsOpen(false)}
-              >
-                <S.DialogWrapper>
-                  <p>Select from the supported data sources</p>
-                  <Selector
-                    items={plugins}
-                    getKey={(it) => it.plugin}
-                    getName={(it) => it.name}
-                    selectedItem={selectedPlugin}
-                    onChangeItem={(selectedItem) => setSelectedPlugin(selectedItem)}
-                  />
-                  {selectedPlugin?.plugin === 'jira' && (
-                    <div className="warning">
-                      <Icon icon="error" />
-                      <span>
-                        Because Jira transformation is specific to every Jira connection, you can only add a
-                        Transformation in Blueprints.
-                      </span>
-                    </div>
-                  )}
-                </S.DialogWrapper>
-              </Dialog>
-            </S.Wrapper>
-          </PageHeader>
-        )}
-      </TransformationContextConsumer>
-    </TransformationContextProvider>
-  );
-};
diff --git a/config-ui/src/pages/transformation/home/styled.ts b/config-ui/src/pages/transformation/home/styled.ts
deleted file mode 100644
index 32c5341..0000000
--- a/config-ui/src/pages/transformation/home/styled.ts
+++ /dev/null
@@ -1,40 +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.
- *
- */
-
-import { Colors } from '@blueprintjs/core';
-import styled from 'styled-components';
-
-export const Wrapper = styled.div`
-  & > .action {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    margin-bottom: 16px;
-  }
-`;
-
-export const DialogWrapper = styled.div`
-  .warning {
-    margin-top: 16px;
-
-    .bp4-icon {
-      color: ${Colors.ORANGE3};
-      margin-right: 4px;
-    }
-  }
-`;
diff --git a/config-ui/src/pages/transformation/index.ts b/config-ui/src/pages/transformation/index.ts
deleted file mode 100644
index d5131a6..0000000
--- a/config-ui/src/pages/transformation/index.ts
+++ /dev/null
@@ -1,20 +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 * from './home';
-export * from './detail';
diff --git a/config-ui/src/plugins/components/data-scope/api.ts b/config-ui/src/plugins/components/data-scope-form/api.ts
similarity index 92%
rename from config-ui/src/plugins/components/data-scope/api.ts
rename to config-ui/src/plugins/components/data-scope-form/api.ts
index dc16c20..19abb11 100644
--- a/config-ui/src/plugins/components/data-scope/api.ts
+++ b/config-ui/src/plugins/components/data-scope-form/api.ts
@@ -18,8 +18,8 @@
 
 import { request } from '@/utils';
 
-export const getDataScope = (plugin: string, connectionId: ID, repoId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${repoId}`);
+export const getDataScope = (plugin: string, connectionId: ID, scopeId: string) =>
+  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`);
 
 export const updateDataScope = (plugin: string, connectionId: ID, payload: any) =>
   request(`/plugins/${plugin}/connections/${connectionId}/scopes`, {
diff --git a/config-ui/src/plugins/components/data-scope-form/index.tsx b/config-ui/src/plugins/components/data-scope-form/index.tsx
new file mode 100644
index 0000000..57de5ba
--- /dev/null
+++ b/config-ui/src/plugins/components/data-scope-form/index.tsx
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
+
+import { Card, MultiSelector } from '@/components';
+import { transformEntities } from '@/config';
+import type { PluginConfigType } from '@/plugins';
+import { PluginConfig, getPluginId } from '@/plugins';
+
+import { GitHubDataScope } from '@/plugins/register/github';
+import { JiraDataScope } from '@/plugins/register/jira';
+import { GitLabDataScope } from '@/plugins/register/gitlab';
+import { JenkinsDataScope } from '@/plugins/register/jenkins';
+import { BitbucketDataScope } from '@/plugins/register/bitbucket';
+import { SonarQubeDataScope } from '@/plugins/register/sonarqube';
+import { ZentaoDataScope } from '@/plugins/register/zentao';
+
+import * as API from './api';
+import * as S from './styled';
+
+interface Props {
+  plugin: string;
+  connectionId: ID;
+  initialScope?: any[];
+  initialEntities?: string[];
+  cancelBtnProps?: {
+    text?: string;
+  };
+  submitBtnProps?: {
+    text?: string;
+  };
+  onCancel?: () => void;
+  onSubmit?: (scope: Array<{ id: string; entities: string[] }>, origin: any) => void;
+}
+
+export const DataScopeForm = ({
+  plugin,
+  connectionId,
+  initialScope,
+  initialEntities,
+  onSubmit,
+  onCancel,
+  cancelBtnProps,
+  submitBtnProps,
+}: Props) => {
+  const [operating, setOperating] = useState(false);
+  const [scope, setScope] = useState<any>([]);
+  const [entities, setEntites] = useState<string[]>([]);
+
+  const config = useMemo(() => PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType, []);
+
+  const error = useMemo(
+    () => (!scope.length || !entities.length ? 'No Data Scope is Selected' : ''),
+    [scope, entities],
+  );
+
+  useEffect(() => {
+    setScope(initialScope ?? []);
+    setEntites(initialEntities ?? config.entities);
+  }, []);
+
+  const getDataScope = async (scope: any) => {
+    try {
+      const res = await API.getDataScope(plugin, connectionId, scope[getPluginId(plugin)]);
+      return {
+        ...scope,
+        transformationRuleId: res.transformationRuleId,
+      };
+    } catch {
+      return scope;
+    }
+  };
+
+  const handleSubmit = async () => {
+    setOperating(true);
+    try {
+      const data = await Promise.all(scope.map((sc: any) => getDataScope(sc)));
+      const res =
+        plugin === 'zentao'
+          ? await Promise.all([
+              API.updateDataScopeWithType(plugin, connectionId, 'product', {
+                data: data.filter((s) => s.type !== 'project'),
+              }),
+              API.updateDataScopeWithType(plugin, connectionId, 'project', {
+                data: data.filter((s) => s.type === 'project'),
+              }),
+            ])
+          : await API.updateDataScope(plugin, connectionId, {
+              data,
+            });
+
+      onSubmit?.(
+        res.map((it: any) => ({
+          id: it[getPluginId(plugin)],
+          entities,
+        })),
+        res,
+      );
+    } finally {
+      setOperating(false);
+    }
+  };
+
+  return (
+    <S.Wrapper>
+      <Card>
+        <div className="block">
+          {plugin === 'github' && (
+            <GitHubDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'jira' && (
+            <JiraDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'gitlab' && (
+            <GitLabDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'jenkins' && (
+            <JenkinsDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'bitbucket' && (
+            <BitbucketDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'sonarqube' && (
+            <SonarQubeDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+
+          {plugin === 'zentao' && (
+            <ZentaoDataScope connectionId={connectionId} selectedItems={scope} onChangeItems={setScope} />
+          )}
+        </div>
+
+        <div className="block">
+          <h3>Data Entities</h3>
+          <p>
+            <span>Select the data entities you wish to collect for the projects.</span>{' '}
+            <a
+              href="https://devlake.apache.org/docs/DataModels/DevLakeDomainLayerSchema/#data-models"
+              target="_blank"
+              rel="noreferrer"
+            >
+              Learn about data entities
+            </a>
+          </p>
+          <MultiSelector
+            items={transformEntities(config.entities)}
+            getKey={(item) => item.value}
+            getName={(item) => item.label}
+            selectedItems={transformEntities(entities)}
+            onChangeItems={(items) => setEntites(items.map((it) => it.value))}
+          />
+        </div>
+      </Card>
+
+      <div className="action">
+        <Button
+          outlined
+          intent={Intent.PRIMARY}
+          text="Cancel"
+          {...cancelBtnProps}
+          disabled={operating}
+          onClick={onCancel}
+        />
+        <Button
+          intent={Intent.PRIMARY}
+          text="Save"
+          {...submitBtnProps}
+          loading={operating}
+          disabled={!!error}
+          onClick={handleSubmit}
+        />
+      </div>
+    </S.Wrapper>
+  );
+};
diff --git a/config-ui/src/pages/transformation/detail/styled.ts b/config-ui/src/plugins/components/data-scope-form/styled.ts
similarity index 83%
rename from config-ui/src/pages/transformation/detail/styled.ts
rename to config-ui/src/plugins/components/data-scope-form/styled.ts
index ec7ce22..e094cce 100644
--- a/config-ui/src/pages/transformation/detail/styled.ts
+++ b/config-ui/src/plugins/components/data-scope-form/styled.ts
@@ -19,23 +19,14 @@
 import styled from 'styled-components';
 
 export const Wrapper = styled.div`
-  .card + .card {
-    margin-top: 32px;
-  }
-
-  .name {
-    h3 {
-      margin: 0 0 8px;
-    }
-
-    p {
-      margin: 0 0 8px;
-    }
+  .block {
+    margin-bottom: 16px;
   }
 
   .action {
     display: flex;
-    justify-content: flex-end;
-    margin-top: 16px;
+    align-items: center;
+    justify-content: space-between;
+    margin-top: 36px;
   }
 `;
diff --git a/config-ui/src/plugins/components/data-scope-list/index.tsx b/config-ui/src/plugins/components/data-scope-list/index.tsx
deleted file mode 100644
index aad9320..0000000
--- a/config-ui/src/plugins/components/data-scope-list/index.tsx
+++ /dev/null
@@ -1,74 +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.
- *
- */
-
-import React from 'react';
-import { Icon, Position } from '@blueprintjs/core';
-import { Tooltip2 } from '@blueprintjs/popover2';
-
-import { Loading } from '@/components';
-
-import type { UseDataScopeList } from './use-data-scope-list';
-import { useDataScopeList } from './use-data-scope-list';
-import * as S from './styled';
-
-interface Props extends UseDataScopeList {
-  groupByTs: boolean;
-  scopeIds: string[];
-}
-
-export const DataScopeList = ({ groupByTs, scopeIds, ...props }: Props) => {
-  const { loading, scope, scopeTsMap } = useDataScopeList({ scopeIds, ...props });
-
-  if (!scope.length) {
-    return <span>No Data Scope Selected</span>;
-  }
-
-  if (loading) {
-    return <Loading />;
-  }
-
-  return (
-    <S.ScopeList>
-      {!groupByTs &&
-        scope.map((sc) => (
-          <S.ScopeItem key={sc.id}>
-            <span>{sc.name}</span>
-          </S.ScopeItem>
-        ))}
-
-      {groupByTs &&
-        Object.keys(scopeTsMap).map((name) => (
-          <S.ScopeItemMap key={name}>
-            <div className="title">
-              <Icon icon="function" />
-              <span>{name}</span>
-            </div>
-            <ul>
-              {scopeTsMap[name].map((sc) => (
-                <li key={sc.id}>
-                  <Tooltip2 className="name" content={sc.name} position={Position.TOP}>
-                    <span>{sc.name}</span>
-                  </Tooltip2>
-                </li>
-              ))}
-            </ul>
-          </S.ScopeItemMap>
-        ))}
-    </S.ScopeList>
-  );
-};
diff --git a/config-ui/src/plugins/components/data-scope-list/styled.ts b/config-ui/src/plugins/components/data-scope-list/styled.ts
deleted file mode 100644
index 2ab1608..0000000
--- a/config-ui/src/plugins/components/data-scope-list/styled.ts
+++ /dev/null
@@ -1,68 +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.
- *
- */
-
-import styled from 'styled-components';
-
-export const ScopeList = styled.ul``;
-
-export const ScopeItem = styled.li`
-  margin-bottom: 4px;
-
-  &:last-child {
-    margin-bottom: 0;
-  }
-
-  .bp4-button {
-    margin-left: 6px;
-  }
-`;
-
-export const ScopeItemMap = styled(ScopeItem)`
-  margin-bottom: 12px;
-
-  .title {
-    display: flex;
-    align-items: center;
-    font-size: 12px;
-
-    span {
-      font-weight: 600;
-    }
-
-    span.bp4-icon {
-      margin-right: 6px;
-    }
-  }
-
-  & > ul {
-    padding-left: 24px;
-    margin-top: 4px;
-
-    li {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-
-      span.name {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-`;
diff --git a/config-ui/src/plugins/components/data-scope-list/use-data-scope-list.ts b/config-ui/src/plugins/components/data-scope-list/use-data-scope-list.ts
deleted file mode 100644
index 5cf3be9..0000000
--- a/config-ui/src/plugins/components/data-scope-list/use-data-scope-list.ts
+++ /dev/null
@@ -1,105 +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.
- *
- */
-
-import { useState, useEffect, useMemo } from 'react';
-import { groupBy } from 'lodash';
-
-import * as API from './api';
-
-type ScopeItem = {
-  id: string;
-  name: string;
-  transformationRuleName?: string;
-};
-
-export interface UseDataScopeList {
-  plugin: string;
-  connectionId: ID;
-  scopeIds: string[];
-}
-
-export const useDataScopeList = ({ plugin, connectionId, scopeIds }: UseDataScopeList) => {
-  const [loading, setLoading] = useState(false);
-  const [scope, setScope] = useState<ScopeItem[]>([]);
-
-  const formatScope = (scope: any) => {
-    return scope.map((sc: any) => {
-      switch (true) {
-        case plugin === 'github':
-          return {
-            id: `${sc.githubId}`,
-            name: sc.name,
-            transformationRuleName: sc.transformationRuleName,
-          };
-        case plugin === 'jira':
-          return {
-            id: `${sc.boardId}`,
-            name: sc.name,
-            transformationRuleName: sc.transformationRuleName,
-          };
-        case plugin === 'gitlab':
-          return {
-            id: `${sc.gitlabId}`,
-            name: sc.name,
-            transformationRuleName: sc.transformationRuleName,
-          };
-        case plugin === 'jenkins':
-          return {
-            id: sc.jobFullName,
-            name: sc.jobFullName,
-            transformationRuleName: sc.transformationRuleName,
-          };
-        case plugin === 'bitbucket':
-          return {
-            id: sc.bitbucketId,
-            name: sc.name,
-            transformationRuleName: sc.transformationRuleName,
-          };
-        default:
-          return {
-            id: sc.id,
-            name: sc.name,
-            transformationRuleName: sc.transformationRuleName,
-          };
-      }
-    });
-  };
-
-  const getScopeDetail = async () => {
-    setLoading(true);
-    try {
-      const res = await Promise.all(scopeIds.map((id) => API.getDataScopeRepo(plugin, connectionId, id)));
-      setScope(formatScope(res));
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  useEffect(() => {
-    getScopeDetail();
-  }, [scopeIds]);
-
-  return useMemo(
-    () => ({
-      loading,
-      scope,
-      scopeTsMap: groupBy(scope, (it) => it.transformationRuleName ?? 'No Transformation'),
-    }),
-    [loading, scope],
-  );
-};
diff --git a/config-ui/src/plugins/components/data-scope/index.tsx b/config-ui/src/plugins/components/data-scope/index.tsx
index 7beed05..c7a76a2 100644
--- a/config-ui/src/plugins/components/data-scope/index.tsx
+++ b/config-ui/src/plugins/components/data-scope/index.tsx
@@ -16,99 +16,169 @@
  *
  */
 
-import React from 'react';
-import { ButtonGroup, Button, Intent } from '@blueprintjs/core';
+import { useState, useMemo } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
 
-import { transformEntities } from '@/config';
-import { GitHubDataScope } from '@/plugins/register/github';
-import { JiraDataScope } from '@/plugins/register/jira';
-import { GitLabDataScope } from '@/plugins/register/gitlab';
-import { JenkinsDataScope } from '@/plugins/register/jenkins';
-import { BitbucketDataScope } from '@/plugins/register/bitbucket';
-import { SonarQubeDataScope } from '@/plugins/register/sonarqube';
-import { MultiSelector } from '@/components';
+import { Table, Dialog } from '@/components';
+import { DataScopeForm } from '@/plugins';
 
-import type { UseDataScope } from './use-data-scope';
-import { useDataScope } from './use-data-scope';
 import * as S from './styled';
-import {ZentaoDataScope} from "@/plugins/register/zentao";
 
-interface Props extends UseDataScope {
+interface Props {
+  connections: MixConnection[];
+  initialScope?: any[];
+  initialEntities?: string[];
+  cancelBtnProps?: {
+    text?: string;
+  };
+  submitBtnProps?: {
+    text?: string;
+  };
   onCancel?: () => void;
+  onSubmit?: (connections: MixConnection[]) => void;
+  onNext?: () => void;
 }
 
-export const DataScope = ({ plugin, connectionId, entities, onCancel, ...props }: Props) => {
-  const { saving, selectedScope, selectedEntities, onChangeScope, onChangeEntites, onSave } = useDataScope({
-    ...props,
-    plugin,
-    connectionId,
-    entities,
-  });
+export const DataScope = ({
+  connections,
+  initialScope,
+  initialEntities,
+  cancelBtnProps,
+  submitBtnProps,
+  onCancel,
+  onSubmit,
+  onNext,
+}: Props) => {
+  const [connection, setConnection] = useState<MixConnection>();
+
+  const error = useMemo(() => (!connections.every((cs) => cs.scope.length) ? true : false), [connections]);
+
+  const handleCancel = () => setConnection(undefined);
+
+  const handleSubmit = (connection: MixConnection, scope: MixConnection['scope'], origin: MixConnection['origin']) => {
+    onSubmit?.(
+      connections.map((cs) => {
+        if (cs.unique === connection.unique) {
+          return {
+            ...cs,
+            scope,
+            origin,
+          };
+        }
+        return cs;
+      }),
+    );
+    handleCancel();
+  };
+
+  if (connections.length === 1) {
+    const [{ plugin, connectionId, ...props }] = connections;
+    return (
+      <DataScopeForm
+        plugin={plugin}
+        connectionId={connectionId}
+        initialScope={initialScope}
+        initialEntities={initialEntities}
+        cancelBtnProps={cancelBtnProps}
+        submitBtnProps={submitBtnProps}
+        onCancel={onCancel}
+        onSubmit={(scope: MixConnection['scope'], origin: MixConnection['origin']) => {
+          onSubmit?.([
+            {
+              plugin,
+              connectionId,
+              ...props,
+              scope,
+              origin,
+            },
+          ]);
+          onNext?.();
+        }}
+      />
+    );
+  }
 
   return (
     <S.Wrapper>
-      <div className="block">
-        {plugin === 'github' && (
-          <GitHubDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'jira' && (
-          <JiraDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'gitlab' && (
-          <GitLabDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'jenkins' && (
-          <JenkinsDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'bitbucket' && (
-          <BitbucketDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'sonarqube' && (
-          <SonarQubeDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-
-        {plugin === 'zentao' && (
-          <ZentaoDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
-        )}
-      </div>
-
-      <div className="block">
-        <h3>Data Entities</h3>
-        <p>
-          <span>Select the data entities you wish to collect for the projects.</span>{' '}
-          <a
-            href="https://devlake.apache.org/docs/DataModels/DevLakeDomainLayerSchema/#data-models"
-            target="_blank"
-            rel="noreferrer"
-          >
-            Learn about data entities
-          </a>
-        </p>
-        <MultiSelector
-          items={transformEntities(entities)}
-          getKey={(item) => item.value}
-          getName={(item) => item.label}
-          selectedItems={selectedEntities}
-          onChangeItems={onChangeEntites}
-        />
-      </div>
-
-      <ButtonGroup>
-        <Button outlined disabled={saving} text="Cancel" onClick={onCancel} />
-        <Button
-          outlined
-          intent={Intent.PRIMARY}
-          loading={saving}
-          disabled={!selectedScope.length || !selectedEntities.length}
-          text="Save"
-          onClick={onSave}
-        />
-      </ButtonGroup>
+      <Table
+        columns={[
+          {
+            title: 'Data Connections',
+            dataIndex: ['icon', 'name'],
+            key: 'connection',
+            render: ({ icon, name }) => (
+              <S.ConnectionColumn>
+                <img src={icon} alt="" />
+                <span>{name}</span>
+              </S.ConnectionColumn>
+            ),
+          },
+          {
+            title: 'Data Scope',
+            dataIndex: 'origin',
+            key: 'scope',
+            render: (scope: MixConnection['origin']) =>
+              !scope.length ? (
+                <span>No Data Scope Selected</span>
+              ) : (
+                <S.ScopeColumn>
+                  {scope.map((sc, i) => (
+                    <S.ScopeItem key={i}>
+                      <span>{sc.name}</span>
+                    </S.ScopeItem>
+                  ))}
+                </S.ScopeColumn>
+              ),
+          },
+          {
+            title: '',
+            dataIndex: 'id',
+            key: 'action',
+            align: 'center',
+            render: (_, connection) => (
+              <Button
+                small
+                minimal
+                intent={Intent.PRIMARY}
+                icon="cog"
+                text="Set Data Scope"
+                onClick={() => setConnection(connection)}
+              />
+            ),
+          },
+        ]}
+        dataSource={connections}
+      />
+      <S.Btns>
+        <Button outlined intent={Intent.PRIMARY} text="Cancel" {...cancelBtnProps} onClick={onCancel} />
+        <Button intent={Intent.PRIMARY} text="Save" {...submitBtnProps} disabled={error} onClick={onNext} />
+      </S.Btns>
+      {connection && (
+        <Dialog
+          isOpen
+          title={
+            <S.DialogTitle>
+              <img src={connection.icon} alt="" />
+              <span>{connection.name}</span>
+              <span>(Set Data Scope)</span>
+            </S.DialogTitle>
+          }
+          footer={null}
+          style={{ width: 820 }}
+          onCancel={handleCancel}
+        >
+          <DataScopeForm
+            plugin={connection.plugin}
+            connectionId={connection.connectionId}
+            initialScope={initialScope}
+            initialEntities={initialEntities}
+            onCancel={handleCancel}
+            onSubmit={(scope: MixConnection['scope'], origin: MixConnection['origin']) =>
+              handleSubmit(connection, scope, origin)
+            }
+          />
+        </Dialog>
+      )}
     </S.Wrapper>
   );
 };
diff --git a/config-ui/src/plugins/components/data-scope/styled.ts b/config-ui/src/plugins/components/data-scope/styled.ts
index 5a9d85f..8ba7909 100644
--- a/config-ui/src/plugins/components/data-scope/styled.ts
+++ b/config-ui/src/plugins/components/data-scope/styled.ts
@@ -18,25 +18,46 @@
 
 import styled from 'styled-components';
 
-export const Wrapper = styled.div`
-  .block {
-    margin-bottom: 16px;
+export const Wrapper = styled.div``;
+
+export const ConnectionColumn = styled.div`
+  display: flex;
+  align-items: center;
+
+  img {
+    margin-right: 4px;
+    width: 20px;
+  }
+`;
+
+export const ScopeColumn = styled.ul``;
+
+export const ScopeItem = styled.li`
+  margin-bottom: 4px;
+
+  &:last-child {
+    margin-bottom: 0;
   }
 
-  h3 {
-    margin: 0 0 8px;
+  .bp4-button {
+    margin-left: 6px;
   }
+`;
 
-  h4 {
-    margin: 8px 0;
-  }
+export const Btns = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 36px;
+`;
 
-  p {
-    margin: 0 0 8px;
-  }
+export const DialogTitle = styled.div`
+  display: flex;
+  align-items: center;
 
-  .bp4-button-group {
-    display: flex;
-    justify-content: flex-end;
+  img {
+    margin-right: 4px;
+    width: 24px;
+    height: 24px;
   }
 `;
diff --git a/config-ui/src/plugins/components/data-scope/use-data-scope.ts b/config-ui/src/plugins/components/data-scope/use-data-scope.ts
deleted file mode 100644
index 136f76d..0000000
--- a/config-ui/src/plugins/components/data-scope/use-data-scope.ts
+++ /dev/null
@@ -1,135 +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.
- *
- */
-
-import { useState, useEffect, useMemo } from 'react';
-import { omit } from 'lodash';
-
-import { transformEntities } from '@/config';
-import { operator } from '@/utils';
-
-import * as API from './api';
-
-export interface UseDataScope {
-  plugin: string;
-  connectionId: ID;
-  entities: string[];
-  initialValues?: {
-    scope?: any;
-    entites?: string[];
-  };
-  onSave?: (scope: any) => void;
-}
-
-export const useDataScope = ({ plugin, connectionId, entities, initialValues, onSave }: UseDataScope) => {
-  const [saving, setSaving] = useState(false);
-  const [selectedScope, setSelectedScope] = useState<any>([]);
-  const [selectedEntities, setSelectedEntities] = useState<any>([]);
-
-  useEffect(() => {
-    (async () => {
-      const scope = await Promise.all(
-        (initialValues?.scope ?? []).map((sc: any) => API.getDataScope(plugin, connectionId, sc.id)),
-      );
-      setSelectedScope(scope);
-    })();
-  }, [initialValues?.scope]);
-
-  useEffect(() => {
-    setSelectedEntities(transformEntities(initialValues?.entites ?? entities));
-  }, [entities, initialValues?.entites]);
-
-  const getPluginId = (scope: any) => {
-    switch (true) {
-      case plugin === 'github':
-        return scope.githubId;
-      case plugin === 'jira':
-        return scope.boardId;
-      case plugin === 'gitlab':
-        return scope.gitlabId;
-      case plugin === 'jenkins':
-        return scope.jobFullName;
-      case plugin === 'bitbucket':
-        return scope.bitbucketId;
-      case plugin === 'zentao':
-        return scope.type === 'project' ? `project/${scope.id}` : `product/${scope.id}`;
-      case plugin === 'sonarqube':
-        return scope.projectKey;
-    }
-  };
-
-  const getDataScope = async (scope: any) => {
-    try {
-      const res = await API.getDataScope(plugin, connectionId, getPluginId(scope));
-      return {
-        ...scope,
-        transformationRuleId: res.transformationRuleId,
-      };
-    } catch {
-      return scope;
-    }
-  };
-
-  const handleSave = async () => {
-    const scope = await Promise.all(selectedScope.map((sc: any) => getDataScope(sc)));
-
-    let request: () => Promise<any>;
-    if (plugin === 'zentao') {
-      request = async () => {
-        return [
-          ...(await API.updateDataScopeWithType(plugin, connectionId, 'product', {
-            data: scope.filter((s) => s.type !== 'project').map((sc: any) => omit(sc, 'from')),
-          })),
-          ...(await API.updateDataScopeWithType(plugin, connectionId, 'project', {
-            data: scope.filter((s) => s.type === 'project').map((sc: any) => omit(sc, 'from')),
-          })),
-        ];
-      };
-    } else {
-      request = () =>
-        API.updateDataScope(plugin, connectionId, {
-          data: scope.map((sc: any) => omit(sc, 'from')),
-        });
-    }
-
-    const [success, res] = await operator(request, {
-      setOperating: setSaving,
-      hideToast: true,
-    });
-
-    if (success) {
-      onSave?.(
-        res.map((it: any) => ({
-          id: `${getPluginId(it)}`,
-          entities: selectedEntities.map((it: any) => it.value),
-        })),
-      );
-    }
-  };
-
-  return useMemo(
-    () => ({
-      saving,
-      selectedScope,
-      selectedEntities,
-      onChangeScope: setSelectedScope,
-      onChangeEntites: setSelectedEntities,
-      onSave: handleSave,
-    }),
-    [saving, selectedScope, selectedEntities],
-  );
-};
diff --git a/config-ui/src/plugins/components/index.ts b/config-ui/src/plugins/components/index.ts
index b98f876..3074824 100644
--- a/config-ui/src/plugins/components/index.ts
+++ b/config-ui/src/plugins/components/index.ts
@@ -17,8 +17,10 @@
  */
 
 export * from './connection-form';
-export * from './data-scope-list';
 export * from './data-scope';
+export * from './data-scope-form';
 export * from './data-scope-miller-columns';
 export * from './data-scope-search';
 export * from './transformation';
+export * from './transformation-form';
+export * from './transformation-select';
diff --git a/config-ui/src/pages/transformation/detail/api.ts b/config-ui/src/plugins/components/transformation-form/api.ts
similarity index 69%
rename from config-ui/src/pages/transformation/detail/api.ts
rename to config-ui/src/plugins/components/transformation-form/api.ts
index ee2c290..1744a8c 100644
--- a/config-ui/src/pages/transformation/detail/api.ts
+++ b/config-ui/src/plugins/components/transformation-form/api.ts
@@ -18,16 +18,19 @@
 
 import { request } from '@/utils';
 
-export const createTransformation = (plugin: string, payload: any) =>
-  request(`/plugins/${plugin}/transformation_rules`, {
-    method: 'post',
-    data: payload,
+export const getTransformation = (plugin: string, connectionId: ID, tid: ID) =>
+  request(`/plugins/${plugin}/transformation_rules/${tid}`, {
+    method: 'get',
   });
 
-export const getTransformation = (plugin: string, id: ID) => request(`/plugins/${plugin}/transformation_rules/${id}`);
+export const createTransformation = (plugin: string, connectionId: ID, paylod: any) =>
+  request(`/plugins/${plugin}/transformation_rules`, {
+    method: 'post',
+    data: paylod,
+  });
 
-export const updateTransformation = (plugin: string, id: ID, payload: any) =>
-  request(`/plugins/${plugin}/transformation_rules/${id}`, {
+export const updateTransformation = (plugin: string, connectionId: ID, tid: ID, payload: any) =>
+  request(`/plugins/${plugin}/transformation_rules/${tid}`, {
     method: 'patch',
     data: payload,
   });
diff --git a/config-ui/src/plugins/components/transformation-form/index.tsx b/config-ui/src/plugins/components/transformation-form/index.tsx
new file mode 100644
index 0000000..acb1b00
--- /dev/null
+++ b/config-ui/src/plugins/components/transformation-form/index.tsx
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ */
+
+import { useState, useEffect, useMemo } from 'react';
+import { InputGroup, Button, Intent } from '@blueprintjs/core';
+
+import { PageLoading, ExternalLink, Card } from '@/components';
+import { useRefreshData, useOperator } from '@/hooks';
+import type { PluginConfigType } from '@/plugins';
+import { PluginConfig } from '@/plugins';
+import { GitHubTransformation } from '@/plugins/register/github';
+import { JiraTransformation } from '@/plugins/register/jira';
+import { GitLabTransformation } from '@/plugins/register/gitlab';
+import { JenkinsTransformation } from '@/plugins/register/jenkins';
+import { BitbucketTransformation } from '@/plugins/register/bitbucket';
+
+import { TIPS_MAP } from './misc';
+import * as API from './api';
+import * as S from './styled';
+
+interface Props {
+  plugin: string;
+  connectionId: ID;
+  id?: ID;
+  onCancel?: () => void;
+}
+
+export const TransformationForm = ({ plugin, connectionId, id, onCancel }: Props) => {
+  const [name, setName] = useState('');
+  const [transformation, setTransformation] = useState({});
+
+  const config = useMemo(() => PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType, []);
+
+  const { ready, data } = useRefreshData(async () => {
+    if (!id) return null;
+    return API.getTransformation(plugin, connectionId, id);
+  }, [id]);
+
+  const { operating, onSubmit } = useOperator(
+    (payload) =>
+      id
+        ? API.updateTransformation(plugin, connectionId, id, payload)
+        : API.createTransformation(plugin, connectionId, payload),
+    {
+      callback: onCancel,
+      formatMessage: () => 'Transformation created successfully',
+    },
+  );
+
+  useEffect(() => {
+    setTransformation(data ?? config.transformation);
+  }, [data, config.transformation]);
+
+  if (!ready) {
+    return <PageLoading />;
+  }
+
+  return (
+    <S.Wrapper>
+      {TIPS_MAP[plugin] && (
+        <S.Tips>
+          To learn about how {TIPS_MAP[plugin].name} transformation is used in DevLake,{' '}
+          <ExternalLink link={TIPS_MAP[plugin].link}>check out this doc</ExternalLink>.
+        </S.Tips>
+      )}
+
+      <Card style={{ marginTop: 24 }}>
+        <h3>Transformation Name *</h3>
+        <p>Give this set of transformation rules a unique name so that you can identify it in the future.</p>
+        <InputGroup placeholder="Enter Transformation Name" value={name} onChange={(e) => setName(e.target.value)} />
+      </Card>
+
+      <Card style={{ marginTop: 24 }}>
+        {plugin === 'github' && (
+          <GitHubTransformation transformation={transformation} setTransformation={setTransformation} />
+        )}
+
+        {plugin === 'jira' && (
+          <JiraTransformation
+            connectionId={connectionId}
+            transformation={transformation}
+            setTransformation={setTransformation}
+          />
+        )}
+
+        {plugin === 'gitlab' && (
+          <GitLabTransformation transformation={transformation} setTransformation={setTransformation} />
+        )}
+
+        {plugin === 'jenkins' && (
+          <JenkinsTransformation transformation={transformation} setTransformation={setTransformation} />
+        )}
+
+        {plugin === 'bitbucket' && (
+          <BitbucketTransformation transformation={transformation} setTransformation={setTransformation} />
+        )}
+      </Card>
+
+      <S.Btns>
+        <Button outlined intent={Intent.PRIMARY} text="Cancel" onClick={onCancel} />
+        <Button
+          intent={Intent.PRIMARY}
+          disabled={!name}
+          loading={operating}
+          text="Save"
+          onClick={() => onSubmit({ ...transformation, name })}
+        />
+      </S.Btns>
+    </S.Wrapper>
+  );
+};
diff --git a/config-ui/src/plugins/components/transformation/misc.ts b/config-ui/src/plugins/components/transformation-form/misc.ts
similarity index 100%
rename from config-ui/src/plugins/components/transformation/misc.ts
rename to config-ui/src/plugins/components/transformation-form/misc.ts
diff --git a/config-ui/src/pages/blueprint/create/components/action/styled.ts b/config-ui/src/plugins/components/transformation-form/styled.ts
similarity index 77%
copy from config-ui/src/pages/blueprint/create/components/action/styled.ts
copy to config-ui/src/plugins/components/transformation-form/styled.ts
index 0660328..cc7bea2 100644
--- a/config-ui/src/pages/blueprint/create/components/action/styled.ts
+++ b/config-ui/src/plugins/components/transformation-form/styled.ts
@@ -18,13 +18,22 @@
 
 import styled from 'styled-components';
 
-export const Container = styled.div`
+export const Wrapper = styled.div``;
+
+export const Tips = styled.div`
+  padding: 24px;
+  background: #f0f4fe;
+  border: 1px solid #bdcefb;
+  border-radius: 4px;
+`;
+
+export const Btns = styled.div`
   display: flex;
   align-items: center;
-  justify-content: space-between;
-  margin-top: 36px;
+  justify-content: end;
+  margin-top: 24px;
 
   .bp4-button + .bp4-button {
-    margin-left: 8px;
+    margin-left: 4px;
   }
 `;
diff --git a/config-ui/src/plugins/components/data-scope-list/api.ts b/config-ui/src/plugins/components/transformation-select/api.ts
similarity index 83%
rename from config-ui/src/plugins/components/data-scope-list/api.ts
rename to config-ui/src/plugins/components/transformation-select/api.ts
index 9035ede..807617a 100644
--- a/config-ui/src/plugins/components/data-scope-list/api.ts
+++ b/config-ui/src/plugins/components/transformation-select/api.ts
@@ -18,5 +18,5 @@
 
 import { request } from '@/utils';
 
-export const getDataScopeRepo = (plugin: string, connectionId: ID, repoId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${repoId}`);
+export const getTransformations = (plugin: string, connectionId: ID) =>
+  request(`/plugins/${plugin}/transformation_rules`);
diff --git a/config-ui/src/plugins/components/transformation-select/index.tsx b/config-ui/src/plugins/components/transformation-select/index.tsx
new file mode 100644
index 0000000..eaa8b84
--- /dev/null
+++ b/config-ui/src/plugins/components/transformation-select/index.tsx
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ */
+
+import { useState, useMemo } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
+
+import { Dialog, PageLoading, Table, IconButton } from '@/components';
+import { useRefreshData } from '@/hooks';
+import { TransformationForm } from '@/plugins';
+
+import * as API from './api';
+import * as S from './styled';
+
+interface Props {
+  plugin: string;
+  connectionId: ID;
+  onCancel: () => void;
+  onSubmit: (tid: ID) => void;
+}
+
+export const TransformationSelect = ({ plugin, connectionId, onCancel, onSubmit }: Props) => {
+  const [step, setStep] = useState(1);
+  const [type, setType] = useState<'add' | 'edit'>('add');
+  const [selectedId, setSelectedId] = useState<ID>();
+  const [updatedId, setUpdatedId] = useState<ID>();
+
+  const { ready, data } = useRefreshData(() => API.getTransformations(plugin, connectionId), [step]);
+
+  const title = useMemo(() => {
+    switch (true) {
+      case step === 1:
+        return 'Select Transformation';
+      case type === 'add':
+        return 'Add New Transformation';
+      case type === 'edit':
+        return 'Edit Transformation';
+    }
+  }, [step, type]);
+
+  const handleNewTransformation = () => {
+    setStep(2);
+    setType('add');
+  };
+
+  const handleEditTransformation = (id: ID) => {
+    setStep(2);
+    setType('edit');
+    setUpdatedId(id);
+  };
+
+  const handleReset = () => {
+    setStep(1);
+    setUpdatedId(undefined);
+  };
+
+  const handleSubmit = () => !!selectedId && onSubmit(selectedId);
+
+  return (
+    <Dialog isOpen title={title} footer={null} style={{ width: 820 }} onCancel={onCancel}>
+      {!ready || !data ? (
+        <PageLoading />
+      ) : step === 1 ? (
+        <S.Wrapper>
+          <S.Aciton>
+            <Button intent={Intent.PRIMARY} icon="add" onClick={handleNewTransformation}>
+              Add New Transformation
+            </Button>
+          </S.Aciton>
+          <Table
+            columns={[
+              { title: 'Transformation', dataIndex: 'name', key: 'name' },
+              {
+                title: '',
+                dataIndex: 'id',
+                key: 'id',
+                width: 100,
+                align: 'right',
+                render: (id) => (
+                  <IconButton icon="annotation" tooltip="Edit" onClick={() => handleEditTransformation(id)} />
+                ),
+              },
+            ]}
+            dataSource={data}
+            rowSelection={{
+              rowKey: 'id',
+              type: 'radio',
+              selectedRowKeys: selectedId ? [selectedId] : [],
+              onChange: (selectedRowKeys) => setSelectedId(selectedRowKeys[0]),
+            }}
+            noShadow
+          />
+          <S.Btns>
+            <Button outlined intent={Intent.PRIMARY} text="Cancel" onClick={onCancel} />
+            <Button disabled={!selectedId} intent={Intent.PRIMARY} text="Save" onClick={handleSubmit} />
+          </S.Btns>
+        </S.Wrapper>
+      ) : (
+        <TransformationForm plugin={plugin} connectionId={connectionId} id={updatedId} onCancel={handleReset} />
+      )}
+    </Dialog>
+  );
+};
diff --git a/config-ui/src/pages/blueprint/create/components/action/styled.ts b/config-ui/src/plugins/components/transformation-select/styled.ts
similarity index 82%
copy from config-ui/src/pages/blueprint/create/components/action/styled.ts
copy to config-ui/src/plugins/components/transformation-select/styled.ts
index 0660328..c4c1bee 100644
--- a/config-ui/src/pages/blueprint/create/components/action/styled.ts
+++ b/config-ui/src/plugins/components/transformation-select/styled.ts
@@ -18,13 +18,19 @@
 
 import styled from 'styled-components';
 
-export const Container = styled.div`
+export const Wrapper = styled.div``;
+
+export const Aciton = styled.div`
+  margin-bottom: 16px;
+`;
+
+export const Btns = styled.div`
   display: flex;
   align-items: center;
-  justify-content: space-between;
-  margin-top: 36px;
+  justify-content: end;
+  margin-top: 24px;
 
   .bp4-button + .bp4-button {
-    margin-left: 8px;
+    margin-left: 4px;
   }
 `;
diff --git a/config-ui/src/plugins/components/transformation/api.ts b/config-ui/src/plugins/components/transformation/api.ts
index 907e943..ef0f793 100644
--- a/config-ui/src/plugins/components/transformation/api.ts
+++ b/config-ui/src/plugins/components/transformation/api.ts
@@ -18,28 +18,11 @@
 
 import { request } from '@/utils';
 
-type GetRulesParams = {
-  page: number;
-  pageSize: number;
-};
+export const getDataScope = (plugin: string, connectionId: ID, scopeId: ID) =>
+  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`);
 
-export const getRules = (plugin: string, params?: GetRulesParams) =>
-  request(`/plugins/${plugin}/transformation_rules`, {
-    method: 'get',
-    data: params,
-  });
-
-export const getDataScopeRepo = (plugin: string, connectionId: ID, repoId: ID) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${repoId}`);
-
-export const updateDataScope = (plugin: string, connectionId: ID, payload: any) =>
-  request(`/plugins/${plugin}/connections/${connectionId}/scopes`, {
-    method: 'put',
+export const updateDataScope = (plugin: string, connectionId: ID, scopeId: ID, payload: any) =>
+  request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`, {
+    method: 'patch',
     data: payload,
   });
-
-export const createTransformation = (plugin: string, paylod: any) =>
-  request(`/plugins/${plugin}/transformation_rules`, {
-    method: 'POST',
-    data: paylod,
-  });
diff --git a/config-ui/src/plugins/components/transformation/index.tsx b/config-ui/src/plugins/components/transformation/index.tsx
index f9a988a..73c97be 100644
--- a/config-ui/src/plugins/components/transformation/index.tsx
+++ b/config-ui/src/plugins/components/transformation/index.tsx
@@ -16,167 +16,148 @@
  *
  */
 
-import React, { useState } from 'react';
-import { RadioGroup, Radio, InputGroup, ButtonGroup, Button, Intent } from '@blueprintjs/core';
+import { useState } from 'react';
+import { Button, Intent } from '@blueprintjs/core';
 
-import { ExternalLink, Divider, Selector, MultiSelector } from '@/components';
+import { Table, IconButton } from '@/components';
+import { TransformationSelect, getPluginId } from '@/plugins';
 
-import { GitHubTransformation } from '@/plugins/register/github';
-import { JiraTransformation } from '@/plugins/register/jira';
-import { GitLabTransformation } from '@/plugins/register/gitlab';
-import { JenkinsTransformation } from '@/plugins/register/jenkins';
-import { BitbucketTransformation } from '@/plugins/register/bitbucket';
-
-import type { TransformationType, RuleItem } from './types';
-import { TIPS_MAP } from './misc';
-import type { UseTransformationProps } from './use-transformation';
-import { useTransformation } from './use-transformation';
+import * as API from './api';
 import * as S from './styled';
 
-interface Props extends Omit<UseTransformationProps, 'name' | 'selectedRule' | 'setSelectedScope'> {
-  from: 'create' | 'update';
+interface Props {
+  connections: MixConnection[];
+  cancelBtnProps?: {
+    text?: string;
+  };
+  submitBtnProps?: {
+    text?: string;
+    loading?: boolean;
+  };
+  noFooter?: boolean;
   onCancel?: () => void;
+  onSubmit?: (connections: MixConnection[]) => void;
+  onNext?: () => void;
 }
 
-export const Transformation = ({ from, plugin, connectionId, onCancel, ...props }: Props) => {
-  const [type, setType] = useState<TransformationType>();
-  const [name, setName] = useState('');
-  const [selectedRule, setSelectedRule] = useState<RuleItem>();
-  const [selectedScope, setSelectedScope] = useState<any>([]);
+export const Transformation = ({
+  connections,
+  cancelBtnProps,
+  submitBtnProps,
+  noFooter,
+  onCancel,
+  onSubmit,
+  onNext,
+}: Props) => {
+  const [selected, setSelected] = useState<Record<string, ID[]>>({});
+  const [connection, setConnection] = useState<MixConnection>();
 
-  const { loading, rules, scope, saving, transformation, getScopeKey, onSave, onUpdateScope, onChangeTransformation } =
-    useTransformation({
-      ...props,
-      plugin,
-      connectionId,
-      name,
-      selectedRule,
-      selectedScope,
+  const handleCancel = () => setConnection(undefined);
+
+  const handleSubmit = async (tid: ID, connection: MixConnection, connections: MixConnection[]) => {
+    const { unique, plugin, connectionId } = connection;
+    const scopeIds = selected[unique];
+    const scopes = await Promise.all(
+      scopeIds.map(async (scopeId) => {
+        const scope = await API.getDataScope(plugin, connectionId, scopeId);
+        return await API.updateDataScope(plugin, connectionId, scopeId, {
+          ...scope,
+          transformationRuleId: tid,
+        });
+      }),
+    );
+    onSubmit?.(
+      connections.map((cs) => {
+        if (cs.unique !== unique) {
+          return cs;
+        }
+
+        const origin = cs.origin.map((sc) => {
+          if (!scopeIds.includes(sc[getPluginId(cs.plugin)])) {
+            return sc;
+          }
+          return scopes.find((it) => it[getPluginId(cs.plugin)] === sc[getPluginId(cs.plugin)]);
+        });
+
+        return { ...cs, origin };
+      }),
+    );
+    setSelected({
+      ...selected,
+      [`${unique}`]: [],
     });
-
-  const handleChangeType = (e: React.FormEvent<HTMLInputElement>) => {
-    setType((e.target as HTMLInputElement).value as TransformationType);
-    setSelectedRule(undefined);
+    handleCancel();
   };
 
   return (
-    <S.Wrapper>
-      {TIPS_MAP[plugin] && (
-        <div className="tips">
-          To learn about how {TIPS_MAP[plugin].name} transformation is used in DevLake,{' '}
-          <ExternalLink link={TIPS_MAP[plugin].link}>check out this doc</ExternalLink>.
-        </div>
-      )}
-
-      <div className="block">
-        <RadioGroup selectedValue={type} onChange={handleChangeType}>
-          <Radio label="Create a new transformation" value="create" />
-          <Radio
-            label={
-              from === 'create'
-                ? 'Create a new transformation by duplicating an exisitng transformation'
-                : 'Duplicating an existing transformation'
-            }
-            value="createByExist"
+    <S.List>
+      {connections.map((cs) => (
+        <S.Item key={cs.unique}>
+          {connections.length !== 1 && (
+            <S.Title>
+              <img src={cs.icon} alt="" />
+              <span>{cs.name}</span>
+            </S.Title>
+          )}
+          <S.Action>
+            <Button
+              intent={Intent.PRIMARY}
+              icon="annotation"
+              disabled={!selected[cs.unique] || !selected[cs.unique].length}
+              onClick={() => setConnection(cs)}
+            >
+              Select Transformation
+            </Button>
+          </S.Action>
+          <Table
+            columns={[
+              { title: 'Data Scope', dataIndex: 'name', key: 'name' },
+              {
+                title: 'Transformation',
+                dataIndex: 'transformationRuleName',
+                key: 'transformation',
+                align: 'center',
+                render: (val, row) => (
+                  <div>
+                    <span>{val ?? 'N/A'}</span>
+                    <IconButton
+                      icon="annotation"
+                      tooltip="Select Transformation"
+                      onClick={() => {
+                        setSelected({
+                          ...selected,
+                          [`${cs.unique}`]: [row[getPluginId(cs.plugin)]],
+                        });
+                        setConnection(cs);
+                      }}
+                    />
+                  </div>
+                ),
+              },
+            ]}
+            dataSource={cs.origin}
+            rowSelection={{
+              rowKey: getPluginId(cs.plugin),
+              selectedRowKeys: selected[cs.unique],
+              onChange: (selectedRowKeys) => setSelected({ ...selected, [`${cs.unique}`]: selectedRowKeys }),
+            }}
           />
-          <Radio label="Select an existing transformation" value="selectExist" />
-        </RadioGroup>
-      </div>
-
-      {type && (
-        <>
-          <Divider />
-          <div className="block">
-            {(type === 'createByExist' || type === 'selectExist') && (
-              <div className="item">
-                <h3>Select a Transformation to Duplicate *</h3>
-                <p>
-                  The selected transformation will be used as template that you can tweak and save as a new
-                  transformation.
-                </p>
-                <Selector
-                  items={rules}
-                  getKey={(it) => it.id}
-                  getName={(it) => it.name}
-                  selectedItem={selectedRule}
-                  onChangeItem={setSelectedRule}
-                />
-              </div>
-            )}
-            {type !== 'selectExist' && (
-              <div className="item">
-                <h3>Transformation Name *</h3>
-                <p>Give this set of transformation rules a unique name so that you can identify it in the future.</p>
-                <InputGroup
-                  placeholder="Enter Transformation Name"
-                  value={name}
-                  onChange={(e) => setName(e.target.value)}
-                />
-              </div>
-            )}
-            <div className="item">
-              <h3>Applied Data Scope</h3>
-              <p>Select the data scope for which you want to apply this transformation for.</p>
-              <MultiSelector
-                loading={loading}
-                items={scope}
-                getKey={getScopeKey}
-                getName={(sc) => sc.name}
-                selectedItems={selectedScope}
-                onChangeItems={setSelectedScope}
-              />
-              {type === 'selectExist' && (
-                <ButtonGroup>
-                  <Button outlined intent={Intent.PRIMARY} text="Cancel and Go Back" onClick={onCancel} />
-                  <Button
-                    outlined
-                    disabled={!selectedRule || !selectedScope.length}
-                    intent={Intent.PRIMARY}
-                    text="Save"
-                    onClick={() => onUpdateScope(selectedRule?.id)}
-                  />
-                </ButtonGroup>
-              )}
-            </div>
-          </div>
-        </>
+        </S.Item>
+      ))}
+      {!noFooter && (
+        <S.Btns>
+          <Button outlined intent={Intent.PRIMARY} text="Previous Step" onClick={onCancel} {...cancelBtnProps} />
+          <Button intent={Intent.PRIMARY} text="Next Step" onClick={onNext} {...submitBtnProps} />
+        </S.Btns>
       )}
-
-      {(type === 'create' || (type === 'createByExist' && selectedRule)) && (
-        <>
-          <Divider />
-          <div className="block">
-            {plugin === 'github' && (
-              <GitHubTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-            )}
-
-            {plugin === 'jira' && (
-              <JiraTransformation
-                connectionId={connectionId}
-                transformation={transformation}
-                setTransformation={onChangeTransformation}
-              />
-            )}
-
-            {plugin === 'gitlab' && (
-              <GitLabTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-            )}
-
-            {plugin === 'jenkins' && (
-              <JenkinsTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-            )}
-
-            {plugin === 'bitbucket' && (
-              <BitbucketTransformation transformation={transformation} setTransformation={onChangeTransformation} />
-            )}
-
-            <ButtonGroup>
-              <Button outlined intent={Intent.PRIMARY} text="Cancel and Go Back" onClick={onCancel} />
-              <Button outlined disabled={!name} loading={saving} intent={Intent.PRIMARY} text="Save" onClick={onSave} />
-            </ButtonGroup>
-          </div>
-        </>
+      {connection && (
+        <TransformationSelect
+          plugin={connection.plugin}
+          connectionId={connection.connectionId}
+          onCancel={handleCancel}
+          onSubmit={(tid) => handleSubmit(tid, connection, connections)}
+        />
       )}
-    </S.Wrapper>
+    </S.List>
   );
 };
diff --git a/config-ui/src/plugins/components/transformation/styled.ts b/config-ui/src/plugins/components/transformation/styled.ts
index dc39bf2..7062ec3 100644
--- a/config-ui/src/plugins/components/transformation/styled.ts
+++ b/config-ui/src/plugins/components/transformation/styled.ts
@@ -18,39 +18,35 @@
 
 import styled from 'styled-components';
 
-export const Wrapper = styled.div`
-  .tips {
-    margin-bottom: 16px;
-    padding: 12px 24px;
-    background: #f0f4fe;
-    border: 1px solid #bdcefb;
-    border-radius: 4px;
+export const List = styled.div``;
+
+export const Item = styled.div`
+  margin-bottom: 36px;
+
+  &:last-child {
+    margin-bottom: 0;
   }
+`;
 
-  .block {
-    padding: 8px 16px;
+export const Title = styled.div`
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+
+  img {
+    margin-right: 4px;
+    width: 24px;
+    height: 24px;
   }
+`;
 
-  .item + .item {
-    margin-top: 16px;
-  }
+export const Action = styled.div`
+  margin-bottom: 16px;
+`;
 
-  .bp4-form-group {
-    display: flex;
-    align-items: center;
-
-    .bp4-label {
-      flex: 0 0 150px;
-    }
-
-    .bp4-form-content {
-      flex: auto;
-    }
-  }
-
-  .bp4-button-group {
-    display: flex;
-    justify-content: flex-end;
-    margin-top: 16px;
-  }
+export const Btns = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 36px;
 `;
diff --git a/config-ui/src/plugins/components/transformation/types.ts b/config-ui/src/plugins/components/transformation/types.ts
deleted file mode 100644
index 6749e9f..0000000
--- a/config-ui/src/plugins/components/transformation/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 TransformationType = 'create' | 'createByExist' | 'selectExist';
-
-export type RuleItem = {
-  id: ID;
-  name: string;
-};
-
-export type ScopeItem = {
-  name: string;
-} & any;
diff --git a/config-ui/src/plugins/components/transformation/use-transformation.ts b/config-ui/src/plugins/components/transformation/use-transformation.ts
deleted file mode 100644
index 9c69453..0000000
--- a/config-ui/src/plugins/components/transformation/use-transformation.ts
+++ /dev/null
@@ -1,158 +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.
- *
- */
-
-import { useState, useEffect, useMemo } from 'react';
-
-import type { PluginConfigType } from '@/plugins';
-import { PluginConfig } from '@/plugins';
-import { operator } from '@/utils';
-
-import type { RuleItem, ScopeItem } from './types';
-import * as API from './api';
-
-export interface UseTransformationProps {
-  plugin: string;
-  connectionId: ID;
-  scopeIds: ID[];
-  name: string;
-  selectedRule?: RuleItem;
-  selectedScope?: ScopeItem[];
-  onSave?: () => void;
-}
-
-export const useTransformation = ({
-  plugin,
-  connectionId,
-  scopeIds,
-  name,
-  selectedRule,
-  selectedScope,
-  onSave,
-}: UseTransformationProps) => {
-  const [loading, setLoading] = useState(false);
-  const [rules, setRules] = useState<RuleItem[]>([]);
-  const [scope, setScope] = useState<ScopeItem[]>([]);
-  const [saving, setSaving] = useState(false);
-  const [transformation, setTransformation] = useState({});
-
-  const config = useMemo(() => PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType, []);
-
-  useEffect(() => {
-    setTransformation(selectedRule ? selectedRule : config.transformation);
-  }, [selectedRule]);
-
-  const getRules = async () => {
-    const res = await API.getRules(plugin);
-    setRules(res);
-  };
-
-  const getScope = async () => {
-    const res = await Promise.all(scopeIds.map((id) => API.getDataScopeRepo(plugin, connectionId, id)));
-    setScope(res);
-  };
-
-  const init = async () => {
-    setLoading(true);
-    try {
-      await getRules();
-      await getScope();
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  useEffect(() => {
-    init();
-  }, []);
-
-  const handleUpdateScope = async (tid?: ID) => {
-    if (!tid) return;
-
-    const payload = (selectedScope ?? []).map((sc: any) => ({
-      ...sc,
-      transformationRuleId: tid,
-    }));
-
-    const [success] = await operator(() =>
-      API.updateDataScope(plugin, connectionId, {
-        data: payload,
-      }),
-    );
-
-    if (success) {
-      onSave?.();
-    }
-  };
-
-  const handleSave = async () => {
-    const [success, res] = await operator(
-      () =>
-        API.createTransformation(plugin, {
-          ...transformation,
-          name,
-        }),
-      {
-        setOperating: setSaving,
-        formatReason: (err) => (err as any).response?.data?.message,
-      },
-    );
-
-    if (success) {
-      const payload = (selectedScope ?? []).map((sc: any) => ({
-        ...sc,
-        transformationRuleId: res.id,
-      }));
-
-      if (payload.length) {
-        await API.updateDataScope(plugin, connectionId, {
-          data: payload,
-        });
-      }
-
-      onSave?.();
-    }
-  };
-
-  return useMemo(
-    () => ({
-      loading,
-      rules,
-      scope,
-      saving,
-      transformation,
-      getScopeKey(sc: any) {
-        switch (true) {
-          case plugin === 'github':
-            return sc.githubId;
-          case plugin === 'jira':
-            return sc.boardId;
-          case plugin === 'gitlab':
-            return sc.gitlabId;
-          case plugin === 'jenkins':
-            return sc.jobFullName;
-          case plugin === 'bitbucket':
-            return sc.bitbucketId;
-        }
-      },
-      onSave: handleSave,
-      onUpdateScope: handleUpdateScope,
-      onChangeTransformation: setTransformation,
-    }),
-    [loading, rules, scope, saving, transformation, plugin, selectedScope, name, onSave],
-  );
-};
diff --git a/config-ui/src/plugins/index.ts b/config-ui/src/plugins/index.ts
index 1dfd94a..1bf0458 100644
--- a/config-ui/src/plugins/index.ts
+++ b/config-ui/src/plugins/index.ts
@@ -17,5 +17,6 @@
  */
 
 export * from './types';
-export * from './config';
+export * from './utils';
 export * from './components';
+export * from './config';
diff --git a/config-ui/src/plugins/register/github/data-scope.tsx b/config-ui/src/plugins/register/github/data-scope.tsx
index bebc03c..d63c71b 100644
--- a/config-ui/src/plugins/register/github/data-scope.tsx
+++ b/config-ui/src/plugins/register/github/data-scope.tsx
@@ -16,11 +16,9 @@
  *
  */
 
-import React from 'react';
-
 import type { ScopeItemType } from './types';
-
 import { MillerColumns, RepoSelector } from './components';
+import * as S from './styled';
 
 interface Props {
   connectionId: ID;
@@ -30,13 +28,13 @@
 
 export const GitHubDataScope = ({ connectionId, selectedItems, onChangeItems }: Props) => {
   return (
-    <>
+    <S.DataScope>
       <h3>Repositories *</h3>
       <p>Select the repositories you would like to sync.</p>
       <MillerColumns connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
       <h4>Add repositories outside of your organizations</h4>
       <p>Search for repositories and add to them</p>
       <RepoSelector connectionId={connectionId} selectedItems={selectedItems} onChangeItems={onChangeItems} />
-    </>
+    </S.DataScope>
   );
 };
diff --git a/config-ui/src/plugins/register/github/styled.ts b/config-ui/src/plugins/register/github/styled.ts
index e8358d9..ae2b38e 100644
--- a/config-ui/src/plugins/register/github/styled.ts
+++ b/config-ui/src/plugins/register/github/styled.ts
@@ -18,6 +18,12 @@
 
 import styled from 'styled-components';
 
+export const DataScope = styled.div`
+  h4 {
+    margin-top: 16px;
+  }
+`;
+
 export const TransformationWrapper = styled.div`
   .issue-tracking {
     .issue-type {
diff --git a/config-ui/src/plugins/register/github/transformation.tsx b/config-ui/src/plugins/register/github/transformation.tsx
index 0c24b68..65d104b 100644
--- a/config-ui/src/plugins/register/github/transformation.tsx
+++ b/config-ui/src/plugins/register/github/transformation.tsx
@@ -98,7 +98,7 @@
             <FormGroup inline label="Feature">
               <InputGroup
                 placeholder="(feat|feature|proposal|requirement)"
-                value={transformation.issueTypeRequirement}
+                value={transformation.issueTypeRequirement ?? ''}
                 onChange={(e) =>
                   setTransformation({
                     ...transformation,
@@ -110,7 +110,7 @@
             <FormGroup inline label="Bug">
               <InputGroup
                 placeholder="(bug|broken)"
-                value={transformation.issueTypeBug}
+                value={transformation.issueTypeBug ?? ''}
                 onChange={(e) =>
                   setTransformation({
                     ...transformation,
@@ -132,7 +132,7 @@
             >
               <InputGroup
                 placeholder="(incident|failure)"
-                value={transformation.issueTypeIncident}
+                value={transformation.issueTypeIncident ?? ''}
                 onChange={(e) =>
                   setTransformation({
                     ...transformation,
@@ -154,7 +154,7 @@
         >
           <InputGroup
             placeholder="(highest|high|medium|low|p0|p1|p2|p3)"
-            value={transformation.issuePriority}
+            value={transformation.issuePriority ?? ''}
             onChange={(e) =>
               setTransformation({
                 ...transformation,
@@ -174,7 +174,7 @@
         >
           <InputGroup
             placeholder="component(.*)"
-            value={transformation.issueComponent}
+            value={transformation.issueComponent ?? ''}
             onChange={(e) =>
               setTransformation({
                 ...transformation,
@@ -194,7 +194,7 @@
         >
           <InputGroup
             placeholder="severity(.*)"
-            value={transformation.issueSeverity}
+            value={transformation.issueSeverity ?? ''}
             onChange={(e) =>
               setTransformation({
                 ...transformation,
@@ -233,7 +233,7 @@
                 <p>The Job name that matches</p>
                 <InputGroup
                   placeholder="(deploy|push-image)"
-                  value={transformation.deploymentPattern}
+                  value={transformation.deploymentPattern ?? ''}
                   onChange={(e) =>
                     setTransformation({
                       ...transformation,
@@ -250,7 +250,7 @@
                 <InputGroup
                   disabled={!transformation.deploymentPattern}
                   placeholder="production"
-                  value={transformation.productionPattern}
+                  value={transformation.productionPattern ?? ''}
                   onChange={(e) =>
                     setTransformation({
                       ...transformation,
@@ -290,7 +290,7 @@
         >
           <InputGroup
             placeholder="type(.*)$"
-            value={transformation.prType}
+            value={transformation.prType ?? ''}
             onChange={(e) => setTransformation({ ...transformation, prType: e.target.value })}
           />
         </FormGroup>
@@ -305,7 +305,7 @@
         >
           <InputGroup
             placeholder="component(.*)$"
-            value={transformation.prComponent}
+            value={transformation.prComponent ?? ''}
             onChange={(e) =>
               setTransformation({
                 ...transformation,
@@ -351,7 +351,7 @@
           }
         >
           <TextArea
-            value={transformation.prBodyClosePattern}
+            value={transformation.prBodyClosePattern ?? ''}
             placeholder="(?mi)(fix|close|resolve|fixes|closes|resolves|fixed|closed|resolved)[s]*.*(((and )?(#|https://github.com/%s/%s/issues/)d+[ ]*)+)"
             onChange={(e) =>
               setTransformation({
@@ -384,7 +384,7 @@
             Compare the last
             <InputGroup
               style={{ width: 60 }}
-              value={transformation.refdiff?.tagsOrder}
+              value={transformation.refdiff?.tagsOrder ?? ''}
               onChange={(e) =>
                 setTransformation({
                   ...transformation,
@@ -399,7 +399,7 @@
             <InputGroup
               style={{ width: 200 }}
               placeholder="(regex)$"
-              value={transformation.refdiff?.tagsPattern}
+              value={transformation.refdiff?.tagsPattern ?? ''}
               onChange={(e) =>
                 setTransformation({
                   ...transformation,
diff --git a/config-ui/src/plugins/register/gitlab/data-scope.tsx b/config-ui/src/plugins/register/gitlab/data-scope.tsx
index c7f46e1..f3a8b63 100644
--- a/config-ui/src/plugins/register/gitlab/data-scope.tsx
+++ b/config-ui/src/plugins/register/gitlab/data-scope.tsx
@@ -16,11 +16,12 @@
  *
  */
 
-import React, { useMemo } from 'react';
+import { useMemo } from 'react';
 
 import { DataScopeMillerColumns, DataScopeSearch } from '@/plugins';
 
 import type { ScopeItemType } from './types';
+import * as S from './styled';
 
 interface Props {
   connectionId: ID;
@@ -35,8 +36,8 @@
   );
 
   return (
-    <>
-      <h4>Projects *</h4>
+    <S.DataScope>
+      <h3>Projects *</h3>
       <p>Select the project you would like to sync.</p>
       <DataScopeMillerColumns
         title="Subgroups/Projects"
@@ -53,6 +54,6 @@
         selectedItems={selectedItems}
         onChangeItems={onChangeItems}
       />
-    </>
+    </S.DataScope>
   );
 };
diff --git a/config-ui/src/plugins/register/gitlab/styled.ts b/config-ui/src/plugins/register/gitlab/styled.ts
index 8c64ba9..78de557 100644
--- a/config-ui/src/plugins/register/gitlab/styled.ts
+++ b/config-ui/src/plugins/register/gitlab/styled.ts
@@ -18,6 +18,12 @@
 
 import styled from 'styled-components';
 
+export const DataScope = styled.div`
+  h4 {
+    margin-top: 16px;
+  }
+`;
+
 export const TransformationWrapper = styled.div`
   h3 {
     margin-top: 16px;
diff --git a/config-ui/src/store/transformations/types.ts b/config-ui/src/plugins/utils.ts
similarity index 67%
copy from config-ui/src/store/transformations/types.ts
copy to config-ui/src/plugins/utils.ts
index 48a344e..ac93c7e 100644
--- a/config-ui/src/store/transformations/types.ts
+++ b/config-ui/src/plugins/utils.ts
@@ -16,8 +16,21 @@
  *
  */
 
-export type TransformationItemType = {
-  id: ID;
-  name: string;
-  plugin: string;
+export const getPluginId = (plugin: string) => {
+  switch (plugin) {
+    case 'github':
+      return 'githubId';
+    case 'jira':
+      return 'boardId';
+    case 'gitlab':
+      return 'gitlabId';
+    case 'jenkins':
+      return 'jobFullName';
+    case 'bitbucket':
+      return 'bitbucketId';
+    case 'sonarqube':
+      return 'projectKey';
+    default:
+      return 'id';
+  }
 };
diff --git a/config-ui/src/store/connections/use-context-value.ts b/config-ui/src/store/connections/use-context-value.ts
index 804c993..036790d 100644
--- a/config-ui/src/store/connections/use-context-value.ts
+++ b/config-ui/src/store/connections/use-context-value.ts
@@ -27,9 +27,10 @@
 export interface UseContextValueProps {
   plugin?: string;
   filterBeta?: boolean;
+  filter?: string[];
 }
 
-export const useContextValue = ({ plugin, filterBeta = false }: UseContextValueProps) => {
+export const useContextValue = ({ plugin, filterBeta = false, filter }: UseContextValueProps) => {
   const [loading, setLoading] = useState(false);
   const [connections, setConnections] = useState<ConnectionItemType[]>([]);
 
@@ -140,7 +141,7 @@
   return useMemo(
     () => ({
       loading,
-      connections,
+      connections: filter ? connections.filter((cs) => !filter.includes(cs.unique)) : connections,
       onRefresh: handleRefresh,
       onTest: handleTest,
     }),
diff --git a/config-ui/src/store/index.ts b/config-ui/src/store/index.ts
index e3b1a81..4fbdb69 100644
--- a/config-ui/src/store/index.ts
+++ b/config-ui/src/store/index.ts
@@ -18,4 +18,3 @@
 
 export * from './version';
 export * from './connections';
-export * from './transformations';
diff --git a/config-ui/src/store/transformations/api.ts b/config-ui/src/store/transformations/api.ts
deleted file mode 100644
index 92ab915..0000000
--- a/config-ui/src/store/transformations/api.ts
+++ /dev/null
@@ -1,27 +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.
- *
- */
-
-import { request } from '@/utils';
-
-type GetTransformationParams = {
-  page: number;
-  pageSize: number;
-};
-
-export const getTransformation = (plugin: string, params: GetTransformationParams) =>
-  request(`/plugins/${plugin}/transformation_rules`, { data: params });
diff --git a/config-ui/src/store/transformations/context.tsx b/config-ui/src/store/transformations/context.tsx
deleted file mode 100644
index efbfb73..0000000
--- a/config-ui/src/store/transformations/context.tsx
+++ /dev/null
@@ -1,60 +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.
- *
- */
-
-import React, { useContext } from 'react';
-
-import { PageLoading } from '@/components';
-import type { PluginConfigType } from '@/plugins';
-
-import type { TransformationItemType } from './types';
-import { useContextValue } from './use-context-value';
-
-const TransformationContext = React.createContext<{
-  plugins: PluginConfigType[];
-  transformations: TransformationItemType[];
-}>({
-  plugins: [],
-  transformations: [],
-});
-
-interface Props {
-  children?: React.ReactNode;
-}
-
-export const TransformationContextProvider = ({ children }: Props) => {
-  const { loading, plugins, transformations } = useContextValue();
-
-  if (loading) {
-    return <PageLoading />;
-  }
-
-  return (
-    <TransformationContext.Provider
-      value={{
-        plugins,
-        transformations,
-      }}
-    >
-      {children}
-    </TransformationContext.Provider>
-  );
-};
-
-export const TransformationContextConsumer = TransformationContext.Consumer;
-
-export const useTransformation = () => useContext(TransformationContext);
diff --git a/config-ui/src/store/transformations/use-context-value.ts b/config-ui/src/store/transformations/use-context-value.ts
deleted file mode 100644
index 6cf03ff..0000000
--- a/config-ui/src/store/transformations/use-context-value.ts
+++ /dev/null
@@ -1,75 +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.
- *
- */
-
-import { useState, useEffect, useCallback, useMemo } from 'react';
-
-import { PluginConfig, PluginType } from '@/plugins';
-
-import type { TransformationItemType } from './types';
-import * as API from './api';
-
-export const useContextValue = () => {
-  const [loading, setLoading] = useState(false);
-  const [transformations, setTransformations] = useState<TransformationItemType[]>([]);
-
-  const allConnections = useMemo(
-    () => PluginConfig.filter((p) => p.type === PluginType.Connection && !p.isBeta && p.transformation),
-    [],
-  );
-
-  const getTransformation = async (plugin: string) => {
-    try {
-      return await API.getTransformation(plugin, { page: 1, pageSize: 200 });
-    } catch {
-      return [];
-    }
-  };
-
-  const handleRefresh = useCallback(async () => {
-    setLoading(true);
-
-    const res = await Promise.all(allConnections.map((cs) => getTransformation(cs.plugin)));
-
-    const resWithPlugin = res.map((ts, i) =>
-      ts.map((it: any) => {
-        const { plugin } = allConnections[i];
-
-        return {
-          ...it,
-          plugin,
-        };
-      }),
-    );
-
-    setTransformations(resWithPlugin.flat());
-    setLoading(false);
-  }, [allConnections]);
-
-  useEffect(() => {
-    handleRefresh();
-  }, []);
-
-  return useMemo(
-    () => ({
-      loading,
-      plugins: allConnections,
-      transformations,
-    }),
-    [loading, allConnections, transformations],
-  );
-};
diff --git a/config-ui/tsconfig.json b/config-ui/tsconfig.json
index 3a78cfd..c0c4a9d 100644
--- a/config-ui/tsconfig.json
+++ b/config-ui/tsconfig.json
@@ -18,7 +18,7 @@
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
-    "jsx": "react"
+    "jsx": "react-jsx"
   },
   "include": ["src"],
   "references": [{ "path": "./tsconfig.node.json" }]