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

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

commit c8a4af1c461044ea765108db5b4d391b749bf8c1
Author: mintsweet <[email protected]>
AuthorDate: Tue Aug 15 20:55:55 2023 +1200

    fix(config-ui): add pagination params for all tables
---
 config-ui/src/global.d.ts                          |   5 +
 .../pages/blueprint/connection-detail/index.tsx    |   4 +-
 config-ui/src/pages/blueprint/home/api.ts          |   9 +-
 config-ui/src/pages/blueprint/home/index.tsx       |  64 ++++++-------
 config-ui/src/pages/connection/detail/api.ts       |   3 +-
 config-ui/src/pages/connection/detail/index.tsx    |  19 +++-
 config-ui/src/pages/project/home/api.ts            |   2 +-
 config-ui/src/pages/project/home/index.tsx         |  16 +++-
 .../plugins/components/data-scope-select/api.ts    |   9 +-
 .../plugins/components/data-scope-select/index.tsx | 103 +++++++++++----------
 10 files changed, 136 insertions(+), 98 deletions(-)

diff --git a/config-ui/src/global.d.ts b/config-ui/src/global.d.ts
index 0f3a050aa..c65a6126b 100644
--- a/config-ui/src/global.d.ts
+++ b/config-ui/src/global.d.ts
@@ -18,6 +18,11 @@
 
 type ID = string | number;
 
