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

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

commit 572c6310e25caed748793d07c208d0e1f37b6cae
Author: mintsweet <[email protected]>
AuthorDate: Thu Dec 14 17:26:06 2023 +1300

    refactor(config-ui): use new ui select to replace selector
---
 config-ui/src/components/index.ts                  |   1 -
 config-ui/src/components/selector/index.ts         |  20 ---
 .../components/selector/multi-selector/index.tsx   | 147 ---------------------
 .../src/components/selector/selector/index.tsx     |  91 -------------
 .../plugins/components/data-scope-select/index.tsx |  19 ++-
 .../plugins/components/scope-config-form/index.tsx |  16 +--
 .../plugins/register/bitbucket/transformation.tsx  |  54 ++++----
 .../src/plugins/register/jira/transformation.tsx   |  91 +++++++------
 .../src/plugins/register/tapd/transformation.tsx   | 106 ++++++---------
 config-ui/src/routes/api-keys/api-keys.tsx         |  23 ++--
 10 files changed, 142 insertions(+), 426 deletions(-)

diff --git a/config-ui/src/components/index.ts 
b/config-ui/src/components/index.ts
index 707c3506d..143c30b7f 100644
--- a/config-ui/src/components/index.ts
+++ b/config-ui/src/components/index.ts
@@ -24,5 +24,4 @@ export * from './logo';
 export * from './message';
 export * from './no-data';
 export * from './page-header';
-export * from './selector';
 export * from './tooltip';
