Browse Source

resolve conflict

tumao 4 years ago
parent
commit
45afcafb13

+ 2 - 0
client/package.json

@@ -16,6 +16,7 @@
     "@types/node": "^12.0.0",
     "@types/node": "^12.0.0",
     "@types/react": "^17.0.0",
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",
     "@types/react-dom": "^17.0.0",
+    "@types/react-highlight-words": "^0.16.2",
     "@types/react-router-dom": "^5.1.7",
     "@types/react-router-dom": "^5.1.7",
     "axios": "^0.21.1",
     "axios": "^0.21.1",
     "dayjs": "^1.10.5",
     "dayjs": "^1.10.5",
@@ -23,6 +24,7 @@
     "react": "^17.0.2",
     "react": "^17.0.2",
     "react-app-rewired": "^2.1.8",
     "react-app-rewired": "^2.1.8",
     "react-dom": "^17.0.2",
     "react-dom": "^17.0.2",
+    "react-highlight-words": "^0.17.0",
     "react-i18next": "^11.10.0",
     "react-i18next": "^11.10.0",
     "react-router-dom": "^5.2.0",
     "react-router-dom": "^5.2.0",
     "react-scripts": "4.0.3",
     "react-scripts": "4.0.3",

+ 93 - 37
client/src/components/customInput/SearchInput.tsx

@@ -1,5 +1,7 @@
 import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
 import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
-import { useRef, FC, useState } from 'react';
+import { useRef, FC, useState, useEffect, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useHistory } from 'react-router-dom';
 import Icons from '../icons/Icons';
 import Icons from '../icons/Icons';
 import { SearchType } from './Types';
 import { SearchType } from './Types';
 
 
@@ -9,10 +11,12 @@ const useSearchStyles = makeStyles(theme => ({
   },
   },
   input: {
   input: {
     backgroundColor: '#fff',
     backgroundColor: '#fff',
-    borderRadius: '4px 4px 0 0',
-    padding: (props: any) => `${props.showInput ? theme.spacing(0, 1) : 0}`,
-    boxSizing: 'border-box',
-    width: (props: any) => `${props.showInput ? '255px' : 0}`,
+    borderRadius: '4px',
+    padding: theme.spacing(1),
+    width: '240px',
+    border: '1px solid #e9e9ed',
+    fontSize: '14px',
+
     transition: 'all 0.2s',
     transition: 'all 0.2s',
 
 
     '& .MuiAutocomplete-endAdornment': {
     '& .MuiAutocomplete-endAdornment': {
@@ -26,73 +30,126 @@ const useSearchStyles = makeStyles(theme => ({
     '& .MuiInput-underline:after': {
     '& .MuiInput-underline:after': {
       border: 'none',
       border: 'none',
     },
     },
+
+    /**
+     * when input focus
+     * 1. change parent wrapper border color
+     * 2. hide input start search icon
+     */
+    '&:focus-within': {
+      border: `1px solid ${theme.palette.primary.main}`,
+
+      '& $searchIcon': {
+        width: 0,
+      },
+    },
+  },
+  textfield: {
+    padding: 0,
+    height: '16px',
+
+    '&:focus': {
+      caretColor: theme.palette.primary.main,
+    },
   },
   },
   searchIcon: {
   searchIcon: {
-    paddingLeft: theme.spacing(1),
-    color: theme.palette.primary.main,
+    color: '#aeaebb',
     cursor: 'pointer',
     cursor: 'pointer',
-    fontSize: '24px',
+    fontSize: '20px',
+    width: (props: { searched: boolean }) => `${props.searched ? 0 : '20px'}`,
+
+    transition: 'width 0.2s',
   },
   },
   clearIcon: {
   clearIcon: {
-    color: 'rgba(0, 0, 0, 0.6)',
+    color: theme.palette.primary.main,
     cursor: 'pointer',
     cursor: 'pointer',
   },
   },
   iconWrapper: {
   iconWrapper: {
-    opacity: (props: any) => `${props.searched ? 1 : 0}`,
+    opacity: (props: { searched: boolean }) => `${props.searched ? 1 : 0}`,
     transition: 'opacity 0.2s',
     transition: 'opacity 0.2s',
   },
   },
   searchWrapper: {
   searchWrapper: {
-    display: (props: any) => `${props.showInput ? 'flex' : 'none'}`,
+    display: 'flex',
     justifyContent: 'center',
     justifyContent: 'center',
     alignItems: 'center',
     alignItems: 'center',
   },
   },
 }));
 }));
 
 
