Browse Source

Merge branch 'main' of github.com:zilliztech/attu into fix-auth-login

nameczz 2 years ago
parent
commit
68c78a38bd
88 changed files with 949 additions and 936 deletions
  1. BIN
      .github/images/create_collection.png
  2. BIN
      .github/images/create_index.png
  3. BIN
      .github/images/data_preview.png
  4. BIN
      .github/images/query_advanced_filter.png
  5. BIN
      .github/images/system_view.png
  6. BIN
      .github/images/vector_search.png
  7. 3 56
      .github/workflows/dev.yml
  8. 48 33
      README.md
  9. 6 6
      client/package.json
  10. 5 1
      client/src/components/customDialog/DialogTemplate.tsx
  11. 2 0
      client/src/components/customDialog/Types.ts
  12. 11 53
      client/src/components/customInput/SearchInput.tsx
  13. 2 0
      client/src/components/customInput/Types.ts
  14. 2 1
      client/src/components/customSelector/CustomSelector.tsx
  15. 2 0
      client/src/components/customSelector/Types.ts
  16. 1 3
      client/src/components/customToolTip/CustomToolTip.tsx
  17. 2 0
      client/src/components/icons/Icons.tsx
  18. 1 0
      client/src/components/icons/Types.ts
  19. 3 3
      client/src/components/layout/Header.tsx
  20. 0 152
      client/src/components/layout/Layout.tsx
  21. 1 2
      client/src/components/menu/Types.ts
  22. 2 2
      client/src/consts/Milvus.tsx
  23. 12 1
      client/src/http/Collection.ts
  24. 15 10
      client/src/i18n/cn/button.ts
  25. 25 11
      client/src/i18n/cn/collection.ts
  26. 1 2
      client/src/i18n/cn/common.ts
  27. 2 3
      client/src/i18n/cn/dialog.ts
  28. 3 2
      client/src/i18n/cn/index.ts
  29. 5 0
      client/src/i18n/cn/insert.ts
  30. 3 3
      client/src/i18n/cn/partition.ts
  31. 1 2
      client/src/i18n/cn/search.ts
  32. 2 1
      client/src/i18n/cn/success.ts
  33. 1 1
      client/src/i18n/cn/user.ts
  34. 2 1
      client/src/i18n/cn/warning.ts
  35. 1 0
      client/src/i18n/en/button.ts
  36. 23 9
      client/src/i18n/en/collection.ts
  37. 1 1
      client/src/i18n/en/common.ts
  38. 1 1
      client/src/i18n/en/dialog.ts
  39. 3 3
      client/src/i18n/en/partition.ts
  40. 1 1
      client/src/i18n/en/success.ts
  41. 2 1
      client/src/i18n/en/warning.ts
  42. 4 8
      client/src/index.tsx
  43. 132 0
      client/src/pages/collections/Aliases.tsx
  44. 3 3
      client/src/pages/collections/Collection.tsx
  45. 77 78
      client/src/pages/collections/Collections.tsx
  46. 22 10
      client/src/pages/collections/Create.tsx
  47. 24 11
      client/src/pages/collections/CreateAlias.tsx
  48. 103 77
      client/src/pages/collections/CreateFields.tsx
  49. 8 0
      client/src/pages/collections/Types.ts
  50. 4 4
      client/src/pages/connect/AuthForm.tsx
  51. 11 3
      client/src/pages/connect/Connect.tsx
  52. 1 1
      client/src/pages/connect/ConnectContainer.tsx
  53. 135 0
      client/src/pages/index.tsx
  54. 7 6
      client/src/pages/overview/collectionCard/CollectionCard.tsx
  55. 42 62
      client/src/pages/partitions/Partitions.tsx
  56. 7 9
      client/src/pages/preview/Preview.tsx
  57. 2 6
      client/src/pages/query/Query.tsx
  58. 1 0
      client/src/pages/schema/Create.tsx
  59. 1 0
      client/src/pages/schema/CreateForm.tsx
  60. 1 1
      client/src/pages/schema/IndexTypeElement.tsx
  61. 0 0
      client/src/pages/search/Constants.ts
  62. 5 6
      client/src/pages/search/SearchParams.tsx
  63. 0 0
      client/src/pages/search/Styles.ts
  64. 2 2
      client/src/pages/search/Types.ts
  65. 5 2
      client/src/pages/search/VectorSearch.tsx
  66. 0 0
      client/src/pages/system/BaseCard.tsx
  67. 0 0
      client/src/pages/system/DataCard.tsx
  68. 0 0
      client/src/pages/system/LineChartCard.tsx
  69. 0 0
      client/src/pages/system/MiniTopology.tsx
  70. 0 0
      client/src/pages/system/NodeListView.tsx
  71. 0 0
      client/src/pages/system/Progress.tsx
  72. 0 0
      client/src/pages/system/ProgressCard.tsx
  73. 0 0
      client/src/pages/system/SystemView.tsx
  74. 0 0
      client/src/pages/system/Topology.tsx
  75. 0 0
      client/src/pages/system/Types.ts
  76. 0 13
      client/src/plugins/search/config.json
  77. 0 11
      client/src/plugins/system/config.json
  78. 0 58
      client/src/router/Config.ts
  79. 40 51
      client/src/router/Router.tsx
  80. 0 6
      client/src/router/Types.ts
  81. 5 1
      client/src/utils/Common.ts
  82. 63 126
      client/yarn.lock
  83. 2 2
      server/package.json
  84. 11 0
      server/src/collections/collections.controller.ts
  85. 1 0
      server/src/collections/collections.service.ts
  86. 3 1
      server/src/collections/dto.ts
  87. 4 0
      server/src/utils/Helper.ts
  88. 28 13
      server/yarn.lock

BIN
.github/images/create_collection.png


BIN
.github/images/create_index.png


BIN
.github/images/data_preview.png


BIN
.github/images/query_advanced_filter.png


BIN
.github/images/system_view.png


BIN
.github/images/vector_search.png


+ 3 - 56
.github/workflows/dev.yml

@@ -4,11 +4,13 @@ on:
   pull_request_target:
   pull_request_target:
     branches: [main]
     branches: [main]
     types: [closed]
     types: [closed]
+  push:
+    branches:
+      - main
 
 
 jobs:
 jobs:
   build:
   build:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
-    if: github.event.pull_request.merged == true
     steps:
     steps:
       - uses: actions/checkout@v2
       - uses: actions/checkout@v2
       - name: Setup Node.js
       - name: Setup Node.js
@@ -41,58 +43,3 @@ jobs:
 
 
       - name: Docker Push Dev
       - name: Docker Push Dev
         run: docker push zilliz/attu:dev
         run: docker push zilliz/attu:dev
-
-  k8s:
-    runs-on: ubuntu-latest
-    needs: build
-    steps:
-      - uses: actions/checkout@v2
-      - name: Setup Node.js
-        uses: actions/setup-node@v1
-        with:
-          node-version: 12
-
-      - name: Install OpenVPN and kubectl
-        run: |
-          sudo apt-get update
-          sudo apt-get install openvpn -y
-          sudo apt-get install -y apt-transport-https ca-certificates curl
-          sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
-          echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
-          sudo apt-get update
-          sudo apt-get install kubectl -y
-
-      - name: Connect VPN
-        uses: golfzaptw/action-connect-ovpn@master
-        id: connect_vpn
-        with:
-          FILE_OVPN: ".github/workflows/client.ovpn"
-        env:
-          CA_CRT: ${{ secrets.VPN_CA}}
-          USER_CRT: ${{ secrets.VPN_CRT }}
-          USER_KEY: ${{ secrets.VPN_KEY }}
-
-      - name: Deploy to cluster
-        run: |
-          echo ${{ secrets.kubeconfig }} > config64
-          base64 -d config64 > kubeconfig
-          kubectl delete pods -n ued -l app=attu --kubeconfig=kubeconfig
-          sleep 60
-
-  check:
-    runs-on: ubuntu-latest
-    needs: [build, k8s]
-    steps:
-      - uses: actions/checkout@v2
-      - name: Setup Node.js
-        uses: actions/setup-node@v1
-        with:
-          node-version: 12
-
-      - name: Check insight status
-        env:
-          INSIGHT_URL: ${{ secrets.INSIGHT_URL }}
-        run: |
-          yarn add axios
-          yarn add @actions/core
-          node checkInsight.js

+ 48 - 33
README.md