diff --git a/config-ui/src/components/selector/index.ts 
b/config-ui/src/components/selector/index.ts
deleted file mode 100644
index eb2f71e3d..000000000
--- a/config-ui/src/components/selector/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 './selector';
-export * from './multi-selector';
diff --git a/config-ui/src/components/selector/multi-selector/index.tsx 
b/config-ui/src/components/selector/multi-selector/index.tsx
deleted file mode 100644
index 4d48c6c4b..000000000
--- a/config-ui/src/components/selector/multi-selector/index.tsx
+++ /dev/null
@@ -1,147 +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 { MenuItem, Checkbox, Intent } from '@blueprintjs/core';
-import { MultiSelect2 } from '@blueprintjs/select';
-import { useDebounce } from 'ahooks';
-
-interface Props<T> {
-  placeholder?: string;
-  loading?: boolean;
-  disabled?: boolean;
-  items: T[];
-  disabledItems?: T[];
-  getKey?: (item: T) => string | number;
-  getName?: (item: T) => string;
-  getIcon?: (item: T) => string;
-  selectedItems?: T[];
-  onChangeItems?: (selectedItems: T[]) => void;
-  noResult?: string;
-  onQueryChange?: (query: string) => void;
-}
-
-export const MultiSelector = <T,>({
-  placeholder,
-  loading = false,
-  disabled = false,
-  items,
-  disabledItems = [],
-  getKey = (it) => it as string,
-  getName = (it) => it as string,
-  getIcon,
-  onChangeItems,
-  noResult = 'No results found.',
-  onQueryChange,
-  ...props
-}: Props<T>) => {
-  const [selectedItems, setSelectedItems] = useState<T[]>([]);
-  const [query, setQuery] = useState('');
-  const search = useDebounce(query, { wait: 500 });
-
-  const filteredItems = useMemo(
-    () =>
-      items.filter((it) => {
-        const name = getName(it);
-        return name.toLocaleLowerCase().includes(search.toLocaleLowerCase());
-      }),
-    [items, search],
-  );
-
-  useEffect(() => {
-    setSelectedItems(props.selectedItems ?? []);
-  }, [props.selectedItems]);
-
-  const tagRenderer = (item: T) => {
-    const name = getName(item);
-    return <span>{name}</span>;
-  };
-
-  const itemRenderer = (item: T, { handleClick }: any) => {
-    const key = getKey(item);
-    const name = getName(item);
-    const icon = getIcon?.(item);
-    const selected = !!selectedItems.find((it) => getKey(it) === key);
-    const disabled = !!disabledItems.find((it) => getKey(it) === key);
-
-    return (
-      <MenuItem
-        key={key}
-        disabled={selected || disabled}
-        onClick={(e) => {
-          e.preventDefault();
-          handleClick();
-        }}
-        labelElement={icon ? <img src={icon} width={16} alt="" /> : null}
-        text={<Checkbox disabled={selected || disabled} checked={selected || 
disabled} readOnly label={name} />}
-      />
-    );
-  };
-
-  const handleItemSelect = (item: T) => {
-    const newSelectedItems = [...selectedItems, item];
-    if (onChangeItems) {
-      onChangeItems(newSelectedItems);
-    } else {
-      setSelectedItems(newSelectedItems);
-    }
-  };
-
-  const handleItemRemove = (item: T) => {
-    const newSelectedItems = selectedItems.filter((it) => getKey(it) !== 
getKey(item));
-    if (onChangeItems) {
-      onChangeItems(newSelectedItems);
-    } else {
-      setSelectedItems(newSelectedItems);
-    }
-  };
-
-  const handleQueryChange = (query: string) => {
-    if (onQueryChange && query) {
-      onQueryChange(query);
-    } else {
-      setQuery(query);
-    }
-  };
-
-  return (
-    <MultiSelect2
-      disabled={disabled}
-      resetOnSelect
-      fill
-      placeholder={placeholder ?? 'Select...'}
-      items={filteredItems}
-      // https://github.com/palantir/blueprint/issues/3596
-      // set activeItem to null will fixed the scrollBar to top when the 
selectedItems changed
-      activeItem={null}
-      selectedItems={selectedItems}
-      itemRenderer={itemRenderer}
-      tagRenderer={tagRenderer}
-      tagInputProps={{
-        tagProps: {
-          intent: Intent.PRIMARY,
-          minimal: true,
-        },
-      }}
-      onItemSelect={handleItemSelect}
-      onRemove={handleItemRemove}
-      onQueryChange={handleQueryChange}
-      noResults={<MenuItem disabled={true} text={loading ? 'Fetching...' : 
noResult} />}
-    />
-  );
-};
diff --git a/config-ui/src/components/selector/selector/index.tsx 
b/config-ui/src/components/selector/selector/index.tsx
deleted file mode 100644
index e425c5139..000000000
--- a/config-ui/src/components/selector/selector/index.tsx
+++ /dev/null
@@ -1,91 +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 { MenuItem, Button, Alignment } from '@blueprintjs/core';
-import { Select2 } from '@blueprintjs/select';
-
-interface Props<T> {
-  items: T[];
-  disabledItems?: T[];
-  getKey?: (item: T) => ID;
-  getName?: (item: T) => string;
-  getIcon?: (item: T) => string;
-  selectedItem?: T;
-  onChangeItem?: (selectedItem: T) => void;
-}
-
-export const Selector = <T,>({
-  items,
-  disabledItems = [],
-  getKey = (it) => it as string,
-  getName = (it) => it as string,
-  getIcon,
-  onChangeItem,
-  ...props
-}: Props<T>) => {
-  const [selectedItem, setSelectedItem] = useState<T>();
-
-  useEffect(() => {
-    setSelectedItem(props.selectedItem);
-  }, [props.selectedItem]);
-
-  const btnText = useMemo(() => (selectedItem ? getName(selectedItem) : 
'Select...'), [selectedItem]);
-
-  const itemPredicate = (query: string, item: T) => {
-    const name = getName(item);
-    return name.toLowerCase().indexOf(query.toLowerCase()) >= 0;
-  };
-
-  const itemRenderer = (item: T, { handleClick }: any) => {
-    const key = getKey(item);
-    const name = getName(item);
-    const icon = getIcon?.(item) ?? null;
-    const disabled = !!disabledItems.find((it) => getKey(it) === getKey(item)) 
|| selectedItem === item;
-
-    return (
-      <MenuItem
-        key={key}
-        disabled={disabled}
-        text={<span>{name}</span>}
-        icon={icon ? <img src={icon} alt="" width={20} /> : null}
-        onClick={handleClick}
-      />
-    );
-  };
-
-  const handleItemSelect = (item: T) => {
-    if (onChangeItem) {
-      onChangeItem(item);
-    } else {
-      setSelectedItem(item);
-    }
-  };
-
-  return (
-    <Select2
-      items={items}
-      activeItem={selectedItem}
-      itemPredicate={itemPredicate}
-      itemRenderer={itemRenderer}
-      onItemSelect={handleItemSelect}
-    >
-      <Button outlined fill alignText={Alignment.LEFT} rightIcon="caret-down" 
text={btnText} />
-    </Select2>
-  );
-};
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 f9bf2254e..3f1e86092 100644
--- a/config-ui/src/plugins/components/data-scope-select/index.tsx
+++ b/config-ui/src/plugins/components/data-scope-select/index.tsx
@@ -17,14 +17,14 @@
  */
 
 import { useState, useEffect, useMemo } from 'react';