+type Pagination = {
+  page?: number;
+  pageSize?: number;
+};
+
 declare module '*.svg' {
   const content: any;
   export default content;
diff --git a/config-ui/src/pages/blueprint/connection-detail/index.tsx 
b/config-ui/src/pages/blueprint/connection-detail/index.tsx
index 2c66ce8cd..92a98708e 100644
--- a/config-ui/src/pages/blueprint/connection-detail/index.tsx
+++ b/config-ui/src/pages/blueprint/connection-detail/index.tsx
@@ -50,7 +50,7 @@ export const BlueprintConnectionDetailPage = () => {
 
   const { ready, data } = useRefreshData(async () => {
     const [plugin, connectionId] = unique.split('-');
-    const [blueprint, connection, scopes] = await Promise.all([
+    const [blueprint, connection, scopesRes] = await Promise.all([
       getBlueprint(pname, bid),
       API.getConnection(plugin, connectionId),
       API.getDataScopes(plugin, connectionId),
@@ -68,7 +68,7 @@ export const BlueprintConnectionDetailPage = () => {
         id: +connectionId,
         name: connection.name,
       },
-      scopes: scopes.filter((sc: any) => 
scopeIds.includes(getPluginScopeId(plugin, sc))),
+      scopes: scopesRes.scopes.filter((sc: any) => 
scopeIds.includes(getPluginScopeId(plugin, sc))),
     };
   }, [version, pname, bid]);
 
diff --git a/config-ui/src/pages/blueprint/home/api.ts 
b/config-ui/src/pages/blueprint/home/api.ts
index 62e7c80ed..d6554b6aa 100644
--- a/config-ui/src/pages/blueprint/home/api.ts
+++ b/config-ui/src/pages/blueprint/home/api.ts
@@ -20,17 +20,12 @@ import { request } from '@/utils';
 
 import { BlueprintType } from '../types';
 
-type GetBlueprintsParams = {
-  page: number;
-  pageSize: number;
-};
-
-type GetBlueprintResponse = {
+type ResponseType = {
   blueprints: Array<BlueprintType>;
   count: number;
 };
 
-export const getBlueprints = (params: GetBlueprintsParams): 
Promise<GetBlueprintResponse> =>
+export const getBlueprints = (params: Pagination & { type: string }): 
Promise<ResponseType> =>
   request('/blueprints', { data: params });
 
 export const createBlueprint = (payload: any) =>
diff --git a/config-ui/src/pages/blueprint/home/index.tsx 
b/config-ui/src/pages/blueprint/home/index.tsx
index f37e3d558..1541bb657 100644
--- a/config-ui/src/pages/blueprint/home/index.tsx
+++ b/config-ui/src/pages/blueprint/home/index.tsx
@@ -32,43 +32,37 @@ import * as API from './api';
 import * as S from './styled';
 
 export const BlueprintHomePage = () => {
-  const [type, setType] = useState('all');
   const [version, setVersion] = useState(1);
+  const [type, setType] = useState('all');
+  const [page, setPage] = useState(1);
+  const [pageSize] = useState(20);
   const [isOpen, setIsOpen] = useState(false);
   const [name, setName] = useState('');
   const [mode, setMode] = useState(ModeEnum.normal);
   const [saving, setSaving] = useState(false);
 
   const { onGet } = useConnections();
-  const { ready, data } = useRefreshData(() => API.getBlueprints({ page: 1, 
pageSize: 200 }), [version]);
+  const { ready, data } = useRefreshData(
+    () => API.getBlueprints({ type: type.toLocaleUpperCase(), page, pageSize 
}),
+    [version, type, page, pageSize],
+  );
 
   const [options, presets] = useMemo(() => [getCronOptions(), 
cronPresets.map((preset) => preset.config)], []);
-  const dataSource = useMemo(
-    () =>
-      (data?.blueprints ?? [])
-        .filter((it) => {
-          switch (type) {
-            case 'all':
-              return true;
-            case 'manual':
-              return it.isManual;
-            case 'custom':
-              return !presets.includes(it.cronConfig);
-            default:
-              return !it.isManual && it.cronConfig === type;
-          }
-        })
-        .map((it) => {
-          const connections =
-            it.settings?.connections
-              .filter((cs) => cs.plugin !== 'webhook')
-              .map((cs) => onGet(`${cs.plugin}-${cs.connectionId}`) || 
`${cs.plugin}-${cs.connectionId}`) ?? [];
-          return {
-            ...it,
-            connections: connections.map((cs) => cs.name),
-          };
-        }),
-    [data, type],
+  const [dataSource, total] = useMemo(
+    () => [
+      (data?.blueprints ?? []).map((it) => {
+        const connections =
+          it.settings?.connections
+            .filter((cs) => cs.plugin !== 'webhook')
+            .map((cs) => onGet(`${cs.plugin}-${cs.connectionId}`) || 
`${cs.plugin}-${cs.connectionId}`) ?? [];
+        return {
+          ...it,
+          connections: connections.map((cs) => cs.name),
+        };
+      }),
+      data?.count ?? 0,
+    ],
+    [data],
   );
 
   const handleShowDialog = () => setIsOpen(true);
@@ -123,12 +117,12 @@ export const BlueprintHomePage = () => {
         <div className="action">
           <ButtonGroup>
             <Button intent={type === 'all' ? Intent.PRIMARY : Intent.NONE} 
text="All" onClick={() => setType('all')} />
-            {options.map(({ label, value }) => (
+            {options.map(({ label }) => (
               <Button
-                key={value}
-                intent={type === value ? Intent.PRIMARY : Intent.NONE}
+                key={label}
+                intent={type === label ? Intent.PRIMARY : Intent.NONE}
                 text={label}
-                onClick={() => setType(value)}
+                onClick={() => setType(label)}
               />
             ))}
           </ButtonGroup>
@@ -221,6 +215,12 @@ export const BlueprintHomePage = () => {
             },
           ]}
           dataSource={dataSource}
+          pagination={{
+            page,
+            pageSize,
+            total,
+            onChange: setPage,
+          }}
           noData={{
             text: 'There is no Blueprint yet. Please add a new Blueprint here 
or from a Project.',
             btnText: 'New Blueprint',
diff --git a/config-ui/src/pages/connection/detail/api.ts 
b/config-ui/src/pages/connection/detail/api.ts
index 23a7270ed..9b8a999a0 100644
--- a/config-ui/src/pages/connection/detail/api.ts
+++ b/config-ui/src/pages/connection/detail/api.ts
@@ -21,10 +21,11 @@ import { request } from '@/utils';
 export const deleteConnection = (plugin: string, id: ID) =>
   request(`/plugins/${plugin}/connections/${id}`, { method: 'delete' });
 
-export const getDataScopes = (plugin: string, id: ID) =>
+export const getDataScopes = (plugin: string, id: ID, payload: Pagination) =>
   request(`/plugins/${plugin}/connections/${id}/scopes`, {
     data: {
       blueprints: true,
+      ...payload,
     },
   });
 
diff --git a/config-ui/src/pages/connection/detail/index.tsx 
b/config-ui/src/pages/connection/detail/index.tsx
index dc8d3493e..1456a4409 100644
--- a/config-ui/src/pages/connection/detail/index.tsx
+++ b/config-ui/src/pages/connection/detail/index.tsx
@@ -60,6 +60,8 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) => 
{
   >();
   const [operating, setOperating] = useState(false);
   const [version, setVersion] = useState(1);
+  const [page, setPage] = useState(1);
+  const [pageSize] = useState(10);
   const [scopeId, setScopeId] = useState<ID>();
   const [scopeIds, setScopeIds] = useState<ID[]>([]);
   const [scopeConfigId, setScopeConfigId] = useState<ID>();
@@ -69,12 +71,17 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
   const navigate = useNavigate();
   const { onGet, onTest, onRefresh } = useConnections();
   const { setTips } = useTips();
-  const { ready, data } = useRefreshData(() => API.getDataScopes(plugin, 
connectionId), [version]);
+  const { ready, data } = useRefreshData(
+    () => API.getDataScopes(plugin, connectionId, { page, pageSize }),
+    [version, page, pageSize],
+  );
 
   const { unique, status, name, icon } = onGet(`${plugin}-${connectionId}`) || 
{};
 
   const pluginConfig = useMemo(() => getPluginConfig(plugin), [plugin]);
 
+  const [dataSource, total] = useMemo(() => [data?.scopes ?? [], data?.count 
?? 0], [data]);
+
   useEffect(() => {
     onTest(`${plugin}-${connectionId}`);
   }, [plugin, connectionId]);
@@ -322,7 +329,13 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
               ),
             },
           ]}
-          dataSource={data}
+          dataSource={dataSource}
+          pagination={{
+            page,
+            pageSize,
+            total,
+            onChange: setPage,
+          }}
           noData={{
             text: 'Add data to this connection.',
             btnText: 'Add Data Scope',
@@ -383,7 +396,7 @@ const ConnectionDetail = ({ plugin, connectionId }: Props) 
=> {
           <DataScopeSelectRemote
             plugin={plugin}
             connectionId={connectionId}
-            disabledScope={data}
+            disabledScope={dataSource}
             onCancel={handleHideDialog}
             onSubmit={handleCreateDataScope}
           />
diff --git a/config-ui/src/pages/project/home/api.ts 
b/config-ui/src/pages/project/home/api.ts
index e8dea41c7..539e91b38 100644
--- a/config-ui/src/pages/project/home/api.ts
+++ b/config-ui/src/pages/project/home/api.ts
@@ -33,7 +33,7 @@ type GetProjectsResponse = {
     blueprint: BlueprintType;
     lastPipeline: PipelineType;
   }>;
-  counts: number;
+  count: number;
 };
 
 export const getProjects = (params: GetProjectsParams): 
Promise<GetProjectsResponse> =>
diff --git a/config-ui/src/pages/project/home/index.tsx 
b/config-ui/src/pages/project/home/index.tsx
index 49d61723b..c9dd39005 100644
--- a/config-ui/src/pages/project/home/index.tsx
+++ b/config-ui/src/pages/project/home/index.tsx
@@ -36,19 +36,21 @@ import * as S from './styled';
 
 export const ProjectHomePage = () => {
   const [version, setVersion] = useState(1);
+  const [page, setPage] = useState(1);
+  const [pageSize] = useState(20);
   const [isOpen, setIsOpen] = useState(false);
   const [name, setName] = useState('');
   const [enableDora, setEnableDora] = useState(true);
   const [saving, setSaving] = useState(false);
 
-  const { ready, data } = useRefreshData(() => API.getProjects({ page: 1, 
pageSize: 200 }), [version]);
+  const { ready, data } = useRefreshData(() => API.getProjects({ page, 
pageSize }), [version, page, pageSize]);
   const { onGet } = useConnections();
 
   const navigate = useNavigate();
 
   const presets = useMemo(() => cronPresets.map((preset) => preset.config), 
[]);
-  const dataSource = useMemo(
-    () =>
+  const [dataSource, total] = useMemo(
+    () => [
       (data?.projects ?? []).map((it) => {
         return {
           name: it.name,
@@ -60,6 +62,8 @@ export const ProjectHomePage = () => {
           lastRunStatus: it.lastPipeline?.status,
         };
       }),
+      data?.count ?? 0,
+    ],
     [data],
   );
 
@@ -190,6 +194,12 @@ export const ProjectHomePage = () => {
           },
         ]}
         dataSource={dataSource}
+        pagination={{
+          page,
+          pageSize,
+          total,
+          onChange: setPage,
+        }}
         noData={{
           text: 'Add new projects to see engineering metrics based on 
projects.',
           btnText: 'New Project',
diff --git a/config-ui/src/plugins/components/data-scope-select/api.ts 
b/config-ui/src/plugins/components/data-scope-select/api.ts
index a6b1cb541..941dcbdc7 100644
--- a/config-ui/src/plugins/components/data-scope-select/api.ts
+++ b/config-ui/src/plugins/components/data-scope-select/api.ts
@@ -19,10 +19,15 @@
 import { request } from '@/utils';
 
 type ParamsType = {
-  searchTerm: string;
+  searchTerm?: string;
+} & Pagination;
+
+type ResponseType = {
+  scopes: Array<{ name: string }>;
+  count: number;
 };
 
-export const getDataScope = (plugin: string, connectionId: ID, params?: 
ParamsType) =>
+export const getDataScope = (plugin: string, connectionId: ID, params?: 
ParamsType): Promise<ResponseType> =>
   request(`/plugins/${plugin}/connections/${connectionId}/scopes`, {
     data: params,
   });
diff --git a/config-ui/src/plugins/components/data-scope-select/index.tsx 
b/config-ui/src/plugins/components/data-scope-select/index.tsx
index 145e87778..fdf64de6d 100644
--- a/config-ui/src/plugins/components/data-scope-select/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-select/index.tsx
@@ -16,11 +16,13 @@
  *
  */
 
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useMemo } from 'react';
 import { Button, Intent } from '@blueprintjs/core';
 import { useDebounce } from 'ahooks';
+import type { McsID, McsItem } from 'miller-columns-select';
+import MillerColumnsSelect from 'miller-columns-select';
 
-import { PageLoading, FormItem, ExternalLink, Message, Buttons, MultiSelector, 
Table } from '@/components';
+import { FormItem, ExternalLink, Message, Buttons, MultiSelector } from 
'@/components';
 import { useRefreshData } from '@/hooks';
 import { getPluginScopeId } from '@/plugins';
 
@@ -44,38 +46,59 @@ export const DataScopeSelect = ({
   onSubmit,
   onCancel,
 }: Props) => {
-  const [version, setVersion] = useState(1);
-  const [scopeIds, setScopeIds] = useState<ID[]>([]);
   const [query, setQuery] = useState('');
+  const [items, setItems] = useState<McsItem<{ data: any }>[]>([]);
+  const [selectedItems, setSelecteItems] = useState<any>([]);
+  const [page, setPage] = useState(1);
+  const [pageSize] = useState(10);
+  const [total, setTotal] = useState(0);
 
   useEffect(() => {
-    setScopeIds((initialScope ?? []).map((sc: any) => getPluginScopeId(plugin, 
sc)) ?? []);
+    setSelecteItems(initialScope ?? []);
   }, []);
 
+  const selectedIds = useMemo(() => selectedItems.map((it: any) => 
getPluginScopeId(plugin, it)), [selectedItems]);
+
+  const handleChangeSelectItemsIds = (ids: McsID[]) => {
+    setSelecteItems(items.filter((it) => ids.includes(it.id)).map((it) => 
it.data));
+  };
+
+  const getDataScope = async (page: number) => {
+    const res = await API.getDataScope(plugin, connectionId, { page, pageSize 
});
+    setItems([
+      ...items,
+      ...res.scopes.map((sc) => ({
+        parentId: null,
+        id: getPluginScopeId(plugin, sc),
+        title: sc.name,
+        data: sc,
+      })),
+    ]);
+    if (page === 1) {
+      setTotal(res.count);
+    }
+  };
+
+  useEffect(() => {
+    getDataScope(page);
+  }, [page]);
+
   const search = useDebounce(query, { wait: 500 });
 
-  const { ready, data } = useRefreshData(() => API.getDataScope(plugin, 
connectionId), [version]);
-  const { ready: searchReady, data: searchItems } = useRefreshData<[{ name: 
string }]>(
+  const { ready, data } = useRefreshData(
     () => API.getDataScope(plugin, connectionId, { searchTerm: search }),
     [search],
   );
 
-  const handleRefresh = () => setVersion((v) => v + 1);
-
-  const handleSubmit = () => {
-    const scope = data.filter((it: any) => 
scopeIds.includes(getPluginScopeId(plugin, it)));
-    onSubmit?.(scope);
-  };
+  const handleScroll = () => setPage(page + 1);
 
-  if (!ready || !data) {
-    return <PageLoading />;
-  }
+  const handleSubmit = () => onSubmit?.(selectedItems);
 
   return (
     <FormItem
       label="Select Data Scope"
       subLabel={
-        data.length ? (
+        items.length ? (
           <>
             Select the data scope in this Connection that you wish to 
associate with this Project. If you wish to add
             more Data Scope to this Connection, please{' '}
@@ -93,7 +116,7 @@ export const DataScopeSelect = ({
       }
       required
     >
-      {data.length ? (
+      {items.length ? (
         <S.Wrapper>
           {showWarning ? (
             <Message
@@ -109,48 +132,34 @@ export const DataScopeSelect = ({
             />
           ) : (
             <Buttons>
-              <Button intent={Intent.PRIMARY} icon="refresh" text="Refresh 
Data Scope" onClick={handleRefresh} />
+              <Button intent={Intent.PRIMARY} icon="refresh" text="Refresh 
Data Scope" />
             </Buttons>
           )}
           <div className="search">
             <MultiSelector
-              loading={!searchReady}
-              items={searchItems ?? []}
+              loading={!ready}
+              items={data?.scopes ?? []}
               getName={(it: any) => it.name}
               getKey={(it) => getPluginScopeId(plugin, it)}
               noResult="No Data Scopes Available."
               onQueryChange={(query) => setQuery(query)}
-              selectedItems={data.filter((it: any) => 
scopeIds.includes(getPluginScopeId(plugin, it))) ?? []}
-              onChangeItems={(items) => setScopeIds(items.map((it: any) => 
getPluginScopeId(plugin, it)))}
+              selectedItems={selectedItems}
+              onChangeItems={setSelecteItems}
             />
           </div>
-          <Table
-            noShadow
-            loading={!ready}
-            columns={[
-              {
-                title: 'Data Scope',
-                dataIndex: 'name',
-                key: 'name',
-              },
-              {
-                title: 'Scope Config',
-                dataIndex: 'scopeConfig',
-                key: 'scopeConfig',
-                render: (_, row) => (row.scopeConfigId ? row.scopeConfig?.name 
: 'N/A'),
-              },
-            ]}
-            dataSource={data}
-            rowSelection={{
-              getRowKey: (data) => getPluginScopeId(plugin, data),
-              type: 'checkbox',
-              selectedRowKeys: scopeIds as string[],
-              onChange: (selectedRowKeys) => setScopeIds(selectedRowKeys),
-            }}
+          <MillerColumnsSelect
+            showSelectAll
+            columnCount={1}
+            columnHeight={200}
+            items={items}
+            getHasMore={() => items.length < total}
+            onScroll={handleScroll}
+            selectedIds={selectedIds}
+            onSelectItemIds={handleChangeSelectItemsIds}
           />
           <Buttons position="bottom" align="right">
             <Button outlined intent={Intent.PRIMARY} text="Cancel" 
onClick={onCancel} />
-            <Button disabled={!scopeIds.length} intent={Intent.PRIMARY} 
text="Save" onClick={handleSubmit} />
+            <Button disabled={!selectedItems.length} intent={Intent.PRIMARY} 
text="Save" onClick={handleSubmit} />
           </Buttons>
         </S.Wrapper>
       ) : (

Reply via email to