+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>(searchText);
-  const searched = searchValue !== '';
-  const [showInput, setShowInput] = useState<boolean>(searchValue !== '');
+  const [searchValue, setSearchValue] = useState<string | null>(
+    searchText || null
+  );
+
+  const [isInit, setIsInit] = useState<boolean>(true);
+
+  const searched = useMemo(
+    () => searchValue !== '' && searchValue !== null,
+    [searchValue]
+  );
+
+  const classes = useSearchStyles({ searched });
+  const { t: commonTrans } = useTranslation();
 
 
-  const classes = useSearchStyles({ searched, showInput });
+  const history = useHistory();
 
 
   const inputRef = useRef<any>(null);
   const inputRef = useRef<any>(null);
 
 
-  const onIconClick = () => {
-    setShowInput(true);
-    if (inputRef.current) {
-      inputRef.current.focus();
+  const savedSearchFn = useRef<(value: string) => void>(() => {});
+  useEffect(() => {
+    savedSearchFn.current = onSearch;
+  }, [onSearch]);
+
+  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() });
 
 
-    if (searched) {
-      onSearch(searchValue);
+        savedSearchFn.current(searchValue);
+      }, 300);
     }
     }
-  };
 
 
-  const handleInputBlur = () => {
-    setShowInput(searched);
+    return () => {
+      timer && clearTimeout(timer);
+    };
+  }, [searchValue, history, isInit]);
+
+  const handleSearch = (value: string | null) => {
+    if (value !== null) {
+      onSearch(value);
+    }
   };
   };
 
 
   return (
   return (
     <div className={classes.wrapper}>
     <div className={classes.wrapper}>
-      {!showInput && (
-        <div className={classes.searchIcon} onClick={onIconClick}>
-          {Icons.search()}
-        </div>
-      )}
       <TextField
       <TextField
         inputRef={inputRef}
         inputRef={inputRef}
-        autoFocus={true}
         variant="standard"
         variant="standard"
         classes={{ root: classes.input }}
         classes={{ root: classes.input }}
         InputProps={{
         InputProps={{
           disableUnderline: true,
           disableUnderline: true,
+          classes: { input: classes.textfield },
           endAdornment: (
           endAdornment: (
             <InputAdornment position="end">
             <InputAdornment position="end">
               <span
               <span
                 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();
                 }}
                 }}
@@ -105,31 +162,30 @@ const SearchInput: FC<SearchType> = props => {
             <InputAdornment position="start">
             <InputAdornment position="start">
               <span
               <span
                 className={classes.searchWrapper}
                 className={classes.searchWrapper}
-                onClick={() => onSearch(searchValue)}
+                onClick={() => handleSearch(searchValue)}
               >
               >
                 {Icons.search({ classes: { root: classes.searchIcon } })}
                 {Icons.search({ classes: { root: classes.searchIcon } })}
               </span>
               </span>
             </InputAdornment>
             </InputAdornment>
           ),
           ),
         }}
         }}
-        onBlur={handleInputBlur}
         onChange={e => {
         onChange={e => {
-          // console.log('change', e.target.value);
-          const value = e.target.value;
+          const value = e.target.value.trim();
           setSearchValue(value);
           setSearchValue(value);
+          setIsInit(false);
           if (value === '') {
           if (value === '') {
             onClear();
             onClear();
           }
           }
         }}
         }}
         onKeyPress={e => {
         onKeyPress={e => {
-          // console.log(`Pressed keyCode ${e.key}`);
           if (e.key === 'Enter') {
           if (e.key === 'Enter') {
             // Do code here
             // Do code here
-            onSearch(searchValue);
+            handleSearch(searchValue);
             e.preventDefault();
             e.preventDefault();
           }
           }
         }}
         }}
-        value={searchValue}
+        value={searchValue || ''}
+        placeholder={commonTrans('search')}
       />
       />
     </div>
     </div>
   );
   );

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