-import { Flex } from 'antd';
+import { Flex, Select } from 'antd';
 import { Button, Intent } from '@blueprintjs/core';
 import { useDebounce } from 'ahooks';
 import type { McsItem } from 'miller-columns-select';
 import MillerColumnsSelect from 'miller-columns-select';
 
 import API from '@/api';
-import { Loading, Block, ExternalLink, Message, MultiSelector } from 
'@/components';
+import { Loading, Block, ExternalLink, Message } from '@/components';
 import { useRefreshData } from '@/hooks';
 import { getPluginScopeId } from '@/plugins';
 
@@ -133,15 +133,14 @@ export const DataScopeSelect = ({
               <Button intent={Intent.PRIMARY} icon="refresh" text="Refresh 
Data Scope" />
             </Flex>
           )}
-          <MultiSelector
+          <Select
             loading={!ready}
-            items={searchItems}
-            getName={(it) => it.name}
-            getKey={(it) => getPluginScopeId(plugin, it)}
-            noResult="No Data Scopes Available."
-            onQueryChange={(query) => setQuery(query)}
-            selectedItems={searchItems.filter((it) => 
selectedIds.includes(getPluginScopeId(plugin, it)))}
-            onChangeItems={(selectedItems) => 
setSelectedIds(selectedItems.map((it) => getPluginScopeId(plugin, it)))}
+            showSearch
+            mode="multiple"
+            options={searchItems.map((it) => ({ label: it.fullName, value: 
getPluginScopeId(plugin, it) }))}
+            value={selectedIds}
+            onChange={(value) => setSelectedIds(value)}
+            onSearch={(value) => setQuery(value)}
           />
           <MillerColumnsSelect
             showSelectAll
diff --git a/config-ui/src/plugins/components/scope-config-form/index.tsx 
b/config-ui/src/plugins/components/scope-config-form/index.tsx
index abb6c18db..4d1be1e88 100644
--- a/config-ui/src/plugins/components/scope-config-form/index.tsx
+++ b/config-ui/src/plugins/components/scope-config-form/index.tsx
@@ -18,11 +18,11 @@
 
 import { useState, useEffect, useMemo } from 'react';
 import { omit } from 'lodash';
-import { Flex, Form, Input, Card, Alert, Divider } from 'antd';
+import { Flex, Form, Input, Card, Alert, Divider, Select } from 'antd';
 import { Button, Intent } from '@blueprintjs/core';
 
 import API from '@/api';
-import { ExternalLink, Block, MultiSelector, Message } from '@/components';
+import { ExternalLink, Block, Message } from '@/components';
 import { transformEntities, EntitiesLabel } from '@/config';
 import { getPluginConfig } from '@/plugins';
 import { GitHubTransformation } from '@/plugins/register/github';