@@ -4,47 +4,50 @@
 [![downloads](https://img.shields.io/docker/pulls/zilliz/attu)](https://img.shields.io/docker/pulls/zilliz/attu)
 [![downloads](https://img.shields.io/docker/pulls/zilliz/attu)](https://img.shields.io/docker/pulls/zilliz/attu)
 [![codecov](https://codecov.io/gh/zilliztech/attu/branch/main/graph/badge.svg?token=jvIEVF9IwW)](https://codecov.io/gh/zilliztech/attu)
 [![codecov](https://codecov.io/gh/zilliztech/attu/branch/main/graph/badge.svg?token=jvIEVF9IwW)](https://codecov.io/gh/zilliztech/attu)
 
 
-Attu provides an intuitive and efficient GUI for Milvus, allowing you to interact with your databases and manage your data with just few clicks.
+Attu is an all-in-one milvus administration tool. With Attu, you can dramatically reduce the cost of managing milvus.
 
 
-<img src="./.github/images/screenshot.png" alt="attu" />
+<img src="./.github/images/screenshot.png" alt="attu" width="800" alt="attu" />
 
 
-## Attu Doc
-
-- [English](./doc/en.md)
-- [中文](./doc/zh-cn.md)
-
-## Features and Roadmap
-
-Attu is under rapid development and we are adding new features weekly, here are the current plan, we will release a version once a feature is available.
+## Features
 
 
+- Basic dashboard
+  - View basic collection statistics
+  - Quick search from loaded collection
+  - Quick release loaded collection
 - Manage collections/partitions
 - Manage collections/partitions
-- Manage index
-- Basic statistics overview
-- Load/release collections for search
-- Insert entities
-- Vector search with advanced filter
+  - Create collection/partion
+  - Delete collection/partions
+  - View collection schema
+  - Create/drop index with parameters
+  - Load/release collections for search
+- Data Management
+  - Insert entities
+  - Data preview
+  - Data query
+- Vector search/query with advanced filter
 - System view
 - System view
-- Data query
-- View root node configuration
-- Support TLS connection and username,password
-- Vector Visualization(TBD)
-- More...
+  - View milvus nodes system info
+  - View milvus nodes configuration
+- Manage Milvus user
+- More are comming...
 
 
 ## Quick start
 ## Quick start
 
 
-### Before you start
+> If you prefer desktop application, you can download the [desktop version of Attu](https://github.com/zilliztech/attu/releases/).
 
 
-Ensure you have Milvus installed on [your server](https://milvus.io/docs/install_standalone-docker.md) or [cluster](https://milvus.io/docs/install_cluster-docker.md), and attu only supports Milvus 2.x.
+## Run attu from docker
 
 
-### ⭐️ Start a attu instance
+> Ensure you have Milvus installed on [your server](https://milvus.io/docs/install_standalone-docker.md) or [cluster](https://milvus.io/docs/install_cluster-docker.md), and attu only supports Milvus 2.x.
 
 
-**_ Before attu v2.1.0 , [check here](https://github.com/zilliztech/attu/tree/v2.0.5) _**
+> _ Before attu v2.1.0 , [check here](https://github.com/zilliztech/attu/tree/v2.0.5) _\*\*
+
+### ✈️ Start a attu container
 
 
 ```code
 ```code
 docker run -p 8000:3000 -e MILVUS_URL={milvus server ip}:19530 zilliz/attu:latest
 docker run -p 8000:3000 -e MILVUS_URL={milvus server ip}:19530 zilliz/attu:latest
 ```
 ```
 
 
-Once you start the docker, open the browser, type `http://{ attu ip }:8000`, you can view the attu.
+Once you start the container, open the browser, type `http://{ attu ip }:8000`, you can view the attu GUI.
 
 
 #### Params
 #### Params
 
 
@@ -54,15 +57,24 @@ Once you start the docker, open the browser, type `http://{ attu ip }:8000`, you
 
 
 Tip: **127.0.0.1 or localhost will not work when runs on docker**
 Tip: **127.0.0.1 or localhost will not work when runs on docker**
 
 
-#### Try the dev build
+## Try the dev build
 
 
-**_note_** We plan to release attu once a feature is done. Also, if you want to try the nightly build, please pull the docker image with the `dev` tag.
+> We plan to release attu once a feature is done. Also, if you want to try the nightly build, please pull the docker image with the `dev` tag.
 
 
 ```code
 ```code
 docker run -p 8000:3000 -e MILVUS_URL={ your machine IP }:19530 zilliz/attu:dev
 docker run -p 8000:3000 -e MILVUS_URL={ your machine IP }:19530 zilliz/attu:dev
 ```
 ```
 
 
-## ✨ Building and Running attu, and/or Contributing Code
+## Screenshots
+
+<img src="./.github/images/screenshot.png" alt="attu" width="800" alt="attu" />
+<img src="./.github/images/create_collection.png" width="800" alt="attu" />
+<img src="./.github/images/create_index.png" width="800" alt="attu" />
+<img src="./.github/images/data_preview.png" width="800" alt="attu" />
+<img src="./.github/images/query_advanced_filter.png" width="800" alt="attu" />
+<img src="./.github/images/vector_search.png" width="800" alt="attu" />
+
+## ✨ Contributing Code
 
 
 You might want to build Attu locally to contribute some code, test out the latest features, or try
 You might want to build Attu locally to contribute some code, test out the latest features, or try
 out an open PR:
 out an open PR:
@@ -83,20 +95,23 @@ out an open PR:
 
 
 ### Milvus
 ### Milvus
 
 
-New to milvus? Milvus is an open-source vector database built to power AI applications and embedding similarity search.
+New to milvus? [Milvus](https://milvus.io) is an open-source vector database built to power AI applications and embedding similarity search.
 
 
 ### Userful links
 ### Userful links
 
 
-- [Milvus installation guide](https://milvus.io/docs/v2.0.0/install_standalone-docker.md)
-- [Milvus python sdk](https://milvus.io/docs/v2.0.0/explore_pymilvus.md)
-- [Milvus bootcamp](https://milvus.io/bootcamp)
+- [Milvus docs](https://milvus.io/docs)
+- [Milvus python sdk](https://github.com/milvus-io/pymilvus)
+- [Milvus java sdk](https://github.com/milvus-io/milvus-sdk-java)
+- [Milvus gp sdk](https://github.com/milvus-io/milvus-sdk-go)
+- [Milvus node sdk](https://github.com/milvus-io/milvus-sdk-node)
+- [Feder](https://github.com/zilliztech/feder)
 
 
 #### ❓ Questions? Problems?
 #### ❓ Questions? Problems?
 
 
 - If you've found a bug or want to request a feature, please create a [GitHub Issue](https://github.com/zilliztech/attu/issues/new/choose).
 - If you've found a bug or want to request a feature, please create a [GitHub Issue](https://github.com/zilliztech/attu/issues/new/choose).
   Please check to make sure someone else hasn't already created an issue for the same topic.
   Please check to make sure someone else hasn't already created an issue for the same topic.
 
 
-[milvus-doc]: https://milvus.io/docs/home
+[milvus-doc]: https://milvus.io/docs
 
 
 ## Community
 ## Community
 
 

+ 6 - 6
client/package.json

@@ -18,11 +18,11 @@
     "file-saver": "^2.0.5",
     "file-saver": "^2.0.5",
     "i18next": "^20.3.1",
     "i18next": "^20.3.1",
     "papaparse": "^5.3.1",
     "papaparse": "^5.3.1",
-    "react": "^17.0.2",
-    "react-dom": "^17.0.2",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
     "react-highlight-words": "^0.17.0",
     "react-highlight-words": "^0.17.0",
     "react-i18next": "^12.0.0",
     "react-i18next": "^12.0.0",
-    "react-router-dom": "^5.2.0",
+    "react-router-dom": "^6.4.3",
     "react-syntax-highlighter": "^15.4.4",
     "react-syntax-highlighter": "^15.4.4",
     "socket.io-client": "^4.1.3",
     "socket.io-client": "^4.1.3",
     "typescript": "^4.1.2",
     "typescript": "^4.1.2",
@@ -41,10 +41,10 @@
     "@types/loadable__component": "^5.13.4",
     "@types/loadable__component": "^5.13.4",
     "@types/node": "^12.0.0",
     "@types/node": "^12.0.0",
     "@types/papaparse": "^5.2.6",
     "@types/papaparse": "^5.2.6",
-    "@types/react": "^17.0.0",
-    "@types/react-dom": "^17.0.0",
+    "@types/react": "^18.0.25",
+    "@types/react-dom": "^18.0.8",
     "@types/react-highlight-words": "^0.16.2",
     "@types/react-highlight-words": "^0.16.2",
-    "@types/react-router-dom": "^5.1.7",
+    "@types/react-router-dom": "^5.3.3",
     "@types/react-syntax-highlighter": "^13.5.2",
     "@types/react-syntax-highlighter": "^13.5.2",
     "@types/webpack-env": "^1.16.3",
     "@types/webpack-env": "^1.16.3",
     "@vitest/coverage-c8": "^0.25.0",
     "@vitest/coverage-c8": "^0.25.0",

+ 5 - 1
client/src/components/customDialog/DialogTemplate.tsx

@@ -54,6 +54,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   // needed for code mode
   // needed for code mode
   showCode = false,
   showCode = false,
   codeBlocksData = [],
   codeBlocksData = [],
+  dialogClass = '',
 }) => {
 }) => {
   const { t } = useTranslation('btn');
   const { t } = useTranslation('btn');
   const cancel = cancelLabel || t('cancel');
   const cancel = cancelLabel || t('cancel');
@@ -77,7 +78,10 @@ const DialogTemplate: FC<DialogContainerProps> = ({
 
 
   return (
   return (
     <section className={classes.wrapper}>
     <section className={classes.wrapper}>
-      <div ref={dialogRef} className={`${classes.dialog} ${classes.block}`}>
+      <div
+        ref={dialogRef}
+        className={`${classes.dialog} ${classes.block} ${dialogClass}`}
+      >
         <CustomDialogTitle onClose={handleClose} showCloseIcon={showCloseIcon}>
         <CustomDialogTitle onClose={handleClose} showCloseIcon={showCloseIcon}>
           {title}
           {title}
         </CustomDialogTitle>
         </CustomDialogTitle>

+ 2 - 0
client/src/components/customDialog/Types.ts

@@ -36,4 +36,6 @@ export type DialogContainerProps = {
   // code mode requirement
   // code mode requirement
   showCode?: boolean;
   showCode?: boolean;
   codeBlocksData?: CodeViewData[];
   codeBlocksData?: CodeViewData[];
+  children: ReactElement;
+  dialogClass?: string;
 };
 };

+ 11 - 53
client/src/components/customInput/SearchInput.tsx

@@ -1,7 +1,7 @@
 import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
 import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
 import { useRef, FC, useState, useEffect, useMemo } from 'react';
 import { useRef, FC, useState, useEffect, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
-import { useHistory } from 'react-router-dom';
+import { useSearchParams } from 'react-router-dom';
 import Icons from '../icons/Icons';
 import Icons from '../icons/Icons';
 import { SearchType } from './Types';
 import { SearchType } from './Types';
 
 
@@ -75,64 +75,24 @@ const useSearchStyles = makeStyles(theme => ({
   },
   },
 }));
 }));
 
 
-let timer: NodeJS.Timeout | null = null;
-
 const SearchInput: FC<SearchType> = props => {
 const SearchInput: FC<SearchType> = props => {
   const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
   const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
-  const [searchValue, setSearchValue] = useState<string | null>(
-    searchText || null
-  );
-
-  const [isInit, setIsInit] = useState<boolean>(true);
-
-  const searched = useMemo(
-    () => searchValue !== '' && searchValue !== null,
-    [searchValue]
-  );
-
+  const [searchParams, setSearchParams] = useSearchParams();
+  const [searchValue, setSearchValue] = useState<string>(searchText || '');
+  const searched = useMemo(() => searchValue !== '', [searchValue]);
   const classes = useSearchStyles({ searched });
   const classes = useSearchStyles({ searched });
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
-
-  const history = useHistory();
-
   const inputRef = useRef<any>(null);
   const inputRef = useRef<any>(null);
 
 
-  const savedSearchFn = useRef<(value: string) => void>(() => {});
-  useEffect(() => {
-    savedSearchFn.current = onSearch;
-  }, [onSearch]);
+  const handleSearch = (value: string) => {
+    onSearch(value);
+  };
 
 
   useEffect(() => {
   useEffect(() => {
-    if (timer) {
-      clearTimeout(timer);
-    }
-    if (searchValue !== null && !isInit) {
-      timer = setTimeout(() => {
-        // save other params data and remove last time search info
-        const location = history.location;
-        const params = new URLSearchParams(location.search);
-        params.delete('search');
-
-        if (searchValue) {
-          params.append('search', searchValue);
-        }
-        // add search value in url
-        history.push({ search: params.toString() });
-
-        savedSearchFn.current(searchValue);
-      }, 300);
-    }
-
-    return () => {
-      timer && clearTimeout(timer);
-    };
-  }, [searchValue, history, isInit]);
-
-  const handleSearch = (value: string | null) => {
-    if (value !== null) {
-      onSearch(value);
-    }
-  };
+    searchParams[searchValue ? 'set' : 'delete']('search', searchValue);
+    setSearchParams(searchParams);
+    handleSearch(searchValue);
+  }, [searchValue]);
 
 
   return (
   return (
     <div className={classes.wrapper}>
     <div className={classes.wrapper}>
@@ -150,7 +110,6 @@ const SearchInput: FC<SearchType> = props => {
                 className={`flex-center ${classes.iconWrapper}`}
                 className={`flex-center ${classes.iconWrapper}`}
                 onClick={e => {
                 onClick={e => {
                   setSearchValue('');
                   setSearchValue('');
-                  setIsInit(false);
                   inputRef.current.focus();
                   inputRef.current.focus();
                   onClear();
                   onClear();
                 }}
                 }}
@@ -173,7 +132,6 @@ const SearchInput: FC<SearchType> = props => {
         onChange={e => {
         onChange={e => {
           const value = e.target.value.trim();
           const value = e.target.value.trim();
           setSearchValue(value);
           setSearchValue(value);
-          setIsInit(false);
           if (value === '') {
           if (value === '') {
             onClear();
             onClear();
           }
           }

+ 2 - 0
client/src/components/customInput/Types.ts

@@ -1,4 +1,5 @@
 import { ReactElement } from 'react';
 import { ReactElement } from 'react';
+import { InputLabelProps } from '@material-ui/core';
 import { IValidationItem } from '../../hooks/Form';
 import { IValidationItem } from '../../hooks/Form';
 import { IExtraParam, ValidType } from '../../utils/Validation';
 import { IExtraParam, ValidType } from '../../utils/Validation';
 
 
@@ -78,6 +79,7 @@ export interface ITextfieldConfig {
   type?: string;
   type?: string;
   onBlur?: (event: any) => void;
   onBlur?: (event: any) => void;
   onChange?: (event: any) => void;
   onChange?: (event: any) => void;
+  InputLabelProps?: Partial<InputLabelProps>;
 }
 }
 
 
 export interface IAdornmentConfig {
 export interface IAdornmentConfig {

+ 2 - 1
client/src/components/customSelector/CustomSelector.tsx

@@ -16,12 +16,13 @@ const CustomSelector: FC<CustomSelectorType> = props => {
     variant,
     variant,
     wrapperClass = '',
     wrapperClass = '',
     labelClass = '',
     labelClass = '',
+    size = 'medium',
     ...others
     ...others
   } = props;
   } = props;
   const id = generateId('selector');
   const id = generateId('selector');
 
 
   return (
   return (
-    <FormControl variant={variant} className={wrapperClass}>
+    <FormControl variant={variant} className={wrapperClass} size={size}>
       {label && (
       {label && (
         <InputLabel classes={{ root: labelClass }} htmlFor={id}>
         <InputLabel classes={{ root: labelClass }} htmlFor={id}>
           {label}
           {label}

+ 2 - 0
client/src/components/customSelector/Types.ts

@@ -20,6 +20,8 @@ export type CustomSelectorType = SelectProps & {
   variant?: 'filled' | 'outlined' | 'standard';
   variant?: 'filled' | 'outlined' | 'standard';
   labelClass?: string;
   labelClass?: string;
   wrapperClass?: string;
   wrapperClass?: string;
+  hiddenLabel?: boolean;
+  size?: 'small' | 'medium' | undefined;
 };
 };
 
 
 export interface ICustomGroupSelect {
 export interface ICustomGroupSelect {

+ 1 - 3
client/src/components/customToolTip/CustomToolTip.tsx

@@ -5,9 +5,7 @@ import { makeStyles, Theme, createStyles } from '@material-ui/core';
 
 
 const useStyles = makeStyles((theme: Theme) =>
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
   createStyles({
-    tooltip: {
-      textTransform: 'capitalize',
-    },
+    tooltip: {},
   })
   })
 );
 );
 
 

+ 2 - 0
client/src/components/icons/Icons.tsx

@@ -2,6 +2,7 @@ import React from 'react';
 import { IconsType } from './Types';
 import { IconsType } from './Types';
 import SearchIcon from '@material-ui/icons/Search';
 import SearchIcon from '@material-ui/icons/Search';
 import AddIcon from '@material-ui/icons/Add';
 import AddIcon from '@material-ui/icons/Add';
+import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutlineOutlined';
 import DeleteIcon from '@material-ui/icons/Delete';
 import DeleteIcon from '@material-ui/icons/Delete';
 import FileCopyIcon from '@material-ui/icons/FileCopy';
 import FileCopyIcon from '@material-ui/icons/FileCopy';
 import Visibility from '@material-ui/icons/Visibility';
 import Visibility from '@material-ui/icons/Visibility';
@@ -44,6 +45,7 @@ import { ReactComponent as SystemIcon } from '../../assets/icons/system.svg';
 const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
 const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
   search: (props = {}) => <SearchIcon {...props} />,
   search: (props = {}) => <SearchIcon {...props} />,
   add: (props = {}) => <AddIcon {...props} />,
   add: (props = {}) => <AddIcon {...props} />,
+  addOutline:  (props = {}) => <AddCircleOutlineIcon {...props} />,
   delete: (props = {}) => <DeleteIcon {...props} />,
   delete: (props = {}) => <DeleteIcon {...props} />,
   list: (props = {}) => <ReorderIcon {...props} />,
   list: (props = {}) => <ReorderIcon {...props} />,
   copy: (props = {}) => <FileCopyIcon {...props} />,
   copy: (props = {}) => <FileCopyIcon {...props} />,

+ 1 - 0
client/src/components/icons/Types.ts

@@ -1,6 +1,7 @@
 export type IconsType =
 export type IconsType =
   | 'search'
   | 'search'
   | 'add'
   | 'add'
+  | 'addOutline'
   | 'delete'
   | 'delete'
   | 'list'
   | 'list'
   | 'copy'
   | 'copy'

+ 3 - 3
client/src/components/layout/Header.tsx

@@ -3,7 +3,7 @@ import { makeStyles, Theme, createStyles, Typography } from '@material-ui/core';
 import { HeaderType } from './Types';
 import { HeaderType } from './Types';
 import { navContext } from '../../context/Navigation';
 import { navContext } from '../../context/Navigation';
 import icons from '../icons/Icons';
 import icons from '../icons/Icons';
-import { useHistory } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
 import { authContext } from '../../context/Auth';
 import { authContext } from '../../context/Auth';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { MILVUS_ADDRESS } from '../../consts/Localstorage';
 import { MILVUS_ADDRESS } from '../../consts/Localstorage';
@@ -60,14 +60,14 @@ const Header: FC<HeaderType> = props => {
   const classes = useStyles();
   const classes = useStyles();
   const { navInfo } = useContext(navContext);
   const { navInfo } = useContext(navContext);
   const { address, setAddress, setIsAuth } = useContext(authContext);
   const { address, setAddress, setIsAuth } = useContext(authContext);
-  const history = useHistory();
+  const navigate = useNavigate();
   const { t: commonTrans } = useTranslation();
   const { t: commonTrans } = useTranslation();
   const statusTrans = commonTrans('status');
   const statusTrans = commonTrans('status');
   const BackIcon = icons.back;
   const BackIcon = icons.back;
   const LogoutIcon = icons.logout;
   const LogoutIcon = icons.logout;
 
 
   const handleBack = (path: string) => {
   const handleBack = (path: string) => {
-    history.push(path);
+    navigate(path);
   };
   };
 
 
   const handleLogout = () => {
   const handleLogout = () => {

+ 0 - 152
client/src/components/layout/Layout.tsx

@@ -1,152 +0,0 @@
-import GlobalEffect from './GlobalEffect';
-import Header from './Header';
-import { makeStyles, Theme, createStyles } from '@material-ui/core';
-import NavMenu from '../menu/NavMenu';
-import { NavMenuItem } from '../menu/Types';
-import { useContext, useMemo } from 'react';
-import icons from '../icons/Icons';
-import { useTranslation } from 'react-i18next';
-import { useHistory, useLocation } from 'react-router-dom';
-import { authContext } from '../../context/Auth';
-import { rootContext } from '../../context/Root';
-import { IconsType } from '../icons/Types';
-
-const useStyles = makeStyles((theme: Theme) =>
-  createStyles({
-    root: {
-      minHeight: '100vh',
-      backgroundColor: '#f5f5f5',
-    },
-    content: {
-      display: 'flex',
-
-      '& .normalSearchIcon': {
-        '& path': {
-          fill: theme.palette.attuGrey.dark,
-        },
-      },
-
-      '& .activeSearchIcon': {
-        '& path': {
-          fill: theme.palette.primary.main,
-        },
-      },
-    },
-    body: {
-      flex: 1,
-      display: 'flex',
-      flexDirection: 'column',
-      height: '100vh',
-      overflowY: 'scroll',
-    },
-  })
-);
-
-const Layout = (props: any) => {
-  const history = useHistory();
-  const { isAuth } = useContext(authContext);
-  const { versionInfo } = useContext(rootContext);
-
-  const { t: navTrans } = useTranslation('nav');
-  const classes = useStyles();
-  const location = useLocation();
-  const defaultActive = useMemo(() => {
-    if (location.pathname.includes('collection')) {
-      return navTrans('collection');
-    }
-
-    if (location.pathname.includes('search')) {
-      return navTrans('search');
-    }
-
-    if (location.pathname.includes('system')) {
-      return navTrans('system');
-    }
-
-    if (location.pathname.includes('users')) {
-      return navTrans('user');
-    }
-
-    return navTrans('overview');
-  }, [location, navTrans]);
-
-  const menuItems: NavMenuItem[] = [
-    {
-      icon: icons.navOverview,
-      label: navTrans('overview'),
-      onClick: () => history.push('/'),
-    },
-    {
-      icon: icons.navCollection,
-      label: navTrans('collection'),
-      onClick: () => history.push('/collections'),
-    },
-    {
-      icon: icons.navPerson,
-      label: navTrans('user'),
-      onClick: () => history.push('/users'),
-    },
-    // {
-    //   icon: icons.navSearch,
-    //   label: navTrans('search'),
-    //   onClick: () => history.push('/search'),
-    //   iconActiveClass: 'activeSearchIcon',
-    //   iconNormalClass: 'normalSearchIcon',
-    // },
-  ];
-
-  function importAll(r: any) {
-    Object.keys(r).forEach((key: any) => {
-      const content = r[key];
-      const pathName = content.client?.path;
-
-      if (!pathName) return;
-      const result: NavMenuItem = {
-        icon: icons.navOverview,
-        label: content.client?.label || 'PLGUIN',
-      };
-      result.onClick = () => history.push(`/${pathName}`);
-      const iconName: IconsType = content.client?.iconName;
-      if (iconName) {
-        result.icon = icons[iconName];
-      }
-      content.client?.iconActiveClass &&
-        (result.iconActiveClass = content.client?.iconActiveClass);
-      content.client?.iconNormalClass &&
-        (result.iconNormalClass = content.client?.iconNormalClass);
-
-      menuItems.push(result);
-    });
-  }
-  const pluginConfigs = import.meta.glob(`../../plugins/**/config.json`, {
-    eager: true,
-  });
-
-  importAll(pluginConfigs);
-
-  return (
-    <div className={classes.root}>
-      <GlobalEffect>
-        <div className={classes.content}>
-          {isAuth && (
-            <NavMenu
-              width="200px"
-              data={menuItems}
-              defaultActive={defaultActive}
-              // used for nested child menu
-              defaultOpen={{ [navTrans('overview')]: true }}
-              versionInfo={versionInfo}
-            />
-          )}
-
-          <div className={classes.body}>
-            {isAuth && <Header />}
-            {props.children}
-          </div>
-        </div>
-      </GlobalEffect>
-    </div>
-  );
-};
-
-export default Layout;

+ 1 - 2
client/src/components/menu/Types.ts

@@ -1,6 +1,5 @@
 import { ButtonProps } from '@material-ui/core/Button';
 import { ButtonProps } from '@material-ui/core/Button';
 import { ReactElement } from 'react';
 import { ReactElement } from 'react';
-import { LoadableClassComponent } from '@loadable/component';
 
 
 export type SimpleMenuType = {
 export type SimpleMenuType = {
   label: string;
   label: string;
@@ -20,7 +19,7 @@ type CustomIcon = (
 ) => React.ReactElement<any, string | React.JSXElementConstructor<any>>;
 ) => React.ReactElement<any, string | React.JSXElementConstructor<any>>;
 
 
 export type NavMenuItem = {
 export type NavMenuItem = {
-  icon: CustomIcon | LoadableClassComponent<any>;
+  icon: CustomIcon;
   iconActiveClass?: string;
   iconActiveClass?: string;
   iconNormalClass?: string;
   iconNormalClass?: string;
   label: string;
   label: string;

+ 2 - 2
client/src/consts/Milvus.tsx

@@ -86,7 +86,7 @@ export const FLOAT_INDEX_CONFIG: indexConfigType = {
   //   search: ['nprobe'],
   //   search: ['nprobe'],
   // },
   // },
   FLAT: {
   FLAT: {
-    create: [''],
+    create: [],
     search: ['nprobe'],
     search: ['nprobe'],
   },
   },
   HNSW: {
   HNSW: {
@@ -107,7 +107,7 @@ export const BINARY_INDEX_CONFIG: indexConfigType = {
   // },
   // },
   BIN_FLAT: {
   BIN_FLAT: {
     create: ['nlist'],
     create: ['nlist'],
-    search: ['nprobe'],
+    search: [],
   },
   },
   BIN_IVF_FLAT: {
   BIN_IVF_FLAT: {
     create: ['nlist'],
     create: ['nlist'],

+ 12 - 1
client/src/http/Collection.ts

@@ -3,7 +3,7 @@ import {
   CollectionView,
   CollectionView,
   DeleteEntitiesReq,
   DeleteEntitiesReq,
   InsertDataParam,
   InsertDataParam,
-  LoadSampleParam
+  LoadSampleParam,
 } from '../pages/collections/Types';
 } from '../pages/collections/Types';
 import { Field } from '../pages/schema/Types';
 import { Field } from '../pages/schema/Types';
 import { VectorSearchParam } from '../types/SearchTypes';
 import { VectorSearchParam } from '../types/SearchTypes';
@@ -16,6 +16,7 @@ import dayjs from 'dayjs';
 import { LOADING_STATE } from '../consts/Milvus';
 import { LOADING_STATE } from '../consts/Milvus';
 
 
 export class CollectionHttp extends BaseModel implements CollectionView {
 export class CollectionHttp extends BaseModel implements CollectionView {
+  private aliases!: string[];
   private autoID!: string;
   private autoID!: string;
   private collection_name!: string;
   private collection_name!: string;
   private description!: string;
   private description!: string;
@@ -117,6 +118,12 @@ export class CollectionHttp extends BaseModel implements CollectionView {
     });
     });
   }
   }
 
 
+  static dropAlias(collectionName: string, params: { alias: string }) {
+    return super.delete({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/alias/${params.alias}`,
+    });
+  }
+
   static queryData(collectionName: string, params: QueryParam) {
   static queryData(collectionName: string, params: QueryParam) {
     return super.query({
     return super.query({
       path: `${this.COLLECTIONS_URL}/${collectionName}/query`,
       path: `${this.COLLECTIONS_URL}/${collectionName}/query`,
@@ -128,6 +135,10 @@ export class CollectionHttp extends BaseModel implements CollectionView {
     return this.autoID;
     return this.autoID;
   }
   }
 
 
+  get _aliases() {
+    return this.aliases;
+  }
+
   get _desc() {
   get _desc() {
     return this.description || '--';
     return this.description || '--';
   }
   }

+ 15 - 10
client/src/i18n/cn/button.ts

@@ -1,22 +1,27 @@
 const btnTrans = {
 const btnTrans = {
-  cancel: '取消',
-  save: '保存',
-  reset: '重置',
-  update: '更新',
-  search: '搜索',
-  confirm: '确认',
-  connect: '连接',
-  import: '导入',
-  delete: '删除',
-  release: 'Release',
+  cancel: 'Cancel',
+  save: 'Save',
   create: 'Create',
   create: 'Create',
+  reset: 'Reset',
+  update: 'Update',
+  search: 'Search',
+  confirm: 'Confirm',
+  connect: 'Connect',
+  import: 'Import',
+  delete: 'Delete',
+  drop: 'Drop',
+  release: 'Release',
   load: 'Load',
   load: 'Load',
   insert: 'Import Data',
   insert: 'Import Data',
+  refresh: 'Refresh',
   next: 'Next',
   next: 'Next',
   previous: 'Previous',
   previous: 'Previous',
   done: 'Done',
   done: 'Done',
   vectorSearch: 'Vector search',
   vectorSearch: 'Vector search',
   query: 'Query',
   query: 'Query',
+  importSampleData: 'Import Sample data',
+  loading: 'Loading...',
+  importing: 'Importing...'
 };
 };
 
 
 export default btnTrans;
 export default btnTrans;

+ 25 - 11
client/src/i18n/cn/collection.ts

@@ -2,13 +2,12 @@ const collectionTrans = {
   noLoadData: 'No Loaded Collection',
   noLoadData: 'No Loaded Collection',
   noData: 'No Collection',
   noData: 'No Collection',
 
 
-  rowCount: 'Entity Count',
-  tooltip: 'data in one entity',
+  rowCount: 'Approx Entity Count',
 
 
   create: 'Create Collection',
   create: 'Create Collection',
   delete: 'delete',
   delete: 'delete',
   deleteTooltip: 'Please select at least one item to delete.',
   deleteTooltip: 'Please select at least one item to delete.',
-  alias: 'alias',
+  alias: 'Alias',
   aliasTooltip: 'Please select one collection to create alias',
   aliasTooltip: 'Please select one collection to create alias',
   download: 'Download',
   download: 'Download',
   downloadTooltip: 'Download all query results',
   downloadTooltip: 'Download all query results',
@@ -19,29 +18,40 @@ const collectionTrans = {
   // table
   // table
   id: 'ID',
   id: 'ID',
   name: 'Name',
   name: 'Name',
+  nameTip: 'Collection Name',
   status: 'Status',
   status: 'Status',
   desc: 'Description',
   desc: 'Description',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
   maxLength: 'Max Length',
   maxLength: 'Max Length',
 
 
+  // table tooltip
+  aliasInfo: 'Alias can be used as collection name in vector search.',
+  consistencyLevelInfo:
+    'Consistency refers to the property that ensures every node or replica has the same view of data when writing or reading data at a given time.',
+  entityCountInfo: 'Approximately entity count.',
+
   // create dialog
   // create dialog
   createTitle: 'Create Collection',
   createTitle: 'Create Collection',
-  general: '1. General Info',
-  schema: '2. Define Schema',
-  consistency: '3. Consistency Level',
+  general: 'General information',
+  schema: 'Schema',
+  consistency: 'Consistency Level',
   consistencyLevel: 'Consistency Level',
   consistencyLevel: 'Consistency Level',
-  description: 'Description (Optional)',
+  description: 'Description',
   fieldType: 'Field Type',
   fieldType: 'Field Type',
   vectorFieldType: 'Vector Field Type',
   vectorFieldType: 'Vector Field Type',
   fieldName: 'Field Name',
   fieldName: 'Field Name',
+  idFieldName: 'ID Name',
+  vectorFieldName: 'Vector Name',
   autoId: 'Auto ID',
   autoId: 'Auto ID',
+  vectorType: 'Vector Type',
+  idType: 'ID Type',
   dimension: 'Dimension',
   dimension: 'Dimension',
   dimensionTooltip: 'Only vector type has dimension',
   dimensionTooltip: 'Only vector type has dimension',
   dimensionMutipleWarning: 'Dimension should be 8 multiple',
   dimensionMutipleWarning: 'Dimension should be 8 multiple',
-  dimensionPositiveWarning: 'Dimension should be positive number',
+  dimensionPositiveWarning: 'Positive number only',
   newBtn: 'add new field',
   newBtn: 'add new field',
   nameLengthWarning: 'Name length should be less than 256',
   nameLengthWarning: 'Name length should be less than 256',
-  nameContentWarning: 'Name can only contain numbers, letters, and underscores',
+  nameContentWarning: 'Only numbers, letters, and underscores are allowed.',
   nameFirstLetterWarning:
   nameFirstLetterWarning:
     'Name first character must be underscore or character(a~z, A~Z)',
     'Name first character must be underscore or character(a~z, A~Z)',
 
 
@@ -58,16 +68,20 @@ const collectionTrans = {
   releaseConfirmLabel: 'Release',
   releaseConfirmLabel: 'Release',
 
 
   // delete dialog
   // delete dialog
-  deleteWarning:
-    'You are trying to delete a collection with data. This action cannot be undone.',
+  deleteWarning: `You are trying to delete a collection with data. This action cannot be undone.`,
   deleteDataWarning: `You are trying to delete entites. This action cannot be undone.`,
   deleteDataWarning: `You are trying to delete entites. This action cannot be undone.`,
+  deleteAliasWarning: `You are trying to drop an alias. This action cannot be undone.`,
 
 
   // collection tabs
   // collection tabs
   partitionTab: 'Partitions',
   partitionTab: 'Partitions',
   schemaTab: 'Schema',
   schemaTab: 'Schema',
   queryTab: 'Data Query',
   queryTab: 'Data Query',
+  previewTab: 'Data Preview',
   startTip: 'Start your data query',
   startTip: 'Start your data query',
   exprPlaceHolder: 'Please enter your query by using advanced filter ->',
   exprPlaceHolder: 'Please enter your query by using advanced filter ->',
+
+  // alias dialog
+  aliasCreatePlaceholder: 'Alias name',
 };
 };
 
 
 export default collectionTrans;
 export default collectionTrans;

+ 1 - 2
client/src/i18n/cn/common.ts

@@ -8,7 +8,7 @@ const commonTrans = {
     ssl: 'SSL',
     ssl: 'SSL',
   },
   },
   status: {
   status: {
-    loaded: 'loaded for search',
+    loaded: 'loaded',
     unloaded: 'unloaded',
     unloaded: 'unloaded',
     error: 'error',
     error: 'error',
     running: 'running',
     running: 'running',
@@ -42,7 +42,6 @@ const commonTrans = {
     join: 'Join our growing social community today',
     join: 'Join our growing social community today',
     get: 'Get insight, tips and share ideas',
     get: 'Get insight, tips and share ideas',
   },
   },
-
   capacity: {
   capacity: {
     b: 'B',
     b: 'B',
     kb: 'KB',
     kb: 'KB',

+ 2 - 3
client/src/i18n/cn/dialog.ts

@@ -1,10 +1,9 @@
 const dialogTrans = {
 const dialogTrans = {
   deleteTipAction: 'Type',
   deleteTipAction: 'Type',
   deleteTipPurpose: 'to confirm.',
   deleteTipPurpose: 'to confirm.',
-  deleteTitle: `Delete {{type}}`,
-  createAlias: `Create alias for {{type}}`,
-
+  deleteTitle: `Drop {{type}}`,
   releaseTitle: `Release {{type}}`,
   releaseTitle: `Release {{type}}`,
+  createAlias: `Create alias for {{type}}`,
   loadTitle: `Load {{type}}`,
   loadTitle: `Load {{type}}`,
 
 
   loadContent: `You are trying to load a {{type}} with data. Only loaded {{type}} can be searched.`,
   loadContent: `You are trying to load a {{type}} with data. Only loaded {{type}} can be searched.`,

+ 3 - 2
client/src/i18n/cn/index.ts

@@ -1,13 +1,14 @@
 const indexTrans = {
 const indexTrans = {
   type: 'Index Type',
   type: 'Index Type',
   param: 'Index Parameters',
   param: 'Index Parameters',
-  create: 'Create Index',
 
 
+  create: 'Create Index',
   index: 'Index',
   index: 'Index',
-  metric: 'Metric Type',
   desc: 'Description',
   desc: 'Description',
+
   creating: 'Creating Index',
   creating: 'Creating Index',
 
 
+  metric: 'Metric Type',
   createSuccess: 'Start creating index',
   createSuccess: 'Start creating index',
   deleteWarning:
   deleteWarning:
     'You are trying to delete an index. This action cannot be undone.',
     'You are trying to delete an index. This action cannot be undone.',

+ 5 - 0
client/src/i18n/cn/insert.ts

@@ -19,6 +19,7 @@ const insertTrans = {
     'Uploaded data column count is not equal to schema count',
     'Uploaded data column count is not equal to schema count',
   uploadAutoIdFieldWarning:
   uploadAutoIdFieldWarning:
     'AutoId field ({{fieldName}}) does not require data',
     'AutoId field ({{fieldName}}) does not require data',
+
   previewTipData: 'Data Preview(Top 4 rows shown)',
   previewTipData: 'Data Preview(Top 4 rows shown)',
   previewTipAction: '*Change header cell selector value to edit field name',
   previewTipAction: '*Change header cell selector value to edit field name',
   requiredFieldName: 'Field Name*',
   requiredFieldName: 'Field Name*',
@@ -27,6 +28,10 @@ const insertTrans = {
   statusLoadingTip: 'Please wait patiently, thank you',
   statusLoadingTip: 'Please wait patiently, thank you',
   statusSuccess: 'Import Data Successfully!',
   statusSuccess: 'Import Data Successfully!',
   statusError: 'Import Data Failed!',
   statusError: 'Import Data Failed!',
+
+  importSampleData: 'Import sample data into {{collection}}',
+  sampleDataSize: 'Choose sample data size',
+  importSampleDataDesc: `Import random data based on the collection's schema.`
 };
 };
 
 
 export default insertTrans;
 export default insertTrans;

+ 3 - 3
client/src/i18n/cn/partition.ts

@@ -8,15 +8,15 @@ const partitionTrans = {
   name: 'Name',
   name: 'Name',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
   status: 'Status',
   status: 'Status',
-  rowCount: 'Entity Count',
-  tooltip: 'data in one entity',
+  rowCount: 'Approx Entity Count',
+  tooltip: 'Approximately entity count.',
 
 
   createTitle: 'Create Partition',
   createTitle: 'Create Partition',
   nameWarning: '_default is reserved, cannot be used as name',
   nameWarning: '_default is reserved, cannot be used as name',
 
 
   deleteWarning:
   deleteWarning:
     'You are trying to delete partition. This action cannot be undone.',
     'You are trying to delete partition. This action cannot be undone.',
-  deletePartitionError: 'default partition cannot be deleted',
+  deletePartitionError: 'default partition cannot be dropped',
 };
 };
 
 
 export default partitionTrans;
 export default partitionTrans;

+ 1 - 2
client/src/i18n/cn/search.ts

@@ -11,8 +11,7 @@ const searchTrans = {
   result: 'Search Results',
   result: 'Search Results',
   topK: 'TopK {{number}}',
   topK: 'TopK {{number}}',
   filter: 'Advanced Filter',
   filter: 'Advanced Filter',
-  vectorValueWarning:
-    'Vector value should be an array of length {{dimension}}(dimension)',
+  vectorValueWarning: 'Vector value should be an array of length {{dimension}}',
   timeTravel: 'Time Travel',
   timeTravel: 'Time Travel',
   timeTravelPrefix: 'Data before',
   timeTravelPrefix: 'Data before',
 };
 };

+ 2 - 1
client/src/i18n/cn/success.ts

@@ -2,8 +2,9 @@ const successTrans = {
   connect: 'Connection to milvus successful',
   connect: 'Connection to milvus successful',
   create: `{{name}} has been created`,
   create: `{{name}} has been created`,
   load: `{{name}} has been loaded`,
   load: `{{name}} has been loaded`,
-  delete: `Delete {{name}} successfully`,
+  delete: `{{name}} successfully dropped`,
   release: `{{name}} has been released`,
   release: `{{name}} has been released`,
+  update: `{{name}} has been updated`,
 };
 };
 
 
 export default successTrans;
 export default successTrans;

+ 1 - 1
client/src/i18n/cn/user.ts

@@ -7,7 +7,7 @@ const userTrans = {
   newPassword: 'New Password',
   newPassword: 'New Password',
   confirmPassword: 'Confirm Password',
   confirmPassword: 'Confirm Password',
   update: 'Update password',
   update: 'Update password',
-  isNotSame: 'Confirm password is not same as new password',
+  isNotSame: 'Not same as new password',
   deleteTip:
   deleteTip:
     'Please select at least one item to delete and root can not be deleted.',
     'Please select at least one item to delete and root can not be deleted.',
 };
 };

+ 2 - 1
client/src/i18n/cn/warning.ts

@@ -1,8 +1,9 @@
 const warningTrans = {
 const warningTrans = {
   required: '{{name}} is required',
   required: '{{name}} is required',
+  requiredOnly: 'Required',
   positive: '{{name}} should be positive',
   positive: '{{name}} should be positive',
   integer: '{{name}} should be integers',
   integer: '{{name}} should be integers',
-  range: 'range is {{min}} ~ {{max}}',
+  range: 'Range is {{min}} ~ {{max}}',
   specValueOrRange:
   specValueOrRange:
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
   noSupportIndexType:
   noSupportIndexType:

+ 1 - 0
client/src/i18n/en/button.ts

@@ -9,6 +9,7 @@ const btnTrans = {
   connect: 'Connect',
   connect: 'Connect',
   import: 'Import',
   import: 'Import',
   delete: 'Delete',
   delete: 'Delete',
+  drop: 'Drop',
   release: 'Release',
   release: 'Release',
   load: 'Load',
   load: 'Load',
   insert: 'Import Data',
   insert: 'Import Data',

+ 23 - 9
client/src/i18n/en/collection.ts

@@ -2,13 +2,12 @@ const collectionTrans = {
   noLoadData: 'No Loaded Collection',
   noLoadData: 'No Loaded Collection',
   noData: 'No Collection',
   noData: 'No Collection',
 
 
-  rowCount: 'Entity Count',
-  tooltip: 'data in one entity',
+  rowCount: 'Approx Entity Count',
 
 
   create: 'Create Collection',
   create: 'Create Collection',
   delete: 'delete',
   delete: 'delete',
   deleteTooltip: 'Please select at least one item to delete.',
   deleteTooltip: 'Please select at least one item to delete.',
-  alias: 'alias',
+  alias: 'Alias',
   aliasTooltip: 'Please select one collection to create alias',
   aliasTooltip: 'Please select one collection to create alias',
   download: 'Download',
   download: 'Download',
   downloadTooltip: 'Download all query results',
   downloadTooltip: 'Download all query results',
@@ -19,29 +18,40 @@ const collectionTrans = {
   // table
   // table
   id: 'ID',
   id: 'ID',
   name: 'Name',
   name: 'Name',
+  nameTip: 'Collection Name',
   status: 'Status',
   status: 'Status',
   desc: 'Description',
   desc: 'Description',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
   maxLength: 'Max Length',
   maxLength: 'Max Length',
 
 
+  // table tooltip
+  aliasInfo: 'Alias can be used as collection name in vector search.',
+  consistencyLevelInfo:
+    'Consistency refers to the property that ensures every node or replica has the same view of data when writing or reading data at a given time.',
+  entityCountInfo: 'Approximately entity count.',
+
   // create dialog
   // create dialog
   createTitle: 'Create Collection',
   createTitle: 'Create Collection',
-  general: '1. General Info',
-  schema: '2. Define Schema',
-  consistency: '3. Consistency Level',
+  general: 'General information',
+  schema: 'Schema',
+  consistency: 'Consistency Level',
   consistencyLevel: 'Consistency Level',
   consistencyLevel: 'Consistency Level',
-  description: 'Description (Optional)',
+  description: 'Description',
   fieldType: 'Field Type',
   fieldType: 'Field Type',
   vectorFieldType: 'Vector Field Type',
   vectorFieldType: 'Vector Field Type',
   fieldName: 'Field Name',
   fieldName: 'Field Name',
+  idFieldName: 'ID Name',
+  vectorFieldName: 'Vector Name',
   autoId: 'Auto ID',
   autoId: 'Auto ID',
+  vectorType: 'Vector Type',
+  idType: 'ID Type',
   dimension: 'Dimension',
   dimension: 'Dimension',
   dimensionTooltip: 'Only vector type has dimension',
   dimensionTooltip: 'Only vector type has dimension',
   dimensionMutipleWarning: 'Dimension should be 8 multiple',
   dimensionMutipleWarning: 'Dimension should be 8 multiple',
-  dimensionPositiveWarning: 'Dimension should be positive number',
+  dimensionPositiveWarning: 'Positive number only',
   newBtn: 'add new field',
   newBtn: 'add new field',
   nameLengthWarning: 'Name length should be less than 256',
   nameLengthWarning: 'Name length should be less than 256',
-  nameContentWarning: 'Name can only contain numbers, letters, and underscores',
+  nameContentWarning: 'Only numbers, letters, and underscores are allowed.',
   nameFirstLetterWarning:
   nameFirstLetterWarning:
     'Name first character must be underscore or character(a~z, A~Z)',
     'Name first character must be underscore or character(a~z, A~Z)',
 
 
@@ -60,6 +70,7 @@ const collectionTrans = {
   // delete dialog
   // delete dialog
   deleteWarning: `You are trying to delete a collection with data. This action cannot be undone.`,
   deleteWarning: `You are trying to delete a collection with data. This action cannot be undone.`,
   deleteDataWarning: `You are trying to delete entites. This action cannot be undone.`,
   deleteDataWarning: `You are trying to delete entites. This action cannot be undone.`,
+  deleteAliasWarning: `You are trying to drop an alias. This action cannot be undone.`,
 
 
   // collection tabs
   // collection tabs
   partitionTab: 'Partitions',
   partitionTab: 'Partitions',
@@ -68,6 +79,9 @@ const collectionTrans = {
   previewTab: 'Data Preview',
   previewTab: 'Data Preview',
   startTip: 'Start your data query',
   startTip: 'Start your data query',
   exprPlaceHolder: 'Please enter your query by using advanced filter ->',
   exprPlaceHolder: 'Please enter your query by using advanced filter ->',
+
+  // alias dialog
+  aliasCreatePlaceholder: 'Alias name',
 };
 };
 
 
 export default collectionTrans;
 export default collectionTrans;

+ 1 - 1
client/src/i18n/en/common.ts

@@ -8,7 +8,7 @@ const commonTrans = {
     ssl: 'SSL',
     ssl: 'SSL',
   },
   },
   status: {
   status: {
-    loaded: 'loaded for search',
+    loaded: 'loaded',
     unloaded: 'unloaded',
     unloaded: 'unloaded',
     error: 'error',
     error: 'error',
     running: 'running',
     running: 'running',

+ 1 - 1
client/src/i18n/en/dialog.ts

@@ -1,7 +1,7 @@
 const dialogTrans = {
 const dialogTrans = {
   deleteTipAction: 'Type',
   deleteTipAction: 'Type',
   deleteTipPurpose: 'to confirm.',
   deleteTipPurpose: 'to confirm.',
-  deleteTitle: `Delete {{type}}`,
+  deleteTitle: `Drop {{type}}`,
   releaseTitle: `Release {{type}}`,
   releaseTitle: `Release {{type}}`,
   createAlias: `Create alias for {{type}}`,
   createAlias: `Create alias for {{type}}`,
   loadTitle: `Load {{type}}`,
   loadTitle: `Load {{type}}`,

+ 3 - 3
client/src/i18n/en/partition.ts

@@ -8,15 +8,15 @@ const partitionTrans = {
   name: 'Name',
   name: 'Name',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
   status: 'Status',
   status: 'Status',
-  rowCount: 'Entity Count',
-  tooltip: 'data in one entity',
+  rowCount: 'Approx Entity Count',
+  tooltip: 'Approximately entity count.',
 
 
   createTitle: 'Create Partition',
   createTitle: 'Create Partition',
   nameWarning: '_default is reserved, cannot be used as name',
   nameWarning: '_default is reserved, cannot be used as name',
 
 
   deleteWarning:
   deleteWarning:
     'You are trying to delete partition. This action cannot be undone.',
     'You are trying to delete partition. This action cannot be undone.',
-  deletePartitionError: 'default partition cannot be deleted',
+  deletePartitionError: 'default partition cannot be dropped',
 };
 };
 
 
 export default partitionTrans;
 export default partitionTrans;

+ 1 - 1
client/src/i18n/en/success.ts

@@ -2,7 +2,7 @@ const successTrans = {
   connect: 'Connection to milvus successful',
   connect: 'Connection to milvus successful',
   create: `{{name}} has been created`,
   create: `{{name}} has been created`,
   load: `{{name}} has been loaded`,
   load: `{{name}} has been loaded`,
-  delete: `{{name}} successfully deleted`,
+  delete: `{{name}} successfully dropped`,
   release: `{{name}} has been released`,
   release: `{{name}} has been released`,
   update: `{{name}} has been updated`,
   update: `{{name}} has been updated`,
 };
 };

+ 2 - 1
client/src/i18n/en/warning.ts

@@ -1,8 +1,9 @@
 const warningTrans = {
 const warningTrans = {
   required: '{{name}} is required',
   required: '{{name}} is required',
+  requiredOnly: 'Required',
   positive: '{{name}} should be positive',
   positive: '{{name}} should be positive',
   integer: '{{name}} should be integers',
   integer: '{{name}} should be integers',
-  range: 'range is {{min}} ~ {{max}}',
+  range: 'Range is {{min}} ~ {{max}}',
   specValueOrRange:
   specValueOrRange:
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
   noSupportIndexType:
   noSupportIndexType:

+ 4 - 8
client/src/index.tsx

@@ -1,16 +1,12 @@
-import { StrictMode } from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
 import './index.css';
 import './index.css';
 import './i18n';
 import './i18n';
 import App from './App';
 import App from './App';
 import reportWebVitals from './reportWebVitals';
 import reportWebVitals from './reportWebVitals';
 
 
-ReactDOM.render(
-  <StrictMode>
-    <App />
-  </StrictMode>,
-  document.getElementById('root')
-);
+const container = document.getElementById('root');
+const root = createRoot(container!); // createRoot(container!) if you use TypeScript
+root.render(<App />);
 
 
 // If you want to start measuring performance in your app, pass a function
 // If you want to start measuring performance in your app, pass a function
 // to log results (for example: reportWebVitals(console.log))
 // to log results (for example: reportWebVitals(console.log))

+ 132 - 0
client/src/pages/collections/Aliases.tsx

@@ -0,0 +1,132 @@
+import { useContext } from 'react';
+import { Chip, IconButton, makeStyles, Theme } from '@material-ui/core';
+import { useTranslation } from 'react-i18next';
+import { rootContext } from '../../context/Root';
+import { AliasesProps } from './Types';
+import icons from '../../components/icons/Icons';
+import DeleteIcon from '@material-ui/icons/Delete';
+import CreateAlias from './CreateAlias';
+import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
+import { CollectionHttp } from '../../http/Collection';
+
+const useStyles = makeStyles((theme: Theme) => ({
+  wrapper: {
+    display: 'flex',
+    flexWrap: 'wrap',
+    gap: 8,
+  },
+  iconBtn: {
+    marginTop: 4,
+    width: '16px',
+    height: '16px',
+  },
+}));
+
+export default function Aliases(props: AliasesProps) {
+  const {
+    aliases,
+    collectionName,
+    onCreate = () => {},
+    onDelete = () => {},
+  } = props;
+  const classes = useStyles();
+  const { setDialog, openSnackBar, handleCloseDialog } =
+    useContext(rootContext);
+  // i18n
+  const { t: btnTrans } = useTranslation('btn');
+  const { t: dialogTrans } = useTranslation('dialog');
+  const { t: collectionTrans } = useTranslation('collection');
+  const { t: successTrans } = useTranslation('success');
+
+  const AddIcon = icons.add;
+
+  const handleCreate = (e: React.MouseEvent) => {
+    setDialog({
+      open: true,
+      type: 'custom',
+      params: {
+        component: (
+          <CreateAlias
+            collectionName={collectionName}
+            cb={() => {
+              onCreate();
+            }}
+          />
+        ),
+      },
+    });
+    e.stopPropagation();
+  };
+
+  if (aliases.length === 0) {
+    return (
+      <>
+        <IconButton
+          onClick={handleCreate}
+          size="small"
+          classes={{ root: classes.iconBtn }}
+          aria-label="add"
+        >
+          <AddIcon width="8" height="8" fontSize="small" />
+        </IconButton>
+      </>
+    );
+  }
+
+  const handleDelete = async (params: {
+    collection: string;
+    alias: string;
+  }) => {
+    await CollectionHttp.dropAlias(params.collection, { alias: params.alias });
+    openSnackBar(successTrans('delete', { name: collectionTrans('alias') }));
+    handleCloseDialog();
+    onDelete();
+  };
+
+  const _onDelete = (alias: { collection: string; alias: string }) => {
+    setDialog({
+      open: true,
+      type: 'custom',
+      params: {
+        component: (
+          <DeleteTemplate
+            label={btnTrans('drop')}
+            title={dialogTrans('deleteTitle', {
+              type: collectionTrans('alias'),
+            })}
+            text={collectionTrans('deleteAliasWarning')}
+            handleDelete={() => handleDelete(alias)}
+          />
+        ),
+      },
+    });
+  };
+
+  return (
+    <div className={classes.wrapper}>
+      {aliases.map(a => (
+        <Chip
+          key={a}
+          size="small"
+          label={a}
+          variant="outlined"
+          deleteIcon={<DeleteIcon />}
+          onClick={(e: React.MouseEvent) => {
+            e.stopPropagation();
+          }}
+          onDelete={() => {
+            _onDelete({ collection: collectionName, alias: a });
+          }}
+        />
+      ))}
+      <IconButton
+        onClick={handleCreate}
+        size="small"
+        classes={{ root: classes.iconBtn }}
+        aria-label="add"
+      >
+        <AddIcon width="8" height="8" fontSize="small" />
+      </IconButton>
+    </div>
+  );
+}

+ 3 - 3
client/src/pages/collections/Collection.tsx

@@ -4,7 +4,7 @@ import { ALL_ROUTER_TYPES } from '../../router/Types';
 import CustomTabList from '../../components/customTabList/CustomTabList';
 import CustomTabList from '../../components/customTabList/CustomTabList';
 import { ITab } from '../../components/customTabList/Types';
 import { ITab } from '../../components/customTabList/Types';
 import Partitions from '../partitions/Partitions';
 import Partitions from '../partitions/Partitions';
-import { useHistory, useLocation, useParams } from 'react-router-dom';
+import { useNavigate, useLocation, useParams } from 'react-router-dom';
 import { useMemo } from 'react';
 import { useMemo } from 'react';
 import { parseLocationSearch } from '../../utils/Format';
 import { parseLocationSearch } from '../../utils/Format';
 import Schema from '../schema/Schema';
 import Schema from '../schema/Schema';
@@ -23,7 +23,7 @@ const Collection = () => {
 
 
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTION_DETAIL, { collectionName });
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTION_DETAIL, { collectionName });
 
 
-  const history = useHistory();
+  const navigate = useNavigate();
   const location = useLocation();
   const location = useLocation();
 
 
   const { t: collectionTrans } = useTranslation('collection');
   const { t: collectionTrans } = useTranslation('collection');
@@ -37,7 +37,7 @@ const Collection = () => {
 
 
   const handleTabChange = (activeIndex: number) => {
   const handleTabChange = (activeIndex: number) => {
     const path = location.pathname;
     const path = location.pathname;
-    history.push(`${path}?activeIndex=${activeIndex}`);
+    navigate(`${path}?activeIndex=${activeIndex}`);
   };
   };
 
 
   const tabs: ITab[] = [
   const tabs: ITab[] = [

+ 77 - 78
client/src/pages/collections/Collections.tsx

@@ -1,5 +1,5 @@
 import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
 import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
-import { Link } from 'react-router-dom';
+import { Link, useSearchParams } from 'react-router-dom';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import AttuGrid from '../../components/grid/Grid';
 import AttuGrid from '../../components/grid/Grid';
@@ -30,7 +30,6 @@ import {
   useLoadAndReleaseDialogHook,
   useLoadAndReleaseDialogHook,
 } from '../../hooks/Dialog';
 } from '../../hooks/Dialog';
 import Highlighter from 'react-highlight-words';
 import Highlighter from 'react-highlight-words';
-import { parseLocationSearch } from '../../utils/Format';
 import InsertContainer from '../../components/insert/Container';
 import InsertContainer from '../../components/insert/Container';
 import ImportSample from '../../components/insert/ImportSample';
 import ImportSample from '../../components/insert/ImportSample';
 import { MilvusHttp } from '../../http/Milvus';
 import { MilvusHttp } from '../../http/Milvus';
@@ -38,7 +37,7 @@ import { LOADING_STATE } from '../../consts/Milvus';
 import { webSokcetContext } from '../../context/WebSocket';
 import { webSokcetContext } from '../../context/WebSocket';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '../../consts/Http';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '../../consts/Http';
 import { checkIndexBuilding, checkLoading } from '../../utils/Validation';
 import { checkIndexBuilding, checkLoading } from '../../utils/Validation';
-// import CreateAlias from './CreateAlias';
+import Aliases from './Aliases';
 
 
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
   emptyWrapper: {
   emptyWrapper: {
@@ -63,16 +62,14 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
   },
 }));
 }));
 
 
-let timer: NodeJS.Timeout | null = null;
-// get init search value from url
-const { urlSearch = '' } = parseLocationSearch(window.location.search);
-
 const Collections = () => {
 const Collections = () => {
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTIONS);
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTIONS);
   const { handleAction } = useLoadAndReleaseDialogHook({ type: 'collection' });
   const { handleAction } = useLoadAndReleaseDialogHook({ type: 'collection' });
   const { handleInsertDialog } = useInsertDialogHook();
   const { handleInsertDialog } = useInsertDialogHook();
-
-  const [search, setSearch] = useState<string>(urlSearch);
+  const [searchParams] = useSearchParams();
+  const [search, setSearch] = useState<string>(
+    (searchParams.get('search') as string) || ''
+  );
   const [loading, setLoading] = useState<boolean>(false);
   const [loading, setLoading] = useState<boolean>(false);
   const [selectedCollections, setSelectedCollections] = useState<
   const [selectedCollections, setSelectedCollections] = useState<
     CollectionView[]
     CollectionView[]
@@ -92,13 +89,38 @@ const Collections = () => {
   const InfoIcon = icons.info;
   const InfoIcon = icons.info;
   const SourceIcon = icons.source;
   const SourceIcon = icons.source;
 
 
-  const searchedCollections = useMemo(
-    () => collections.filter(collection => collection._name.includes(search)),
-    [collections, search]
-  );
+  const fetchData = useCallback(async () => {
+    try {
+      setLoading(true);
+      const res = await CollectionHttp.getCollections();
+      const hasLoadingOrBuildingCollection = res.some(
+        v => checkLoading(v) || checkIndexBuilding(v)
+      );
+
+      // if some collection is building index or loading, start pulling data
+      if (hasLoadingOrBuildingCollection) {
+        MilvusHttp.triggerCron({
+          name: WS_EVENTS.COLLECTION,
+          type: WS_EVENTS_TYPE.START,
+        });
+      }
+
+      setCollections(res);
+    } finally {
+      setLoading(false);
+    }
+  }, [setCollections]);
+
+  useEffect(() => {
+    fetchData();
+  }, [fetchData]);
 
 
   const formatCollections = useMemo(() => {
   const formatCollections = useMemo(() => {
-    const data = searchedCollections.map(v => {
+    const filteredCollections = search
+      ? collections.filter(collection => collection._name.includes(search))
+      : collections;
+
+    const data = filteredCollections.map(v => {
       // const indexStatus = statusRes.find(item => item._name === v._name);
       // const indexStatus = statusRes.find(item => item._name === v._name);
       Object.assign(v, {
       Object.assign(v, {
         nameElement: (
         nameElement: (
@@ -116,12 +138,20 @@ const Collections = () => {
         indexCreatingElement: (
         indexCreatingElement: (
           <StatusIcon type={v._indexState || ChildrenStatusType.FINISH} />
           <StatusIcon type={v._indexState || ChildrenStatusType.FINISH} />
         ),
         ),
+        _aliasElement: (
+          <Aliases
+            aliases={v._aliases}
+            collectionName={v._name}
+            onCreate={fetchData}
+            onDelete={fetchData}
+          />
+        ),
       });
       });
-
       return v;
       return v;
     });
     });
+
     return data;
     return data;
-  }, [classes.highlight, classes.link, search, searchedCollections]);
+  }, [search, collections]);
 
 
   const {
   const {
     pageSize,
     pageSize,
@@ -135,32 +165,6 @@ const Collections = () => {
     orderBy,
     orderBy,
   } = usePaginationHook(formatCollections);
   } = usePaginationHook(formatCollections);
 
 
-  const fetchData = useCallback(async () => {
-    try {
-      setLoading(true);
-      const res = await CollectionHttp.getCollections();
-      const hasLoadingOrBuildingCollection = res.some(
-        v => checkLoading(v) || checkIndexBuilding(v)
-      );
-
-      // if some collection is building index or loading, start pulling data
-      if (hasLoadingOrBuildingCollection) {
-        MilvusHttp.triggerCron({
-          name: WS_EVENTS.COLLECTION,
-          type: WS_EVENTS_TYPE.START,
-        });
-      }
-
-      setCollections(res);
-    } finally {
-      setLoading(false);
-    }
-  }, [setCollections]);
-
-  useEffect(() => {
-    fetchData();
-  }, [fetchData]);
-
   const handleInsert = async (
   const handleInsert = async (
     collectionName: string,
     collectionName: string,
     partitionName: string,
     partitionName: string,
@@ -271,9 +275,6 @@ const Collections = () => {
   };
   };
 
 
   const handleSearch = (value: string) => {
   const handleSearch = (value: string) => {
-    if (timer) {
-      clearTimeout(timer);
-    }
     setSearch(value);
     setSearch(value);
   };
   };
 
 
@@ -336,7 +337,7 @@ const Collections = () => {
           params: {
           params: {
             component: (
             component: (
               <DeleteTemplate
               <DeleteTemplate
-                label={btnTrans('delete')}
+                label={btnTrans('drop')}
                 title={dialogTrans('deleteTitle', {
                 title={dialogTrans('deleteTitle', {
                   type: collectionTrans('collection'),
                   type: collectionTrans('collection'),
                 })}
                 })}
@@ -353,30 +354,7 @@ const Collections = () => {
       disabledTooltip: collectionTrans('deleteTooltip'),
       disabledTooltip: collectionTrans('deleteTooltip'),
       disabled: data => data.length === 0,
       disabled: data => data.length === 0,
     },
     },
-    // Todo: hide alias after we can get all alias from milvus.
-    // {
-    //   type: 'iconBtn',
-    //   onClick: () => {
-    //     setDialog({
-    //       open: true,
-    //       type: 'custom',
-    //       params: {
-    //         component: (
-    //           <CreateAlias
-    //             collectionName={selectedCollections[0]._name}
-    //             cb={() => {
-    //               setSelectedCollections([]);
-    //             }}
-    //           />
-    //         ),
-    //       },
-    //     });
-    //   },
-    //   label: collectionTrans('alias'),
-    //   icon: 'alias',
-    //   disabledTooltip: collectionTrans('aliasTooltip'),
-    //   disabled: data => data.length !== 1,
-    // },
+
     {
     {
       label: 'Search',
       label: 'Search',
       icon: 'search',
       icon: 'search',
@@ -409,7 +387,20 @@ const Collections = () => {
       label: (
       label: (
         <span className="flex-center">
         <span className="flex-center">
           {collectionTrans('rowCount')}
           {collectionTrans('rowCount')}
-          <CustomToolTip title={collectionTrans('tooltip')}>
+          <CustomToolTip title={collectionTrans('entityCountInfo')}>
+            <InfoIcon classes={{ root: classes.icon }} />
+          </CustomToolTip>
+        </span>
+      ),
+    },
+    {
+      id: '_aliasElement',
+      align: 'left',
+      disablePadding: false,
+      label: (
+        <span className="flex-center">
+          {collectionTrans('alias')}
+          <CustomToolTip title={collectionTrans('aliasInfo')}>
             <InfoIcon classes={{ root: classes.icon }} />
             <InfoIcon classes={{ root: classes.icon }} />
           </CustomToolTip>
           </CustomToolTip>
         </span>
         </span>
@@ -419,8 +410,16 @@ const Collections = () => {
       id: 'consistency_level',
       id: 'consistency_level',
       align: 'left',
       align: 'left',
       disablePadding: false,
       disablePadding: false,
-      label: collectionTrans('consistencyLevel'),
+      label: (
+        <span className="flex-center">
+          {collectionTrans('consistencyLevel')}
+          <CustomToolTip title={collectionTrans('consistencyLevelInfo')}>
+            <InfoIcon classes={{ root: classes.icon }} />
+          </CustomToolTip>
+        </span>
+      ),
     },
     },
+
     {
     {
       id: '_desc',
       id: '_desc',
       align: 'left',
       align: 'left',
@@ -433,12 +432,12 @@ const Collections = () => {
       disablePadding: false,
       disablePadding: false,
       label: collectionTrans('createdTime'),
       label: collectionTrans('createdTime'),
     },
     },
-    {
-      id: 'indexCreatingElement',
-      align: 'left',
-      disablePadding: false,
-      label: '',
-    },
+    // {
+    //   id: 'indexCreatingElement',
+    //   align: 'left',
+    //   disablePadding: false,
+    //   label: '',
+    // },
     {
     {
       id: 'action',
       id: 'action',
       align: 'center',
       align: 'center',

+ 22 - 10
client/src/pages/collections/Create.tsx

@@ -23,32 +23,34 @@ const useStyles = makeStyles((theme: Theme) => ({
   fieldset: {
   fieldset: {
     width: '100%',
     width: '100%',
     display: 'flex',
     display: 'flex',
-    justifyContent: 'space-between',
     alignItems: 'center',
     alignItems: 'center',
-
+    marginBottom: '16px',
+    gap: '8px',
     '&:nth-last-child(2)': {
     '&:nth-last-child(2)': {
       flexDirection: 'column',
       flexDirection: 'column',
       alignItems: 'flex-start',
       alignItems: 'flex-start',
     },
     },
 
 
     '& legend': {
     '& legend': {
-      marginBottom: theme.spacing(2),
+      marginBottom: theme.spacing(1),
       color: `#82838e`,
       color: `#82838e`,
       lineHeight: '20px',
       lineHeight: '20px',
       fontSize: '14px',
       fontSize: '14px',
     },
     },
   },
   },
   input: {
   input: {
-    width: '48%',
+    width: '100%',
   },
   },
   select: {
   select: {
     width: '160px',
     width: '160px',
-    marginBottom: '22px',
 
 
     '&:first-child': {
     '&:first-child': {
       marginLeft: 0,
       marginLeft: 0,
     },
     },
   },
   },
+  dialog: {
+    minWidth: '100%',
+  },
 }));
 }));
 
 
 const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
 const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
@@ -130,9 +132,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
         // cannot be empty
         // cannot be empty
         {
         {
           rule: 'require',
           rule: 'require',
-          errorText: warningTrans('required', {
-            name: collectionTrans('name'),
-          }),
+          errorText: warningTrans('requiredOnly'),
         },
         },
         // length <= 255
         // length <= 255
         {
         {
@@ -157,6 +157,10 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
           errorText: collectionTrans('nameFirstLetterWarning'),
           errorText: collectionTrans('nameFirstLetterWarning'),
         },
         },
       ],
       ],
+      InputLabelProps: {
+        shrink: true,
+      },
+      size: 'small',
       className: classes.input,
       className: classes.input,
     },
     },
     {
     {
@@ -166,7 +170,11 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
       onChange: (value: string) => handleInputChange('description', value),
       onChange: (value: string) => handleInputChange('description', value),
       variant: 'filled',
       variant: 'filled',
       validations: [],
       validations: [],
+      size: 'small',
       className: classes.input,
       className: classes.input,
+      InputLabelProps: {
+        shrink: true,
+      },
     },
     },
   ];
   ];
 
 
@@ -196,10 +204,13 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
   return (
   return (
     <DialogTemplate
     <DialogTemplate
       title={collectionTrans('createTitle')}
       title={collectionTrans('createTitle')}
-      handleClose={handleCloseDialog}
+      handleClose={() => {
+        handleCloseDialog();
+      }}
       confirmLabel={btnTrans('create')}
       confirmLabel={btnTrans('create')}
       handleConfirm={handleCreateCollection}
       handleConfirm={handleCreateCollection}
       confirmDisabled={disabled || !allFieldsValid}
       confirmDisabled={disabled || !allFieldsValid}
+      dialogClass={classes.dialog}
     >
     >
       <form>
       <form>
         <fieldset className={classes.fieldset}>
         <fieldset className={classes.fieldset}>
@@ -230,13 +241,14 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
           <legend>{collectionTrans('consistency')}</legend>
           <legend>{collectionTrans('consistency')}</legend>
           <CustomSelector
           <CustomSelector
             wrapperClass={classes.select}
             wrapperClass={classes.select}
+            size="small"
             options={CONSISTENCY_LEVEL_OPTIONS}
             options={CONSISTENCY_LEVEL_OPTIONS}
             onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
             onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
               setConsistencyLevel(e.target.value as ConsistencyLevelEnum);
               setConsistencyLevel(e.target.value as ConsistencyLevelEnum);
             }}
             }}
+            hiddenLabel={true}
             value={consistencyLevel}
             value={consistencyLevel}
             variant="filled"
             variant="filled"
-            label={'Consistency'}
           />
           />
         </fieldset>
         </fieldset>
       </form>
       </form>

+ 24 - 11
client/src/pages/collections/CreateAlias.tsx

@@ -1,10 +1,8 @@
 import { FC, useContext, useMemo, useState } from 'react';
 import { FC, useContext, useMemo, useState } from 'react';
-
+import { Typography, makeStyles, Theme } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { rootContext } from '../../context/Root';
 import { rootContext } from '../../context/Root';
-
 import DialogTemplate from '../../components/customDialog/DialogTemplate';
 import DialogTemplate from '../../components/customDialog/DialogTemplate';
-
 import CustomInput from '../../components/customInput/CustomInput';
 import CustomInput from '../../components/customInput/CustomInput';
 import { formatForm } from '../../utils/Form';
 import { formatForm } from '../../utils/Form';
 import { useFormValidation } from '../../hooks/Form';
 import { useFormValidation } from '../../hooks/Form';
@@ -12,12 +10,20 @@ import { ITextfieldConfig } from '../../components/customInput/Types';
 import { CollectionHttp } from '../../http/Collection';
 import { CollectionHttp } from '../../http/Collection';
 import { CreateAliasProps } from './Types';
 import { CreateAliasProps } from './Types';
 
 
+const useStyles = makeStyles((theme: Theme) => ({
+  desc: {
+    margin: '8px 0 16px 0',
+  },
+}));
+
 const CreateAlias: FC<CreateAliasProps> = props => {
 const CreateAlias: FC<CreateAliasProps> = props => {
   const { cb, collectionName } = props;
   const { cb, collectionName } = props;
   const [form, setForm] = useState({
   const [form, setForm] = useState({
     alias: '',
     alias: '',
   });
   });
 
 
+  const classes = useStyles();
+
   const checkedForm = useMemo(() => {
   const checkedForm = useMemo(() => {
     const { alias } = form;
     const { alias } = form;
     return formatForm({ alias });
     return formatForm({ alias });
@@ -29,6 +35,7 @@ const CreateAlias: FC<CreateAliasProps> = props => {
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: warningTrans } = useTranslation('warning');
   const { t: warningTrans } = useTranslation('warning');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: collectionTrans } = useTranslation('collection');
+  const { t: btnTrans } = useTranslation('btn');
 
 
   const handleInputChange = (value: string) => {
   const handleInputChange = (value: string) => {
     setForm({ alias: value });
     setForm({ alias: value });
@@ -41,11 +48,11 @@ const CreateAlias: FC<CreateAliasProps> = props => {
   };
   };
 
 
   const aliasInputConfig: ITextfieldConfig = {
   const aliasInputConfig: ITextfieldConfig = {
-    label: 'Collection Alias',
+    label: collectionTrans('alias'),
     key: 'alias',
     key: 'alias',
     onChange: handleInputChange,
     onChange: handleInputChange,
     variant: 'filled',
     variant: 'filled',
-    placeholder: 'alias name',
+    placeholder: collectionTrans('aliasCreatePlaceholder'),
     fullWidth: true,
     fullWidth: true,
     validations: [
     validations: [
       {
       {
@@ -68,13 +75,19 @@ const CreateAlias: FC<CreateAliasProps> = props => {
       })}
       })}
       handleClose={handleCloseDialog}
       handleClose={handleCloseDialog}
       children={
       children={
-        <CustomInput
-          type="text"
-          textConfig={aliasInputConfig}
-          checkValid={checkIsValid}
-          validInfo={validation}
-        />
+        <>
+          <Typography variant="body1" component="p" className={classes.desc}>
+            {collectionTrans('aliasInfo')}
+          </Typography>
+          <CustomInput
+            type="text"
+            textConfig={aliasInputConfig}
+            checkValid={checkIsValid}
+            validInfo={validation}
+          />
+        </>
       }
       }
+      confirmLabel={btnTrans('create')}
       handleConfirm={handleConfirm}
       handleConfirm={handleConfirm}
       confirmDisabled={disabled}
       confirmDisabled={disabled}
     />
     />

+ 103 - 77
client/src/pages/collections/CreateFields.tsx

@@ -1,7 +1,6 @@
 import { makeStyles, Theme, TextField, IconButton } from '@material-ui/core';
 import { makeStyles, Theme, TextField, IconButton } from '@material-ui/core';
 import { FC, Fragment, ReactElement, useMemo } from 'react';
 import { FC, Fragment, ReactElement, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
-import CustomButton from '../../components/customButton/CustomButton';
 import CustomSelector from '../../components/customSelector/CustomSelector';
 import CustomSelector from '../../components/customSelector/CustomSelector';
 import icons from '../../components/icons/Icons';
 import icons from '../../components/icons/Icons';
 import { generateId } from '../../utils/Common';
 import { generateId } from '../../utils/Common';
@@ -35,57 +34,50 @@ const useStyles = makeStyles((theme: Theme) => ({
     display: 'flex',
     display: 'flex',
     flexWrap: 'nowrap',
     flexWrap: 'nowrap',
     alignItems: 'center',
     alignItems: 'center',
-    // only Safari 14.1+ support flexbox gap
-    // gap: '10px',
-    width: '100%',
-
-    '& > *': {
-      marginLeft: '10px',
-    },
-
-    '& .dimension': {
-      maxWidth: '160px',
-    },
+    gap: '8px',
+    flex: '1 0 auto',
   },
   },
   input: {
   input: {
     fontSize: '14px',
     fontSize: '14px',
   },
   },
-  primaryInput: {
-    maxWidth: '160px',
-
-    '&:first-child': {
-      marginLeft: 0,
-    },
+  fieldInput: {
+    width: '160px',
   },
   },
   select: {
   select: {
-    width: '160px',
-    marginBottom: '22px',
+    width: '140px',
+    marginTop: '-20px',
 
 
     '&:first-child': {
     '&:first-child': {
       marginLeft: 0,
       marginLeft: 0,
     },
     },
   },
   },
+  autoIdSelect: {
+    width: '120px',
+    marginTop: '-20px',
+  },
+  numberBox: {
+    width: '97px',
+  },
+  maxLength: {
+    maxWidth: '80px',
+  },
   descInput: {
   descInput: {
-    minWidth: '100px',
-    flexGrow: 1,
+    width: '120px',
   },
   },
   btnTxt: {
   btnTxt: {
     textTransform: 'uppercase',
     textTransform: 'uppercase',
   },
   },
   iconBtn: {
   iconBtn: {
     marginLeft: 0,
     marginLeft: 0,
-
     padding: 0,
     padding: 0,
-    width: '20px',
-    height: '20px',
-  },
-  mb2: {
-    marginBottom: theme.spacing(2),
+    width: '16px',
+    height: '16px',
   },
   },
   helperText: {
   helperText: {
     lineHeight: '20px',
     lineHeight: '20px',
-    margin: theme.spacing(0.25, 0),
-    marginLeft: '12px',
+    fontSize: '10px',
+    margin: theme.spacing(0),
+    marginLeft: '11px',
   },
   },
 }));
 }));
 
 
@@ -112,7 +104,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
 
 
   const classes = useStyles();
   const classes = useStyles();
 
 
-  const AddIcon = icons.add;
+  const AddIcon = icons.addOutline;
   const RemoveIcon = icons.remove;
   const RemoveIcon = icons.remove;
 
 
   const { requiredFields, optionalFields } = useMemo(
   const { requiredFields, optionalFields } = useMemo(
@@ -161,6 +153,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
             ? ALL_OPTIONS
             ? ALL_OPTIONS
             : VECTOR_FIELDS_OPTIONS
             : VECTOR_FIELDS_OPTIONS
         }
         }
+        size="small"
         onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
         onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
           onChange(e.target.value as DataTypeEnum);
           onChange(e.target.value as DataTypeEnum);
         }}
         }}
@@ -196,6 +189,10 @@ const CreateFields: FC<CreateFieldsProps> = ({
             input: inputClassName,
             input: inputClassName,
           },
           },
         }}
         }}
+        InputLabelProps={{
+          shrink: true,
+        }}
+        size="small"
         disabled={isReadOnly}
         disabled={isReadOnly}
         error={validate(value) !== ' '}
         error={validate(value) !== ' '}
         helperText={validate(value)}
         helperText={validate(value)}
@@ -208,10 +205,15 @@ const CreateFields: FC<CreateFieldsProps> = ({
     );
     );
   };
   };
 
 
-  const generateFieldName = (field: Field) => {
+  const generateFieldName = (
+    field: Field,
+    label?: string,
+    className?: string
+  ) => {
     return getInput({
     return getInput({
-      label: collectionTrans('fieldName'),
+      label: label || collectionTrans('fieldName'),
       value: field.name,
       value: field.name,
+      className: className || classes.fieldInput,
       handleChange: (value: string) => {
       handleChange: (value: string) => {
         const isValid = checkEmptyValid(value);
         const isValid = checkEmptyValid(value);
         setFieldsValidation(v =>
         setFieldsValidation(v =>
@@ -226,9 +228,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
         if (value === null) return ' ';
         if (value === null) return ' ';
         const isValid = checkEmptyValid(value);
         const isValid = checkEmptyValid(value);
 
 
-        return isValid
-          ? ' '
-          : warningTrans('required', { name: collectionTrans('fieldName') });
+        return isValid ? ' ' : warningTrans('requiredOnly');
       },
       },
     });
     });
   };
   };
@@ -239,7 +239,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       value: field.description,
       value: field.description,
       handleChange: (value: string) =>
       handleChange: (value: string) =>
         changeFields(field.id!, 'description', value),
         changeFields(field.id!, 'description', value),
-      className: classes.descInput,
+      inputClassName: classes.descInput,
     });
     });
   };
   };
 
 
@@ -269,6 +269,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     return getInput({
     return getInput({
       label: collectionTrans('dimension'),
       label: collectionTrans('dimension'),
       value: field.dimension as number,
       value: field.dimension as number,
+      inputClassName: classes.numberBox,
       handleChange: (value: string) => {
       handleChange: (value: string) => {
         const { isPositive, isMutiple } = validateDimension(value);
         const { isPositive, isMutiple } = validateDimension(value);
         const isValid =
         const isValid =
@@ -301,6 +302,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       label: 'Max Length',
       label: 'Max Length',
       value: field.max_length!,
       value: field.max_length!,
       type: 'number',
       type: 'number',
+      inputClassName: classes.maxLength,
       handleChange: (value: string) =>
       handleChange: (value: string) =>
         changeFields(field.id!, 'max_length', value),
         changeFields(field.id!, 'max_length', value),
       validate: (value: any) => {
       validate: (value: any) => {
@@ -313,9 +315,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
           type: 'number',
           type: 'number',
         });
         });
         return !isEmptyValid
         return !isEmptyValid
-          ? warningTrans('required', {
-              name: collectionTrans('fieldName'),
-            })
+          ? warningTrans('requiredOnly')
           : !isRangeValid
           : !isRangeValid
           ? warningTrans('range', {
           ? warningTrans('range', {
               min: 1,
               min: 1,
@@ -339,7 +339,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     setFields(newFields);
     setFields(newFields);
   };
   };
 
 
-  const handleAddNewField = () => {
+  const handleAddNewField = (index: number) => {
     const id = generateId();
     const id = generateId();
     const newDefaultItem: Field = {
     const newDefaultItem: Field = {
       name: null,
       name: null,
@@ -356,14 +356,16 @@ const CreateFields: FC<CreateFieldsProps> = ({
       name: false,
       name: false,
       dimension: true,
       dimension: true,
     };
     };
-    setFields([...fields, newDefaultItem]);
+
+    fields.splice(index + 1, 0, newDefaultItem);
+    setFields([...fields]);
     setFieldsValidation(v => [...v, newValidation]);
     setFieldsValidation(v => [...v, newValidation]);
   };
   };
 
 
-  const handleRemoveField = (field: Field) => {
-    const newFields = fields.filter(f => f.id !== field.id);
+  const handleRemoveField = (id: string) => {
+    const newFields = fields.filter(f => f.id !== id);
     setFields(newFields);
     setFields(newFields);
-    setFieldsValidation(v => v.filter(item => item.id !== field.id));
+    setFieldsValidation(v => v.filter(item => item.id !== id));
   };
   };
 
 
   const generatePrimaryKeyRow = (
   const generatePrimaryKeyRow = (
@@ -374,9 +376,10 @@ const CreateFields: FC<CreateFieldsProps> = ({
     const autoIdOff = isVarChar ? 'false' : autoID ? 'true' : 'false';
     const autoIdOff = isVarChar ? 'false' : autoID ? 'true' : 'false';
     return (
     return (
       <div className={`${classes.rowWrapper}`}>
       <div className={`${classes.rowWrapper}`}>
+        {generateFieldName(field, collectionTrans('idFieldName'))}
         {getSelector(
         {getSelector(
           'vector',
           'vector',
-          `Primary ${collectionTrans('fieldType')} `,
+          `${collectionTrans('idType')} `,
           field.data_type,
           field.data_type,
           (value: DataTypeEnum) => {
           (value: DataTypeEnum) => {
             changeFields(field.id!, 'data_type', value);
             changeFields(field.id!, 'data_type', value);
@@ -386,8 +389,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
           },
           },
           PRIMARY_FIELDS_OPTIONS
           PRIMARY_FIELDS_OPTIONS
         )}
         )}
-
-        {generateFieldName(field)}
+        {generateDesc(field)}
 
 
         <CustomSelector
         <CustomSelector
           label={collectionTrans('autoId')}
           label={collectionTrans('autoId')}
@@ -398,53 +400,51 @@ const CreateFields: FC<CreateFieldsProps> = ({
             setAutoID(autoId);
             setAutoID(autoId);
           }}
           }}
           variant="filled"
           variant="filled"
-          wrapperClass={classes.select}
+          wrapperClass={classes.autoIdSelect}
           disabled={isVarChar}
           disabled={isVarChar}
+          size="small"
         />
         />
-        {isVarChar && generateMaxLength(field)}
 
 
-        {generateDesc(field)}
+        {isVarChar && generateMaxLength(field)}
       </div>
       </div>
     );
     );
   };
   };
 
 
-  const generateDefaultVectorRow = (field: Field): ReactElement => {
+  const generateDefaultVectorRow = (
+    field: Field,
+    index: number
+  ): ReactElement => {
     return (
     return (
       <>
       <>
         <div className={`${classes.rowWrapper}`}>
         <div className={`${classes.rowWrapper}`}>
+          {generateFieldName(field, collectionTrans('vectorFieldName'))}
+
           {getSelector(
           {getSelector(
             'vector',
             'vector',
-            collectionTrans('fieldType'),
+            `${collectionTrans('vectorType')} `,
             field.data_type,
             field.data_type,
             (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
             (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
           )}
           )}
-
-          {generateFieldName(field)}
+          {generateDesc(field)}
 
 
           {generateDimension(field)}
           {generateDimension(field)}
 
 
-          {generateDesc(field)}
+          <IconButton
+            onClick={() => handleAddNewField(index)}
+            classes={{ root: classes.iconBtn }}
+            aria-label="add"
+          >
+            <AddIcon />
+          </IconButton>
         </div>
         </div>
-
-        <CustomButton onClick={handleAddNewField} className={classes.mb2}>
-          <AddIcon />
-          <span className={classes.btnTxt}>{collectionTrans('newBtn')}</span>
-        </CustomButton>
       </>
       </>
     );
     );
   };
   };
 
 
-  const generateNumberRow = (field: Field): ReactElement => {
+  const generateNumberRow = (field: Field, index: number): ReactElement => {
     const isVarChar = field.data_type === DataTypeEnum.VarChar;
     const isVarChar = field.data_type === DataTypeEnum.VarChar;
     return (
     return (
       <div className={`${classes.rowWrapper}`}>
       <div className={`${classes.rowWrapper}`}>
-        <IconButton
-          onClick={() => handleRemoveField(field)}
-          classes={{ root: classes.iconBtn }}
-          aria-label="delete"
-        >
-          <RemoveIcon />
-        </IconButton>
         {generateFieldName(field)}
         {generateFieldName(field)}
         {getSelector(
         {getSelector(
           'all',
           'all',
@@ -452,8 +452,28 @@ const CreateFields: FC<CreateFieldsProps> = ({
           field.data_type,
           field.data_type,
           (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
           (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
         )}
         )}
-        {isVarChar && generateMaxLength(field)}
         {generateDesc(field)}
         {generateDesc(field)}
+
+        {isVarChar && generateMaxLength(field)}
+        <IconButton
+          onClick={() => {
+            handleAddNewField(index);
+          }}
+          classes={{ root: classes.iconBtn }}
+          aria-label="add"
+        >
+          <AddIcon />
+        </IconButton>
+        <IconButton
+          onClick={() => {
+            const id = field.id || '';
+            handleRemoveField(id);
+          }}
+          classes={{ root: classes.iconBtn }}
+          aria-label="delete"
+        >
+          <RemoveIcon />
+        </IconButton>
       </div>
       </div>
     );
     );
   };
   };
@@ -478,35 +498,41 @@ const CreateFields: FC<CreateFieldsProps> = ({
     );
     );
   };
   };
 
 
-  const generateRequiredFieldRow = (field: Field, autoID: boolean) => {
+  const generateRequiredFieldRow = (
+    field: Field,
+    autoID: boolean,
+    index: number
+  ) => {
     // required type is primaryKey or defaultVector
     // required type is primaryKey or defaultVector
     if (field.createType === 'primaryKey') {
     if (field.createType === 'primaryKey') {
       return generatePrimaryKeyRow(field, autoID);
       return generatePrimaryKeyRow(field, autoID);
     }
     }
     // use defaultVector as default return type
     // use defaultVector as default return type
-    return generateDefaultVectorRow(field);
+    return generateDefaultVectorRow(field, index);
   };
   };
 
 
-  const generateOptionalFieldRow = (field: Field) => {
+  const generateOptionalFieldRow = (field: Field, index: number) => {
     // optional type is vector or number
     // optional type is vector or number
     if (field.createType === 'vector') {
     if (field.createType === 'vector') {
       return generateVectorRow(field);
       return generateVectorRow(field);
     }
     }
 
 
     // use number as default createType
     // use number as default createType
-    return generateNumberRow(field);
+    return generateNumberRow(field, index);
   };
   };
 
 
   return (
   return (
     <>
     <>
       {requiredFields.map((field, index) => (
       {requiredFields.map((field, index) => (
-        <Fragment key={index}>
-          {generateRequiredFieldRow(field, autoID)}
+        <Fragment key={field.id}>
+          {generateRequiredFieldRow(field, autoID, index)}
         </Fragment>
         </Fragment>
       ))}
       ))}
       <div className={classes.optionalWrapper}>
       <div className={classes.optionalWrapper}>
         {optionalFields.map((field, index) => (
         {optionalFields.map((field, index) => (
-          <Fragment key={index}>{generateOptionalFieldRow(field)}</Fragment>
+          <Fragment key={field.id}>
+            {generateOptionalFieldRow(field, index + requiredFields.length)}
+          </Fragment>
         ))}
         ))}
       </div>
       </div>
     </>
     </>

+ 8 - 0
client/src/pages/collections/Types.ts

@@ -13,6 +13,7 @@ export interface CollectionData {
   _indexState: ChildrenStatusType;
   _indexState: ChildrenStatusType;
   _fields?: FieldData[];
   _fields?: FieldData[];
   _consistencyLevel?: string;
   _consistencyLevel?: string;
+  _aliases: string[];
 }
 }
 
 
 export interface CollectionView extends CollectionData {
 export interface CollectionView extends CollectionData {
@@ -120,3 +121,10 @@ export interface DeleteEntitiesReq {
   expr: string;
   expr: string;
   partition_name?: string;
   partition_name?: string;
 }
 }
+
+export interface AliasesProps {
+  aliases: string[];
+  collectionName: string;
+  onCreate?: Function;
+  onDelete?: Function;
+}

+ 4 - 4
client/src/pages/connect/AuthForm.tsx

@@ -10,7 +10,7 @@ import { useFormValidation } from '../../hooks/Form';
 import { formatForm } from '../../utils/Form';
 import { formatForm } from '../../utils/Form';
 import { MilvusHttp } from '../../http/Milvus';
 import { MilvusHttp } from '../../http/Milvus';
 import { formatAddress } from '../../utils/Format';
 import { formatAddress } from '../../utils/Format';
-import { useHistory } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
 import { rootContext } from '../../context/Root';
 import { rootContext } from '../../context/Root';
 import { authContext } from '../../context/Auth';
 import { authContext } from '../../context/Auth';
 import { MILVUS_ADDRESS } from '../../consts/Localstorage';
 import { MILVUS_ADDRESS } from '../../consts/Localstorage';
@@ -42,7 +42,7 @@ const useStyles = makeStyles((theme: Theme) => ({
     width: '42px',
     width: '42px',
     height: 'auto',
     height: 'auto',
     marginBottom: '8px',
     marginBottom: '8px',
-    display: 'block'
+    display: 'block',
   },
   },
   input: {
   input: {
     margin: theme.spacing(3, 0, 0.5),
     margin: theme.spacing(3, 0, 0.5),
@@ -54,7 +54,7 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
   },
 }));
 }));
 export const AuthForm = (props: any) => {
 export const AuthForm = (props: any) => {
-  const history = useHistory();
+  const navigate = useNavigate();
   const classes = useStyles();
   const classes = useStyles();
 
 
   const { openSnackBar } = useContext(rootContext);
   const { openSnackBar } = useContext(rootContext);
@@ -161,7 +161,7 @@ export const AuthForm = (props: any) => {
 
 
       openSnackBar(successTrans('connect'));
       openSnackBar(successTrans('connect'));
       window.localStorage.setItem(MILVUS_ADDRESS, address);
       window.localStorage.setItem(MILVUS_ADDRESS, address);
-      history.push('/');
+      navigate('/');
     } catch (error: any) {
     } catch (error: any) {
       if (error?.response?.status === CODE_STATUS.UNAUTHORIZED) {
       if (error?.response?.status === CODE_STATUS.UNAUTHORIZED) {
         showAuthForm
         showAuthForm

+ 11 - 3
client/src/pages/connect/Connect.tsx

@@ -1,11 +1,19 @@
+import { useContext } from 'react';
+import { Navigate } from 'react-router-dom';
 import ConnectContainer from './ConnectContainer';
 import ConnectContainer from './ConnectContainer';
 import { AuthForm } from './AuthForm';
 import { AuthForm } from './AuthForm';
+import { authContext } from '../../context/Auth';
 
 
 const Connect = () => {
 const Connect = () => {
+  const { isAuth } = useContext(authContext);
+
   return (
   return (
-    <ConnectContainer>
-      <AuthForm />
-    </ConnectContainer>
+    <>
+      {isAuth && <Navigate to="/" replace={true} />}
+      <ConnectContainer>
+        <AuthForm />
+      </ConnectContainer>
+    </>
   );
   );
 };
 };
 
 

+ 1 - 1
client/src/pages/connect/ConnectContainer.tsx

@@ -4,7 +4,7 @@ import { ReactElement } from 'react';
 const getContainerStyles = makeStyles((theme: Theme) => ({
 const getContainerStyles = makeStyles((theme: Theme) => ({
   wrapper: {
   wrapper: {
     width: '100%',
     width: '100%',
-    height: '90%',
+    height: '100vh',
     backgroundRepeat: 'no-repeat',
     backgroundRepeat: 'no-repeat',
     backgroundSize: 'cover',
     backgroundSize: 'cover',
   },
   },

+ 135 - 0
client/src/pages/index.tsx

@@ -0,0 +1,135 @@
+import { useMemo, useContext } from 'react';
+import { Outlet, useNavigate, useLocation, Navigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { makeStyles, Theme, createStyles } from '@material-ui/core';
+import GlobalEffect from '../components/layout/GlobalEffect';
+import Header from '../components/layout/Header';
+import NavMenu from '../components/menu/NavMenu';
+import { NavMenuItem } from '../components/menu/Types';
+import icons from '../components/icons/Icons';
+import { authContext } from '../context/Auth';
+import { rootContext } from '../context/Root';
+import Overview from '../pages/overview/Overview';
+
+const useStyles = makeStyles((theme: Theme) =>
+  createStyles({
+    root: {
+      minHeight: '100vh',
+      backgroundColor: '#f5f5f5',
+    },
+    content: {
+      display: 'flex',
+
+      '& .active': {
+        '& path': {
+          fill: theme.palette.attuGrey.dark,
+        },
+      },
+
+      '& .normal': {
+        '& path': {
+          fill: theme.palette.primary.main,
+        },
+      },
+    },
+    body: {
+      flex: 1,
+      display: 'flex',
+      flexDirection: 'column',
+      height: '100vh',
+      overflowY: 'scroll',
+    },
+  })
+);
+
+function Index() {
+  const navigate = useNavigate();
+  const { isAuth } = useContext(authContext);
+  const { versionInfo } = useContext(rootContext);
+  const { t: navTrans } = useTranslation('nav');
+  const classes = useStyles();
+  const location = useLocation();
+  const isIndex = location.pathname === '/';
+  const defaultActive = useMemo(() => {
+    if (location.pathname.includes('collection')) {
+      return navTrans('collection');
+    }
+
+    if (location.pathname.includes('search')) {
+      return navTrans('search');
+    }
+
+    if (location.pathname.includes('system')) {
+      return navTrans('system');
+    }
+
+    if (location.pathname.includes('users')) {
+      return navTrans('user');
+    }
+
+    return navTrans('overview');
+  }, [location, navTrans]);
+
+  const menuItems: NavMenuItem[] = [
+    {
+      icon: icons.navOverview,
+      label: navTrans('overview'),
+      onClick: () => navigate('/'),
+    },
+    {
+      icon: icons.navCollection,
+      label: navTrans('collection'),
+      onClick: () => navigate('/collections'),
+    },
+    {
+      icon: icons.navSearch,
+      label: navTrans('search'),
+      onClick: () => navigate('/search'),
+      iconActiveClass: 'normal',
+      iconNormalClass: 'active',
+    },
+    {
+      icon: icons.navSystem,
+      label: navTrans('system'),
+      onClick: () => navigate('/system'),
+      iconActiveClass: 'normal',
+      iconNormalClass: 'active',
+    },
+    {
+      icon: icons.navPerson,
+      label: navTrans('user'),
+      onClick: () => navigate('/users'),
+    },
+  ];
+
+  // check if is connected
+  if (!isAuth) {
+    return <Navigate to="/connect" />;
+  }
+
+  return (
+    <>
+      <div className={classes.root}>
+        <GlobalEffect>
+          <div className={classes.content}>
+            <NavMenu
+              width="200px"
+              data={menuItems}
+              defaultActive={defaultActive}
+              // used for nested child menu
+              defaultOpen={{ [navTrans('overview')]: true }}
+              versionInfo={versionInfo}
+            />
+
+            <div className={classes.body}>
+              <Header />
+              {isIndex ? <Overview /> : <Outlet />}
+            </div>
+          </div>
+        </GlobalEffect>
+      </div>
+    </>
+  );
+}
+
+export default Index;

+ 7 - 6
client/src/pages/overview/collectionCard/CollectionCard.tsx

@@ -7,7 +7,7 @@ import CustomToolTip from '../../../components/customToolTip/CustomToolTip';
 import { CollectionCardProps } from './Types';
 import { CollectionCardProps } from './Types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import CustomIconButton from '../../../components/customButton/CustomIconButton';
 import CustomIconButton from '../../../components/customButton/CustomIconButton';
-import { useHistory, Link } from 'react-router-dom';
+import { useNavigate, Link } from 'react-router-dom';
 import { LOADING_STATE } from '../../../consts/Milvus';
 import { LOADING_STATE } from '../../../consts/Milvus';
 
 
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
@@ -84,7 +84,7 @@ const CollectionCard: FC<CollectionCardProps> = ({
     _rowCount: rowCount,
     _rowCount: rowCount,
     _loadedPercentage,
     _loadedPercentage,
   } = data;
   } = data;
-  const history = useHistory();
+  const navigate = useNavigate();
   // icons
   // icons
   const RightArrowIcon = icons.rightArrow;
   const RightArrowIcon = icons.rightArrow;
   const InfoIcon = icons.info;
   const InfoIcon = icons.info;
@@ -99,13 +99,14 @@ const CollectionCard: FC<CollectionCardProps> = ({
   };
   };
 
 
   const onVectorSearchClick = () => {
   const onVectorSearchClick = () => {
-    history.push({ pathname: '/search', search: `?collectionName=${name}` });
+    navigate({ pathname: '/search', search: `?collectionName=${name}` });
   };
   };
 
 
   return (
   return (
     <div
     <div
-      className={`card-wrapper ${classes.wrapper} ${wrapperClass} ${data._status === LOADING_STATE.LOADING && classes.loading
-        }`}
+      className={`card-wrapper ${classes.wrapper} ${wrapperClass} ${
+        data._status === LOADING_STATE.LOADING && classes.loading
+      }`}
     >
     >
       <div>
       <div>
         <Status status={status} percentage={_loadedPercentage} />
         <Status status={status} percentage={_loadedPercentage} />
@@ -116,7 +117,7 @@ const CollectionCard: FC<CollectionCardProps> = ({
       </Link>
       </Link>
       <div className={classes.content}>
       <div className={classes.content}>
         <Typography>{collectionTrans('rowCount')}</Typography>
         <Typography>{collectionTrans('rowCount')}</Typography>
-        <CustomToolTip title={collectionTrans('tooltip')} placement="bottom">
+        <CustomToolTip title={collectionTrans('entityCountInfo')} placement="bottom">
           <InfoIcon classes={{ root: classes.icon }} />
           <InfoIcon classes={{ root: classes.icon }} />
         </CustomToolTip>
         </CustomToolTip>
         <Typography className={classes.rowCount}>{rowCount}</Typography>
         <Typography className={classes.rowCount}>{rowCount}</Typography>

+ 42 - 62
client/src/pages/partitions/Partitions.tsx

@@ -1,10 +1,7 @@
 import { makeStyles, Theme } from '@material-ui/core';
 import { makeStyles, Theme } from '@material-ui/core';
-import { FC, useCallback, useContext, useEffect, useState } from 'react';
-import {
-  PartitionManageParam,
-  // PartitionParam,
-  PartitionView,
-} from './Types';
+import { FC, useContext, useEffect, useState } from 'react';
+import { useSearchParams } from 'react-router-dom';
+import { PartitionManageParam, PartitionView } from './Types';
 import AttuGrid from '../../components/grid/Grid';
 import AttuGrid from '../../components/grid/Grid';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';
 import { ColDefinitionsType, ToolBarConfig } from '../../components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
@@ -14,13 +11,9 @@ import CustomToolTip from '../../components/customToolTip/CustomToolTip';
 import { rootContext } from '../../context/Root';
 import { rootContext } from '../../context/Root';
 import CreatePartition from './Create';
 import CreatePartition from './Create';
 import { PartitionHttp } from '../../http/Partition';
 import { PartitionHttp } from '../../http/Partition';
-import Status from '../../components/status/Status';
 import { ManageRequestMethods } from '../../types/Common';
 import { ManageRequestMethods } from '../../types/Common';
-// import { StatusEnum } from '../../components/status/Types';
-// import { useDialogHook } from '../../hooks/Dialog';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 import Highlighter from 'react-highlight-words';
 import Highlighter from 'react-highlight-words';
-import { parseLocationSearch } from '../../utils/Format';
 import { useInsertDialogHook } from '../../hooks/Dialog';
 import { useInsertDialogHook } from '../../hooks/Dialog';
 import InsertContainer from '../../components/insert/Container';
 import InsertContainer from '../../components/insert/Container';
 import { CollectionHttp } from '../../http/Collection';
 import { CollectionHttp } from '../../http/Collection';
@@ -45,8 +38,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 
 
 let timer: NodeJS.Timeout | null = null;
 let timer: NodeJS.Timeout | null = null;
 // get init search value from url
 // get init search value from url
-const { search = '' } = parseLocationSearch(window.location.search);
-
+// const { search = '' } = parseLocationSearch(window.location.search);
 const Partitions: FC<{
 const Partitions: FC<{
   collectionName: string;
   collectionName: string;
 }> = ({ collectionName }) => {
 }> = ({ collectionName }) => {
@@ -55,6 +47,10 @@ const Partitions: FC<{
   const { t: successTrans } = useTranslation('success');
   const { t: successTrans } = useTranslation('success');
   const { t: btnTrans } = useTranslation('btn');
   const { t: btnTrans } = useTranslation('btn');
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: dialogTrans } = useTranslation('dialog');
+  const [searchParams] = useSearchParams();
+  const [search, setSearch] = useState<string>(
+    (searchParams.get('search') as string) || ''
+  );
   const InfoIcon = icons.info;
   const InfoIcon = icons.info;
 
 
   const { handleInsertDialog } = useInsertDialogHook();
   const { handleInsertDialog } = useInsertDialogHook();
@@ -81,35 +77,15 @@ const Partitions: FC<{
   const { setDialog, handleCloseDialog, openSnackBar } =
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
     useContext(rootContext);
 
 
-  const fetchPartitions = useCallback(
-    async (collectionName: string) => {
-      try {
-        const res = await PartitionHttp.getPartitions(collectionName);
-
-        const partitions: PartitionView[] = res.map(p =>
-          Object.assign(p, {
-            _nameElement: (
-              <Highlighter
-                textToHighlight={p._formatName}
-                searchWords={[search]}
-                highlightClassName={classes.highlight}
-              />
-            ),
-            _statusElement: <Status status={p._status} />,
-          })
-        );
-        const filteredPartitions = partitions.filter(p =>
-          p._formatName.includes(search)
-        );
-        setLoading(false);
-        setPartitions(partitions);
-        setSearchedPartitions(filteredPartitions);
-      } catch (err) {
-        setLoading(false);
-      }
-    },
-    [classes.highlight]
-  );
+  const fetchPartitions = async (collectionName: string) => {
+    try {
+      const res = await PartitionHttp.getPartitions(collectionName);
+      setLoading(false);
+      setPartitions(res);
+    } catch (err) {
+      setLoading(false);
+    }
+  };
 
 
   const fetchCollectionDetail = async (name: string) => {
   const fetchCollectionDetail = async (name: string) => {
     const res = await CollectionHttp.getCollection(name);
     const res = await CollectionHttp.getCollection(name);
@@ -118,33 +94,18 @@ const Partitions: FC<{
 
 
   useEffect(() => {
   useEffect(() => {
     fetchPartitions(collectionName);
     fetchPartitions(collectionName);
-  }, [collectionName, fetchPartitions]);
-
-  const handleDelete = async () => {
-    for (const partition of selectedPartitions) {
-      const param: PartitionManageParam = {
-        partitionName: partition._name,
-        collectionName,
-        type: ManageRequestMethods.DELETE,
-      };
-      await PartitionHttp.managePartition(param);
-    }
-
-    openSnackBar(successTrans('delete', { name: t('partition') }));
-    fetchPartitions(collectionName);
-    handleCloseDialog();
-  };
+  }, [collectionName]);
 
 
-  const handleSearch = (value: string) => {
+  useEffect(() => {
     if (timer) {
     if (timer) {
       clearTimeout(timer);
       clearTimeout(timer);
     }
     }
     // add loading manually
     // add loading manually
     setLoading(true);
     setLoading(true);
     timer = setTimeout(() => {
     timer = setTimeout(() => {
-      const searchWords = [value];
-      const list = value
-        ? partitions.filter(p => p._formatName.includes(value))
+      const searchWords = [search];
+      const list = search
+        ? partitions.filter(p => p._formatName.includes(search))
         : partitions;
         : partitions;
 
 
       const highlightList = list.map(c => {
       const highlightList = list.map(c => {
@@ -162,6 +123,25 @@ const Partitions: FC<{
       setLoading(false);
       setLoading(false);
       setSearchedPartitions(highlightList);
       setSearchedPartitions(highlightList);
     }, 300);
     }, 300);
+  }, [search, partitions]);
+
+  const handleDelete = async () => {
+    for (const partition of selectedPartitions) {
+      const param: PartitionManageParam = {
+        partitionName: partition._name,
+        collectionName,
+        type: ManageRequestMethods.DELETE,
+      };
+      await PartitionHttp.managePartition(param);
+    }
+
+    openSnackBar(successTrans('delete', { name: t('partition') }));
+    fetchPartitions(collectionName);
+    handleCloseDialog();
+  };
+
+  const handleSearch = (value: string) => {
+    setSearch(value);
   };
   };
 
 
   const handleInsert = async (
   const handleInsert = async (
@@ -248,7 +228,7 @@ const Partitions: FC<{
           params: {
           params: {
             component: (
             component: (
               <DeleteTemplate
               <DeleteTemplate
-                label={btnTrans('delete')}
+                label={btnTrans('drop')}
                 title={dialogTrans('deleteTitle', { type: t('partition') })}
                 title={dialogTrans('deleteTitle', { type: t('partition') })}
                 text={t('deleteWarning')}
                 text={t('deleteWarning')}
                 handleDelete={handleDelete}
                 handleDelete={handleDelete}

+ 7 - 9
client/src/pages/preview/Preview.tsx

@@ -11,6 +11,7 @@ import { ToolBarConfig } from '../../components/grid/Types';
 import CustomToolBar from '../../components/grid/ToolBar';
 import CustomToolBar from '../../components/grid/ToolBar';
 import { DataTypeStringEnum } from '../collections/Types';
 import { DataTypeStringEnum } from '../collections/Types';
 import { generateVector } from '../../utils/Common';
 import { generateVector } from '../../utils/Common';
+import { DataTypeEnum } from '../../pages/collections/Types';
 
 
 import {
 import {
   INDEX_CONFIG,
   INDEX_CONFIG,
@@ -89,10 +90,11 @@ const Preview: FC<{
     const vectorField = schemaList.find(
     const vectorField = schemaList.find(
       v => v.data_type === 'FloatVector' || v.data_type === 'BinaryVector'
       v => v.data_type === 'FloatVector' || v.data_type === 'BinaryVector'
     );
     );
-
     const anns_field = vectorField?._fieldName!;
     const anns_field = vectorField?._fieldName!;
     const dim = Number(vectorField?._dimension);
     const dim = Number(vectorField?._dimension);
-    const vectors = [generateVector(dim)];
+    const vectors = [
+      generateVector(vectorField?.data_type === 'FloatVector' ? dim : dim / 8),
+    ];
     // get search params
     // get search params
     const indexesInfo = await IndexHttp.getIndexInfo(collectionName);
     const indexesInfo = await IndexHttp.getIndexInfo(collectionName);
     const indexType =
     const indexType =
@@ -105,13 +107,9 @@ const Preview: FC<{
     const searchParam = { [searchParamKey]: searchParamValue };
     const searchParam = { [searchParamKey]: searchParamValue };
     const params = `${JSON.stringify(searchParam)}`;
     const params = `${JSON.stringify(searchParam)}`;
     setPrimaryKey(primaryKey);
     setPrimaryKey(primaryKey);
-    // Temporarily hide bool field due to incorrect return from SDK.
-    const fieldWithoutBool = nameList.filter(
-      i => i.type !== DataTypeStringEnum.Bool
-    );
 
 
     // set fields
     // set fields
-    setFields(fieldWithoutBool);
+    setFields(nameList);
 
 
     // set loading
     // set loading
     setTableLoading(true);
     setTableLoading(true);
@@ -128,7 +126,7 @@ const Preview: FC<{
         expr: '',
         expr: '',
         vectors,
         vectors,
         output_fields: [primaryKey],
         output_fields: [primaryKey],
-        vector_type: Number(vectorField?._fieldId),
+        vector_type: DataTypeEnum[vectorField!._fieldType],
       });
       });
 
 
       // compose random id list expression
       // compose random id list expression
@@ -139,7 +137,7 @@ const Preview: FC<{
       // query by random id
       // query by random id
       const res = await CollectionHttp.queryData(collectionName, {
       const res = await CollectionHttp.queryData(collectionName, {
         expr: expr,
         expr: expr,
-        output_fields: fieldWithoutBool.map(i => i.name),
+        output_fields: nameList.map(i => i.name),
       });
       });
 
 
       const result = res.data;
       const result = res.data;

+ 2 - 6
client/src/pages/query/Query.tsx

@@ -115,11 +115,7 @@ const Query: FC<{
     const primaryKey =
     const primaryKey =
       schemaList.find(v => v._isPrimaryKey === true)?._fieldName || '';
       schemaList.find(v => v._isPrimaryKey === true)?._fieldName || '';
     setPrimaryKey(primaryKey);
     setPrimaryKey(primaryKey);
-    // Temporarily hide bool field due to incorrect return from SDK.
-    const fieldWithoutBool = nameList.filter(
-      i => i.type !== DataTypeStringEnum.Bool
-    );
-    setFields(fieldWithoutBool);
+    setFields(nameList);
   };
   };
 
 
   // Get fields at first or collection name changed.
   // Get fields at first or collection name changed.
@@ -185,7 +181,7 @@ const Query: FC<{
           params: {
           params: {
             component: (
             component: (
               <DeleteTemplate
               <DeleteTemplate
-                label={btnTrans('delete')}
+                label={btnTrans('drop')}
                 title={dialogTrans('deleteTitle', {
                 title={dialogTrans('deleteTitle', {
                   type: collectionTrans('entites'),
                   type: collectionTrans('entites'),
                 })}
                 })}

+ 1 - 0
client/src/pages/schema/Create.tsx

@@ -256,6 +256,7 @@ const CreateIndex = (props: {
     // setDisabled(true);
     // setDisabled(true);
     setIndexSetting(v => ({
     setIndexSetting(v => ({
       ...v,
       ...v,
+      index_name: '',
       metric_type: defaultMetricType,
       metric_type: defaultMetricType,
       M: '',
       M: '',
       m: '4',
       m: '4',

+ 1 - 0
client/src/pages/schema/CreateForm.tsx

@@ -174,6 +174,7 @@ const CreateForm = (
         type="text"
         type="text"
         textConfig={indexNameConfig}
         textConfig={indexNameConfig}
         checkValid={checkIsValid}
         checkValid={checkIsValid}
+        validInfo={validation}
       />
       />
       <CustomSelector
       <CustomSelector
         label={indexTrans('type')}
         label={indexTrans('type')}

+ 1 - 1
client/src/pages/schema/IndexTypeElement.tsx

@@ -183,7 +183,7 @@ const IndexTypeElement: FC<{
       params: {
       params: {
         component: (
         component: (
           <DeleteTemplate
           <DeleteTemplate
-            label={btnTrans('delete')}
+            label={btnTrans('drop')}
             title={dialogTrans('deleteTitle', { type: indexTrans('index') })}
             title={dialogTrans('deleteTitle', { type: indexTrans('index') })}
             text={indexTrans('deleteWarning')}
             text={indexTrans('deleteWarning')}
             handleDelete={requestDeleteIndex}
             handleDelete={requestDeleteIndex}

+ 0 - 0
client/src/plugins/search/Constants.ts → client/src/pages/search/Constants.ts


+ 5 - 6
client/src/plugins/search/SearchParams.tsx → client/src/pages/search/SearchParams.tsx

@@ -176,7 +176,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           max: 10,
           max: 10,
           isInt: true,
           isInt: true,
           handleChange: value => {
           handleChange: value => {
-            handleInputChange('round_decimal', value);
+            handleInputChange('round_decimal', Number(value));
           },
           },
           className: classes.inlineInput,
           className: classes.inlineInput,
         },
         },
@@ -188,11 +188,10 @@ const SearchParams: FC<SearchParamsProps> = ({
           max: nlist,
           max: nlist,
           isInt: true,
           isInt: true,
           handleChange: value => {
           handleChange: value => {
-            handleInputChange('nprobe', value);
+            handleInputChange('nprobe', Number(value));
           },
           },
           className: classes.inlineInput,
           className: classes.inlineInput,
         },
         },
-
         ef: {
         ef: {
           label: 'ef',
           label: 'ef',
           key: 'ef',
           key: 'ef',
@@ -201,7 +200,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           max: 32768,
           max: 32768,
           isInt: true,
           isInt: true,
           handleChange: value => {
           handleChange: value => {
-            handleInputChange('ef', value);
+            handleInputChange('ef', Number(value));
           },
           },
         },
         },
         search_k: {
         search_k: {
@@ -213,7 +212,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           max: Infinity,
           max: Infinity,
           isInt: true,
           isInt: true,
           handleChange: value => {
           handleChange: value => {
-            handleInputChange('search_k', value);
+            handleInputChange('search_k', Number(value));
           },
           },
         },
         },
         search_length: {
         search_length: {
@@ -224,7 +223,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           max: 300,
           max: 300,
           isInt: true,
           isInt: true,
           handleChange: value => {
           handleChange: value => {
-            handleInputChange('search_length', value);
+            handleInputChange('search_length', Number(value));
           },
           },
         },
         },
       };
       };

+ 0 - 0
client/src/plugins/search/Styles.ts → client/src/pages/search/Styles.ts


+ 2 - 2
client/src/plugins/search/Types.ts → client/src/pages/search/Types.ts

@@ -3,8 +3,8 @@ import { searchKeywordsType } from '../../consts/Milvus';
 import {
 import {
   DataTypeEnum,
   DataTypeEnum,
   DataTypeStringEnum,
   DataTypeStringEnum,
-} from '../../pages/collections/Types';
-import { IndexView } from '../../pages/schema/Types';
+} from '../collections/Types';
+import { IndexView } from '../schema/Types';
 
 
 export interface SearchParamsProps {
 export interface SearchParamsProps {
   // if user created index, pass metric type choosed when creating
   // if user created index, pass metric type choosed when creating

+ 5 - 2
client/src/plugins/search/VectorSearch.tsx → client/src/pages/search/VectorSearch.tsx

@@ -16,7 +16,7 @@ import SimpleMenu from '../../components/menu/SimpleMenu';
 import { TOP_K_OPTIONS } from './Constants';
 import { TOP_K_OPTIONS } from './Constants';
 import { Option } from '../../components/customSelector/Types';
 import { Option } from '../../components/customSelector/Types';
 import { CollectionHttp } from '../../http/Collection';
 import { CollectionHttp } from '../../http/Collection';
-import { CollectionData, DataTypeEnum } from '../../pages/collections/Types';
+import { CollectionData, DataTypeEnum } from '../collections/Types';
 import { IndexHttp } from '../../http/Index';
 import { IndexHttp } from '../../http/Index';
 import { getVectorSearchStyles } from './Styles';
 import { getVectorSearchStyles } from './Styles';
 import { parseValue } from '../../utils/Insert';
 import { parseValue } from '../../utils/Insert';
@@ -33,6 +33,7 @@ import Filter from '../../components/advancedSearch';
 import { Field } from '../../components/advancedSearch/Types';
 import { Field } from '../../components/advancedSearch/Types';
 import { useLocation } from 'react-router-dom';
 import { useLocation } from 'react-router-dom';
 import { parseLocationSearch } from '../../utils/Format';
 import { parseLocationSearch } from '../../utils/Format';
+import { cloneObj } from '../../utils/Common';
 import { CustomDatePicker } from '../../components/customDatePicker/CustomDatePicker';
 import { CustomDatePicker } from '../../components/customDatePicker/CustomDatePicker';
 import { useTimeTravelHook } from '../../hooks/TimeTravel';
 import { useTimeTravelHook } from '../../hooks/TimeTravel';
 
 
@@ -296,8 +297,10 @@ const VectorSearch = () => {
   };
   };
 
 
   const handleSearch = async (topK: number, expr = expression) => {
   const handleSearch = async (topK: number, expr = expression) => {
+    const clonedSearchParams = cloneObj(searchParam);
+    delete clonedSearchParams.round_decimal;
     const searhParamPairs = {
     const searhParamPairs = {
-      params: JSON.stringify(searchParam),
+      params: JSON.stringify(clonedSearchParams),
       anns_field: selectedField,
       anns_field: selectedField,
       topk: topK,
       topk: topK,
       metric_type: selectedMetricType,
       metric_type: selectedMetricType,

+ 0 - 0
client/src/plugins/system/BaseCard.tsx → client/src/pages/system/BaseCard.tsx


+ 0 - 0
client/src/plugins/system/DataCard.tsx → client/src/pages/system/DataCard.tsx


+ 0 - 0
client/src/plugins/system/LineChartCard.tsx → client/src/pages/system/LineChartCard.tsx


+ 0 - 0
client/src/plugins/system/MiniTopology.tsx → client/src/pages/system/MiniTopology.tsx


+ 0 - 0
client/src/plugins/system/NodeListView.tsx → client/src/pages/system/NodeListView.tsx


+ 0 - 0
client/src/plugins/system/Progress.tsx → client/src/pages/system/Progress.tsx


+ 0 - 0
client/src/plugins/system/ProgressCard.tsx → client/src/pages/system/ProgressCard.tsx


+ 0 - 0
client/src/plugins/system/SystemView.tsx → client/src/pages/system/SystemView.tsx


+ 0 - 0
client/src/plugins/system/Topology.tsx → client/src/pages/system/Topology.tsx


+ 0 - 0
client/src/plugins/system/Types.ts → client/src/pages/system/Types.ts


+ 0 - 13
client/src/plugins/search/config.json

@@ -1,13 +0,0 @@
-{
-  "name": "search",
-  "version": "0.1.0",
-  "client": {
-    "path": "search",
-    "entry": "VectorSearch",
-    "label": "Vector Search",
-    "iconName": "navSearch",
-    "auth": true,
-    "iconActiveClass": "activeSearchIcon",
-    "iconNormalClass": "normalSearchIcon"
-  }
-}

+ 0 - 11
client/src/plugins/system/config.json

@@ -1,11 +0,0 @@
-{
-  "name": "system-view",
-  "version": "0.1.0",
-  "client": {
-    "path": "system",
-    "entry": "SystemView",
-    "label": "System View",
-    "iconName": "navSystem",
-    "auth": true
-  }
-}

+ 0 - 58
client/src/router/Config.ts

@@ -1,58 +0,0 @@
-import Collection from '../pages/collections/Collection';
-import Collections from '../pages/collections/Collections';
-import Connect from '../pages/connect/Connect';
-import Overview from '../pages/overview/Overview';
-// import VectorSearch from '../pages/seach/VectorSearch';
-import { RouterConfigType } from './Types';
-import loadable from '@loadable/component';
-import Users from '../pages/user/User';
-
-const RouterConfig: RouterConfigType[] = [
-  {
-    path: '/',
-    component: Overview,
-    auth: true,
-  },
-  {
-    path: '/connect',
-    component: Connect,
-    auth: false,
-  },
-  {
-    path: '/collections',
-    component: Collections,
-    auth: true,
-  },
-  {
-    path: '/collections/:collectionName',
-    component: Collection,
-    auth: true,
-  },
-  { path: '/users', component: Users, auth: true },
-];
-
-async function importAll(r: any) {
-  Object.keys(r).forEach((key: any) => {
-    const content = r[key];
-    const dirName = key.split('/config.json').shift().split('/')[1];
-    const pathName = content.client?.path;
-    const fileEntry = content.client?.entry;
-    if (!pathName || !fileEntry) return;
-    const auth = content.client?.auth || false;
-    const OtherComponent = loadable(
-      () => import(`../${dirName}/${pathName}/${fileEntry}.tsx`)
-    );
-    RouterConfig.push({
-      path: `/${pathName}`,
-      component: OtherComponent,
-      auth,
-    });
-  });
-}
-
-const pluginConfigs = import.meta.glob(`../plugins/**/config.json`, {
-  eager: true,
-});
-importAll(pluginConfigs);
-
-export default RouterConfig;

+ 40 - 51
client/src/router/Router.tsx

@@ -1,54 +1,43 @@
-import { Switch, Route, Redirect, HashRouter } from 'react-router-dom';
-import routerConfig from './Config';
-import Layout from '../components/layout/Layout';
-import { useContext } from 'react';
-import { authContext } from '../context/Auth';
-/**
- * Global responsible for global effect
- * Layout responsible for ui view
- *
- */
-const RouterWrapper = () => {
-  const { isAuth } = useContext(authContext);
+import { createHashRouter, RouterProvider } from 'react-router-dom';
+import Collection from '../pages/collections/Collection';
+import Collections from '../pages/collections/Collections';
+import Connect from '../pages/connect/Connect';
+import Users from '../pages/user/User';
+import Index from '../pages/index';
+import Search from '../pages/search/VectorSearch';
+import System from '../pages/system/SystemView';
 
 
-  return (
-    <HashRouter>
-      <Layout>
-        <Switch>
-          {routerConfig.map(v => (
-            <Route
-              exact
-              key={v.path}
-              path={v.path}
-              render={() => {
-                const Page = v.component;
-                return isAuth || !v.auth ? (
-                  <Page />
-                ) : (
-                  <Redirect
-                    to={{
-                      pathname: '/connect',
-                    }}
-                  />
-                );
-              }}
-            />
-          ))}
+const router = createHashRouter([
+  {
+    path: '/',
+    element: <Index />,
+    children: [
+      {
+        path: '/collections',
+        element: <Collections />,
+      },
+      {
+        path: '/collections/:collectionName',
+        element: <Collection />,
+      },
+      {
+        path: '/users',
+        element: <Users />,
+      },
+      {
+        path: '/search',
+        element: <Search />,
+      },
+      {
+        path: '/system',
+        element: <System />,
+      },
+    ],
+  },
+  { path: '/connect', element: <Connect /> },
+]);
 
 
-          <Route
-            render={() => {
-              return (
-                <Redirect
-                  to={{
-                    pathname: '/connect',
-                  }}
-                />
-              );
-            }}
-          ></Route>
-        </Switch>
-      </Layout>
-    </HashRouter>
-  );
+const Router = () => {
+  return <RouterProvider router={router}></RouterProvider>;
 };
 };
-export default RouterWrapper;
+export default Router;

+ 0 - 6
client/src/router/Types.ts

@@ -18,9 +18,3 @@ export type NavInfo = {
   navTitle: string;
   navTitle: string;
   backPath: string;
   backPath: string;
 };
 };
-
-export type RouterConfigType = {
-  path: string;
-  component: any;
-  auth: boolean;
-};

+ 5 - 1
client/src/utils/Common.ts

@@ -63,5 +63,9 @@ export const generateIdByHash = (salt?: string) => {
 };
 };
 
 
 export const generateVector = (dim: number) => {
 export const generateVector = (dim: number) => {
-  return Array.from({ length: dim }).map(() => Math.random());
+  return Array.from({ length: dim }).map(() => (Math.random() > 0.5 ? 1 : 0));
+};
+
+export const cloneObj = (obj: any) => {
+  return JSON.parse(JSON.stringify(obj));
 };
 };

+ 63 - 126
client/yarn.lock

@@ -378,7 +378,7 @@
     "@babel/plugin-syntax-jsx" "^7.18.6"
     "@babel/plugin-syntax-jsx" "^7.18.6"
     "@babel/types" "^7.19.0"
     "@babel/types" "^7.19.0"
 
 
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7":
   version "7.16.0"
   version "7.16.0"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.0.tgz#e27b977f2e2088ba24748bf99b5e1dece64e4f0b"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.0.tgz#e27b977f2e2088ba24748bf99b5e1dece64e4f0b"
   integrity sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==
   integrity sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==
@@ -692,6 +692,11 @@
     prop-types "^15.7.2"
     prop-types "^15.7.2"
     reselect "^4.0.0"
     reselect "^4.0.0"
 
 
+"@remix-run/router@1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.3.tgz#953b88c20ea00d0eddaffdc1b115c08474aa295d"
+  integrity sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==
+
 "@rollup/pluginutils@^4.1.1":
 "@rollup/pluginutils@^4.1.1":
   version "4.2.1"
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
   resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
@@ -884,6 +889,11 @@
   resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724"
   resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724"
   integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
   integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
 
 
+"@types/history@^4.7.11":
+  version "4.7.11"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
+  integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
+
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
   version "2.0.3"
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -950,13 +960,20 @@
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
   integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
   integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
 
 
-"@types/react-dom@>=16.9.0", "@types/react-dom@^17.0.0":
+"@types/react-dom@>=16.9.0":
   version "17.0.11"
   version "17.0.11"
   resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466"
   resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466"
   integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==
   integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==
   dependencies:
   dependencies:
     "@types/react" "*"
     "@types/react" "*"
 
 
+"@types/react-dom@^18.0.8":
+  version "18.0.8"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.8.tgz#d2606d855186cd42cc1b11e63a71c39525441685"
+  integrity sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-highlight-words@^0.16.2":
 "@types/react-highlight-words@^0.16.2":
   version "0.16.3"
   version "0.16.3"
   resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.3.tgz#a62e8d31197a33559c38fcbe4c7c1e10d3f78cd8"
   resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.3.tgz#a62e8d31197a33559c38fcbe4c7c1e10d3f78cd8"
@@ -971,12 +988,12 @@
   dependencies:
   dependencies:
     "@types/react" "*"
     "@types/react" "*"
 
 
-"@types/react-router-dom@^5.1.7":
-  version "5.3.2"
-  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.2.tgz#ebd8e145cf056db5c66eb1dac63c72f52e8542ee"
-  integrity sha512-ELEYRUie2czuJzaZ5+ziIp9Hhw+juEw8b7C11YNA4QdLCVbQ3qLi2l4aq8XnlqM7V31LZX8dxUuFUCrzHm6sqQ==
+"@types/react-router-dom@^5.3.3":
+  version "5.3.3"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
+  integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
   dependencies:
   dependencies:
-    "@types/history" "*"
+    "@types/history" "^4.7.11"
     "@types/react" "*"
     "@types/react" "*"
     "@types/react-router" "*"
     "@types/react-router" "*"
 
 
@@ -1009,7 +1026,7 @@
   dependencies:
   dependencies:
     "@types/react" "*"
     "@types/react" "*"
 
 
-"@types/react@*", "@types/react@>=16.9.0", "@types/react@^17.0.0":
+"@types/react@*", "@types/react@>=16.9.0":
   version "17.0.34"
   version "17.0.34"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102"
   integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg==
   integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg==
@@ -1018,6 +1035,15 @@
     "@types/scheduler" "*"
     "@types/scheduler" "*"
     csstype "^3.0.2"
     csstype "^3.0.2"
 
 
+"@types/react@^18.0.25":
+  version "18.0.25"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44"
+  integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==
+  dependencies:
+    "@types/prop-types" "*"
+    "@types/scheduler" "*"
+    csstype "^3.0.2"
+
 "@types/scheduler@*":
 "@types/scheduler@*":
   version "0.16.2"
   version "0.16.2"
   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
@@ -2030,19 +2056,7 @@ highlight.js@^10.4.1, highlight.js@~10.7.0:
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
   resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
   integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
   integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
 
 
-history@^4.9.0:
-  version "4.10.1"
-  resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
-  integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
-  dependencies:
-    "@babel/runtime" "^7.1.2"
-    loose-envify "^1.2.0"
-    resolve-pathname "^3.0.0"
-    tiny-invariant "^1.0.2"
-    tiny-warning "^1.0.0"
-    value-equal "^1.0.1"
-
-hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
   version "3.3.2"
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
   resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
   integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
   integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -2227,23 +2241,11 @@ is-number@^7.0.0:
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
   integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
   integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
 
 
-is-plain-object@^2.0.4:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
-  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
-  dependencies:
-    isobject "^3.0.1"
-
 is-potential-custom-element-name@^1.0.1:
 is-potential-custom-element-name@^1.0.1:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
   resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
   integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
   integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
 
 
-is-primitive@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-3.0.1.tgz#98c4db1abff185485a657fc2905052b940524d05"
-  integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==
-
 is-regex@^1.1.4:
 is-regex@^1.1.4:
   version "1.1.4"
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@@ -2295,11 +2297,6 @@ is-weakset@^2.0.1:
     call-bind "^1.0.2"
     call-bind "^1.0.2"
     get-intrinsic "^1.1.1"
     get-intrinsic "^1.1.1"
 
 
-isarray@0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
-  integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
-
 isarray@^2.0.5:
 isarray@^2.0.5:
   version "2.0.5"
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
@@ -2310,11 +2307,6 @@ isexe@^2.0.0:
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
   integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
   integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
 
 
-isobject@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
-  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-
 istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
 istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
   version "3.2.0"
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
@@ -2548,7 +2540,7 @@ lodash@^4.17.15:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
 
-loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+loose-envify@^1.1.0, loose-envify@^1.4.0:
   version "1.4.0"
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
   integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -2619,14 +2611,6 @@ min-indent@^1.0.0:
   resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
   resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
   integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
   integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
 
 
-mini-create-react-context@^0.4.0:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
-  integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-    tiny-warning "^1.0.3"
-
 minimatch@^3.0.4, minimatch@^3.1.1:
 minimatch@^3.0.4, minimatch@^3.1.1:
   version "3.1.2"
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -2801,13 +2785,6 @@ path-parse@^1.0.7:
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
 
-path-to-regexp@^1.7.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
-  integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
-  dependencies:
-    isarray "0.0.1"
-
 path-type@^4.0.0:
 path-type@^4.0.0:
   version "4.0.0"
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -2906,14 +2883,13 @@ querystringify@^2.1.1:
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
   integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
   integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
 
 
-react-dom@^17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
-  integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    scheduler "^0.20.2"
+    scheduler "^0.23.0"
 
 
 react-error-boundary@^3.1.0:
 react-error-boundary@^3.1.0:
   version "3.1.4"
   version "3.1.4"
@@ -2939,7 +2915,7 @@ react-i18next@^12.0.0:
     "@babel/runtime" "^7.14.5"
     "@babel/runtime" "^7.14.5"
     html-parse-stringify "^3.0.1"
     html-parse-stringify "^3.0.1"
 
 
-react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
+react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1:
   version "16.13.1"
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -2964,34 +2940,20 @@ react-refresh@^0.14.0:
   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
   integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
   integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
 
 
-react-router-dom@^5.2.0:
-  version "5.3.0"
-  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363"
-  integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==
+react-router-dom@^6.4.3:
+  version "6.4.3"
+  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.3.tgz#70093b5f65f85f1df9e5d4182eb7ff3a08299275"
+  integrity sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==
   dependencies:
   dependencies:
-    "@babel/runtime" "^7.12.13"
-    history "^4.9.0"
-    loose-envify "^1.3.1"
-    prop-types "^15.6.2"
-    react-router "5.2.1"
-    tiny-invariant "^1.0.2"
-    tiny-warning "^1.0.0"
+    "@remix-run/router" "1.0.3"
+    react-router "6.4.3"
 
 
-react-router@5.2.1:
-  version "5.2.1"
-  resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d"
-  integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==
-  dependencies:
-    "@babel/runtime" "^7.12.13"
-    history "^4.9.0"
-    hoist-non-react-statics "^3.1.0"
-    loose-envify "^1.3.1"
-    mini-create-react-context "^0.4.0"
-    path-to-regexp "^1.7.0"
-    prop-types "^15.6.2"
-    react-is "^16.6.0"
-    tiny-invariant "^1.0.2"
-    tiny-warning "^1.0.0"
+react-router@6.4.3:
+  version "6.4.3"
+  resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.3.tgz#9ed3ee4d6e95889e9b075a5d63e29acc7def0d49"
+  integrity sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==
+  dependencies:
+    "@remix-run/router" "1.0.3"
 
 
 react-syntax-highlighter@^15.4.4:
 react-syntax-highlighter@^15.4.4:
   version "15.4.4"
   version "15.4.4"
@@ -3014,13 +2976,12 @@ react-transition-group@^4.0.0, react-transition-group@^4.4.0:
     loose-envify "^1.4.0"
     loose-envify "^1.4.0"
     prop-types "^15.6.2"
     prop-types "^15.6.2"
 
 
-react@^17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
-  integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 
 redent@^3.0.0:
 redent@^3.0.0:
   version "3.0.0"
   version "3.0.0"
@@ -3078,11 +3039,6 @@ resolve-from@^4.0.0:
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
   integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
   integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
 
 
-resolve-pathname@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
-  integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
-
 resolve@^1.22.1:
 resolve@^1.22.1:
   version "1.22.1"
   version "1.22.1"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
@@ -3130,27 +3086,18 @@ saxes@^6.0.0:
   dependencies:
   dependencies:
     xmlchars "^2.2.0"
     xmlchars "^2.2.0"
 
 
-scheduler@^0.20.2:
-  version "0.20.2"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
-  integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 
 semver@^6.0.0, semver@^6.3.0:
 semver@^6.0.0, semver@^6.3.0:
   version "6.3.0"
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
 
-set-value@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.1.0.tgz#aa433662d87081b75ad88a4743bd450f044e7d09"
-  integrity sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==
-  dependencies:
-    is-plain-object "^2.0.4"
-    is-primitive "^3.0.1"
-
 shebang-command@^2.0.0:
 shebang-command@^2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -3302,12 +3249,7 @@ test-exclude@^6.0.0:
     glob "^7.1.4"
     glob "^7.1.4"
     minimatch "^3.0.4"
     minimatch "^3.0.4"
 
 
-tiny-invariant@^1.0.2:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
-  integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
-
-tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3:
+tiny-warning@^1.0.2:
   version "1.0.3"
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
   resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
   integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
   integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
@@ -3403,11 +3345,6 @@ v8-to-istanbul@^9.0.0:
     "@types/istanbul-lib-coverage" "^2.0.1"
     "@types/istanbul-lib-coverage" "^2.0.1"
     convert-source-map "^1.6.0"
     convert-source-map "^1.6.0"
 
 
-value-equal@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
-  integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
-
 vite-plugin-svgr@^0.3.0:
 vite-plugin-svgr@^0.3.0:
   version "0.3.0"
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-0.3.0.tgz#c81ac7541df98d361249f9ac06b901ae22744a1b"
   resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-0.3.0.tgz#c81ac7541df98d361249f9ac06b901ae22744a1b"

+ 2 - 2
server/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "attu",
   "name": "attu",
-  "version": "2.1.4",
+  "version": "2.2.0",
   "license": "MIT",
   "license": "MIT",
   "author": {
   "author": {
     "name": "zilliz",
     "name": "zilliz",
@@ -12,7 +12,7 @@
     "url": "https://github.com/zilliztech/attu"
     "url": "https://github.com/zilliztech/attu"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@zilliz/milvus2-sdk-node": "^2.1.3",
+    "@zilliz/milvus2-sdk-node": "^2.2.0",
     "chalk": "^4.1.2",
     "chalk": "^4.1.2",
     "class-sanitizer": "^1.0.1",
     "class-sanitizer": "^1.0.1",
     "class-transformer": "^0.4.0",
     "class-transformer": "^0.4.0",

+ 11 - 0
server/src/collections/collections.controller.ts

@@ -47,6 +47,7 @@ export class CollectionController {
     );
     );
 
 
     this.router.delete('/:name', this.dropCollection.bind(this));
     this.router.delete('/:name', this.dropCollection.bind(this));
+    this.router.delete('/:name/alias/:alias', this.dropAlias.bind(this));
 
 
     this.router.get('/:name', this.describeCollection.bind(this));
     this.router.get('/:name', this.describeCollection.bind(this));
 
 
@@ -294,4 +295,14 @@ export class CollectionController {
       next(error);
       next(error);
     }
     }
   }
   }
+
+  async dropAlias(req: Request, res: Response, next: NextFunction) {
+    const alias = req.params?.alias;
+    try {
+      const result = await this.collectionsService.dropAlias({ alias });
+      res.send(result);
+    } catch (error) {
+      next(error);
+    }
+  }
 }
 }

+ 1 - 0
server/src/collections/collections.service.ts

@@ -176,6 +176,7 @@ export class CollectionsService {
           : loadCollection.loadedPercentage;
           : loadCollection.loadedPercentage;
 
 
         data.push({
         data.push({
+          aliases: collectionInfo.aliases,
           collection_name: name,
           collection_name: name,
           schema: collectionInfo.schema,
           schema: collectionInfo.schema,
           description: collectionInfo.schema.description,
           description: collectionInfo.schema.description,

+ 3 - 1
server/src/collections/dto.ts

@@ -72,7 +72,9 @@ export class VectorSearchDto {
   @IsOptional()
   @IsOptional()
   output_fields?: string[];
   output_fields?: string[];
 
 
-  @IsEnum(VectorTypes, { message: 'Type allow all->0 inmemory->1' })
+  @IsEnum(VectorTypes, {
+    message: ({ value }) => `Wrong vector type, ${value}`,
+  })
   vector_type: DataType.BinaryVector | DataType.FloatVector;
   vector_type: DataType.BinaryVector | DataType.FloatVector;
 }
 }
 
 

+ 4 - 0
server/src/utils/Helper.ts

@@ -20,6 +20,10 @@ export const genDataByType = ({ data_type, type_params }: FieldSchema) => {
       return Array.from({ length: (type_params as any)[0].value }).map(() =>
       return Array.from({ length: (type_params as any)[0].value }).map(() =>
         Math.random()
         Math.random()
       );
       );
+    case 'BinaryVector':
+      return Array.from({ length: (type_params as any)[0].value / 8 }).map(() =>
+        Math.random() > 0.5 ? 1 : 0
+      );
     case 'VarChar':
     case 'VarChar':
       return makeRandomId((type_params as any)[0].value);
       return makeRandomId((type_params as any)[0].value);
   }
   }

+ 28 - 13
server/yarn.lock

@@ -412,7 +412,7 @@
     "@grpc/proto-loader" "^0.6.4"
     "@grpc/proto-loader" "^0.6.4"
     "@types/node" ">=12.12.47"
     "@types/node" ">=12.12.47"
 
 
-"@grpc/proto-loader@^0.6.0", "@grpc/proto-loader@^0.6.4":
+"@grpc/proto-loader@^0.6.4":
   version "0.6.6"
   version "0.6.6"
   resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.6.tgz#d8e51ea808ec5fa54d9defbecbf859336fb2da71"
   resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.6.tgz#d8e51ea808ec5fa54d9defbecbf859336fb2da71"
   integrity sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==
   integrity sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==
@@ -423,6 +423,17 @@
     protobufjs "^6.10.0"
     protobufjs "^6.10.0"
     yargs "^16.1.1"
     yargs "^16.1.1"
 
 
+"@grpc/proto-loader@^0.7.3":
+  version "0.7.3"
+  resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.3.tgz#75a6f95b51b85c5078ac7394da93850c32d36bb8"
+  integrity sha512-5dAvoZwna2Py3Ef96Ux9jIkp3iZ62TUsV00p3wVBPNX5K178UbNi8Q7gQVqwXT1Yq9RejIGG9G2IPEo93T6RcA==
+  dependencies:
+    "@types/long" "^4.0.1"
+    lodash.camelcase "^4.3.0"
+    long "^4.0.0"
+    protobufjs "^7.0.0"
+    yargs "^16.2.0"
+
 "@istanbuljs/load-nyc-config@^1.0.0":
 "@istanbuljs/load-nyc-config@^1.0.0":
   version "1.1.0"
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
   resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -1151,18 +1162,18 @@
   dependencies:
   dependencies:
     "@types/node" "*"
     "@types/node" "*"
 
 
-"@zilliz/milvus2-sdk-node@^2.1.3":
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.1.3.tgz#f8f54e1385f67edc4485ba3724b8b20f45537303"
-  integrity sha512-HZEd+iQ/YfYxsrxqA8fdozNHPXqrIbMW9Zo8LEFcCSqMtLx4dFhw2gQNA6KWdGyRICCNffUtAazHWTKBrRXBmA==
+"@zilliz/milvus2-sdk-node@^2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.2.0.tgz#c90eef9af424ec804e3b9b891f35251cb6e9209d"
+  integrity sha512-Vs5eIp5ydoHI2TztY7ilpEfj+SlNK7oMVtyDLZE0lVP253NF+fm/1jc/RKuKM+Jy8pQSkiEP1BFz85wPB0qemQ==
   dependencies:
   dependencies:
     "@grpc/grpc-js" "^1.2.12"
     "@grpc/grpc-js" "^1.2.12"
-    "@grpc/proto-loader" "^0.6.0"
+    "@grpc/proto-loader" "^0.7.3"
     "@microsoft/api-documenter" "^7.13.39"
     "@microsoft/api-documenter" "^7.13.39"
     "@microsoft/api-extractor" "^7.18.5"
     "@microsoft/api-extractor" "^7.18.5"
     json-schema "^0.4.0"
     json-schema "^0.4.0"
     node-forge "^1.0.0"
     node-forge "^1.0.0"
-    protobufjs "^6.11.3"
+    protobufjs "^7.1.2"
 
 
 abab@^2.0.3, abab@^2.0.5:
 abab@^2.0.3, abab@^2.0.5:
   version "2.0.5"
   version "2.0.5"
@@ -3805,6 +3816,11 @@ long@^4.0.0:
   resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
   resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
   integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
   integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
 
 
+long@^5.0.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f"
+  integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==
+
 lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
 lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
   resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@@ -4318,10 +4334,10 @@ protobufjs@^6.10.0:
     "@types/node" ">=13.7.0"
     "@types/node" ">=13.7.0"
     long "^4.0.0"
     long "^4.0.0"
 
 
-protobufjs@^6.11.3:
-  version "6.11.3"
-  resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74"
-  integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==
+protobufjs@^7.0.0, protobufjs@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.1.2.tgz#a0cf6aeaf82f5625bffcf5a38b7cd2a7de05890c"
+  integrity sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==
   dependencies:
   dependencies:
     "@protobufjs/aspromise" "^1.1.2"
     "@protobufjs/aspromise" "^1.1.2"
     "@protobufjs/base64" "^1.1.2"
     "@protobufjs/base64" "^1.1.2"
@@ -4333,9 +4349,8 @@ protobufjs@^6.11.3:
     "@protobufjs/path" "^1.1.2"
     "@protobufjs/path" "^1.1.2"
     "@protobufjs/pool" "^1.1.0"
     "@protobufjs/pool" "^1.1.0"
     "@protobufjs/utf8" "^1.1.0"
     "@protobufjs/utf8" "^1.1.0"
-    "@types/long" "^4.0.1"
     "@types/node" ">=13.7.0"
     "@types/node" ">=13.7.0"
-    long "^4.0.0"
+    long "^5.0.0"
 
 
 proxy-addr@~2.0.5:
 proxy-addr@~2.0.5:
   version "2.0.7"
   version "2.0.7"