@@ -95,6 +95,7 @@ export interface IAdornmentConfig {
 
 
 export type SearchType = {
 export type SearchType = {
   searchText?: string;
   searchText?: string;
+  placeholder?: string;
   onClear?: () => void;
   onClear?: () => void;
   onSearch: (value: string) => void;
   onSearch: (value: string) => void;
 };
 };

+ 5 - 1
client/src/components/grid/Table.tsx

@@ -311,7 +311,11 @@ const EnhancedTable: FC<TableType> = props => {
                 <tr>
                 <tr>
                   <td
                   <td
                     className={classes.noData}
                     className={classes.noData}
-                    colSpan={colDefinitions.length}
+                    colSpan={
+                      openCheckBox
+                        ? colDefinitions.length + 1
+                        : colDefinitions.length
+                    }
                   >
                   >
                     {noData}
                     {noData}
                   </td>
                   </td>

+ 1 - 0
client/src/components/grid/ToolBar.tsx

@@ -116,6 +116,7 @@ const CustomToolBar: FC<ToolBarType> = props => {
                     onClear={c.onClear}
                     onClear={c.onClear}
                     onSearch={c.onSearch}
                     onSearch={c.onSearch}
                     searchText={c.searchText}
                     searchText={c.searchText}
+                    placeholder={c.placeholder}
                     key={i}
                     key={i}
                   />
                   />
                 );
                 );

+ 2 - 2
client/src/http/Partition.ts

@@ -2,12 +2,12 @@ import { StatusEnum } from '../components/status/Types';
 import {
 import {
   PartitionManageParam,
   PartitionManageParam,
   PartitionParam,
   PartitionParam,
-  PartitionView,
+  PartitionData,
 } from '../pages/partitions/Types';
 } from '../pages/partitions/Types';
 import { formatNumber } from '../utils/Common';
 import { formatNumber } from '../utils/Common';
 import BaseModel from './BaseModel';
 import BaseModel from './BaseModel';
 
 
-export class PartitionHttp extends BaseModel implements PartitionView {
+export class PartitionHttp extends BaseModel implements PartitionData {
   private id!: string;
   private id!: string;
   private name!: string;
   private name!: string;
   private rowCount!: string;
   private rowCount!: string;

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

@@ -22,6 +22,7 @@ const commonTrans = {
     copied: 'Copied',
     copied: 'Copied',
   },
   },
   param: 'Parameter',
   param: 'Parameter',
+  search: 'Search by name',
 };
 };
 
 
 export default commonTrans;
 export default commonTrans;

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

@@ -22,6 +22,7 @@ const commonTrans = {
     copied: 'Copied',
     copied: 'Copied',
   },
   },
   param: 'Parameter',
   param: 'Parameter',
+  search: 'Search by name',
 };
 };
 
 
 export default commonTrans;
 export default commonTrans;

+ 82 - 19
client/src/pages/collections/Collections.tsx

@@ -20,6 +20,8 @@ import CreateCollection from './Create';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 import { CollectionHttp } from '../../http/Collection';
 import { CollectionHttp } from '../../http/Collection';
 import { useDialogHook } from '../../hooks/Dialog';
 import { useDialogHook } from '../../hooks/Dialog';
+import Highlighter from 'react-highlight-words';
+import { parseLocationSearch } from '../../utils/Format';
 
 
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
   emptyWrapper: {
   emptyWrapper: {
@@ -38,12 +40,23 @@ const useStyles = makeStyles((theme: Theme) => ({
   link: {
   link: {
     color: theme.palette.common.black,
     color: theme.palette.common.black,
   },
   },
+  highlight: {
+    color: theme.palette.primary.main,
+    backgroundColor: 'transparent',
+  },
 }));
 }));
 
 
+let timer: NodeJS.Timeout | null = null;
+// get init search value from url
+const { search = '' } = parseLocationSearch(window.location.search);
+
 const Collections = () => {
 const Collections = () => {
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTIONS);
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTIONS);
   const { handleAction } = useDialogHook({ type: 'collection' });
   const { handleAction } = useDialogHook({ type: 'collection' });
   const [collections, setCollections] = useState<CollectionView[]>([]);
   const [collections, setCollections] = useState<CollectionView[]>([]);