@@ -145,12 +145,12 @@ export const ScopeConfigForm = ({
               }
               required
             >
-              <MultiSelector
-                items={transformEntities(config.scopeConfig?.entities ?? [])}
-                getKey={(it) => it.value}
-                getName={(it) => it.label}
-                selectedItems={entities.map((it) => ({ label: 
EntitiesLabel[it], value: it }))}
-                onChangeItems={(its) => setEntities(its.map((it) => it.value))}
+              <Select
+                style={{ width: '100%' }}
+                mode="multiple"
+                options={transformEntities(config.scopeConfig?.entities ?? [])}
+                value={entities}
+                onChange={(value) => setEntities(value)}
               />
             </Block>
             {showWarning && (
diff --git a/config-ui/src/plugins/register/bitbucket/transformation.tsx 
b/config-ui/src/plugins/register/bitbucket/transformation.tsx
index f0562a538..6bae7b8a7 100644
--- a/config-ui/src/plugins/register/bitbucket/transformation.tsx
+++ b/config-ui/src/plugins/register/bitbucket/transformation.tsx
@@ -18,9 +18,9 @@
 
 import { useMemo, useState, useEffect } from 'react';
 import { CaretRightOutlined } from '@ant-design/icons';
-import { theme, Collapse, Tag, Form, Input, Checkbox } from 'antd';
+import { theme, Collapse, Tag, Form, Input, Checkbox, Select } from 'antd';
 
-import { ExternalLink, HelpTooltip, MultiSelector } from '@/components';
+import { ExternalLink, HelpTooltip } from '@/components';
 import { DOC_URL } from '@/release';
 
 import ExampleJpg from './assets/bitbucket-example.jpg';
@@ -127,55 +127,53 @@ const renderCollapseItems = ({
       children: (
         <div className="list">
           <Form.Item label="TODO">
-            <MultiSelector
-              items={ALL_STATES}
-              disabledItems={selectedStates}
-              selectedItems={transformation.issueStatusTodo ? 
transformation.issueStatusTodo.split(',') : []}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={ALL_STATES.map((it) => ({ label: it, value: it }))}
+              value={transformation.issueStatusTodo ? 
transformation.issueStatusTodo.split(',') : []}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
-                  issueStatusTodo: selectedItems.join(','),
+                  issueStatusTodo: value.join(','),
                 })
               }
             />
           </Form.Item>
           <Form.Item label="IN-PROGRESS">
-            <MultiSelector
-              items={ALL_STATES}
-              disabledItems={selectedStates}
-              selectedItems={
-                transformation.issueStatusInProgress ? 
transformation.issueStatusInProgress.split(',') : []
-              }
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={ALL_STATES.map((it) => ({ label: it, value: it }))}
+              value={transformation.issueStatusInProgress ? 
transformation.issueStatusInProgress.split(',') : []}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
-                  issueStatusInProgress: selectedItems.join(','),
+                  issueStatusInProgress: value.join(','),
                 })
               }
             />
           </Form.Item>
           <Form.Item label="DONE">
-            <MultiSelector
-              items={ALL_STATES}
-              disabledItems={selectedStates}
-              selectedItems={transformation.issueStatusDone ? 
transformation.issueStatusDone.split(',') : []}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={ALL_STATES.map((it) => ({ label: it, value: it }))}
+              value={transformation.issueStatusDone ? 
transformation.issueStatusDone.split(',') : []}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
-                  issueStatusDone: selectedItems.join(','),
+                  issueStatusDone: value.join(','),
                 })
               }
             />
           </Form.Item>
           <Form.Item label="OTHER">
-            <MultiSelector
-              items={ALL_STATES}
-              disabledItems={selectedStates}
-              selectedItems={transformation.issueStatusOther ? 
transformation.issueStatusOther.split(',') : []}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={ALL_STATES.map((it) => ({ label: it, value: it }))}
+              value={transformation.issueStatusOther ? 
transformation.issueStatusOther.split(',') : []}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
-                  issueStatusOther: selectedItems.join(','),
+                  issueStatusOther: value.join(','),
                 })
               }
             />
diff --git a/config-ui/src/plugins/register/jira/transformation.tsx 
b/config-ui/src/plugins/register/jira/transformation.tsx
index 7964674e5..90fd62037 100644
--- a/config-ui/src/plugins/register/jira/transformation.tsx
+++ b/config-ui/src/plugins/register/jira/transformation.tsx
@@ -19,10 +19,10 @@
 import { useState, useEffect, useMemo } from 'react';
 import { uniqWith } from 'lodash';
 import { CaretRightOutlined } from '@ant-design/icons';
