This is an automated email from the ASF dual-hosted git repository. mintsweet pushed a commit to branch feat-5640 in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit 9f8cc63bf89dd7c24a12d2fe77a1b37f1f790961 Author: mintsweet <[email protected]> AuthorDate: Wed Aug 23 11:20:49 2023 +1200 refactor(config-ui): improve the plugin component data-scope-select-remote --- .../components/data-scope-select-remote/api.ts | 18 +++ .../components/data-scope-select-remote/index.tsx | 168 ++++++++++++++++----- .../components/data-scope-select-remote/styled.ts | 5 + .../{styled.ts => types.ts} | 18 ++- 4 files changed, 168 insertions(+), 41 deletions(-) diff --git a/config-ui/src/plugins/components/data-scope-select-remote/api.ts b/config-ui/src/plugins/components/data-scope-select-remote/api.ts index 19abb110d..1d8c79f41 100644 --- a/config-ui/src/plugins/components/data-scope-select-remote/api.ts +++ b/config-ui/src/plugins/components/data-scope-select-remote/api.ts @@ -18,6 +18,24 @@ import { request } from '@/utils'; +import * as T from './types'; + +export const getRemoteScope = (plugin: string, connectionId: ID, params: T.GetRemoteScopeParams) => + request(`/plugins/${plugin}/connections/${connectionId}/remote-scopes`, { + method: 'get', + data: params, + }); + +export const searchRemoteScope = ( + plugin: string, + connectionId: ID, + params: T.SearchRemoteScopeParams, +): Promise<{ children: T.ResItem[] }> => + request(`/plugins/${plugin}/connections/${connectionId}/search-remote-scopes`, { + method: 'get', + data: params, + }); + export const getDataScope = (plugin: string, connectionId: ID, scopeId: string) => request(`/plugins/${plugin}/connections/${connectionId}/scopes/${scopeId}`); diff --git a/config-ui/src/plugins/components/data-scope-select-remote/index.tsx b/config-ui/src/plugins/components/data-scope-select-remote/index.tsx index 3b5565224..e454087c4 100644 --- a/config-ui/src/plugins/components/data-scope-select-remote/index.tsx +++ b/config-ui/src/plugins/components/data-scope-select-remote/index.tsx @@ -16,16 +16,18 @@ * */ -import { useMemo, useState } from 'react'; -import { Button, Intent } from '@blueprintjs/core'; +import { useEffect, useMemo, useState } from 'react'; +import { Button, Intent, InputGroup } from '@blueprintjs/core'; +import type { McsID, McsItem, McsColumn } from 'miller-columns-select'; +import MillerColumnsSelect from 'miller-columns-select'; +import { useDebounce } from 'ahooks'; +import { uniqBy } from 'lodash'; -import { Buttons } from '@/components'; +import { FormItem, MultiSelector, Loading, Buttons } from '@/components'; import { getPluginConfig, getPluginScopeId } from '@/plugins'; import { operator } from '@/utils'; -import { DataScopeMillerColumns } from '../data-scope-miller-columns'; -import { DataScopeSearch } from '../data-scope-search'; - +import * as T from './types'; import * as API from './api'; import * as S from './styled'; @@ -39,16 +41,80 @@ interface Props { export const DataScopeSelectRemote = ({ plugin, connectionId, disabledScope, onCancel, onSubmit }: Props) => { const [operating, setOperating] = useState(false); - const [selectedItems, setSelectedItems] = useState<Array<{ data: any }>>([]); - const disabledItems = useMemo( - () => (disabledScope ?? []).map((it) => ({ id: getPluginScopeId(plugin, it) })), - [disabledScope], + // miller columns + const [items, setItems] = useState<McsItem<T.ResItem>[]>([]); + const [selectedItems, setSelectedItems] = useState<T.ResItem[]>([]); + const [loadedIds, setLoadedIds] = useState<ID[]>([]); + const [nextTokenMap, setNextTokenMap] = useState<Record<ID, string>>({}); + + // search + const [query, setQuery] = useState(''); + const [items2, setItems2] = useState<McsItem<T.ResItem>[]>([]); + const [selectedItems2, setSelectedItems2] = useState<T.ResItem[]>([]); + const [page, setPage] = useState(1); + const [total, setTotal] = useState(0); + const search = useDebounce(query, { wait: 500 }); + + const config = useMemo(() => getPluginConfig(plugin).dataScope, [plugin]); + + const selectedScope = useMemo( + () => uniqBy([...selectedItems, ...selectedItems2], 'id'), + [selectedItems, selectedItems2], ); - const pluginConfig = useMemo(() => getPluginConfig(plugin), [plugin]); + const getItems = async (groupId: ID | null, currentPageToken?: string) => { + const res = await API.getRemoteScope(plugin, connectionId, { + groupId, + pageToken: currentPageToken, + }); + + setItems([ + ...items, + ...(res.children ?? []).map((it: any) => ({ + ...it, + title: it.name, + })), + ]); + + if (!res.nextPageToken) { + setLoadedIds([...loadedIds, groupId ? groupId : 'root']); + } else { + setNextTokenMap({ + ...nextTokenMap, + [`${groupId ? groupId : 'root'}`]: res.nextPageToken, + }); + } + }; + + useEffect(() => { + getItems(null); + }, []); - const error = useMemo(() => (!selectedItems.length ? 'No Data Scope is Selected' : ''), [selectedItems]); + const searchItems = async () => { + if (!search) return; + + const res = await API.searchRemoteScope(plugin, connectionId, { + search, + page, + pageSize: 50, + }); + + setItems2( + res.children.map((it) => ({ + ...it, + title: it.fullName, + })), + ); + + if (page === 1) { + // setTotal(res.count); + } + }; + + useEffect(() => { + searchItems(); + }, [search, page]); const handleSubmit = async () => { const [success, res] = await operator( @@ -66,40 +132,64 @@ export const DataScopeSelectRemote = ({ plugin, connectionId, disabledScope, onC return ( <S.Wrapper> - {pluginConfig.dataScope.render ? ( - pluginConfig.dataScope.render({ + {config.render ? ( + config.render({ plugin, connectionId, - disabledItems, + disabledItems: (disabledScope ?? []).map((scope) => ({ id: getPluginScopeId(plugin, scope) })), selectedItems, onChangeItems: setSelectedItems, }) ) : ( <> - <h4>{pluginConfig.dataScope.millerColumns?.title}</h4> - <p>{pluginConfig.dataScope.millerColumns?.subTitle}</p> - <DataScopeMillerColumns - title={pluginConfig.dataScope.millerColumns?.firstColumnTitle} - columnCount={pluginConfig.dataScope.millerColumns?.columnCount ?? 3} - plugin={plugin} - connectionId={connectionId} - disabledItems={disabledItems} - selectedItems={selectedItems} - onChangeItems={setSelectedItems} - /> - {pluginConfig.dataScope.search && ( - <> - <h5 style={{ marginTop: 16 }}>{pluginConfig.dataScope.search.title}</h5> - <p>{pluginConfig.dataScope.search.subTitle}</p> - <DataScopeSearch - plugin={plugin} - connectionId={connectionId} - disabledItems={disabledItems} - selectedItems={selectedItems} - onChangeItems={setSelectedItems} + <FormItem label={config.title} required> + <MultiSelector + disabled + items={selectedScope} + getKey={(it) => it.id} + getName={(it) => it.fullName} + selectedItems={selectedScope} + /> + </FormItem> + <FormItem> + <InputGroup leftIcon="search" value={query} onChange={(e) => setQuery(e.target.value)} /> + {!search ? ( + <MillerColumnsSelect + items={items} + columnCount={config.millerColumnCount ?? 1} + columnHeight={300} + getCanExpand={(it) => it.type === 'group'} + getHasMore={(id) => !loadedIds.includes(id ?? 'root')} + onExpand={(id: McsID) => getItems(id, nextTokenMap[id])} + onScroll={(id: McsID | null) => getItems(id, nextTokenMap[id ?? 'root'])} + renderTitle={(column: McsColumn) => + !column.parentId && + config.millerFirstTitle && <S.ColumnTitle>{config.millerFirstTitle}</S.ColumnTitle> + } + renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} + disabledIds={(disabledScope ?? []).map((it) => getPluginScopeId(plugin, it))} + selectedIds={selectedScope.map((it) => it.id)} + onSelectItemIds={(selectedIds: ID[]) => + setSelectedItems(items.filter((it) => selectedIds.includes(it.id))) + } + /> + ) : ( + <MillerColumnsSelect + items={items2} + columnCount={1} + columnHeight={300} + getCanExpand={() => false} + getHasMore={() => total === 0} + onScroll={() => setPage(page + 1)} + renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />} + disabledIds={(disabledScope ?? []).map((it) => getPluginScopeId(plugin, it))} + selectedIds={selectedScope.map((it) => it.id)} + onSelectItemIds={(selectedIds: ID[]) => + setSelectedItems2(items2.filter((it) => selectedIds.includes(it.id))) + } /> - </> - )} + )} + </FormItem> </> )} <Buttons position="bottom" align="right"> @@ -109,7 +199,7 @@ export const DataScopeSelectRemote = ({ plugin, connectionId, disabledScope, onC intent={Intent.PRIMARY} text="Save" loading={operating} - disabled={!!error} + disabled={!selectedItems.length} onClick={handleSubmit} /> </Buttons> diff --git a/config-ui/src/plugins/components/data-scope-select-remote/styled.ts b/config-ui/src/plugins/components/data-scope-select-remote/styled.ts index 4bf74a53c..41e76ecb3 100644 --- a/config-ui/src/plugins/components/data-scope-select-remote/styled.ts +++ b/config-ui/src/plugins/components/data-scope-select-remote/styled.ts @@ -19,3 +19,8 @@ import styled from 'styled-components'; export const Wrapper = styled.div``; + +export const ColumnTitle = styled.div` + padding: 6px 12px; + font-weight: 600; +`; diff --git a/config-ui/src/plugins/components/data-scope-select-remote/styled.ts b/config-ui/src/plugins/components/data-scope-select-remote/types.ts similarity index 73% copy from config-ui/src/plugins/components/data-scope-select-remote/styled.ts copy to config-ui/src/plugins/components/data-scope-select-remote/types.ts index 4bf74a53c..02f5b8b40 100644 --- a/config-ui/src/plugins/components/data-scope-select-remote/styled.ts +++ b/config-ui/src/plugins/components/data-scope-select-remote/types.ts @@ -16,6 +16,20 @@ * */ -import styled from 'styled-components'; +export type ResItem = { + type: 'group' | 'scope'; + parentId: ID | null; + id: ID; + name: string; + fullName: string; + data: any; +}; -export const Wrapper = styled.div``; +export type GetRemoteScopeParams = { + groupId: ID | null; + pageToken?: string; +}; + +export type SearchRemoteScopeParams = { + search?: string; +} & Pagination;