+  const [searchedCollections, setSearchedCollections] = useState<
+    CollectionView[]
+  >([]);
   const {
   const {
     pageSize,
     pageSize,
     handlePageSize,
     handlePageSize,
@@ -51,7 +64,7 @@ const Collections = () => {
     handleCurrentPage,
     handleCurrentPage,
     total,
     total,
     data: collectionList,
     data: collectionList,
-  } = usePaginationHook(collections);
+  } = usePaginationHook(searchedCollections);
   const [loading, setLoading] = useState<boolean>(true);
   const [loading, setLoading] = useState<boolean>(true);
   const [selectedCollections, setSelectedCollections] = useState<
   const [selectedCollections, setSelectedCollections] = useState<
     CollectionView[]
     CollectionView[]
@@ -76,30 +89,40 @@ const Collections = () => {
       const statusRes = await CollectionHttp.getCollectionsIndexState();
       const statusRes = await CollectionHttp.getCollectionsIndexState();
       setLoading(false);
       setLoading(false);
 
 
-      setCollections(
-        res.map(v => {
-          const indexStatus = statusRes.find(item => item._name === v._name);
-          Object.assign(v, {
-            nameElement: (
-              <Link to={`/collections/${v._name}`} className={classes.link}>
-                {v._name}
-              </Link>
-            ),
-            statusElement: <Status status={v._status} />,
-            indexCreatingElement: (
-              <StatusIcon
-                type={indexStatus?._indexState || ChildrenStatusType.FINISH}
+      const collections = res.map(v => {
+        const indexStatus = statusRes.find(item => item._name === v._name);
+        Object.assign(v, {
+          nameElement: (
+            <Link to={`/collections/${v._name}`} className={classes.link}>
+              <Highlighter
+                textToHighlight={v._name}
+                searchWords={[search]}
+                highlightClassName={classes.highlight}
               />
               />
-            ),
-          });
+            </Link>
+          ),
+          statusElement: <Status status={v._status} />,
+          indexCreatingElement: (
+            <StatusIcon
+              type={indexStatus?._indexState || ChildrenStatusType.FINISH}
+            />
+          ),
+        });
+
+        return v;
+      });
 
 
-          return v;
-        })
+      // filter collection if url contains search param
+      const filteredCollections = collections.filter(collection =>
+        collection._name.includes(search)
       );
       );
+
+      setCollections(collections);
+      setSearchedCollections(filteredCollections);
     } catch (err) {
     } catch (err) {
       setLoading(false);
       setLoading(false);
     }
     }