-import { theme, Collapse, Tag, Form } from 'antd';
+import { theme, Collapse, Tag, Form, Select } from 'antd';
 
 import API from '@/api';
-import { PageLoading, HelpTooltip, ExternalLink, MultiSelector, Selector } 
from '@/components';
+import { PageLoading, HelpTooltip, ExternalLink } from '@/components';
 import { useProxyPrefix, useRefreshData } from '@/hooks';
 import { DOC_URL } from '@/release';
 
@@ -165,14 +165,26 @@ const renderCollapseItems = ({
   transformation: any;
   onChangeTransformation: any;
   connectionId: ID;
-  issueTypes: any;
+  issueTypes: Array<{
+    id: string;
+    name: string;
+  }>;
   fields: Array<{
     id: string;
     name: string;
   }>;
-  requirementItems: any;
-  bugItems: any;
-  incidentItems: any;
+  requirementItems: Array<{
+    id: string;
+    name: string;
+  }>;
+  bugItems: Array<{
+    id: string;
+    name: string;
+  }>;
+  incidentItems: Array<{
+    id: string;
+    name: string;
+  }>;
   transformaType: any;
 }) =>
   [
@@ -194,18 +206,18 @@ const renderCollapseItems = ({
             <ExternalLink link={DOC_URL.METRICS.MTTR}>DORA - Median Time to 
Restore Service</ExternalLink>, etc.
           </p>
           <Form.Item label="Requirement">
-            <MultiSelector
-              items={issueTypes}
-              disabledItems={[...bugItems, ...incidentItems]}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              getIcon={(it) => it.iconUrl}
-              selectedItems={requirementItems}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={issueTypes.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={requirementItems.map((it) => it.id)}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
-                    ...transformaType(selectedItems, StandardType.Requirement),
+                    ...transformaType(
+                      requirementItems.filter((it) => value.includes(it.id)),
+                      StandardType.Requirement,
+                    ),
                     ...transformaType(bugItems, StandardType.Bug),
                     ...transformaType(incidentItems, StandardType.Incident),
                   },
@@ -214,19 +226,19 @@ const renderCollapseItems = ({
             />
           </Form.Item>
           <Form.Item label="Bug">
-            <MultiSelector
-              items={issueTypes}
-              disabledItems={[...requirementItems, ...incidentItems]}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              getIcon={(it) => it.iconUrl}
-              selectedItems={bugItems}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={issueTypes.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={bugItems.map((it) => it.id)}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
                     ...transformaType(requirementItems, 
StandardType.Requirement),
-                    ...transformaType(selectedItems, StandardType.Bug),
+                    ...transformaType(
+                      bugItems.filter((it) => value.includes(it.id)),
+                      StandardType.Bug,
+                    ),
                     ...transformaType(incidentItems, StandardType.Incident),
                   },
                 })
@@ -243,20 +255,21 @@ const renderCollapseItems = ({
               </>
             }
           >
-            <MultiSelector
-              items={issueTypes}
-              disabledItems={[...requirementItems, ...bugItems]}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              getIcon={(it) => it.iconUrl}
-              selectedItems={incidentItems}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={issueTypes.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={incidentItems.map((it) => it.id)}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
                     ...transformaType(requirementItems, 
StandardType.Requirement),
                     ...transformaType(bugItems, StandardType.Bug),
-                    ...transformaType(selectedItems, StandardType.Incident),
+                    ...transformaType(
+                      incidentItems.filter((it) => value.includes(it.id)),
+
+                      StandardType.Incident,
+                    ),
                   },
                 })
               }
@@ -270,15 +283,13 @@ const renderCollapseItems = ({
               </>
             }
           >
-            <Selector
-              items={fields}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItem={fields.find((it) => it.id === 
transformation.storyPointField)}
-              onChangeItem={(selectedItem) =>
+            <Select
+              options={fields.map((it) => ({ label: it.name, value: it.id }))}
+              value={transformation.storyPointField}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
-                  storyPointField: selectedItem.id,
+                  storyPointField: value,
                 })
               }
             />
