Browse Source

Merge branch 'main' of github.com:milvus-io/milvus-insight into main

nameczz 4 năm trước cách đây
mục cha
commit
375ae6c1f1

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 1 - 1
client/public/index.html

@@ -30,7 +30,7 @@
       work correctly both with client-side routing and a non-root public URL.
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
-    <title>Milvus Admin</title>
+    <title>Milvus Insight</title>
   </head>
 
   <body>

+ 18 - 0
client/src/components/grid/Table.tsx

@@ -117,6 +117,7 @@ const EnhancedTable: FC<TableType> = props => {
     noData,
     showHoverStyle,
     isLoading,
+    setPageSize,
   } = props;
   const classes = useStyles();
   const [order, setOrder] = React.useState('asc');
@@ -125,6 +126,7 @@ const EnhancedTable: FC<TableType> = props => {
   const [loadingRowCount, setLoadingRowCount] = useState<number>(0);
 
   const containerRef = useRef(null);
+  const rowRef = useRef(null);
 
   const handleRequestSort = (event: any, property: string) => {
     const isAsc = orderBy === property && order === 'asc';
@@ -139,6 +141,21 @@ const EnhancedTable: FC<TableType> = props => {
     setLoadingRowCount(count);
   }, []);
 
+  useEffect(() => {
+    if (setPageSize) {
+      const containerHeight: number = (containerRef.current as any)!
+        .offsetHeight;
+      const rowHeight: number = (rowRef.current as any)?.offsetHeight || 0;
+      const tableHeaderHeight: number = 57;
+      if (rowHeight > 0) {
+        const pageSize = Math.floor(
+          (containerHeight - tableHeaderHeight) / rowHeight
+        );
+        setPageSize(pageSize);
+      }
+    }
+  }, [setPageSize]);
+
   return (
     <TableContainer ref={containerRef} className={classes.root}>
       <Box height="100%" className={classes.box}>
@@ -183,6 +200,7 @@ const EnhancedTable: FC<TableType> = props => {
 
                     return (
                       <TableRow
+                        ref={rowRef}
                         hover={showHoverStyle}
                         key={'row' + row[primaryKey] + index}
                         onClick={event => onSelected(event, row)}

+ 3 - 0
client/src/components/grid/Types.ts

@@ -66,6 +66,7 @@ export type TableType = {
   noData?: string;
   showHoverStyle?: boolean;
   isLoading?: boolean;
+  setPageSize?: (size: number) => void;
 };
 
 export type ColDefinitionsType = {
@@ -93,6 +94,8 @@ export type ColDefinitionsType = {
 export type MilvusGridType = ToolBarType & {
   rowCount: number;
   rowsPerPage?: number;
+  // used to dynamic set page size by table container and row height
+  setRowsPerPage?: (size: number) => void;
   primaryKey: string;
   onChangePage?: (e: any, nextPageNum: number) => void;
   labelDisplayedRows?: (obj: any) => string;

+ 2 - 0
client/src/components/grid/index.tsx

@@ -102,6 +102,7 @@ const MilvusGrid: FC<MilvusGridType> = props => {
     showHoverStyle = true,
     selected = [],
     setSelected = () => {},
+    setRowsPerPage = () => {},
   } = props;
 
   const _isSelected = (row: { [x: string]: any }) => {
@@ -206,6 +207,7 @@ const MilvusGrid: FC<MilvusGridType> = props => {
           noData={noData}
           showHoverStyle={showHoverStyle}
           isLoading={isLoading}
+          setPageSize={setRowsPerPage}
         ></Table>
         {rowCount ? (
           <TablePagination

+ 10 - 5
client/src/hooks/Pagination.ts

@@ -1,26 +1,31 @@
 import { useMemo, useState } from 'react';
 
-const PAGE_SIZE = 10;
 export const usePaginationHook = (list: any[]) => {
   const [currentPage, setCurrentPage] = useState(0);
+  const [pageSize, setPageSize] = useState(10);
 
   const total = list.length;
   const { data, offset } = useMemo(() => {
-    const offset = PAGE_SIZE * currentPage;
+    const offset = pageSize * currentPage;
     return {
       offset,
-      data: list.slice(offset, offset + PAGE_SIZE),
+      data: list.slice(offset, offset + pageSize),
     };
-  }, [list, currentPage]);
+  }, [list, currentPage, pageSize]);
 
   const handleCurrentPage = (page: number) => {
     setCurrentPage(page);
   };
 
+  const handlePageSize = (size: number) => {
+    setPageSize(size);
+  };
+
   return {
     offset,
     currentPage,
-    pageSize: PAGE_SIZE,
+    pageSize,
+    handlePageSize,
     handleCurrentPage,
     total,
     data,

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

@@ -9,6 +9,7 @@ export class CollectionHttp extends BaseModel implements CollectionView {
   private description!: string;
   private rowCount!: string;
   private index_status!: string;
+  private id!: string;
 
   static COLLECTIONS_URL = '/collections';
   static COLLECTIONS_INDEX_STATUS_URL = '/collections/indexes/status';
@@ -60,7 +61,7 @@ export class CollectionHttp extends BaseModel implements CollectionView {
   }
 
   get _id() {
-    return '12';
+    return this.id;
   }
 
   get _name() {

+ 11 - 1
client/src/http/Index.ts

@@ -1,8 +1,10 @@
 import {
   IndexCreateParam,
+  IndexManageParam,
   IndexView,
   ParamPair,
 } from '../pages/structure/Types';
+import { ManageRequestMethods } from '../types/Common';
 import { IndexState } from '../types/Milvus';
 import BaseModel from './BaseModel';
 
@@ -40,13 +42,21 @@ export class IndexHttp extends BaseModel implements IndexView {
 
   static async createIndex(param: IndexCreateParam) {
     const path = this.BASE_URL;
+    const type: ManageRequestMethods = ManageRequestMethods.CREATE;
 
     return super.create({
       path,
-      data: { ...param },
+      data: { ...param, type },
     });
   }
 
+  static async deleteIndex(param: IndexManageParam) {
+    const path = this.BASE_URL;
+    const type: ManageRequestMethods = ManageRequestMethods.DELETE;
+
+    return super.batchDelete({ path, data: { ...param, type } });
+  }
+
   get _indexType() {
     return this.params.find(p => p.key === 'index_type')?.value || '';
   }

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

@@ -7,6 +7,8 @@ const indexTrans = {
   metric: 'Metric Type',
 
   createSuccess: 'Creating index successfully',
+  deleteWarning:
+    'You are trying to delete an index. This action cannot be undone.',
 };
 
 export default indexTrans;

+ 2 - 0
client/src/i18n/en/index.ts

@@ -7,6 +7,8 @@ const indexTrans = {
 
   metric: 'Metric Type',
   createSuccess: 'Creating index successfully',
+  deleteWarning:
+    'You are trying to delete an index. This action cannot be undone.',
 };
 
 export default indexTrans;

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

@@ -25,7 +25,7 @@ const Collection = () => {
   const history = useHistory();
   const location = useLocation();
 
-  const { t } = useTranslation('collection');
+  const { t: collectionTrans } = useTranslation('collection');
 
   const activeTabIndex = useMemo(() => {
     const { activeIndex } = location.search
@@ -41,11 +41,11 @@ const Collection = () => {
 
   const tabs: ITab[] = [
     {
-      label: t('partitionTab'),
+      label: collectionTrans('partitionTab'),
       component: <Partitions collectionName={collectionName} />,
     },
     {
-      label: t('structureTab'),
+      label: collectionTrans('structureTab'),
       component: <Structure collectionName={collectionName} />,
     },
   ];

+ 26 - 16
client/src/pages/collections/Collections.tsx

@@ -46,6 +46,7 @@ const Collections = () => {
   const [collections, setCollections] = useState<CollectionView[]>([]);
   const {
     pageSize,
+    handlePageSize,
     currentPage,
     handleCurrentPage,
     total,
@@ -58,7 +59,7 @@ const Collections = () => {
 
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
-  const { t } = useTranslation('collection');
+  const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: successTrans } = useTranslation('success');
@@ -118,20 +119,24 @@ const Collections = () => {
     );
     await CollectionHttp.createCollection(data);
     handleCloseDialog();
-    openSnackBar(successTrans('create', { name: t('collection') }));
+    openSnackBar(
+      successTrans('create', { name: collectionTrans('collection') })
+    );
     fetchData();
   };
 
   const handleRelease = async (data: CollectionView) => {
     const res = await CollectionHttp.releaseCollection(data._name);
-    openSnackBar(successTrans('release', { name: t('collection') }));
+    openSnackBar(
+      successTrans('release', { name: collectionTrans('collection') })
+    );
     fetchData();
     return res;
   };
 
   const handleLoad = async (data: CollectionView) => {
     const res = await CollectionHttp.loadCollection(data._name);
-    openSnackBar(successTrans('load', { name: t('collection') }));
+    openSnackBar(successTrans('load', { name: collectionTrans('collection') }));
     fetchData();
     return res;
   };
@@ -140,7 +145,9 @@ const Collections = () => {
     for (const item of selectedCollections) {
       await CollectionHttp.deleteCollection(item._name);
     }
-    openSnackBar(successTrans('delete', { name: t('collection') }));
+    openSnackBar(
+      successTrans('delete', { name: collectionTrans('collection') })
+    );
     fetchData();
     handleCloseDialog();
     setSelectedCollections([]);
@@ -148,7 +155,7 @@ const Collections = () => {
 
   const toolbarConfigs: ToolBarConfig[] = [
     {
-      label: t('create'),
+      label: collectionTrans('create'),
       onClick: () => {
         setDialog({
           open: true,
@@ -172,15 +179,17 @@ const Collections = () => {
             component: (
               <DeleteTemplate
                 label={btnTrans('delete')}
-                title={dialogTrans('deleteTitle', { type: t('collection') })}
-                text={t('deleteWarning')}
+                title={dialogTrans('deleteTitle', {
+                  type: collectionTrans('collection'),
+                })}
+                text={collectionTrans('deleteWarning')}
                 handleDelete={handleDelete}
               />
             ),
           },
         });
       },
-      label: t('delete'),
+      label: collectionTrans('delete'),
       icon: 'delete',
       disabled: data => data.length === 0,
     },
@@ -191,19 +200,19 @@ const Collections = () => {
       id: '_id',
       align: 'left',
       disablePadding: true,
-      label: t('id'),
+      label: collectionTrans('id'),
     },
     {
       id: 'nameElement',
       align: 'left',
       disablePadding: true,
-      label: t('name'),
+      label: collectionTrans('name'),
     },
     {
       id: 'statusElement',
       align: 'left',
       disablePadding: false,
-      label: t('status'),
+      label: collectionTrans('status'),
     },
     {
       id: '_rowCount',
@@ -211,8 +220,8 @@ const Collections = () => {
       disablePadding: false,
       label: (
         <span className="flex-center">
-          {t('rowCount')}
-          <CustomToolTip title={t('tooltip')}>
+          {collectionTrans('rowCount')}
+          <CustomToolTip title={collectionTrans('tooltip')}>
             <InfoIcon classes={{ root: classes.icon }} />
           </CustomToolTip>
         </span>
@@ -222,7 +231,7 @@ const Collections = () => {
       id: '_desc',
       align: 'left',
       disablePadding: false,
-      label: t('desc'),
+      label: collectionTrans('desc'),
     },
     {
       id: 'indexCreatingElement',
@@ -283,6 +292,7 @@ const Collections = () => {
           page={currentPage}
           onChangePage={handlePageChange}
           rowsPerPage={pageSize}
+          setRowsPerPage={handlePageSize}
           isLoading={loading}
         />
       ) : (
@@ -291,7 +301,7 @@ const Collections = () => {
           <EmptyCard
             wrapperClass={`page-empty-card ${classes.emptyWrapper}`}
             icon={<CollectionIcon />}
-            text={t('noData')}
+            text={collectionTrans('noData')}
           />
         </>
       )}

+ 9 - 7
client/src/pages/collections/Create.tsx

@@ -42,7 +42,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
   const classes = useStyles();
   const { handleCloseDialog } = useContext(rootContext);
-  const { t } = useTranslation('collection');
+  const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
   const { t: warningTrans } = useTranslation('warning');
 
@@ -105,7 +105,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
 
   const generalInfoConfigs: ITextfieldConfig[] = [
     {
-      label: t('name'),
+      label: collectionTrans('name'),
       key: 'collection_name',
       value: form.collection_name,
       onChange: (value: string) => handleInputChange('collection_name', value),
@@ -113,13 +113,15 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
       validations: [
         {
           rule: 'require',
-          errorText: warningTrans('required', { name: t('name') }),
+          errorText: warningTrans('required', {
+            name: collectionTrans('name'),
+          }),
         },
       ],
       className: classes.input,
     },
     {
-      label: t('description'),
+      label: collectionTrans('description'),
       key: 'description',
       value: form.description,
       onChange: (value: string) => handleInputChange('description', value),
@@ -148,7 +150,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
 
   return (
     <DialogTemplate
-      title={t('createTitle')}
+      title={collectionTrans('createTitle')}
       handleCancel={handleCloseDialog}
       confirmLabel={btnTrans('create')}
       handleConfirm={handleCreateCollection}
@@ -156,7 +158,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
     >
       <form>
         <fieldset className={classes.fieldset}>
-          <legend>{t('general')}</legend>
+          <legend>{collectionTrans('general')}</legend>
           {generalInfoConfigs.map(config => (
             <CustomInput
               key={config.key}
@@ -169,7 +171,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
         </fieldset>
 
         <fieldset className={classes.fieldset}>
-          <legend>{t('structure')}</legend>
+          <legend>{collectionTrans('structure')}</legend>
           <CreateFields
             fields={fields}
             setFields={setFields}

+ 2 - 2
client/src/pages/connect/Connect.tsx

@@ -48,9 +48,9 @@ const Connect = () => {
   const { setAddress } = useContext(authContext);
   const { openSnackBar } = useContext(rootContext);
   const classes = useStyles();
-  const { t } = useTranslation();
+  const { t: commonTrans } = useTranslation();
   const { t: warningTrans } = useTranslation('warning');
-  const milvusTrans: { [key in string]: string } = t('milvus');
+  const milvusTrans: { [key in string]: string } = commonTrans('milvus');
   const { t: btnTrans } = useTranslation('btn');
   const { t: successTrans } = useTranslation('success');
 

+ 10 - 6
client/src/pages/overview/Overview.tsx

@@ -28,24 +28,26 @@ const useStyles = makeStyles((theme: Theme) => ({
 const Overview = () => {
   useNavigationHook(ALL_ROUTER_TYPES.OVERVIEW);
   const classes = useStyles();
-  const { t } = useTranslation('overview');
+  const { t: overviewTrans } = useTranslation('overview');
   const { t: collectionTrans } = useTranslation('collection');
 
   const mockStatistics: StatisticsCardProps = {
     data: [
       {
-        label: t('load'),
+        label: overviewTrans('load'),
         value: formatNumber(4337),
         valueColor: '#07d197',
       },
       {
-        label: t('all'),
+        label: overviewTrans('all'),
         value: formatNumber(30000),
         valueColor: '#06aff2',
       },
       {
-        label: t('data'),
-        value: t('rows', { number: formatNumber(209379100) }) as string,
+        label: overviewTrans('data'),
+        value: overviewTrans('rows', {
+          number: formatNumber(209379100),
+        }) as string,
         valueColor: '#0689d2',
       },
     ],
@@ -94,7 +96,9 @@ const Overview = () => {
   return (
     <section className="page-wrapper">
       <StatisticsCard data={mockStatistics.data} />
-      <Typography className={classes.collectionTitle}>{t('load')}</Typography>
+      <Typography className={classes.collectionTitle}>
+        {overviewTrans('load')}
+      </Typography>
       {mockCollections.length > 0 ? (
         <div className={classes.cardsWrapper}>
           {mockCollections.map(collection => (

+ 3 - 3
client/src/pages/overview/collectionCard/CollectionCard.tsx

@@ -60,7 +60,7 @@ const CollectionCard: FC<CollectionCardProps> = ({
   const RightArrowIcon = icons.rightArrow;
   const InfoIcon = icons.info;
   const ReleaseIcon = icons.release;
-  const { t } = useTranslation('collection');
+  const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
 
   const handleRelease = () => {};
@@ -79,8 +79,8 @@ const CollectionCard: FC<CollectionCardProps> = ({
         <RightArrowIcon classes={{ root: classes.icon }} />
       </Link>
       <div className={classes.content}>
-        <Typography>{t('rowCount')}</Typography>
-        <CustomToolTip title={t('tooltip')} placement="bottom">
+        <Typography>{collectionTrans('rowCount')}</Typography>
+        <CustomToolTip title={collectionTrans('tooltip')} placement="bottom">
           <InfoIcon classes={{ root: classes.icon }} />
         </CustomToolTip>
         <Typography className={classes.rowCount}>{rowCount}</Typography>

+ 5 - 5
client/src/pages/partitions/Create.tsx

@@ -18,7 +18,7 @@ const CreatePartition: FC<PartitionCreateProps> = ({
   handleCreate,
   handleClose,
 }) => {
-  const { t } = useTranslation('partition');
+  const { t: partitionTrans } = useTranslation('partition');
   const { t: btnTrans } = useTranslation('btn');
   const { t: warningTrans } = useTranslation('warning');
 
@@ -38,7 +38,7 @@ const CreatePartition: FC<PartitionCreateProps> = ({
   };
 
   const nameInputConfig: ITextfieldConfig = {
-    label: t('name'),
+    label: partitionTrans('name'),
     variant: 'filled',
     key: 'name',
     fullWidth: true,
@@ -47,11 +47,11 @@ const CreatePartition: FC<PartitionCreateProps> = ({
     validations: [
       {
         rule: 'require',
-        errorText: warningTrans('required', { name: t('name') }),
+        errorText: warningTrans('required', { name: partitionTrans('name') }),
       },
       {
         rule: 'partitionName',
-        errorText: t('nameWarning'),
+        errorText: partitionTrans('nameWarning'),
       },
     ],
   };
@@ -62,7 +62,7 @@ const CreatePartition: FC<PartitionCreateProps> = ({
 
   return (
     <DialogTemplate
-      title={t('createTitle')}
+      title={partitionTrans('createTitle')}
       handleCancel={handleClose}
       confirmLabel={btnTrans('create')}
       handleConfirm={handleCreatePartition}

+ 2 - 0
client/src/pages/partitions/partitions.tsx

@@ -45,6 +45,7 @@ const Partitions: FC<{
   const [partitions, setPartitions] = useState<PartitionView[]>([]);
   const {
     pageSize,
+    handlePageSize,
     currentPage,
     handleCurrentPage,
     total,
@@ -255,6 +256,7 @@ const Partitions: FC<{
         page={currentPage}
         onChangePage={handlePageChange}
         rowsPerPage={pageSize}
+        setRowsPerPage={handlePageSize}
         isLoading={loading}
       />
     </section>

+ 4 - 2
client/src/pages/structure/CreateForm.tsx

@@ -44,7 +44,7 @@ const CreateForm = (
     metricOptions,
   } = props;
 
-  const { t } = useTranslation();
+  const { t: commonTrans } = useTranslation();
   const { t: indexTrans } = useTranslation('index');
   const { t: warningTrans } = useTranslation('warning');
 
@@ -162,7 +162,9 @@ const CreateForm = (
         classes={{ root: classes.select }}
       />
 
-      <Typography className={classes.paramTitle}>{t('param')}</Typography>
+      <Typography className={classes.paramTitle}>
+        {commonTrans('param')}
+      </Typography>
       <CustomSelector
         label={indexTrans('metric')}
         value={formValue.metric_type}

+ 45 - 10
client/src/pages/structure/IndexTypeElement.tsx

@@ -3,7 +3,12 @@ import Chip from '@material-ui/core/Chip';
 import CustomButton from '../../components/customButton/CustomButton';
 import { IndexHttp } from '../../http/Index';
 import { IndexState } from '../../types/Milvus';
-import { FieldView, IndexCreateParam, ParamPair } from './Types';
+import {
+  FieldView,
+  IndexCreateParam,
+  IndexManageParam,
+  ParamPair,
+} from './Types';
 import StatusIcon from '../../components/status/StatusIcon';
 import { ChildrenStatusType } from '../../components/status/Types';
 import { useTranslation } from 'react-i18next';
@@ -11,7 +16,7 @@ import { makeStyles, Theme } from '@material-ui/core';
 import icons from '../../components/icons/Icons';
 import { rootContext } from '../../context/Root';
 import CreateIndex from './Create';
-import { ManageRequestMethods } from '../../types/Common';
+import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 
 const useStyles = makeStyles((theme: Theme) => ({
   item: {
@@ -31,12 +36,16 @@ const useStyles = makeStyles((theme: Theme) => ({
 const IndexTypeElement: FC<{
   data: FieldView;
   collectionName: string;
-  createCb: (collectionName: string) => void;
-}> = ({ data, collectionName, createCb }) => {
+  cb: (collectionName: string) => void;
+}> = ({ data, collectionName, cb }) => {
   const classes = useStyles();
 
   const [status, setStatus] = useState<string>('');
-  const { t } = useTranslation('index');
+
+  const { t: indexTrans } = useTranslation('index');
+  const { t: btnTrans } = useTranslation('btn');
+  const { t: dialogTrans } = useTranslation('dialog');
+  const { t: successTrans } = useTranslation('success');
 
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
@@ -60,15 +69,14 @@ const IndexTypeElement: FC<{
 
   const requestCreateIndex = async (params: ParamPair[]) => {
     const indexCreateParam: IndexCreateParam = {
-      type: ManageRequestMethods.CREATE,
       collection_name: collectionName,
       field_name: data._fieldName,
       extra_params: params,
     };
     await IndexHttp.createIndex(indexCreateParam);
     handleCloseDialog();
-    openSnackBar(t('createSuccess'));
-    createCb(collectionName);
+    openSnackBar(indexTrans('createSuccess'));
+    cb(collectionName);
   };
 
   const handleCreate = () => {
@@ -88,7 +96,34 @@ const IndexTypeElement: FC<{
     });
   };
 
-  const handleDelete = () => {};
+  const requestDeleteIndex = async () => {
+    const indexDeleteParam: IndexManageParam = {
+      collection_name: collectionName,
+      field_name: data._fieldName,
+    };
+
+    await IndexHttp.deleteIndex(indexDeleteParam);
+    handleCloseDialog();
+    openSnackBar(successTrans('delete', { name: indexTrans('index') }));
+    cb(collectionName);
+  };
+
+  const handleDelete = () => {
+    setDialog({
+      open: true,
+      type: 'custom',
+      params: {
+        component: (
+          <DeleteTemplate
+            label={btnTrans('delete')}
+            title={dialogTrans('deleteTitle', { type: indexTrans('index') })}
+            text={indexTrans('deleteWarning')}
+            handleDelete={requestDeleteIndex}
+          />
+        ),
+      },
+    });
+  };
 
   const generateElement = () => {
     if (
@@ -107,7 +142,7 @@ const IndexTypeElement: FC<{
             onClick={handleCreate}
           >
             <AddIcon />
-            {t('create')}
+            {indexTrans('create')}
           </CustomButton>
         );
       }

+ 3 - 1
client/src/pages/structure/Structure.tsx

@@ -63,6 +63,7 @@ const Structure: FC<{
 
   const {
     pageSize,
+    handlePageSize,
     currentPage,
     handleCurrentPage,
     total,
@@ -130,7 +131,7 @@ const Structure: FC<{
               <IndexTypeElement
                 data={f}
                 collectionName={collectionName}
-                createCb={fetchFields}
+                cb={fetchFields}
               />
             ),
           })
@@ -207,6 +208,7 @@ const Structure: FC<{
         page={currentPage}
         onChangePage={handlePageChange}
         rowsPerPage={pageSize}
+        setRowsPerPage={handlePageSize}
         isLoading={loading}
       />
     </section>

+ 4 - 2
client/src/pages/structure/Types.ts

@@ -48,10 +48,12 @@ export type IndexType =
   | 'HNSW'
   | 'ANNOY';
 
-export interface IndexCreateParam {
-  type: ManageRequestMethods;
+export interface IndexManageParam {
   collection_name: string;
   field_name: string;
+}
+
+export interface IndexCreateParam extends IndexManageParam {
   extra_params: ParamPair[];
 }
 

+ 4 - 4
client/src/utils/__test__/Validation.spec.ts

@@ -1,5 +1,5 @@
 import {
-  checkIsEmpty,
+  checkEmptyValid,
   checkEmail,
   checkPasswordStrength,
   checkRange,
@@ -9,9 +9,9 @@ import {
 } from '../Validation';
 
 describe('Test validation utils', () => {
-  test('test checkIsEmpty function', () => {
-    expect(checkIsEmpty('')).toBeFalsy();
-    expect(checkIsEmpty('test')).toBeTruthy();
+  test('test checkEmptyValid function', () => {
+    expect(checkEmptyValid('')).toBeFalsy();
+    expect(checkEmptyValid('test')).toBeTruthy();
   });
 
   test('test checkEmail function', () => {

+ 1 - 1
server/package.json

@@ -30,7 +30,7 @@
     "@nestjs/swagger": "^4.8.0",
     "@types/passport-jwt": "^3.0.5",
     "@types/passport-local": "^1.0.33",
-    "@zilliz/milvus-sdk-node-dev": "^0.1.2",
+    "@zilliz/milvus-sdk-node-dev": "^0.1.4",
     "class-transformer": "^0.4.0",
     "class-validator": "^0.13.1",
     "passport": "^0.4.1",

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

@@ -91,7 +91,7 @@ export class CollectionsService {
           description: collectionInfo.schema.description,
           autoID: collectionInfo.schema.autoID,
           rowCount: findKeyValue(collectionStatistics.stats, ROW_COUNT),
-          // id: collectionInfo.collectionId
+          id: collectionInfo.collectionID,
         });
       }
     }

+ 4 - 4
server/yarn.lock

@@ -1287,10 +1287,10 @@
   resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
   integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
 
-"@zilliz/milvus-sdk-node-dev@^0.1.2":
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/@zilliz/milvus-sdk-node-dev/-/milvus-sdk-node-dev-0.1.2.tgz#be5d546afec4e6a87732beadce2bd8f3261dac58"
-  integrity sha512-WScOEWG0Y9va5JBRoQsh1vfH9W0LOjl5HIZVwP/vcT7Kh0pC1E4N6iwaEpnSkne055D9SxJA7FhN23udrHTrMQ==
+"@zilliz/milvus-sdk-node-dev@^0.1.4":
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/@zilliz/milvus-sdk-node-dev/-/milvus-sdk-node-dev-0.1.4.tgz#858e83b0ba0a309cbdf0b87e313b3b41de0052a4"
+  integrity sha512-4REU6TxqjsIZ7a7mL1M19IPg3oBBfE9kJmC7XUmcFz1+F2fYWpl7Iw4R8g69trpzUNnaFIBgwjAnSb0oVrRLeQ==
   dependencies:
     "@grpc/grpc-js" "^1.2.12"
     "@grpc/proto-loader" "^0.6.0"