-  }, [classes.link]);
+  }, [classes.link, classes.highlight]);
 
 
   useEffect(() => {
   useEffect(() => {
     fetchData();
     fetchData();
@@ -153,6 +176,38 @@ const Collections = () => {
     setSelectedCollections([]);
     setSelectedCollections([]);
   };
   };
 
 
+  const handleSearch = (value: string) => {
+    if (timer) {
+      clearTimeout(timer);
+    }
+    // add loading manually
+    setLoading(true);
+    timer = setTimeout(() => {
+      const searchWords = [value];
+      const list = value
+        ? collections.filter(c => c._name.includes(value))
+        : collections;
+
+      const highlightList = list.map(c => {
+        Object.assign(c, {
+          nameElement: (
+            <Link to={`/collections/${c._name}`} className={classes.link}>
+              <Highlighter
+                textToHighlight={c._name}
+                searchWords={searchWords}
+                highlightClassName={classes.highlight}
+              />
+            </Link>
+          ),
+        });
+        return c;
+      });
+
+      setLoading(false);
+      setSearchedCollections(highlightList);
+    }, 300);
+  };
+
   const toolbarConfigs: ToolBarConfig[] = [
   const toolbarConfigs: ToolBarConfig[] = [
     {
     {
       label: collectionTrans('create'),
       label: collectionTrans('create'),
@@ -193,6 +248,14 @@ const Collections = () => {
       icon: 'delete',
       icon: 'delete',
       disabled: data => data.length === 0,
       disabled: data => data.length === 0,
     },
     },
+    {
+      label: 'Search',
+      icon: 'search',
+      searchText: search,
+      onSearch: (value: string) => {
+        handleSearch(value);
+      },
+    },
   ];
   ];
 
 
   const colDefinitions: ColDefinitionsType[] = [
   const colDefinitions: ColDefinitionsType[] = [

+ 84 - 18
client/src/pages/partitions/Partitions.tsx

@@ -1,5 +1,5 @@
 import { makeStyles, Theme } from '@material-ui/core';
 import { makeStyles, Theme } from '@material-ui/core';
-import { FC, useContext, useEffect, useState } from 'react';
+import { FC, useCallback, useContext, useEffect, useState } from 'react';
 import {
 import {
   PartitionManageParam,
   PartitionManageParam,
   // PartitionParam,
   // PartitionParam,
@@ -19,6 +19,8 @@ import { ManageRequestMethods } from '../../types/Common';
 // import { StatusEnum } from '../../components/status/Types';
 // import { StatusEnum } from '../../components/status/Types';
 // import { useDialogHook } from '../../hooks/Dialog';
 // import { useDialogHook } from '../../hooks/Dialog';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
+import Highlighter from 'react-highlight-words';
+import { parseLocationSearch } from '../../utils/Format';
 
 
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
   wrapper: {
@@ -28,8 +30,16 @@ const useStyles = makeStyles((theme: Theme) => ({
     fontSize: '20px',
     fontSize: '20px',
     marginLeft: theme.spacing(0.5),
     marginLeft: theme.spacing(0.5),
   },
   },
+  highlight: {
+    color: theme.palette.primary.main,
+    backgroundColor: 'transparent',
+  },
 }));
 }));
 
 
+let timer: NodeJS.Timeout | null = null;
+// get init search value from url
+const { search = '' } = parseLocationSearch(window.location.search);
+
 const Partitions: FC<{
 const Partitions: FC<{
   collectionName: string;
   collectionName: string;
 }> = ({ collectionName }) => {
 }> = ({ collectionName }) => {
@@ -47,6 +57,9 @@ const Partitions: FC<{
     []
     []
   );
   );
   const [partitions, setPartitions] = useState<PartitionView[]>([]);
   const [partitions, setPartitions] = useState<PartitionView[]>([]);
+  const [searchedPartitions, setSearchedPartitions] = useState<PartitionView[]>(
+    []
+  );
   const {
   const {
     pageSize,
     pageSize,
     handlePageSize,
     handlePageSize,
@@ -54,28 +67,44 @@ const Partitions: FC<{
     handleCurrentPage,
     handleCurrentPage,
     total,
     total,
     data: partitionList,
     data: partitionList,
-  } = usePaginationHook(partitions);
+  } = usePaginationHook(searchedPartitions);
   const [loading, setLoading] = useState<boolean>(true);
   const [loading, setLoading] = useState<boolean>(true);
   const { setDialog, handleCloseDialog, openSnackBar } =
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
     useContext(rootContext);
 
 
-  useEffect(() => {
-    fetchPartitions(collectionName);
-  }, [collectionName]);
+  const fetchPartitions = useCallback(
+    async (collectionName: string) => {
+      try {
+        const res = await PartitionHttp.getPartitions(collectionName);
 
 
-  const fetchPartitions = 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 partitons: PartitionView[] = res.map(p =>
-        Object.assign(p, { _statusElement: <Status status={p._status} /> })
-      );
-      setLoading(false);
-      setPartitions(partitons);
-    } catch (err) {
-      setLoading(false);
-    }
-  };
+  useEffect(() => {
+    fetchPartitions(collectionName);
+  }, [collectionName, fetchPartitions]);
 
 
   const handleDelete = async () => {
   const handleDelete = async () => {
     for (const partition of selectedPartitions) {
     for (const partition of selectedPartitions) {
@@ -114,6 +143,35 @@ const Partitions: FC<{
   //   return res;
   //   return res;
   // };
   // };
 
 
+  const handleSearch = (value: string) => {
+    if (timer) {
+      clearTimeout(timer);
+    }
+    // add loading manually
+    setLoading(true);
+    timer = setTimeout(() => {
+      const searchWords = [value];
+      const list = value
+        ? partitions.filter(p => p._formatName.includes(value))
+        : partitions;
+
+      const highlightList = list.map(c => {
+        Object.assign(c, {
+          _nameElement: (
+            <Highlighter
+              textToHighlight={c._formatName}
+              searchWords={searchWords}
+              highlightClassName={classes.highlight}
+            />
+          ),
+        });
+        return c;
+      });
+      setLoading(false);
+      setSearchedPartitions(highlightList);
+    }, 300);
+  };
+
   const toolbarConfigs: ToolBarConfig[] = [
   const toolbarConfigs: ToolBarConfig[] = [
     {
     {
       label: t('create'),
       label: t('create'),
@@ -161,6 +219,14 @@ const Partitions: FC<{
         ? t('deletePartitionError')
         ? t('deletePartitionError')
         : '',
         : '',
     },
     },
+    {
+      label: 'Search',
+      icon: 'search',
+      searchText: search,
+      onSearch: (value: string) => {
+        handleSearch(value);
+      },
+    },
   ];
   ];
 
 
   const colDefinitions: ColDefinitionsType[] = [
   const colDefinitions: ColDefinitionsType[] = [
@@ -171,7 +237,7 @@ const Partitions: FC<{
       label: t('id'),
       label: t('id'),
     },
     },
     {
     {
-      id: '_formatName',
+      id: '_nameElement',
       align: 'left',
       align: 'left',
       disablePadding: false,
       disablePadding: false,
       label: t('name'),
       label: t('name'),

+ 6 - 2
client/src/pages/partitions/Types.ts

@@ -2,15 +2,19 @@ import { ReactElement } from 'react';
 import { StatusEnum } from '../../components/status/Types';
 import { StatusEnum } from '../../components/status/Types';
 import { ManageRequestMethods } from '../../types/Common';
 import { ManageRequestMethods } from '../../types/Common';
 
 
-export interface PartitionView {
+export interface PartitionData {
   _id: string;
   _id: string;
   _name: string;
   _name: string;
   _status: StatusEnum;
   _status: StatusEnum;
-  _statusElement?: ReactElement;
   _rowCount: string;
   _rowCount: string;
   _formatName: string;
   _formatName: string;
 }
 }
 
 
+export interface PartitionView extends PartitionData {
+  _nameElement?: ReactElement;
+  _statusElement?: ReactElement;
+}
+
 // delete and create
 // delete and create
 export interface PartitionManageParam {
 export interface PartitionManageParam {
   collectionName: string;
   collectionName: string;

+ 27 - 1
client/yarn.lock

@@ -1947,6 +1947,13 @@
   dependencies:
   dependencies:
     "@types/react" "*"
     "@types/react" "*"
 
 
+"@types/react-highlight-words@^0.16.2":
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.2.tgz#c196d1016db7519c607997699f51cb262789fc90"
+  integrity sha512-l9HsbTTLGUQB6CwrI6/e89QDdnkTjLZt0K1MVAYyrzwVbWUNR6FiwwUpECwPoOcAg0iJjYf2uqonSuurdE91oQ==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-router-dom@^5.1.7":
 "@types/react-router-dom@^5.1.7":
   version "5.1.7"
   version "5.1.7"
   resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271"
   resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271"
@@ -5573,6 +5580,11 @@ hex-color-regex@^1.1.0:
   resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
   resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
   integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
   integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
 
 
+highlight-words-core@^1.2.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa"
+  integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg==
+
 history@^4.9.0:
 history@^4.9.0:
   version "4.10.1"
   version "4.10.1"
   resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
   resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
@@ -7321,6 +7333,11 @@ media-typer@0.3.0:
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
   integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
   integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
 
 
+memoize-one@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906"
+  integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA==
+
 memory-fs@^0.4.1:
 memory-fs@^0.4.1:
   version "0.4.1"
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -9065,7 +9082,7 @@ prompts@^2.0.1:
     kleur "^3.0.3"
     kleur "^3.0.3"
     sisteransi "^1.0.5"
     sisteransi "^1.0.5"
 
 
-prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
   version "15.7.2"
   version "15.7.2"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
   integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
   integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -9287,6 +9304,15 @@ react-error-overlay@^6.0.9:
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
   integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
   integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
 
 
+react-highlight-words@^0.17.0:
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.17.0.tgz#e79a559a2de301548339d7216264d6cd0f1eed6f"
+  integrity sha512-uX1Qh5IGjnLuJT0Zok234QDwRC8h4hcVMnB99Cb7aquB1NlPPDiWKm0XpSZOTdSactvnClCk8LOmVlP+75dgHA==
+  dependencies:
+    highlight-words-core "^1.2.0"
+    memoize-one "^4.0.0"
+    prop-types "^15.5.8"
+
 react-i18next@^11.10.0:
 react-i18next@^11.10.0:
   version "11.10.0"
   version "11.10.0"
   resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.10.0.tgz#f34257447e18e710e36d8fd3f721dd7d37c7004f"
   resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.10.0.tgz#f34257447e18e710e36d8fd3f721dd7d37c7004f"