diff --git a/config-ui/src/plugins/register/tapd/transformation.tsx 
b/config-ui/src/plugins/register/tapd/transformation.tsx
index cb4d9e624..b010ed7de 100644
--- a/config-ui/src/plugins/register/tapd/transformation.tsx
+++ b/config-ui/src/plugins/register/tapd/transformation.tsx
@@ -19,10 +19,10 @@
 import { useEffect, useState } from 'react';
 import { uniqWith } from 'lodash';
 import { CaretRightOutlined } from '@ant-design/icons';
-import { theme, Collapse, Tag, Form } from 'antd';
+import { theme, Collapse, Tag, Form, Select } from 'antd';
 
 import API from '@/api';
-import { MultiSelector, PageLoading } from '@/components';
+import { PageLoading } from '@/components';
 import { useProxyPrefix, useRefreshData } from '@/hooks';
 
 enum StandardType {
@@ -211,20 +211,15 @@ const renderCollapseItems = ({
             and `Bug age` in built-in dashboards.
           </p>
           <Form.Item label="Requirement">
-            <MultiSelector
-              items={typeList}
-              disabledItems={typeList.filter((v) => [...bugTypeList, 
...incidentTypeList].includes(v.id))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={typeList.filter((v) => 
featureTypeList.includes(v.id))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={typeList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={featureTypeList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
-                    ...transformaType(
-                      selectedItems.map((v) => v.id),
-                      StandardType.Requirement,
-                    ),
+                    ...transformaType(value, StandardType.Requirement),
                     ...transformaType(bugTypeList, StandardType.Bug),
                     ...transformaType(incidentTypeList, StandardType.Incident),
                   },
@@ -233,21 +228,16 @@ const renderCollapseItems = ({
             />
           </Form.Item>
           <Form.Item label="Bug">
-            <MultiSelector
-              items={typeList}
-              disabledItems={typeList.filter((v) => [...featureTypeList, 
...incidentTypeList].includes(v.id))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={typeList.filter((v) => 
bugTypeList.includes(v.id))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={typeList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={bugTypeList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
                     ...transformaType(featureTypeList, 
StandardType.Requirement),
-                    ...transformaType(
-                      selectedItems.map((v) => v.id),
-                      StandardType.Bug,
-                    ),
+                    ...transformaType(value, StandardType.Bug),
                     ...transformaType(incidentTypeList, StandardType.Incident),
                   },
                 })
@@ -264,22 +254,17 @@ const renderCollapseItems = ({
               </>
             }
           >
-            <MultiSelector
-              items={typeList}
-              disabledItems={typeList.filter((v) => [...featureTypeList, 
...bugTypeList].includes(v.id))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={typeList.filter((v) => 
incidentTypeList.includes(v.id))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={typeList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={incidentTypeList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   typeMappings: {
                     ...transformaType(featureTypeList, 
StandardType.Requirement),
                     ...transformaType(bugTypeList, StandardType.Bug),
-                    ...transformaType(
-                      selectedItems.map((v) => v.id),
-                      StandardType.Incident,
-                    ),
+                    ...transformaType(value, StandardType.Incident),
                   },
                 })
               }
@@ -290,20 +275,15 @@ const renderCollapseItems = ({
             Delivery Rate` in built-in dashboards.
           </p>
           <Form.Item label="TODO">
-            <MultiSelector
-              items={statusList}
-              disabledItems={statusList.filter((v) => 
[...inProgressStatusList, ...doneStatusList].includes(v.name))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={statusList.filter((v) => 
todoStatusList.includes(v.name))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={statusList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={todoStatusList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   statusMappings: {
-                    ...transformaType(
-                      selectedItems.map((v) => v.name),
-                      StandardStatus.Todo,
-                    ),
+                    ...transformaType(value, StandardStatus.Todo),
                     ...transformaType(inProgressStatusList, 
StandardStatus.InProgress),
                     ...transformaType(doneStatusList, StandardStatus.Done),
                   },
@@ -312,21 +292,16 @@ const renderCollapseItems = ({
             />
           </Form.Item>
           <Form.Item label="IN-PROGRESS">
-            <MultiSelector
-              items={statusList}
-              disabledItems={statusList.filter((v) => [...todoStatusList, 
...doneStatusList].includes(v.name))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={statusList.filter((v) => 
inProgressStatusList.includes(v.name))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={statusList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={inProgressStatusList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   statusMappings: {
                     ...transformaType(todoStatusList, StandardStatus.Todo),
-                    ...transformaType(
-                      selectedItems.map((v) => v.name),
-                      StandardStatus.InProgress,
-                    ),
+                    ...transformaType(value, StandardStatus.InProgress),
                     ...transformaType(doneStatusList, StandardStatus.Done),
                   },
                 })
@@ -334,22 +309,17 @@ const renderCollapseItems = ({
             />
           </Form.Item>
           <Form.Item label="DONE">
-            <MultiSelector
-              items={statusList}
-              disabledItems={statusList.filter((v) => [...todoStatusList, 
...inProgressStatusList].includes(v.name))}
-              getKey={(it) => it.id}
-              getName={(it) => it.name}
-              selectedItems={statusList.filter((v) => 
doneStatusList.includes(v.name))}
-              onChangeItems={(selectedItems) =>
+            <Select
+              mode="multiple"
+              options={statusList.map((it) => ({ label: it.name, value: it.id 
}))}
+              value={doneStatusList}
+              onChange={(value) =>
                 onChangeTransformation({
                   ...transformation,
                   statusMappings: {
                     ...transformaType(todoStatusList, StandardStatus.Todo),
                     ...transformaType(inProgressStatusList, 
StandardStatus.InProgress),
-                    ...transformaType(
-                      selectedItems.map((v) => v.name),
-                      StandardStatus.Done,
-                    ),
+                    ...transformaType(value, StandardStatus.Done),
                   },
                 })
               }
diff --git a/config-ui/src/routes/api-keys/api-keys.tsx 
b/config-ui/src/routes/api-keys/api-keys.tsx
index 252ac553f..6271b4674 100644
--- a/config-ui/src/routes/api-keys/api-keys.tsx
+++ b/config-ui/src/routes/api-keys/api-keys.tsx
@@ -17,12 +17,12 @@
  */
 
 import { useState, useMemo } from 'react';
-import { Table, Modal, Input } from 'antd';
+import { Table, Modal, Input, Select } from 'antd';
 import { Button, Tag, Intent } from '@blueprintjs/core';
 import dayjs from 'dayjs';
 
 import API from '@/api';
-import { PageHeader, Block, Selector, ExternalLink, CopyText, Message } from 
'@/components';
+import { PageHeader, Block, ExternalLink, CopyText, Message } from 
'@/components';
 import { useRefreshData } from '@/hooks';
 import { operator, formatTime } from '@/utils';
 
@@ -53,8 +53,8 @@ export const ApiKeys = () => {
   const [dataSource, total] = useMemo(() => [data?.apikeys ?? [], data?.count 
?? 0], [data]);
   const hasError = useMemo(() => !form.name || !form.allowedPath, [form]);
 
-  const timeSelectedItem = useMemo(() => {
-    return C.timeOptions.find((it) => it.value === form.expiredAt || 
!it.value);
+  const timeSelectedValue = useMemo(() => {
+    return C.timeOptions.find((it) => it.value === form.expiredAt || 
!it.value)?.value;
   }, [form.expiredAt]);
 
   const handleCancel = () => {
@@ -174,15 +174,12 @@ export const ApiKeys = () => {
             />
           </Block>
           <Block title="Expiration" description="Set an expiration time for 
your API key." required>
-            <div style={{ width: 386 }}>
-              <Selector
-                items={C.timeOptions}
-                getKey={(it) => it.value}
-                getName={(it) => it.label}
-                selectedItem={timeSelectedItem}
-                onChangeItem={(it) => setForm({ ...form, expiredAt: it.value ? 
it.value : undefined })}
-              />
-            </div>
+            <Select
+              style={{ width: 386 }}
+              options={C.timeOptions}
+              value={timeSelectedValue}
+              onChange={(value) => setForm({ ...form, expiredAt: value ? value 
: undefined })}
+            />
           </Block>
           <Block
             title="Allowed Path"


Reply via email to