Browse Source

chore: UI upgrade (#883)

* remove makeStyle for User.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for Roles.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for UpdateUserPassDialog.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for UpdateRoleDialog.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for UpdateUserRole

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for CreateUserDialog.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for PrivilegeGroupOptions.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyle for privilegeGroups.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makestyles for UserAndRoles.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyles ofr gird/toolbar.tsx

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makestyle for insert/Dialog and Progress

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makestyle for dialog/insert/status and syscard

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove more makeStyles

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove more makeStyle

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove more

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* ui update

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove unused code

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove makeStyels for login page

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* update adv filter UI

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* upgrade

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 1 month ago
parent
commit
35e5a8ed5d
75 changed files with 1404 additions and 4963 deletions
  1. 45 52
      client/src/components/advancedSearch/Condition.tsx
  2. 59 74
      client/src/components/advancedSearch/ConditionGroup.tsx
  3. 1 9
      client/src/components/advancedSearch/CopyButton.tsx
  4. 51 108
      client/src/components/advancedSearch/Dialog.tsx
  5. 14 19
      client/src/components/advancedSearch/Filter.tsx
  6. 0 61
      client/src/components/code/CodeBlock.tsx
  7. 0 102
      client/src/components/code/CodeView.tsx
  8. 0 23
      client/src/components/code/Types.ts
  9. 1 5
      client/src/components/customDialog/DialogTemplate.tsx
  10. 0 2
      client/src/components/customDialog/Types.ts
  11. 5 2
      client/src/components/customSelector/CustomGroupedSelect.tsx
  12. 10 2
      client/src/components/customSelector/CustomSelector.tsx
  13. 15 28
      client/src/components/grid/ToolBar.tsx
  14. 8 0
      client/src/components/layout/Wrapper.tsx
  15. 50 56
      client/src/components/menu/NavMenu.tsx
  16. 165 23
      client/src/pages/connect/AuthForm.tsx
  17. 88 80
      client/src/pages/connect/ConnectContainer.tsx
  18. 0 134
      client/src/pages/connect/style.ts
  19. 15 31
      client/src/pages/databases/collections/Aliases.tsx
  20. 4 14
      client/src/pages/databases/collections/CollectionColHeader.tsx
  21. 39 58
      client/src/pages/databases/collections/Collections.tsx
  22. 57 53
      client/src/pages/databases/collections/StatusAction.tsx
  23. 14 23
      client/src/pages/databases/collections/partitions/Partitions.tsx
  24. 3 19
      client/src/pages/databases/collections/properties/Properties.tsx
  25. 15 22
      client/src/pages/databases/collections/schema/CreateForm.tsx
  26. 30 62
      client/src/pages/databases/collections/schema/IndexTypeElement.tsx
  27. 1 28
      client/src/pages/databases/collections/search/Search.tsx
  28. 0 77
      client/src/pages/dialogs/CodeDialog.tsx
  29. 21 24
      client/src/pages/dialogs/EditJSONDialog.tsx
  30. 1 1
      client/src/pages/dialogs/Types.ts
  31. 2 2
      client/src/pages/dialogs/create/ExtraInfoSection.tsx
  32. 3 11
      client/src/pages/dialogs/insert/Dialog.tsx
  33. 105 140
      client/src/pages/dialogs/insert/Import.tsx
  34. 60 95
      client/src/pages/dialogs/insert/Preview.tsx
  35. 22 32
      client/src/pages/dialogs/insert/Status.tsx
  36. 69 47
      client/src/pages/home/SysCard.tsx
  37. 0 1
      client/src/pages/search/Constants.ts
  38. 13 39
      client/src/pages/search/SearchParams.tsx
  39. 0 130
      client/src/pages/search/Styles.ts
  40. 0 612
      client/src/pages/search/VectorSearch.tsx
  41. 85 62
      client/src/pages/system/BaseCard.tsx
  42. 112 111
      client/src/pages/system/DataCard.tsx
  43. 0 171
      client/src/pages/system/LineChartCard.tsx
  44. 53 57
      client/src/pages/system/NodeListView.tsx
  45. 7 15
      client/src/pages/system/Progress.tsx
  46. 0 32
      client/src/pages/system/ProgressCard.tsx
  47. 46 67
      client/src/pages/system/SystemView.tsx
  48. 35 52
      client/src/pages/system/Types.ts
  49. 0 204
      client/src/pages/systemHealthy/HealthyIndexDetailView.tsx
  50. 0 58
      client/src/pages/systemHealthy/HealthyIndexLegend.tsx
  51. 0 142
      client/src/pages/systemHealthy/HealthyIndexOverview.tsx
  52. 0 32
      client/src/pages/systemHealthy/HealthyIndexRow.tsx
  53. 0 95
      client/src/pages/systemHealthy/LineChartLarge.tsx
  54. 0 125
      client/src/pages/systemHealthy/LineChartSmall.tsx
  55. 0 151
      client/src/pages/systemHealthy/SystemHealthyView.tsx
  56. 0 169
      client/src/pages/systemHealthy/ThresholdSetting.tsx
  57. 0 60
      client/src/pages/systemHealthy/TimeRangeTabs.tsx
  58. 0 259
      client/src/pages/systemHealthy/Topology.tsx
  59. 0 79
      client/src/pages/systemHealthy/Types.ts
  60. 0 48
      client/src/pages/systemHealthy/consts.ts
  61. 0 154
      client/src/pages/systemHealthy/dataHandler.ts
  62. 0 15
      client/src/pages/systemHealthy/getIcon.tsx
  63. 0 198
      client/src/pages/systemHealthy/prometheusDataCase.json
  64. 6 15
      client/src/pages/user/PrivilegeGroups.tsx
  65. 24 38
      client/src/pages/user/Roles.tsx
  66. 4 11
      client/src/pages/user/User.tsx
  67. 16 33
      client/src/pages/user/UsersAndRoles.tsx
  68. 9 20
      client/src/pages/user/dialogs/CreateUserDialog.tsx
  69. 3 25
      client/src/pages/user/dialogs/PrivilegeGroupOptions.tsx
  70. 5 22
      client/src/pages/user/dialogs/UpdateRoleDialog.tsx
  71. 5 18
      client/src/pages/user/dialogs/UpdateUserPassDialog.tsx
  72. 7 17
      client/src/pages/user/dialogs/UpdateUserRole.tsx
  73. 0 4
      client/src/router/Router.tsx
  74. 1 102
      client/src/utils/search.ts
  75. 0 1
      server/src/collections/collections.service.ts

+ 45 - 52
client/src/components/advancedSearch/Condition.tsx

@@ -1,6 +1,11 @@
 import React, { useState, useEffect, useMemo, useCallback } from 'react';
-import { Theme, IconButton, TextField, SelectChangeEvent } from '@mui/material';
-import { makeStyles } from '@mui/styles';
+import {
+  Theme,
+  IconButton,
+  TextField,
+  SelectChangeEvent,
+  Box,
+} from '@mui/material';
 import CloseIcon from '@mui/icons-material/Close';
 import CustomSelector from '../customSelector/CustomSelector';
 import { LOGICAL_OPERATORS, DataTypeStringEnum } from '@/consts';
@@ -63,8 +68,6 @@ const Condition: FC<ConditionProps> = props => {
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [conditionField, operator, conditionValue, jsonKeyValue]);
 
-  const classes = useStyles();
-
   const logicalOperators = useMemo(() => {
     if (conditionField.data_type === DataTypeStringEnum.Bool) {
       const data = LOGICAL_OPERATORS.filter(v => v.value === '==');
@@ -107,18 +110,32 @@ const Condition: FC<ConditionProps> = props => {
       : true;
 
   return (
-    <div className={`${classes.wrapper} ${className}`} {...others}>
-      <CustomSelector
-        label={conditionField.data_type}
-        value={conditionField?.name}
-        onChange={handleFieldNameChange}
-        options={fields.map(i => ({ value: i.name, label: i.name }))}
-        variant="filled"
-        wrapperClass={classes.fieldName}
-      />
+    <Box
+      sx={(theme: Theme) => ({
+        background: theme.palette.background.paper,
+        border: `1px solid ${theme.palette.divider}`,
+        p: theme.spacing(1, 2),
+        display: 'flex',
+        flexDirection: 'row',
+        borderRadius: 2,
+        alignItems: 'center',
+      })}
+      className={className}
+      {...others}
+    >
+      <Box>
+        <CustomSelector
+          label={conditionField.data_type}
+          value={conditionField?.name}
+          onChange={handleFieldNameChange}
+          options={fields.map(i => ({ value: i.name, label: i.name }))}
+          variant="filled"
+          sx={{ minWidth: '130px', m: 0 }}
+        />
+      </Box>
       {conditionField?.data_type === DataTypeStringEnum.JSON && (
         <TextField
-          className={classes.key}
+          sx={{ width: '150px', m: 0 }}
           label="key"
           variant="filled"
           value={jsonKeyValue}
@@ -126,55 +143,31 @@ const Condition: FC<ConditionProps> = props => {
           error={!isKeyLegal}
         />
       )}
-      <CustomSelector
-        label="Logic"
-        value={operator}
-        onChange={handleOpChange}
-        options={logicalOperators}
-        variant="filled"
-        wrapperClass={classes.logic}
-      />
+      <Box sx={{ margin: '0 8px' }}>
+        <CustomSelector
+          label="Logic"
+          value={operator}
+          onChange={handleOpChange}
+          options={logicalOperators}
+          variant="filled"
+          sx={{ minWidth: '100px', m: 0 }}
+        />
+      </Box>
       <TextField
-        className={classes.value}
+        sx={{ minWidth: '130px' }}
         label="Value"
         variant="filled"
         value={conditionValue}
         onChange={handleValueChange}
         error={!isValueLegal}
       />
-      <IconButton
-        aria-label="close"
-        className={classes.closeButton}
-        onClick={onDelete}
-        size="small"
-      >
-        <CloseIcon />
+      <IconButton aria-label="close" onClick={onDelete} size="small">
+        <CloseIcon sx={{ fontSize: '14px' }} />
       </IconButton>
-    </div>
+    </Box>
   );
 };
 
 Condition.displayName = 'Condition';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {},
-  wrapper: {
-    minWidth: '466px',
-    minHeight: '62px',
-    background: theme.palette.background.paper,
-    padding: theme.spacing(1.5, 2),
-    display: 'flex',
-    flexDirection: 'row',
-    alignItems: 'center',
-  },
-  closeButton: {},
-  fieldName: {
-    minHeight: '38px',
-    minWidth: '130px',
-  },
-  logic: { minHeight: '38px', minWidth: '100px', margin: '0 24px' },
-  key: { minHeight: '38px', width: '150px', margin: '0 0' },
-  value: { minHeight: '38px', minWidth: '130px' },
-}));
-
 export default Condition;

+ 59 - 74
client/src/components/advancedSearch/ConditionGroup.tsx

@@ -1,7 +1,6 @@
 import React, { useState, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
-import { Theme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
+import { Theme, Box } from '@mui/material';
 import { ToggleButton, ToggleButtonGroup } from '@mui/material';
 import ConditionItem from './Condition';
 import icons from '../icons/Icons';
@@ -12,16 +11,14 @@ import type {
   BinaryLogicalOpProps,
   AddConditionProps,
 } from './Types';
+import { ThemeContext } from '@emotion/react';
 
 // "And & or" operator component.
 const BinaryLogicalOp: FC<BinaryLogicalOpProps> = props => {
-  const { onChange, className, initValue = 'and' } = props;
+  const { onChange, initValue = 'and' } = props;
   const [operator, setOperator] = useState(initValue);
   const handleChange = useCallback(
-    (
-      event: React.MouseEvent<HTMLElement>,
-      newOp: string
-    ) => {
+    (event: React.MouseEvent<HTMLElement>, newOp: string) => {
       if (newOp !== null) {
         setOperator(newOp);
         onChange(newOp);
@@ -30,22 +27,60 @@ const BinaryLogicalOp: FC<BinaryLogicalOpProps> = props => {
     [onChange]
   );
   return (
-    <div className={`${className} op-${operator}`}>
+    <Box
+      sx={(theme: Theme) => ({
+        width: '100%',
+        display: 'flex',
+        alignItems: 'center',
+        justifyContent: 'center',
+        background: theme.palette.background.paper,
+      })}
+      className={`op-${operator}`}
+    >
       <ToggleButtonGroup
         value={operator}
         exclusive
         onChange={handleChange}
         aria-label="Binary Logical Operator"
       >
-        <ToggleButton value="and" aria-label="And">
+        <ToggleButton
+          value="and"
+          aria-label="And"
+          sx={(theme: Theme) => ({
+            borderRadius: 0,
+            width: 52,
+            borderTop:
+              operator === 'and'
+                ? 'none'
+                : `1px solid ${theme.palette.divider}`,
+            borderBottom:
+              operator === 'and'
+                ? 'none'
+                : `1px solid ${theme.palette.divider}`,
+          })}
+        >
           AND
         </ToggleButton>
-        <ToggleButton value="or" aria-label="Or">
+        <ToggleButton
+          value="or"
+          aria-label="Or"
+          sx={(theme: Theme) => ({
+            borderRadius: 0,
+            borderTop:
+              operator === 'and'
+                ? 'none'
+                : `1px solid ${theme.palette.divider}`,
+            borderBottom:
+              operator === 'and'
+                ? 'none'
+                : `1px solid ${theme.palette.divider}`,
+          })}
+        >
           OR
         </ToggleButton>
       </ToggleButtonGroup>
       <div className="op-split" />
-    </div>
+    </Box>
   );
 };
 
@@ -61,6 +96,7 @@ const AddCondition: FC<AddConditionProps> = props => {
       color="primary"
       className={className}
       startIcon={<AddIcon />}
+      sx={{ margin: 1 }}
     >
       {searchTrans('addCondition')}
     </CustomButton>
@@ -81,21 +117,7 @@ const ConditionGroup = (props: ConditionGroupProps) => {
     updateConditionData,
   } = handleConditions;
 
-  const classes = useStyles();
-
-  const generateClassName = (conditions: any, currentIndex: number) => {
-    let className = '';
-    if (currentIndex === 0 || conditions[currentIndex - 1].type === 'break') {
-      className += 'radius-top';
-    }
-    if (
-      currentIndex === conditions.length - 1 ||
-      conditions[currentIndex + 1].type === 'break'
-    ) {
-      className ? (className = 'radius-all') : (className = 'radius-bottom');
-    }
-    return className;
-  };
+  // 已去除 generateClassName 逻辑
 
   // Generate condition items with operators and add condition btn.
   const generateConditionItems = (conditions: any[]) => {
@@ -112,13 +134,11 @@ const ConditionGroup = (props: ConditionGroupProps) => {
             fields={fields}
             triggerChange={updateConditionData}
             initData={condition?.data}
-            className={generateClassName(conditions, currentIndex)}
           />
         );
         prev.push(
           <AddCondition
             key={`${condition.id}-add`}
-            className={classes.addBtn}
             onClick={() => {
               addCondition(condition.id);
             }}
@@ -129,7 +149,6 @@ const ConditionGroup = (props: ConditionGroupProps) => {
         prev.push(
           <AddCondition
             key={`${condition.id}-add`}
-            className={classes.addBtn}
             onClick={() => {
               addCondition(condition.id, true);
             }}
@@ -141,7 +160,6 @@ const ConditionGroup = (props: ConditionGroupProps) => {
             onChange={newOp => {
               changeBinaryLogicalOp(newOp, condition.id);
             }}
-            className={classes.binaryLogicOp}
             initValue="or"
           />
         );
@@ -156,7 +174,6 @@ const ConditionGroup = (props: ConditionGroupProps) => {
             fields={fields}
             triggerChange={updateConditionData}
             initData={condition?.data}
-            className={generateClassName(conditions, currentIndex)}
           />
         );
         prev.push(
@@ -165,7 +182,6 @@ const ConditionGroup = (props: ConditionGroupProps) => {
             onChange={newOp => {
               changeBinaryLogicalOp(newOp, condition.id);
             }}
-            className={classes.binaryLogicOp}
           />
         );
       }
@@ -175,59 +191,28 @@ const ConditionGroup = (props: ConditionGroupProps) => {
   };
 
   return (
-    <div className={classes.wrapper}>
+    <Box
+      sx={(theme: Theme) => ({
+        display: 'flex',
+        flexDirection: 'column',
+        alignItems: 'center',
+        '& .op-or': {
+          margin: '8px 0',
+        },
+      })}
+    >
       {generateConditionItems(flatConditions)}
       {flatConditions?.length === 0 && (
         <AddCondition
-          className={classes.addBtn}
           onClick={() => {
             addCondition();
           }}
         />
       )}
-    </div>
+    </Box>
   );
 };
 
 ConditionGroup.displayName = 'ConditionGroup';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {},
-  wrapper: {
-    display: 'flex',
-    flexDirection: 'column',
-    alignItems: 'center',
-
-    '& .op-or': {
-      backgroundColor: 'unset',
-      margin: '16px 0',
-    },
-  },
-  addBtn: {},
-  binaryLogicOp: {
-    width: '100%',
-    backgroundColor: theme.palette.background.paper,
-    display: 'flex',
-    flexDirection: 'row',
-    alignItems: 'center',
-    '& .op-split': {
-      height: '1px',
-      backgroundColor: theme.palette.divider,
-      width: '100%',
-    },
-    '& button': {
-      width: '42px',
-      height: '32px',
-    },
-    '& button.Mui-selected': {
-      backgroundColor: theme.palette.background.default,
-      color: theme.palette.text.primary,
-    },
-    '& button.Mui-selected:hover': {
-      backgroundColor: theme.palette.background.default,
-      color: theme.palette.text.primary,
-    },
-  },
-}));
-
 export default ConditionGroup;

+ 1 - 9
client/src/components/advancedSearch/CopyButton.tsx

@@ -1,6 +1,4 @@
 import React, { useState, useCallback } from 'react';
-import { Theme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
 import icons from '../icons/Icons';
 import CustomIconButton from '../customButton/CustomIconButton';
 import { useTranslation } from 'react-i18next';
@@ -11,7 +9,6 @@ const CopyIcon = icons.copyExpression;
 
 const CopyButton: FC<CopyButtonProps> = props => {
   const { label, icon, className, value = '', ...others } = props;
-  const classes = useStyles();
   const { t: commonTrans } = useTranslation();
   const [tooltipTitle, setTooltipTitle] = useState('Copy');
 
@@ -55,7 +52,7 @@ const CopyButton: FC<CopyButtonProps> = props => {
     <CustomIconButton
       tooltip={tooltipTitle}
       aria-label={label}
-      className={`${classes.button} ${className}`}
+      className={className}
       onClick={event => handleClick(event, value || '')}
       {...others}
     >
@@ -66,9 +63,4 @@ const CopyButton: FC<CopyButtonProps> = props => {
 
 CopyButton.displayName = 'CopyButton';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {},
-  button: {},
-  tooltip: {},
-}));
 export default CopyButton;

+ 51 - 108
client/src/components/advancedSearch/Dialog.tsx

@@ -6,7 +6,7 @@ import Dialog from '@mui/material/Dialog';
 import DialogActions from '@mui/material/DialogActions';
 import DialogContent from '@mui/material/DialogContent';
 import DialogTitle from '@mui/material/DialogTitle';
-import { makeStyles } from '@mui/styles';
+import Box from '@mui/material/Box';
 import CustomButton from '../customButton/CustomButton';
 import ConditionGroup from './ConditionGroup';
 import icons from '../icons/Icons';
@@ -34,7 +34,6 @@ const AdvancedDialog = (props: DialogProps) => {
     ...others
   } = props;
   const { addCondition } = handleConditions;
-  const classes = useStyles();
   const ResetIcon = icons.refresh;
   const CloseIcon = icons.clear;
 
@@ -53,46 +52,78 @@ const AdvancedDialog = (props: DialogProps) => {
         aria-labelledby="customized-dialog-title"
         open={open}
         maxWidth="xl"
-        className={classes.wrapper}
+        sx={{
+          '& .disable-exp': {
+            userSelect: 'none',
+            color: (theme: Theme) => theme.palette.text.disabled, // Changed for better theme coordination
+          },
+        }}
         {...others}
       >
-        <DialogTitle className={classes.dialogTitle}>
+        <DialogTitle
+          sx={{
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'space-between',
+          }}
+        >
           <Typography variant="h5" component="div">
             {title}
           </Typography>
           <IconButton
             aria-label="close"
-            className={classes.closeButton}
+            sx={{ color: (theme: Theme) => theme.palette.action.active }} // Changed for better theme coordination
             onClick={onCancel}
             size="small"
           >
             <CloseIcon />
           </IconButton>
         </DialogTitle>
-        <div
-          className={`${classes.expResult} ${
-            shouldSowPlaceholder && 'disable-exp'
-          }`}
-        >
-          {`${shouldSowPlaceholder ? 'Filter Expression' : filterExpression}`}
-          {!shouldSowPlaceholder && (
-            <CopyBtn label="copy expression" value={filterExpression} />
-          )}
-        </div>
         <DialogContent>
-          <div className={classes.expWrapper}>
+          <Box
+            sx={{
+              display: 'flex',
+              justifyContent: 'space-between',
+              alignItems: 'center',
+              ...(shouldSowPlaceholder && {
+                userSelect: 'none',
+                color: (theme: Theme) => theme.palette.text.secondary, // Changed for placeholder
+              }),
+              backgroundColor: (theme: Theme) => theme.palette.action.hover, // Changed for subtle background
+              minHeight: '32px',
+              fontSize: '13px',
+              fontFamily: 'monospace',
+              padding: (theme: Theme) => theme.spacing(1, 2),
+            }}
+          >
+            {`${shouldSowPlaceholder ? 'Filter Expression' : filterExpression}`}
+            {!shouldSowPlaceholder && (
+              <CopyBtn
+                label="copy expression"
+                value={filterExpression}
+                sx={{
+                  padding: (theme: Theme) => theme.spacing(0.25, 0.5),
+                  '& svg': {
+                    fontSize: '14px',
+                  },
+                }}
+              />
+            )}
+          </Box>
+        </DialogContent>
+        <DialogContent>
+          <Box>
             <ConditionGroup
               fields={fields}
               handleConditions={handleConditions}
               conditions={flatConditions}
             />
-          </div>
+          </Box>
         </DialogContent>
-        <DialogActions className={classes.dialogActions}>
+        <DialogActions sx={{ justifyContent: 'space-between' }}>
           <CustomButton
             onClick={onReset}
             color="primary"
-            className={classes.resetBtn}
             size="small"
             startIcon={<ResetIcon />}
           >
@@ -103,7 +134,7 @@ const AdvancedDialog = (props: DialogProps) => {
               autoFocus
               onClick={onCancel}
               color="primary"
-              className={classes.cancelBtn}
+              sx={{ marginRight: (theme: Theme) => theme.spacing(1) }}
             >
               <Typography variant="button"> {btnTrans('cancel')}</Typography>
             </CustomButton>
@@ -112,7 +143,6 @@ const AdvancedDialog = (props: DialogProps) => {
               onClick={onSubmit}
               variant="contained"
               color="primary"
-              className={classes.applyBtn}
               disabled={!isLegal}
             >
               {btnTrans('applyFilter')}
@@ -120,97 +150,10 @@ const AdvancedDialog = (props: DialogProps) => {
           </div>
         </DialogActions>
       </Dialog>
-      {/* <DialogTemplate
-      title={title}
-      handleClose={onClose}
-      showCloseIcon
-      handleConfirm={onSubmit}
-      confirmLabel="Apply Filters"
-      confirmDisabled={!isLegal}
-      handleCancel={onCancel}
-      cancelLabel="Cancel"
-      leftActions={
-        <Button
-          onClick={onReset}
-          color="primary"
-          className={classes.resetBtn}
-          size="small"
-        >
-          <CachedIcon />
-          Reset
-        </Button>
-      }
-    >
-      <div
-        className={`${classes.expResult} ${
-          !isLegal && 'disable-exp'
-        } testcopy`}
-      >
-        {`${isLegal ? filterExpression : 'Filter Expression'}`}
-        {isLegal && (
-          <CopyBtn label="copy expression" value={filterExpression} />
-        )}
-      </div>
-      <div className={classes.expWrapper}>
-        <ConditionGroup
-          fields={fields}
-          handleConditions={handleConditions}
-          conditions={flatConditions}
-        />
-      </div>
-    </DialogTemplate> */}
     </>
   );
 };
 
 AdvancedDialog.displayName = 'AdvancedDialog';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {},
-  wrapper: {
-    '& .disable-exp': {
-      userSelect: 'none',
-      color: theme.palette.text.primary,
-    },
-  },
-  closeButton: {
-    color: theme.palette.text.primary,
-  },
-  dialogTitle: {
-    display: 'flex',
-    alignItems: 'center',
-    justifyContent: 'space-between',
-  },
-  dialogActions: {
-    justifyContent: 'space-between',
-  },
-  resetBtn: {},
-  cancelBtn: {
-    marginRight: theme.spacing(1),
-  },
-  applyBtn: {
-    backgroundColor: theme.palette.primary.main,
-  },
-  copyButton: {},
-  expResult: {
-    background: theme.palette.background.paper,
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-    minHeight: '40px',
-    margin: theme.spacing(1, 4),
-    padding: theme.spacing(0, 2),
-    fontStyle: 'normal',
-    fontWeight: 'normal',
-    fontSize: '16px',
-    lineHeight: '24px',
-  },
-  expWrapper: {
-    background: theme.palette.background.default,
-    minWidth: '480px',
-    minHeight: '104px',
-    padding: theme.spacing(1.5),
-  },
-}));
-
 export default AdvancedDialog;

+ 14 - 19
client/src/components/advancedSearch/Filter.tsx

@@ -1,13 +1,12 @@
 import { forwardRef, useState, useEffect, useImperativeHandle } from 'react';
 import Chip from '@mui/material/Chip';
 import Tooltip from '@mui/material/Tooltip';
-import { makeStyles } from '@mui/styles';
+import { Box } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import icons from '@/components/icons/Icons';
 import { generateIdByHash } from '@/utils/Common';
 import AdvancedDialog from './Dialog';
 import CustomButton from '../customButton/CustomButton';
-import type { Theme } from '@mui/material/styles';
 import type { FilterProps, ConditionData } from './Types';
 
 const Filter = forwardRef((props: FilterProps, ref) => {
@@ -22,7 +21,6 @@ const Filter = forwardRef((props: FilterProps, ref) => {
     fields = [],
     ...others
   } = props;
-  const classes = useStyles();
 
   // i18n
   const { t: searchTrans } = useTranslation('search');
@@ -98,7 +96,11 @@ const Filter = forwardRef((props: FilterProps, ref) => {
       let newExpr = `${n} ${op} ${value}`;
 
       // rewrite expression if the op is JSON_CONTAINS/ARRAY_CONTAINS
-      if (op === 'JSON_CONTAINS' || op === 'ARRAY_CONTAINS' || op ==='TEXT_MATCH') {
+      if (
+        op === 'JSON_CONTAINS' ||
+        op === 'ARRAY_CONTAINS' ||
+        op === 'TEXT_MATCH'
+      ) {
         newExpr = `${op}(${n}, ${value})`;
       }
       // rewrite expression if the op is ARRAY_CONTAINS_ALL/ARRAY_CONTAINS_ANY
@@ -303,10 +305,15 @@ const Filter = forwardRef((props: FilterProps, ref) => {
 
   return (
     <>
-      <div className={`${classes.wrapper} ${className}`} {...others}>
+      <Box className={className} {...others}>
         <CustomButton
           disabled={filterDisabled}
-          className={classes.afBtn}
+          sx={{
+            color: theme => theme.palette.primary.main,
+            minWidth: 32,
+            p: '8px 0',
+            '& .MuiButton-endIcon': { ml: 0 },
+          }}
           onClick={handleClickOpen}
           size="small"
           endIcon={<FilterIcon />}
@@ -338,23 +345,11 @@ const Filter = forwardRef((props: FilterProps, ref) => {
             expression={filterExpression}
           />
         )}
-      </div>
+      </Box>
     </>
   );
 });
 
 Filter.displayName = 'AdvancedFilter';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {},
-  afBtn: {
-    color: theme.palette.primary.main,
-    minWidth: 32,
-    padding: '8px 0',
-    '& .MuiButton-endIcon': {
-      marginLeft: 0,
-    },
-  },
-}));
-
 export default Filter;

+ 0 - 61
client/src/components/code/CodeBlock.tsx

@@ -1,61 +0,0 @@
-import { useTheme } from '@mui/material/styles';
-import { useTranslation } from 'react-i18next';
-import CopyButton from '../advancedSearch/CopyButton';
-import SyntaxHighlighter from 'react-syntax-highlighter';
-import { vs2015, github } from 'react-syntax-highlighter/dist/esm/styles/hljs';
-import { FC } from 'react';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
-import type { CodeBlockProps } from './Types';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    position: 'relative',
-    backgroundColor: theme.palette.background.paper,
-  },
-  copy: {
-    position: 'absolute',
-    top: theme.spacing(1),
-    right: theme.spacing(1),
-    '& svg': {
-      width: 16,
-      height: 16,
-    },
-  },
-}));
-
-const CodeStyle = {
-  fontSize: 12,
-};
-
-const CodeBlock: FC<CodeBlockProps> = ({
-  code,
-  language,
-  wrapperClass = '',
-  style = {},
-}) => {
-  const theme = useTheme();
-  const classes = getStyles();
-  const { t: commonTrans } = useTranslation();
-  const highlightTheme = theme.palette.mode === 'dark' ? vs2015 : github;
-
-  return (
-    <div className={`${classes.wrapper} ${wrapperClass}`}>
-      <CopyButton
-        className={classes.copy}
-        label={commonTrans('copy.label')}
-        value={code}
-      />
-      <SyntaxHighlighter
-        language={language}
-        style={{ ...highlightTheme, ...style }}
-        customStyle={CodeStyle}
-        showLineNumbers
-      >
-        {code}
-      </SyntaxHighlighter>
-    </div>
-  );
-};
-
-export default CodeBlock;

+ 0 - 102
client/src/components/code/CodeView.tsx

@@ -1,102 +0,0 @@
-import Typography from '@mui/material/Typography';
-import { FC } from 'react';
-import { useTranslation } from 'react-i18next';
-import CustomTabList from '../customTabList/CustomTabList';
-import CodeBlock from './CodeBlock';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
-import type { CodeViewProps } from './Types';
-import type { ITab } from '../customTabList/Types';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    boxSizing: 'border-box',
-    width: '100%',
-
-    padding: theme.spacing(4),
-    backgroundColor: theme.palette.text.primary,
-    display: 'flex',
-    flexDirection: 'column',
-    color: '#fff',
-  },
-  title: {
-    marginBottom: theme.spacing(2),
-  },
-
-  // override tab list style
-  tabs: {
-    minHeight: 0,
-
-    '& .MuiTab-wrapper': {
-      fontWeight: 'bold',
-      color: '#fff',
-    },
-
-    '& .MuiTab-root': {
-      minHeight: 18,
-      marginRight: 0,
-    },
-
-    // disable Ripple Effect
-    '& .MuiTouchRipple-root': {
-      display: 'none',
-    },
-
-    '& .Mui-selected': {
-      '& .MuiTab-wrapper': {
-        color: theme.palette.primary.main,
-      },
-    },
-
-    '& .MuiTabs-indicator': {
-      display: 'flex',
-      justifyContent: 'center',
-
-      top: 32,
-      backgroundColor: 'transparent',
-
-      '& .tab-indicator': {
-        height: 1,
-        width: '100%',
-        maxWidth: 26,
-        backgroundColor: theme.palette.primary.main,
-      },
-    },
-
-    '& .MuiTabs-flexContainer': {
-      borderBottom: 'none',
-    },
-  },
-
-  block: {
-    height: `calc(100% - ${theme.spacing(4.5)})`,
-    overflowY: 'auto',
-  },
-}));
-
-const CodeView: FC<CodeViewProps> = ({ wrapperClass = '', data }) => {
-  const classes = getStyles();
-  const { t: commonTrans } = useTranslation();
-
-  const tabs: ITab[] = data.map(item => ({
-    label: item.label,
-    component: (
-      <CodeBlock
-        wrapperClass={classes.block}
-        language={item.language}
-        code={item.code}
-      />
-    ),
-  }));
-
-  return (
-    <section className={`${classes.wrapper} ${wrapperClass}`}>
-      <Typography variant="h5" className={classes.title}>
-        {commonTrans('code')}
-      </Typography>
-      <CustomTabList tabs={tabs} wrapperClass={classes.tabs} />
-    </section>
-  );
-};
-
-export default CodeView;

+ 0 - 23
client/src/components/code/Types.ts

@@ -1,23 +0,0 @@
-export interface CodeViewProps {
-  height?: number;
-  wrapperClass?: string;
-  data: CodeViewData[];
-}
-
-export enum CodeLanguageEnum {
-  javascript = 'javascript',
-  python = 'python',
-  java = 'java',
-  go = 'go',
-}
-
-export interface CodeBlockProps {
-  code: string;
-  language: string;
-  wrapperClass?: string;
-  style?: Record<string, React.CSSProperties>;
-}
-
-export interface CodeViewData extends CodeBlockProps {
-  label: string;
-}

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

@@ -8,7 +8,6 @@ import {
 } from '@mui/material';
 import CustomDialogTitle from './CustomDialogTitle';
 import CustomButton from '../customButton/CustomButton';
-import CodeView from '../code/CodeView';
 import type { DialogContainerProps } from './Types';
 
 const DialogTemplate: FC<DialogContainerProps> = ({
@@ -26,7 +25,6 @@ const DialogTemplate: FC<DialogContainerProps> = ({
   showCloseIcon = true,
   leftActions,
   showCode = false,
-  codeBlocksData = [],
   dialogClass = '',
   sx = {},
 }) => {
@@ -129,9 +127,7 @@ const DialogTemplate: FC<DialogContainerProps> = ({
                 height: '100%',
                 p: showCode ? 4 : 0,
               }}
-            >
-              <CodeView data={codeBlocksData} />
-            </Box>
+            ></Box>
           )}
         </Box>
       </form>

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

@@ -1,6 +1,5 @@
 import { ReactElement } from 'react';
 import { DialogType } from '@/context';
-import { CodeViewData } from '../code/Types';
 import { SxProps, Theme } from '@mui/material';
 
 export type CustomDialogType = DialogType & {
@@ -40,7 +39,6 @@ export type DialogContainerProps = {
   leftActions?: ReactElement;
   // code mode requirement
   showCode?: boolean;
-  codeBlocksData?: CodeViewData[];
   children: ReactElement;
   dialogClass?: string;
   sx?: SxProps<Theme>;

+ 5 - 2
client/src/components/customSelector/CustomGroupedSelect.tsx

@@ -6,7 +6,9 @@ import Select from '@mui/material/Select';
 import { FC } from 'react';
 import type { GroupOption, ICustomGroupSelect } from './Types';
 
-const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
+const CustomGroupedSelect: FC<
+  ICustomGroupSelect & { style?: React.CSSProperties }
+> = props => {
   const {
     options,
     className = '',
@@ -15,6 +17,7 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
     placeholder = '',
     value,
     onChange,
+    style = {},
   } = props;
 
   const renderSelectGroup = (option: GroupOption) => {
@@ -55,7 +58,7 @@ const CustomGroupedSelect: FC<ICustomGroupSelect> = props => {
   };
 
   return (
-    <div className={className} style={{ width: '100%' }}>
+    <div className={className} style={{ width: '100%', ...style }}>
       <FormControl variant="filled" sx={{ width: '100%' }}>
         {haveLabel && <InputLabel htmlFor="grouped-select">{label}</InputLabel>}
         <Select

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

@@ -6,7 +6,9 @@ import Select from '@mui/material/Select';
 import { generateId } from '../../utils/Common';
 import type { CustomSelectorType } from './Types';
 
-const CustomSelector: FC<CustomSelectorType> = props => {
+const CustomSelector: FC<
+  CustomSelectorType & { style?: React.CSSProperties }
+> = props => {
   const {
     label,
     value,
@@ -17,12 +19,18 @@ const CustomSelector: FC<CustomSelectorType> = props => {
     wrapperClass = '',
     labelClass = '',
     size = 'medium',
+    style = {},
     ...others
   } = props;
   const id = generateId('selector');
 
   return (
-    <FormControl variant={variant} className={wrapperClass} size={size}>
+    <FormControl
+      variant={variant}
+      className={wrapperClass}
+      size={size}
+      style={style}
+    >
       {label && (
         <InputLabel classes={{ root: labelClass }} htmlFor={id}>
           {label}

+ 15 - 28
client/src/components/grid/ToolBar.tsx

@@ -7,35 +7,10 @@ import Icons from '../icons/Icons';
 import SearchInput from '../customInput/SearchInput';
 import { throwErrorForDev } from '../../utils/Common';
 import CustomIconButton from '../customButton/CustomIconButton';
-import { makeStyles } from '@mui/styles';
-import type { Theme } from '@mui/material/styles';
 import type { ToolBarConfig, ToolBarType } from './Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  countLabel: {
-    display: 'flex',
-    alignItems: 'center',
-    justifyContent: 'flex-end',
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.default,
-    opacity: 0.4,
-  },
-  btn: {
-    marginRight: theme.spacing(0.5),
-  },
-  gridEnd: {
-    display: 'flex',
-    justifyContent: 'flex-end',
-    alignItems: 'center',
-  },
-  toolbar: {
-    marginBottom: theme.spacing(1),
-  },
-}));
-
 const CustomToolBar: FC<ToolBarType> = props => {
   const { toolbarConfigs, selected = [], hideOnDisable = false } = props;
-  const classes = useStyles();
 
   // remove hidden button
   const leftConfigs = useMemo(() => {
@@ -56,7 +31,11 @@ const CustomToolBar: FC<ToolBarType> = props => {
 
   return (
     <>
-      <Grid container role="toolbar" className={classes.toolbar}>
+      <Grid
+        container
+        role="toolbar"
+        sx={theme => ({ marginBottom: theme.spacing(1) })}
+      >
         <Grid item xs={10}>
           {leftConfigs.map((c, i) => {
             const isSelect = c.type === 'select' || c.type === 'groupSelect';
@@ -92,7 +71,7 @@ const CustomToolBar: FC<ToolBarType> = props => {
                 // use contained variant as default
                 variant={c.btnVariant || 'contained'}
                 tooltip={tooltip}
-                className={classes.btn}
+                sx={theme => ({ marginRight: theme.spacing(0.5) })}
                 role="button"
               >
                 <Typography variant="button">{c.label}</Typography>
@@ -116,7 +95,15 @@ const CustomToolBar: FC<ToolBarType> = props => {
         </Grid>
 
         {rightConfigs.length > 0 && (
-          <Grid className={classes.gridEnd} item xs={2}>
+          <Grid
+            item
+            xs={2}
+            sx={{
+              display: 'flex',
+              justifyContent: 'flex-end',
+              alignItems: 'center',
+            }}
+          >
             {rightConfigs.map((c, i) => {
               if (c.icon === 'search') {
                 if (!c.onSearch) {

+ 8 - 0
client/src/components/layout/Wrapper.tsx

@@ -2,26 +2,34 @@ import { useTranslation } from 'react-i18next';
 import Box from '@mui/material/Box';
 import React from 'react';
 
+import type { SxProps, Theme } from '@mui/material';
+
 interface WrapperProps {
   hasPermission?: boolean;
   children?: React.ReactNode;
   className?: string;
+  style?: React.CSSProperties;
+  sx?: SxProps<Theme>;
 }
 
 const Wrapper = ({
   hasPermission = true,
   children,
   className,
+  style,
+  sx,
 }: WrapperProps) => {
   const { t } = useTranslation();
 
   return (
     <Box
       className={className}
+      style={style}
       sx={{
         width: '100%',
         height: '100%',
         position: 'relative',
+        ...((Array.isArray(sx) ? Object.assign({}, ...sx) : sx) || {})
       }}
     >
       {children}

+ 50 - 56
client/src/components/menu/NavMenu.tsx

@@ -1,64 +1,18 @@
 import { useState, FC, useEffect, useContext } from 'react';
 import clsx from 'clsx';
-import { Theme } from '@mui/material/styles';
 import List from '@mui/material/List';
 import ListItem from '@mui/material/ListItem';
 import Tooltip from '@mui/material/Tooltip';
 import ListItemIcon from '@mui/material/ListItemIcon';
 import Typography from '@mui/material/Typography';
-import { makeStyles } from '@mui/styles';
+import Box from '@mui/material/Box';
 import icons from '@/components/icons/Icons';
 import type { NavMenuItem, NavMenuType } from './Types';
 import { dataContext } from '@/context';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
-    borderRight: `1px solid ${theme.palette.divider}`,
-    width: 48,
-    paddingTop: 0,
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.default,
-  },
-  item: {
-    width: 'initial',
-    borderRadius: 8,
-    margin: theme.spacing(0.5),
-    marginBottom: theme.spacing(1.5),
-    cursor: 'pointer',
-    '&:hover': {
-      backgroundColor: theme.palette.primary.main,
-      color: '#fff',
-      '& .icon': { color: '#fff' },
-    },
-    '&.attu .icon': {
-      color: theme.palette.primary.main,
-      '&:hover': { color: '#fff' },
-      '&.active:hover': { color: '#fff' },
-    },
-    '& .itemIcon': {
-      marginLeft: -8,
-      minWidth: 24,
-    },
-    '&.active': {
-      borderRadius: 8,
-      backgroundColor: theme.palette.primary.main,
-      color: '#fff',
-      '& .icon path': { fill: '#fff' },
-    },
-  },
-
-  version: {
-    position: 'absolute',
-    fontSize: 10,
-    bottom: 8,
-    left: 8,
-  },
-}));
-
 const NavMenu: FC<NavMenuType> = props => {
-  const { width, data, defaultActive = '', versionInfo } = props;
-  const classes = useStyles({ width });
+  const { data, defaultActive = '', versionInfo } = props;
+  // Styles moved inline using sx prop
   const [active, setActive] = useState<string>(defaultActive);
 
   const { databases } = useContext(dataContext);
@@ -71,7 +25,6 @@ const NavMenu: FC<NavMenuType> = props => {
 
   const NestList = (props: { data: NavMenuItem[]; className?: string }) => {
     const { className = '', data } = props;
-
     return (
       <>
         {data.map((v: NavMenuItem) => {
@@ -92,7 +45,34 @@ const NavMenu: FC<NavMenuType> = props => {
               key={v.label}
               title={v.label}
               disabled={disabled}
-              className={clsx(classes.item, {
+              sx={{
+                width: 'initial',
+                borderRadius: 1,
+                m: 0.5,
+                mb: 1.5,
+                cursor: 'pointer',
+                '&:hover': {
+                  backgroundColor: theme => theme.palette.primary.main,
+                  color: '#fff',
+                  '& .icon': { color: '#fff' },
+                },
+                '&.attu .icon': {
+                  color: theme => theme.palette.primary.main,
+                  '&:hover': { color: '#fff' },
+                  '&.active:hover': { color: '#fff' },
+                },
+                '& .itemIcon': {
+                  marginLeft: -1,
+                  minWidth: 24,
+                },
+                '&.active': {
+                  borderRadius: 1,
+                  backgroundColor: theme => theme.palette.primary.main,
+                  color: '#fff',
+                  '& .icon path': { fill: '#fff' },
+                },
+              }}
+              className={clsx({
                 [className]: className,
                 ['active']: isActive,
                 ['attu']: v.icon === icons.attu,
@@ -119,17 +99,31 @@ const NavMenu: FC<NavMenuType> = props => {
   };
 
   return (
-    <List component="nav" className={clsx(classes.root)}>
-      <div>
+    <List
+      component="nav"
+      sx={{
+        boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
+        borderRight: theme => `1px solid ${theme.palette.divider}`,
+        width: 48,
+        pt: 0,
+        color: theme => theme.palette.text.primary,
+        backgroundColor: theme => theme.palette.background.default,
+        position: 'relative',
+      }}
+    >
+      <Box>
         <NestList data={data} />
         <Typography
-          classes={{
-            root: classes.version,
+          sx={{
+            position: 'absolute',
+            fontSize: 10,
+            bottom: 8,
+            left: 8,
           }}
         >
           v {versionInfo.attu}
         </Typography>
-      </div>
+      </Box>
     </List>
   );
 };

+ 165 - 23
client/src/pages/connect/AuthForm.tsx

@@ -14,9 +14,12 @@ import { CustomRadio } from '@/components/customRadio/CustomRadio';
 import Icons from '@/components/icons/Icons';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import CustomIconButton from '@/components/customButton/CustomIconButton';
-import { useStyles } from './style';
 import type { AuthReq } from '@server/types';
 import FormControlLabel from '@mui/material/FormControlLabel';
+import Box from '@mui/material/Box';
+import type { Theme } from '@mui/material';
+
+// Add Connection type definition back
 type Connection = AuthReq & {
   time: number;
 };
@@ -35,7 +38,7 @@ const DEFAULT_CONNECTION = {
 
 export const AuthForm = () => {
   // styles
-  const classes = useStyles();
+  // const classes = useStyles(); // Removed useStyles
 
   // context
   const { openSnackBar } = useContext(rootContext);
@@ -240,15 +243,32 @@ export const AuthForm = () => {
 
   return (
     <form onSubmit={handleConnect}>
-      <section className={classes.wrapper}>
-        <div className={classes.titleWrapper}>
+      <Box
+        sx={{
+          display: 'flex',
+          flexDirection: 'column',
+          padding: (theme: Theme) => theme.spacing(0, 3),
+          position: 'relative',
+        }}
+      >
+        <Box
+          sx={{
+            textAlign: 'left',
+            alignSelf: 'flex-start',
+            padding: (theme: Theme) => theme.spacing(3, 0),
+            '& svg': {
+              fontSize: 15,
+              marginLeft: (theme: Theme) => theme.spacing(0.5),
+            },
+          }}
+        >
           <Typography variant="h4" component="h4">
             {commonTrans('attu.connectTitle')}
             <CustomToolTip title={commonTrans('attu.connectionTip')}>
               <Icons.info />
             </CustomToolTip>
           </Typography>
-        </div>
+        </Box>
 
         {/* address  */}
         <CustomInput
@@ -259,13 +279,27 @@ export const AuthForm = () => {
             onChange: (val: string) =>
               handleInputChange('address', String(val)),
             variant: 'filled',
-            className: classes.input,
+            sx: {
+              margin: (theme: Theme) => theme.spacing(0.5, 0, 0),
+              '& .MuiFilledInput-adornedEnd': {
+                paddingRight: 0,
+              },
+            },
             placeholder: commonTrans('attu.address'),
             fullWidth: true,
             InputProps: {
               endAdornment: (
                 <CustomIconButton
-                  className={classes.menuBtn}
+                  sx={{
+                    display: 'flex',
+                    paddingLeft: 1,
+                    paddingRight: 1,
+                    fontSize: 14,
+                    '& button': {
+                      width: 36,
+                      height: 36,
+                    },
+                  }}
                   onClick={handleMenuClick}
                 >
                   <Icons.link />
@@ -295,7 +329,12 @@ export const AuthForm = () => {
             key: 'database',
             onChange: (value: string) => handleInputChange('database', value),
             variant: 'filled',
-            className: classes.input,
+            sx: {
+              margin: (theme: Theme) => theme.spacing(0.5, 0, 0),
+              '& .MuiFilledInput-adornedEnd': {
+                paddingRight: 0,
+              },
+            },
             placeholder: dbTrans('database'),
             fullWidth: true,
             value: authReq.database,
@@ -306,13 +345,19 @@ export const AuthForm = () => {
         />
 
         {/* toggle auth */}
-        <div className={classes.toggle}>
+        <Box
+          sx={{
+            display: 'flex',
+            width: '100%',
+            justifyContent: 'flex-start',
+          }}
+        >
           <CustomRadio
             checked={withPass}
             label={commonTrans('attu.authentication')}
             handleChange={handleEnableAuth}
           />
-        </div>
+        </Box>
 
         {/* token  */}
         {withPass && (
@@ -324,7 +369,12 @@ export const AuthForm = () => {
                 key: 'token',
                 onChange: (val: string) => handleInputChange('token', val),
                 variant: 'filled',
-                className: classes.input,
+                sx: {
+                  margin: (theme: Theme) => theme.spacing(0.5, 0, 0),
+                  '& .MuiFilledInput-adornedEnd': {
+                    paddingRight: 0,
+                  },
+                },
                 placeholder: commonTrans('attu.token'),
                 fullWidth: true,
                 value: authReq.token,
@@ -342,7 +392,12 @@ export const AuthForm = () => {
                 onChange: (value: string) =>
                   handleInputChange('username', value),
                 variant: 'filled',
-                className: classes.input,
+                sx: {
+                  margin: (theme: Theme) => theme.spacing(0.5, 0, 0),
+                  '& .MuiFilledInput-adornedEnd': {
+                    paddingRight: 0,
+                  },
+                },
                 placeholder: commonTrans('attu.username'),
                 fullWidth: true,
                 value: authReq.username,
@@ -360,7 +415,12 @@ export const AuthForm = () => {
                 onChange: (value: string) =>
                   handleInputChange('password', value),
                 variant: 'filled',
-                className: classes.input,
+                sx: {
+                  margin: (theme: Theme) => theme.spacing(0.5, 0, 0),
+                  '& .MuiFilledInput-adornedEnd': {
+                    paddingRight: 0,
+                  },
+                },
                 placeholder: commonTrans('attu.password'),
                 fullWidth: true,
                 type: 'password',
@@ -374,7 +434,13 @@ export const AuthForm = () => {
         )}
 
         {/* SSL toggle */}
-        <div className={classes.toggle}>
+        <Box
+          sx={{
+            display: 'flex',
+            width: '100%',
+            justifyContent: 'flex-start',
+          }}
+        >
           <FormControlLabel
             control={
               <Checkbox
@@ -384,13 +450,28 @@ export const AuthForm = () => {
             }
             label={commonTrans('attu.ssl')}
           />
-        </div>
+        </Box>
 
         <CustomButton type="submit" variant="contained" disabled={btnDisabled}>
           {btnTrans(isConnecting ? 'connecting' : 'connect')}
         </CustomButton>
 
-        <div className={classes.checkHealth}>
+        <Box
+          sx={{
+            display: 'flex',
+            alignItems: 'center',
+            marginTop: 4,
+            '& .MuiCheckbox-root': {
+              margin: 0,
+              padding: '8px 4px 8px 0',
+            },
+            '& span': {
+              cursor: 'pointer',
+              fontSize: 12,
+              fontStyle: 'italic',
+            },
+          }}
+        >
           <label>
             <Checkbox
               size="small"
@@ -403,13 +484,20 @@ export const AuthForm = () => {
               {commonTrans('attu.checkHealth')}
             </Typography>
           </label>
-        </div>
-      </section>
+        </Box>
+      </Box>
 
       <Menu
         anchorEl={anchorEl}
         keepMounted
-        className={classes.menu}
+        sx={{
+          // Added sx prop
+          '& ul': {
+            padding: 0,
+            maxHeight: '400px',
+            overflowY: 'auto',
+          },
+        }}
         anchorOrigin={{
           vertical: 'bottom',
           horizontal: 'right',
@@ -422,15 +510,69 @@ export const AuthForm = () => {
         onClose={handleMenuClose}
       >
         {connections.map((connection, index) => (
-          <li
+          <Box
+            component="li"
             key={index}
-            className={classes.connection}
+            sx={{
+              display: 'flex',
+              justifyContent: 'space-between',
+              fontSize: '14px',
+              width: 380,
+              padding: `0 8px`,
+              cursor: 'pointer',
+              '&:hover': {
+                backgroundColor: (theme: Theme) => theme.palette.action.hover,
+              },
+              '& .address': {
+                display: 'grid',
+                gridTemplateColumns: '24px 1fr',
+                gap: 4,
+                color: (theme: Theme) => theme.palette.text.primary,
+                fontSize: '14px',
+                padding: '12px 0',
+                '& .text': {
+                  overflow: 'hidden',
+                  textOverflow: 'ellipsis',
+                  width: 200,
+                  wordWrap: 'break-word',
+                },
+              },
+              '& .icon': {
+                verticalAlign: '-3px',
+                marginRight: 8,
+                fontSize: '14px',
+              },
+              '& .time': {
+                color: (theme: Theme) => theme.palette.text.secondary,
+                fontSize: 11,
+                lineHeight: 1.5,
+                padding: '12px 0',
+                width: 130,
+                fontStyle: 'italic',
+              },
+              '& .deleteIconBtn': {
+                padding: '8px 0',
+                '& svg': {
+                  fontSize: '14px',
+                },
+                height: 16,
+                lineHeight: '16px',
+                margin: 0,
+              },
+            }}
             onClick={() => {
               handleClickOnHisotry(connection);
             }}
           >
             <div className="address">
-              <Icons.link className="icon"></Icons.link>
+              <Icons.link
+                // className="icon" // Removed className
+                sx={{
+                  // Added sx prop
+                  verticalAlign: '-5px',
+                  marginRight: (theme: Theme) => theme.spacing(1),
+                }}
+              ></Icons.link>
               <div className="text">
                 {connection.address}/{connection.database}
               </div>
@@ -454,7 +596,7 @@ export const AuthForm = () => {
                 </CustomIconButton>
               )}
             </div>
-          </li>
+          </Box>
         ))}
       </Menu>
     </form>

+ 88 - 80
client/src/pages/connect/ConnectContainer.tsx

@@ -5,78 +5,12 @@ import Icons from '@/components/icons/Icons';
 import { AuthForm } from './AuthForm';
 import CustomButton from '@/components/customButton/CustomButton';
 import { MilvusService } from '@/http';
-import { makeStyles } from '@mui/styles';
+import Box from '@mui/material/Box';
 import type { Theme } from '@mui/material/styles';
 
-const getContainerStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    width: '100%',
-    height: '100vh',
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.default,
-  },
-  box: {
-    display: 'flex',
-    flexDirection: 'row',
-    backgroundColor: theme.palette.background.default,
-    border: `1px solid ${theme.palette.divider}`,
-    borderRadius: 8,
-    boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
-    minHeight: 644,
-  },
-  logo: {
-    width: 64,
-    height: 'auto',
-    marginBottom: theme.spacing(1),
-    display: 'block',
-    color: theme.palette.primary.main,
-  },
-  links: {
-    marginTop: theme.spacing(4),
-    display: 'flex',
-    flexDirection: 'column',
-    alignItems: 'center',
-    justifyContent: 'center',
-    width: '100%',
-    padding: theme.spacing(2, 0),
-    '& button': {
-      borderColor: 'transparent',
-    },
-  },
-  attu: {
-    width: 299,
-    display: 'flex',
-    flexDirection: 'column',
-    padding: theme.spacing(0, 3),
-    backgroundColor: theme.palette.background.default,
-    borderRadius: 8,
-  },
-  form: {
-    width: 481,
-    borderRadius: 8,
-    padding: theme.spacing(5, 0),
-    boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
-    backgroundColor: theme.palette.background.paper,
-  },
-  brand: {
-    fontSize: 24,
-    fontWeight: 'bold',
-    color: theme.palette.text.primary,
-    marginTop: theme.spacing(2),
-    height: 24,
-  },
-  sub: {
-    marginTop: theme.spacing(1),
-    fontSize: 12,
-    color: theme.palette.text.secondary,
-    height: 12,
-  },
-}));
-
 // used for user connect process
 const ConnectContainer = () => {
   const [version, setVersion] = useState('loading');
-  const classes = getContainerStyles();
   const { t: commonTrans } = useTranslation();
   const { t: btnTrans } = useTranslation('btn');
 
@@ -87,20 +21,86 @@ const ConnectContainer = () => {
   }, []);
 
   return (
-    <main className={`flex-center ${classes.wrapper}`}>
-      <section className={classes.box}>
-        <section className={`flex-center ${classes.attu}`}>
-          <Icons.attu classes={{ root: classes.logo }} />
-          <Typography variant="body2" className={classes.brand}>
+    <Box
+      className="flex-center"
+      sx={{
+        width: '100%',
+        height: '100vh',
+        color: 'text.primary',
+        backgroundColor: 'background.default',
+      }}
+    >
+      <Box
+        sx={{
+          display: 'flex',
+          flexDirection: 'row',
+          backgroundColor: 'background.default',
+          border: (theme: Theme) => `1px solid ${theme.palette.divider}`,
+          borderRadius: 2,
+          boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
+          minHeight: 644,
+        }}
+      >
+        <Box
+          className="flex-center"
+          sx={{
+            width: 299,
+            display: 'flex',
+            flexDirection: 'column',
+            padding: (theme: Theme) => theme.spacing(0, 3),
+            backgroundColor: 'background.default',
+            borderRadius: 2,
+          }}
+        >
+          <Icons.attu
+            sx={{
+              width: 64,
+              height: 'auto',
+              marginBottom: (theme: Theme) => theme.spacing(1),
+              display: 'block',
+              color: 'primary.main',
+            }}
+          />
+          <Typography
+            variant="body2"
+            sx={{
+              fontSize: 24,
+              fontWeight: 'bold',
+              color: 'text.primary',
+              marginTop: (theme: Theme) => theme.spacing(2),
+              height: 24,
+            }}
+          >
             {commonTrans('attu.admin')}
           </Typography>
           {version && (
-            <Typography component="sub" className={classes.sub}>
+            <Typography
+              component="sub"
+              sx={{
+                marginTop: (theme: Theme) => theme.spacing(1),
+                fontSize: 12,
+                color: 'text.secondary',
+                height: 12,
+              }}
+            >
               {commonTrans('attu.version')}: {version}
             </Typography>
           )}
 
-          <div className={classes.links}>
+          <Box
+            sx={{
+              marginTop: (theme: Theme) => theme.spacing(4),
+              display: 'flex',
+              flexDirection: 'column',
+              alignItems: 'center',
+              justifyContent: 'center',
+              width: '100%',
+              padding: (theme: Theme) => theme.spacing(2, 0),
+              '& button': {
+                borderColor: 'transparent',
+              },
+            }}
+          >
             <CustomButton
               startIcon={<Icons.star />}
               fullWidth={true}
@@ -134,13 +134,21 @@ const ConnectContainer = () => {
             >
               {commonTrans('attu.discord')}
             </CustomButton>
-          </div>
-        </section>
-        <section className={classes.form}>
+          </Box>
+        </Box>
+        <Box
+          sx={{
+            width: 481,
+            borderRadius: 2,
+            padding: (theme: Theme) => theme.spacing(5, 0),
+            boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
+            backgroundColor: 'background.paper',
+          }}
+        >
           <AuthForm />
-        </section>
-      </section>
-    </main>
+        </Box>
+      </Box>
+    </Box>
   );
 };
 

+ 0 - 134
client/src/pages/connect/style.ts

@@ -1,134 +0,0 @@
-import { Theme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
-
-export const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
-    flexDirection: 'column',
-    padding: theme.spacing(0, 3),
-    position: 'relative',
-  },
-  titleWrapper: {
-    textAlign: 'left',
-    alignSelf: 'flex-start',
-    padding: theme.spacing(3, 0),
-    '& svg': {
-      fontSize: 15,
-      marginLeft: theme.spacing(0.5),
-    },
-  },
-  input: {
-    margin: theme.spacing(0.5, 0, 0),
-    '& .MuiFilledInput-adornedEnd': {
-      paddingRight: 0,
-    },
-  },
-  toggle: {
-    display: 'flex',
-    width: '100%',
-    justifyContent: 'flex-start',
-  },
-  checkHealth: {
-    display: 'flex',
-    alignItems: 'center',
-    marginTop: 4,
-    '& .MuiCheckbox-root': {
-      margin: 0,
-      padding: '8px 4px 8px 0',
-    },
-    '& span': {
-      cursor: 'pointer',
-      fontSize: 12,
-      fontStyle: 'italic',
-    },
-  },
-  star: {
-    position: 'absolute',
-    top: -48,
-    right: -8,
-    marginTop: theme.spacing(1),
-    alignItems: 'center',
-    height: '32px',
-    lineHeight: '32px',
-    color: theme.palette.text.primary,
-    backgroundColor: theme.palette.background.paper,
-    padding: theme.spacing(0.5, 0, 0.5, 1),
-    fontSize: 13,
-    display: 'block',
-    width: '132px',
-    textDecoration: 'none',
-    marginRight: theme.spacing(1),
-    fontWeight: 500,
-    '&:hover': {
-      fontWeight: 'bold',
-    },
-  },
-  menuBtn: {
-    display: 'flex',
-    paddingLeft: 8,
-    paddingRight: 8,
-    fontSize: 14,
-    '& button': {
-      width: 36,
-      height: 36,
-    },
-  },
-  menu: {
-    '& ul': {
-      padding: 0,
-      maxHeight: '400px',
-      overflowY: 'auto',
-    },
-  },
-  icon: {
-    verticalAlign: '-5px',
-    marginRight: theme.spacing(1),
-  },
-  connection: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    fontSize: '14px',
-    width: 380,
-    padding: `0 8px`,
-    cursor: 'pointer',
-    '&:hover': {
-      backgroundColor: theme.palette.action.hover,
-    },
-    '& .address': {
-      display: 'grid',
-      gridTemplateColumns: '24px 1fr',
-      gap: 4,
-      color: theme.palette.text.primary,
-      fontSize: '14px',
-      padding: '12px 0',
-      '& .text': {
-        overflow: 'hidden',
-        textOverflow: 'ellipsis',
-        width: 200,
-        wordWrap: 'break-word',
-      },
-    },
-    '& .icon': {
-      verticalAlign: '-3px',
-      marginRight: 8,
-      fontSize: '14px',
-    },
-    '& .time': {
-      color: theme.palette.text.secondary,
-      fontSize: 11,
-      lineHeight: 1.5,
-      padding: '12px 0',
-      width: 130,
-      fontStyle: 'italic',
-    },
-    '& .deleteIconBtn': {
-      padding: '8px 0',
-      '& svg': {
-        fontSize: '14px',
-      },
-      height: 16,
-      lineHeight: '16px',
-      margin: 0,
-    },
-  },
-}));

+ 15 - 31
client/src/pages/databases/collections/Aliases.tsx

@@ -1,27 +1,13 @@
 import { useContext } from 'react';
-import { Chip, IconButton, Theme } from '@mui/material';
+import { Chip, IconButton, Box } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { rootContext, dataContext } from '@/context';
 import icons from '@/components/icons/Icons';
 import CreateAliasDialog from '@/pages/dialogs/CreateAliasDialog';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
-import { makeStyles } from '@mui/styles';
 import type { CollectionObject } from '@server/types';
 import { CollectionService } from '@/http';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
-    flexWrap: 'wrap',
-    gap: 8,
-  },
-  iconBtn: {
-    marginTop: 4,
-    width: '16px',
-    height: '16px',
-  },
-}));
-
 export interface AliasesProps {
   aliases: string[];
   collection: CollectionObject;
@@ -38,7 +24,6 @@ export default function Aliases(props: AliasesProps) {
     onCreate = () => {},
     onDelete = () => {},
   } = props;
-  const classes = useStyles();
   const { setDialog, openSnackBar, handleCloseDialog } =
     useContext(rootContext);
   // i18n
@@ -70,16 +55,14 @@ export default function Aliases(props: AliasesProps) {
 
   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>
-      </>
+      <IconButton
+        onClick={handleCreate}
+        size="small"
+        sx={{ mt: 0.5, width: 16, height: 16 }}
+        aria-label="add"
+      >
+        <AddIcon width="8" height="8" fontSize="small" />
+      </IconButton>
     );
   }
 
@@ -120,7 +103,7 @@ export default function Aliases(props: AliasesProps) {
   };
 
   return (
-    <div className={classes.wrapper}>
+    <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
       {aliases.map(a => (
         <Chip
           key={a}
@@ -128,22 +111,23 @@ export default function Aliases(props: AliasesProps) {
           label={a}
           variant="outlined"
           deleteIcon={<DeleteIcon />}
-          onClick={(e: React.MouseEvent) => {
+          onClick={e => {
             e.stopPropagation();
           }}
           onDelete={() => {
-            _onDelete({ collection: collection, alias: a });
+            _onDelete({ collection, alias: a });
           }}
+          sx={{ mb: 0.5 }}
         />
       ))}
       <IconButton
         onClick={handleCreate}
         size="small"
-        classes={{ root: classes.iconBtn }}
+        sx={{ mt: 0.5, width: 16, height: 16 }}
         aria-label="add"
       >
         <AddIcon width="8" height="8" fontSize="small" />
       </IconButton>
-    </div>
+    </Box>
   );
 }

+ 4 - 14
client/src/pages/databases/collections/CollectionColHeader.tsx

@@ -1,19 +1,10 @@
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { formatFieldType } from '@/utils';
 import Icons from '@/components/icons/Icons';
-import { Theme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
+import { Box } from '@mui/material';
 import type { CollectionFullObject } from '@server/types';
 import type { ColDefinitionsType } from '@/components/grid/Types';
 
-export const style = makeStyles((theme: Theme) => ({
-  icon: {
-    fontSize: '14px',
-    marginLeft: theme.spacing(0.5),
-    verticalAlign: '-3px',
-  },
-}));
-
 const CollectionColHeader = (props: {
   def: ColDefinitionsType;
   collection: CollectionFullObject;
@@ -21,15 +12,14 @@ const CollectionColHeader = (props: {
   const { def, collection } = props;
   const title = def.label;
   const field = collection.schema.fields.find(f => f.name === title);
-  const classes = style();
 
   return (
-    <>
+    <Box component="span" sx={{ display: 'inline-flex', alignItems: 'center' }}>
       {title}
       <CustomToolTip title={field ? formatFieldType(field) : (title as string)}>
-        <Icons.info classes={{ root: classes.icon }} />
+        <Icons.info sx={{ fontSize: 14, ml: 0.5, verticalAlign: '-3px' }} />
       </CustomToolTip>
-    </>
+    </Box>
   );
 };
 

+ 39 - 58
client/src/pages/databases/collections/Collections.tsx

@@ -1,6 +1,6 @@
 import { useContext, useMemo, useState, useEffect } from 'react';
 import { Link, useSearchParams } from 'react-router-dom';
-import { Theme } from '@mui/material';
+import { Box } from '@mui/material';
 import { useNavigate } from 'react-router-dom';
 import { useTranslation } from 'react-i18next';
 import Highlighter from 'react-highlight-words';
@@ -23,54 +23,12 @@ import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { LOADING_STATE } from '@/consts';
 import { formatNumber } from '@/utils';
 import Aliases from './Aliases';
-import { makeStyles } from '@mui/styles';
 import type {
   ColDefinitionsType,
   ToolBarConfig,
 } from '@/components/grid/Types';
 import type { CollectionObject } from '@server/types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-    flexDirection: 'column',
-    height: `100%`,
-  },
-  emptyWrapper: {
-    marginTop: theme.spacing(2),
-  },
-
-  icon: {
-    fontSize: '14px',
-    marginLeft: theme.spacing(0.5),
-  },
-
-  dialogContent: {
-    lineHeight: '24px',
-    fontSize: '16px',
-  },
-  link: {
-    color: theme.palette.text.primary,
-    display: 'inline-block',
-    wordBreak: 'break-all',
-    whiteSpace: 'nowrap',
-    width: '150px',
-    overflow: 'hidden',
-    textOverflow: 'ellipsis',
-    height: '20px',
-    textDecoration: 'none',
-  },
-  highlight: {
-    color: theme.palette.primary.main,
-    backgroundColor: 'transparent',
-  },
-  chip: {
-    color: theme.palette.text.primary,
-    marginRight: theme.spacing(0.5),
-    background: `rgba(0, 0, 0, 0.04)`,
-  },
-}));
-
 const Collections = () => {
   const { isManaged } = useContext(authContext);
   const {
@@ -97,8 +55,6 @@ const Collections = () => {
   const { t: btnTrans } = useTranslation('btn');
   const { t: commonTrans } = useTranslation();
 
-  const classes = useStyles();
-
   const QuestionIcon = icons.question;
 
   const formatCollections = useMemo(() => {
@@ -358,13 +314,26 @@ const Collections = () => {
         return (
           <Link
             to={`/databases/${database}/${collection_name}/overview`}
-            className={classes.link}
+            style={{
+              color: 'inherit',
+              display: 'inline-block',
+              wordBreak: 'break-all',
+              whiteSpace: 'nowrap',
+              width: 150,
+              overflow: 'hidden',
+              textOverflow: 'ellipsis',
+              height: 20,
+              textDecoration: 'none',
+            }}
             title={collection_name}
           >
             <Highlighter
               textToHighlight={collection_name}
               searchWords={[search]}
-              highlightClassName={classes.highlight}
+              highlightStyle={{
+                color: '#1976d2',
+                backgroundColor: 'transparent',
+              }}
             />
           </Link>
         );
@@ -399,12 +368,16 @@ const Collections = () => {
       disablePadding: false,
       sortBy: 'rowCount',
       label: (
-        <span className="flex-center with-max-content">
+        <Box
+          component="span"
+          className="flex-center with-max-content"
+          sx={{ display: 'inline-flex', alignItems: 'center' }}
+        >
           {collectionTrans('rowCount')}
           <CustomToolTip title={collectionTrans('entityCountInfo')}>
-            <QuestionIcon classes={{ root: classes.icon }} />
+            <QuestionIcon sx={{ fontSize: 14, ml: 0.5 }} />
           </CustomToolTip>
-        </span>
+        </Box>
       ),
       formatter(v) {
         return formatNumber(v.rowCount);
@@ -418,9 +391,13 @@ const Collections = () => {
       align: 'left',
       disablePadding: false,
       label: (
-        <span className="flex-center with-max-content">
+        <Box
+          component="span"
+          className="flex-center with-max-content"
+          sx={{ display: 'inline-flex', alignItems: 'center' }}
+        >
           {collectionTrans('description')}
-        </span>
+        </Box>
       ),
       formatter(v) {
         return v.description || '--';
@@ -449,12 +426,16 @@ const Collections = () => {
       align: 'left',
       disablePadding: false,
       label: (
-        <span className="flex-center with-max-content">
+        <Box
+          component="span"
+          className="flex-center with-max-content"
+          sx={{ display: 'inline-flex', alignItems: 'center' }}
+        >
           {collectionTrans('alias')}
           <CustomToolTip title={collectionTrans('aliasInfo')}>
-            <QuestionIcon classes={{ root: classes.icon }} />
+            <QuestionIcon sx={{ fontSize: 14, ml: 0.5 }} />
           </CustomToolTip>
-        </span>
+        </Box>
       ),
       formatter(v) {
         return <Aliases aliases={v.aliases} collection={v} />;
@@ -488,7 +469,7 @@ const Collections = () => {
   }, [collectionList, batchRefreshCollections]);
 
   return (
-    <section className={classes.root}>
+    <Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
       {collections.length > 0 || loading ? (
         <AttuGrid
           toolbarConfigs={toolbarConfigs}
@@ -527,13 +508,13 @@ const Collections = () => {
         <>
           <CustomToolBar toolbarConfigs={toolbarConfigs} />
           <EmptyCard
-            wrapperClass={`page-empty-card ${classes.emptyWrapper}`}
+            wrapperClass="page-empty-card"
             icon={<CollectionIcon />}
             text={collectionTrans('noData')}
           />
         </>
       )}
-    </section>
+    </Box>
   );
 };
 

+ 57 - 53
client/src/pages/databases/collections/StatusAction.tsx

@@ -1,7 +1,7 @@
 import { FC, useMemo, MouseEvent, useContext } from 'react';
 import { StatusActionType } from '@/components/status/Types';
 import { useTranslation } from 'react-i18next';
-import { Theme, Typography, useTheme, Chip } from '@mui/material';
+import { Typography, useTheme, Chip } from '@mui/material';
 import { rootContext } from '@/context';
 import { LOADING_STATE } from '@/consts';
 import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
@@ -10,44 +10,6 @@ import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import CustomButton from '@/components/customButton/CustomButton';
 import LoadCollectionDialog from '@/pages/dialogs/LoadCollectionDialog';
 import ReleaseCollectionDialog from '@/pages/dialogs/ReleaseCollectionDialog';
-import { makeStyles } from '@mui/styles';
-
-// Define styles using MUI's makeStyles
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    display: 'flex',
-    alignItems: 'center',
-  },
-  chip: {
-    border: 'none',
-    marginRight: theme.spacing(0.5),
-    paddingLeft: theme.spacing(0.5),
-  },
-  circle: {
-    width: 8,
-    height: 8,
-    borderRadius: '50%',
-  },
-  loaded: {
-    border: `1px solid ${theme.palette.primary.main}`,
-    backgroundColor: theme.palette.primary.main,
-  },
-  unloaded: {
-    border: `1px solid ${theme.palette.primary.main}`,
-    background: '#fff !important',
-  },
-  noIndex: {
-    border: `1px solid ${theme.palette.text.disabled}`,
-    backgroundColor: '#fff',
-  },
-  loading: {
-    marginRight: theme.spacing(1.25),
-  },
-  extraBtn: {
-    height: 24,
-    padding: '0 8px',
-  },
-}));
 
 const StatusAction: FC<StatusActionType> = props => {
   const {
@@ -60,9 +22,8 @@ const StatusAction: FC<StatusActionType> = props => {
     createIndexElement,
   } = props;
 
-  // Theme and styles
+  // Theme
   const theme = useTheme();
-  const classes = useStyles();
 
   // Context
   const { setDialog } = useContext(rootContext);
@@ -79,14 +40,38 @@ const StatusAction: FC<StatusActionType> = props => {
         return {
           label: commonTrans('status.unloaded'),
           tooltip: collectionTrans('clickToLoad'),
-          icon: <div className={`${classes.circle} ${classes.unloaded}`}></div>,
+          icon: (
+            <span
+              style={{
+                display: 'inline-block',
+                width: 8,
+                height: 8,
+                borderRadius: '50%',
+                border: `1px solid ${theme.palette.primary.main}`,
+                background: '#fff',
+                verticalAlign: 'middle',
+              }}
+            />
+          ),
           deleteIcon: <Icons.load />,
         };
       case LOADING_STATE.LOADED:
         return {
           label: commonTrans('status.loaded'),
           tooltip: collectionTrans('clickToRelease'),
-          icon: <div className={`${classes.circle} ${classes.loaded}`}></div>,
+          icon: (
+            <span
+              style={{
+                display: 'inline-block',
+                width: 8,
+                height: 8,
+                borderRadius: '50%',
+                border: `1px solid ${theme.palette.primary.main}`,
+                background: theme.palette.primary.main,
+                verticalAlign: 'middle',
+              }}
+            />
+          ),
           deleteIcon: <Icons.release />,
         };
       case LOADING_STATE.LOADING:
@@ -94,10 +79,15 @@ const StatusAction: FC<StatusActionType> = props => {
           label: `${percentage}% ${commonTrans('status.loading')}`,
           tooltip: collectionTrans('collectionIsLoading'),
           icon: (
-            <StatusIcon
-              type={LoadingType.CREATING}
-              className={classes.loading}
-            />
+            <span
+              style={{
+                display: 'inline-flex',
+                alignItems: 'center',
+                marginRight: theme.spacing(1.25),
+              }}
+            >
+              <StatusIcon type={LoadingType.CREATING} />
+            </span>
           ),
           deleteIcon: null, // No delete icon during loading
         };
@@ -109,12 +99,22 @@ const StatusAction: FC<StatusActionType> = props => {
           deleteIcon: <Icons.release />,
         };
     }
-  }, [status, percentage, classes, collectionTrans]);
+  }, [status, percentage, theme, collectionTrans]);
 
   // Handle missing vector index
   const noIndex = collection.schema && !collection.schema.hasVectorIndex;
   const noIndexIcon = (
-    <div className={`${classes.circle} ${classes.noIndex}`}></div>
+    <span
+      style={{
+        display: 'inline-block',
+        width: 8,
+        height: 8,
+        borderRadius: '50%',
+        border: `1px solid ${theme.palette.text.disabled}`,
+        background: '#fff',
+        verticalAlign: 'middle',
+      }}
+    />
   );
   const noIndexTooltip = collectionTrans('noVectorIndexTooltip');
 
@@ -144,7 +144,7 @@ const StatusAction: FC<StatusActionType> = props => {
     return (
       <CustomButton
         startIcon={<Icons.load />}
-        className={classes.extraBtn}
+        sx={{ height: 24, padding: '0 8px' }}
         variant="contained"
         tooltip={collectionTrans('clickToLoad')}
         onClick={() => {
@@ -163,10 +163,14 @@ const StatusAction: FC<StatusActionType> = props => {
   }
 
   return (
-    <div className={classes.root}>
+    <div style={{ display: 'flex', alignItems: 'center' }}>
       <CustomToolTip title={noIndex ? noIndexTooltip : tooltip} placement="top">
         <Chip
-          className={classes.chip}
+          sx={{
+            border: 'none',
+            marginRight: theme.spacing(0.5),
+            paddingLeft: theme.spacing(0.5),
+          }}
           label={<Typography>{label}</Typography>}
           onClick={handleChipClick}
           disabled={noIndex}
@@ -181,7 +185,7 @@ const StatusAction: FC<StatusActionType> = props => {
           {status === LOADING_STATE.LOADED && (
             <CustomButton
               startIcon={<Icons.navSearch />}
-              className={classes.extraBtn}
+              sx={{ height: 24, padding: '0 8px' }}
               tooltip={collectionTrans('clickToSearch')}
               onClick={() => {
                 const newHash = window.location.hash.replace(

+ 14 - 23
client/src/pages/databases/collections/partitions/Partitions.tsx

@@ -1,4 +1,4 @@
-import { Theme } from '@mui/material';
+import { Box } from '@mui/material';
 import { useContext, useEffect, useState } from 'react';
 import { useSearchParams, useParams } from 'react-router-dom';
 import Highlighter from 'react-highlight-words';
@@ -15,26 +15,10 @@ import CreatePartitionDialog from '@/pages/dialogs/CreatePartitionDialog';
 import DropPartitionDialog from '@/pages/dialogs/DropPartitionDialog';
 import { formatNumber } from '@/utils';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
-import { makeStyles } from '@mui/styles';
 import type { PartitionData, ResStatus } from '@server/types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    height: `100%`,
-  },
-  icon: {
-    fontSize: '14px',
-    marginLeft: theme.spacing(0.5),
-  },
-  highlight: {
-    color: theme.palette.primary.main,
-    backgroundColor: 'transparent',
-  },
-}));
-
 const Partitions = () => {
   const { collectionName = '' } = useParams<{ collectionName: string }>();
-  const classes = useStyles();
   const { t } = useTranslation('partition');
   const { t: successTrans } = useTranslation('success');
   const { t: btnTrans } = useTranslation('btn');
@@ -215,7 +199,10 @@ const Partitions = () => {
           <Highlighter
             textToHighlight={newName}
             searchWords={[search]}
-            highlightClassName={classes.highlight}
+            highlightStyle={{
+              color: '#1976d2',
+              backgroundColor: 'transparent',
+            }}
           />
         );
       },
@@ -227,12 +214,16 @@ const Partitions = () => {
       align: 'left',
       disablePadding: false,
       label: (
-        <span className="flex-center with-max-content">
+        <Box
+          component="span"
+          className="flex-center with-max-content"
+          sx={{ display: 'inline-flex', alignItems: 'center' }}
+        >
           {t('rowCount')}
           <CustomToolTip title={t('tooltip')}>
-            <Icons.question classes={{ root: classes.icon }} />
+            <Icons.question sx={{ fontSize: 14, ml: 0.5 }} />
           </CustomToolTip>
-        </span>
+        </Box>
       ),
       formatter(data) {
         return formatNumber(Number(data.rowCount));
@@ -290,7 +281,7 @@ const Partitions = () => {
   };
 
   return (
-    <section className={classes.wrapper}>
+    <Box sx={{ height: '100%' }}>
       <AttuGrid
         toolbarConfigs={toolbarConfigs}
         colDefinitions={colDefinitions}
@@ -313,7 +304,7 @@ const Partitions = () => {
           )
         )}
       />
-    </section>
+    </Box>
   );
 };
 

+ 3 - 19
client/src/pages/databases/collections/properties/Properties.tsx

@@ -1,4 +1,4 @@
-import { Theme } from '@mui/material';
+import { Box } from '@mui/material';
 import { useContext, useState, useEffect } from 'react';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types';
@@ -10,25 +10,10 @@ import ResetPropertyDialog from '@/pages/dialogs/ResetPropertyDialog';
 import { rootContext } from '@/context';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { formatNumber } from '@/utils';
-import { makeStyles } from '@mui/styles';
 import { DatabaseService } from '@/http';
 import { databaseDefaults, collectionDefaults, Property } from '@/consts';
 import type { CollectionFullObject, KeyValuePair } from '@server/types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    height: `100%`,
-  },
-  icon: {
-    fontSize: '14px',
-    marginLeft: theme.spacing(0.5),
-  },
-  highlight: {
-    color: theme.palette.primary.main,
-    backgroundColor: 'transparent',
-  },
-}));
-
 const mergeProperties = (
   defaults: Property[],
   custom: KeyValuePair[] | undefined
@@ -49,7 +34,6 @@ interface PropertiesProps {
 const Properties = (props: PropertiesProps) => {
   const { target, type } = props;
 
-  const classes = useStyles();
   const { t } = useTranslation('properties');
   const { t: successTrans } = useTranslation('success');
   const { t: btnTrans } = useTranslation('btn');
@@ -211,7 +195,7 @@ const Properties = (props: PropertiesProps) => {
   }
 
   return (
-    <section className={classes.wrapper}>
+    <Box sx={{ height: '100%' }}>
       <AttuGrid
         toolbarConfigs={toolbarConfigs}
         colDefinitions={colDefinitions}
@@ -232,7 +216,7 @@ const Properties = (props: PropertiesProps) => {
           commonTrans(data.length > 1 ? 'grid.properties' : 'grid.property')
         )}
       />
-    </section>
+    </Box>
   );
 };
 

+ 15 - 22
client/src/pages/databases/collections/schema/CreateForm.tsx

@@ -6,24 +6,9 @@ import CustomInput from '@/components/customInput/CustomInput';
 import CustomSelector from '@/components/customSelector/CustomSelector';
 import CustomGroupedSelect from '@/components/customSelector/CustomGroupedSelect';
 import type { FormHelperType } from '../../../../types/Common';
-import { makeStyles } from '@mui/styles';
-import type { Option, GroupOption } from '@/components/customSelector/Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    maxWidth: '480px',
-  },
-  select: {
-    width: '100%',
-    marginBottom: theme.spacing(2),
-  },
-  paramTitle: {
-    margin: theme.spacing(2, 0),
-    color: theme.palette.text.secondary,
-    lineHeight: '20px',
-    fontSize: '14px',
-  },
-}));
+import { useTheme } from '@mui/material';
+import type { Option, GroupOption } from '@/components/customSelector/Types';
 
 const CreateForm = (
   props: FormHelperType & {
@@ -33,7 +18,7 @@ const CreateForm = (
     indexTypeChange?: (type: string) => void;
   }
 ) => {
-  const classes = useStyles();
+  const theme = useTheme();
   const {
     updateForm,
     formValue,
@@ -211,7 +196,7 @@ const CreateForm = (
   };
 
   return (
-    <div className={`${classes.wrapper} ${wrapperClass}`}>
+    <div className={`${wrapperClass || ''}`} style={{ maxWidth: 480 }}>
       <CustomGroupedSelect
         label={indexTrans('type')}
         options={indexOptions}
@@ -225,7 +210,7 @@ const CreateForm = (
           }
           indexTypeChange && indexTypeChange(type as string);
         }}
-        className={classes.select}
+        style={{ width: '100%', marginBottom: theme.spacing(2) }}
       />
       <CustomInput
         type="text"
@@ -234,7 +219,14 @@ const CreateForm = (
         validInfo={validation}
       />
       {metricOptions.length ? (
-        <Typography className={classes.paramTitle}>
+        <Typography
+          style={{
+            margin: theme.spacing(2, 0),
+            color: theme.palette.text.secondary,
+            lineHeight: '20px',
+            fontSize: 14,
+          }}
+        >
           {commonTrans('param')}
         </Typography>
       ) : null}
@@ -249,7 +241,8 @@ const CreateForm = (
             updateForm('metric_type', type as string);
           }}
           variant="filled"
-          wrapperClass={classes.select}
+          wrapperClass=""
+          style={{ width: '100%', marginBottom: theme.spacing(2) }}
         />
       ) : null}
 

+ 30 - 62
client/src/pages/databases/collections/schema/IndexTypeElement.tsx

@@ -11,62 +11,11 @@ import { IndexState } from '@/consts/Milvus';
 import { NONE_INDEXABLE_DATA_TYPES, DataTypeStringEnum } from '@/consts';
 import CreateIndexDialog from './CreateIndexDialog';
 import CustomButton from '@/components/customButton/CustomButton';
-import { makeStyles } from '@mui/styles';
+import { useTheme } from '@mui/material';
 import { isVectorType } from '@/utils';
 import type { FieldObject } from '@server/types';
 import { CollectionService } from '@/http';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    // give fixed width to prevent table cell stretching
-    width: 'auto',
-  },
-  item: {
-    paddingLeft: theme.spacing(1),
-  },
-  btn: {
-    display: 'flex',
-    alignItems: 'center',
-    whiteSpace: 'nowrap',
-    color: theme.palette.primary.main,
-    height: 26,
-    fontSize: 13,
-    border: `1px solid transparent`,
-    '&:hover': {
-      cursor: 'pointer',
-    },
-    '&.outline': {
-      border: `1px dashed ${theme.palette.primary.main}`,
-    },
-    '& svg': {
-      width: 15,
-    },
-  },
-  btnDisabled: {
-    color: theme.palette.text.secondary,
-    pointerEvents: 'none',
-
-    '&:hover': {
-      cursor: 'default',
-    },
-  },
-  chip: {
-    padding: theme.spacing(0.5),
-
-    '& .icon': {
-      width: '16px',
-      height: '16px',
-    },
-  },
-  chipLabel: {
-    fontSize: '12px',
-  },
-  addIcon: {
-    width: '20px',
-    height: '20px',
-  },
-}));
-
 const IndexTypeElement: FC<{
   field: FieldObject;
   collectionName: string;
@@ -76,7 +25,7 @@ const IndexTypeElement: FC<{
 }> = ({ field, collectionName, cb, disabled }) => {
   const { fetchCollection } = useContext(dataContext);
 
-  const classes = useStyles();
+  const theme = useTheme();
   // set empty string as default status
   const { t: indexTrans } = useTranslation('index');
   const { t: dialogTrans } = useTranslation('dialog');
@@ -156,13 +105,17 @@ const IndexTypeElement: FC<{
 
   const chipComp = (
     text = field.index.indexType,
-    icon = <Icons.delete classes={{ root: 'icon' }} />,
+    icon = <Icons.delete sx={{ width: 16, height: 16 }} />,
     tooltip = ''
   ) => {
+    let labelText = text;
+    if (field.index && field.index.metricType) {
+      labelText = `${text}(${field.index.metricType})`;
+    }
     const IndexElem = () => (
       <Chip
-        label={text}
-        classes={{ root: classes.chip, label: classes.chipLabel }}
+        label={<span style={{ fontSize: 12 }}>{labelText}</span>}
+        sx={{ padding: theme.spacing(0.5) }}
         deleteIcon={icon}
         onDelete={handleDelete}
         disabled={disabled}
@@ -176,9 +129,9 @@ const IndexTypeElement: FC<{
 
     return tooltip ? (
       <Tooltip arrow title={tooltip} placement="top">
-        <div>
+        <span>
           <IndexElem />
-        </div>
+        </span>
       </Tooltip>
     ) : (
       <IndexElem />
@@ -191,7 +144,7 @@ const IndexTypeElement: FC<{
         field.data_type as DataTypeStringEnum
       ) !== -1
     ) {
-      return <div className={classes.item}>--</div>;
+      return <span style={{ paddingLeft: theme.spacing(1) }}>--</span>;
     }
 
     if (!field.index) {
@@ -199,7 +152,18 @@ const IndexTypeElement: FC<{
       return (
         <CustomButton
           startIcon={<Icons.addOutline />}
-          className={`${classes.btn}${isVector ? ' outline' : ''}`}
+          sx={{
+            display: 'flex',
+            alignItems: 'center',
+            whiteSpace: 'nowrap',
+            color: theme.palette.primary.main,
+            height: 26,
+            fontSize: 13,
+            border: isVector
+              ? `1px dashed ${theme.palette.primary.main}`
+              : '1px solid transparent',
+            '& svg': { width: 15 },
+          }}
           onClick={e => handleCreate(e)}
         >
           {btnTrans(isVector ? 'createVectorIndex' : 'createScalarIndex')}
@@ -224,12 +188,16 @@ const IndexTypeElement: FC<{
         /**
          * if creating finished, show chip that contains index type
          */
-        return chipComp(field.index.indexType);
+        return chipComp(`${field.index.indexType}`);
       }
     }
   };
 
-  return <div className={classes.wrapper}>{generateElement()}</div>;
+  return (
+    <span style={{ width: 'auto', display: 'inline-block' }}>
+      {generateElement()}
+    </span>
+  );
 };
 
 export default IndexTypeElement;

+ 1 - 28
client/src/pages/databases/collections/search/Search.tsx

@@ -1,4 +1,4 @@
-import { useState, useMemo, ChangeEvent, useCallback, useContext } from 'react';
+import { useState, useMemo, ChangeEvent, useCallback } from 'react';
 import {
   Typography,
   Accordion,
@@ -8,7 +8,6 @@ import {
 } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { DataService, CollectionService } from '@/http';
-import { rootContext } from '@/context';
 import Icons from '@/components/icons/Icons';
 import AttuGrid from '@/components/grid/Grid';
 import Filter from '@/components/advancedSearch';
@@ -29,7 +28,6 @@ import {
   generateVectorsByField,
   saveCsvAs,
   buildSearchParams,
-  buildSearchCode,
   getColumnWidth,
 } from '@/utils';
 import SearchParams from '../../../search/SearchParams';
@@ -39,7 +37,6 @@ import {
   SearchSingleParams,
 } from '../../types';
 import { DYNAMIC_FIELD } from '@/consts';
-import CodeDialog from '@/pages/dialogs/CodeDialog';
 import CollectionColHeader from '../CollectionColHeader';
 import DataView from '@/components/DataView/DataView';
 import type { GraphData, GraphNode } from '../../types';
@@ -69,9 +66,6 @@ const Search = (props: CollectionDataProps) => {
     i => i.collection_name === collectionName
   ) as CollectionFullObject;
 
-  // context
-  const { setDialog } = useContext(rootContext);
-
   // UI states
   const [tableLoading, setTableLoading] = useState<boolean>();
   const [highlightField, setHighlightField] = useState<string>('');
@@ -597,27 +591,6 @@ const Search = (props: CollectionDataProps) => {
                 >
                   {btnTrans('explore')}
                 </CustomButton>
-                <CustomButton
-                  className={classes.btn}
-                  disabled={disableSearch}
-                  onClick={() => {
-                    // open code dialog
-                    setDialog({
-                      open: true,
-                      type: 'custom',
-                      params: {
-                        component: (
-                          <CodeDialog
-                            data={buildSearchCode(searchParams, collection)}
-                          />
-                        ),
-                      },
-                    });
-                  }}
-                  startIcon={<Icons.code classes={{ root: 'icon' }} />}
-                >
-                  {btnTrans('Code')}
-                </CustomButton>
 
                 <CustomButton
                   className={classes.btn}

+ 0 - 77
client/src/pages/dialogs/CodeDialog.tsx

@@ -1,77 +0,0 @@
-import { FC, useContext, useState } from 'react';
-import { Theme, SelectChangeEvent } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { rootContext } from '@/context';
-import DialogTemplate from '@/components/customDialog/DialogTemplate';
-import CodeBlock from '@/components/code/CodeBlock';
-import CustomSelector from '@/components/customSelector/CustomSelector';
-import { makeStyles } from '@mui/styles';
-
-const useStyles = makeStyles((theme: Theme) => ({
-  code: {
-    backgroundColor: theme.palette.background.default,
-    width: 800,
-    height: '40vh',
-    overflow: 'auto',
-  },
-}));
-
-type CodeDialogProps = {
-  data: { [key: string]: string };
-};
-
-const CodeDialog: FC<CodeDialogProps> = props => {
-  // props
-  const langauges = Object.keys(props.data);
-
-  // build options
-  const options = langauges.map(lang => ({
-    label: lang,
-    value: lang,
-  }));
-  // styles
-  const classes = useStyles();
-  // states
-  const [langauge, setLanguage] = useState(langauges[0]);
-  const code = props.data[langauge];
-
-  // context
-  const { handleCloseDialog } = useContext(rootContext);
-  // translations
-  const { t: btnTrans } = useTranslation('btn');
-
-  const disabled = false;
-
-  return (
-    <DialogTemplate
-      title={'Code View(beta)'}
-      handleClose={handleCloseDialog}
-      children={
-        <>
-          <CustomSelector
-            label="Language"
-            value={langauge}
-            onChange={(event: SelectChangeEvent<unknown>) => {
-              setLanguage(event.target.value as string);
-            }}
-            options={options}
-            variant="filled"
-          />
-          <CodeBlock
-            code={code}
-            language={langauge === 'node.js' ? 'javascript' : langauge}
-            wrapperClass={classes.code}
-          />
-        </>
-      }
-      confirmLabel={btnTrans('Ok')}
-      handleConfirm={() => {
-        handleCloseDialog();
-      }}
-      showCancel={false}
-      confirmDisabled={disabled}
-    />
-  );
-};
-
-export default CodeDialog;

+ 21 - 24
client/src/pages/dialogs/EditJSONDialog.tsx

@@ -1,25 +1,10 @@
 import { FC, useState } from 'react';
-import { Theme } from '@mui/material';
+import { Box, useTheme } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
-import { makeStyles } from '@mui/styles';
 
 import { JSONEditor } from '../play/JSONEditor';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  code: {
-    border: `1px solid ${theme.palette.divider}`,
-    borderRadius: 4,
-    overflow: 'auto',
-  },
-  tip: {
-    fontSize: 12,
-    marginBottom: 8,
-    width: 480,
-    lineHeight: '20px',
-  },
-}));
-
 type EditJSONDialogProps = {
   data: { [key: string]: any };
   handleConfirm: (data: { [key: string]: any }) => void;
@@ -36,8 +21,8 @@ const EditJSONDialog: FC<EditJSONDialogProps> = props => {
   const [disabled, setDisabled] = useState(true);
   // translations
   const { t: btnTrans } = useTranslation('btn');
-  // styles
-  const classes = useStyles();
+  // theme
+  const theme = useTheme();
 
   const originalData = JSON.stringify(data, null, 2) + '\n';
   const [value, setValue] = useState(originalData);
@@ -51,7 +36,8 @@ const EditJSONDialog: FC<EditJSONDialogProps> = props => {
   const handleChange = (docValue: string) => {
     try {
       setValue(docValue);
-      const jsonValue = JSON.parse(docValue);
+      // Validate JSON parsing
+      JSON.parse(docValue);
       setDisabled(docValue === originalData);
     } catch (err) {
       setDisabled(true);
@@ -64,15 +50,26 @@ const EditJSONDialog: FC<EditJSONDialogProps> = props => {
       handleClose={handleCloseDialog}
       children={
         <>
-          <div
-            className={classes.tip}
+          <Box
+            sx={{
+              fontSize: 12,
+              marginBottom: 1, // theme.spacing(1)
+              width: 480,
+              lineHeight: '20px',
+            }}
             dangerouslySetInnerHTML={{
               __html: props.dialogTip,
             }}
-          ></div>
-          <div className={classes.code}>
+          ></Box>
+          <Box
+            sx={{
+              border: `1px solid ${theme.palette.divider}`,
+              borderRadius: 1, // theme.shape.borderRadius
+              overflow: 'auto',
+            }}
+          >
             <JSONEditor value={originalData} onChange={handleChange} />
-          </div>
+          </Box>
         </>
       }
       confirmDisabled={disabled}

+ 1 - 1
client/src/pages/dialogs/Types.ts

@@ -1,4 +1,4 @@
-import type { PartitionData, CollectionObject } from '@server/types';
+import type { CollectionObject } from '@server/types';
 
 export interface DropCollectionProps {
   collections: CollectionObject[];

+ 2 - 2
client/src/pages/dialogs/create/ExtraInfoSection.tsx

@@ -34,14 +34,14 @@ const ExtraInfoSection: React.FC<ExtraInfoSectionProps> = ({
         mt: 2,
         display: 'flex',
         flexDirection: 'column',
-        gap: 1, // Corresponds to gap: 8 in makeStyles
+        gap: 1,
         '& fieldset': {
           border: 'none',
           padding: 0,
           margin: 0,
         },
         '& input': {
-          ml: 0, // Corresponds to marginLeft: 0 in makeStyles
+          ml: 0,
         },
       }}
     >

+ 3 - 11
client/src/pages/dialogs/insert/Dialog.tsx

@@ -21,17 +21,10 @@ import InsertPreview from './Preview';
 import InsertStatus from './Status';
 import { InsertStatusEnum, InsertStepperEnum } from './consts';
 import { DataService } from '@/http';
-import { makeStyles } from '@mui/styles';
 import type { InsertContentProps } from './Types';
 import type { Option } from '@/components/customSelector/Types';
 import type { InsertDataParam } from '@/pages/databases/collections/Types';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  icon: {
-    fontSize: '16px',
-  },
-}));
-
 /**
  * this component contains processes during insert
  * including import, preview and status
@@ -46,8 +39,6 @@ const InsertContainer: FC<InsertContentProps> = ({
   schema,
   onInsert,
 }) => {
-  const classes = getStyles();
-
   const { t: insertTrans } = useTranslation('insert');
   const { t: btnTrans } = useTranslation('btn');
   const { handleCloseDialog, openSnackBar } = useContext(rootContext);
@@ -184,7 +175,7 @@ const InsertContainer: FC<InsertContentProps> = ({
         confirm: btnTrans('importFile'),
         cancel: (
           <>
-            <BackIcon classes={{ root: classes.icon }} />
+            <BackIcon sx={{ fontSize: '16px' }} />
             {btnTrans('previous')}
           </>
         ),
@@ -195,7 +186,7 @@ const InsertContainer: FC<InsertContentProps> = ({
       },
     };
     return labelMap[activeStep];
-  }, [activeStep, btnTrans, BackIcon, classes.icon]);
+  }, [activeStep, btnTrans, BackIcon]);
 
   const { showActions, showCancel } = useMemo(() => {
     return {
@@ -327,6 +318,7 @@ const InsertContainer: FC<InsertContentProps> = ({
       const inserted = await DataService.insertData(collectionValue, param);
       if (inserted.status.error_code !== 'Success') {
         setInsertFailMsg(inserted.status.reason);
+        setInsertStatus(InsertStatusEnum.error);
       } else {
         await DataService.flush(collectionValue);
         // update collections

+ 105 - 140
client/src/pages/dialogs/insert/Import.tsx

@@ -1,106 +1,20 @@
 import { FC } from 'react';
 import { useTranslation } from 'react-i18next';
-import { Theme, Divider, Typography } from '@mui/material';
+import { Divider, Typography } from '@mui/material';
 import CustomSelector from '@/components/customSelector/CustomSelector';
 import Uploader from '@/components/uploader/Uploader';
 import { INSERT_MAX_SIZE } from '@/consts';
 import { parseByte } from '@/utils';
-import { makeStyles } from '@mui/styles';
 import type { InsertImportProps } from './Types';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  tip: {
-    color: theme.palette.text.primary,
-    fontWeight: 500,
-    marginBottom: theme.spacing(1),
-  },
-  selectors: {
-    '& .selectorWrapper': {
-      display: 'flex',
-      justifyContent: 'space-between',
-      alignItems: 'center',
-
-      marginBottom: theme.spacing(3),
-
-      '& .selectLabel': {
-        fontSize: '14px',
-        lineHeight: '20px',
-        color: theme.palette.text.primary,
-      },
-
-      '& .divider': {
-        width: '20px',
-        margin: theme.spacing(0, 4),
-        backgroundcolor: theme.palette.text.secondary,
-      },
-    },
-
-    '& .selector': {
-      flexBasis: '40%',
-      minWidth: '256px',
-    },
-  },
-
-  uploadWrapper: {
-    marginTop: theme.spacing(3),
-    padding: theme.spacing(1),
-    backgroundColor: theme.palette.background.default,
-
-    '& .text': {
-      color: theme.palette.text.secondary,
-    },
-
-    '& .file': {
-      marginBottom: theme.spacing(1),
-    },
-
-    '& .uploaderWrapper': {
-      display: 'flex',
-      alignItems: 'center',
-
-      border: `1px solid ${theme.palette.divider}`,
-      padding: theme.spacing(1),
-
-      backgroundColor: theme.palette.background.paper,
-
-      '& .uploader': {
-        marginRight: theme.spacing(1),
-      },
-    },
-
-    '& .sampleWrapper': {
-      '& .sample': {
-        backgroundColor: theme.palette.background.paper,
-        padding: theme.spacing(2),
-        margin: theme.spacing(1, 0),
-      },
-    },
-
-    '& .title': {
-      marginTop: theme.spacing(1),
-    },
-
-    '& .noteList': {
-      marginTop: theme.spacing(1),
-      paddingLeft: theme.spacing(3),
-    },
-
-    '& .noteItem': {
-      maxWidth: '560px',
-    },
-  },
-}));
+import Box from '@mui/material/Box';
 
 const InsertImport: FC<InsertImportProps> = ({
   collectionOptions,
   partitionOptions,
-
   selectedCollection,
   selectedPartition,
-
   handleCollectionChange,
   handlePartitionChange,
-
   handleUploadedData,
   handleUploadFileChange,
   fileName,
@@ -109,58 +23,104 @@ const InsertImport: FC<InsertImportProps> = ({
   const { t: insertTrans } = useTranslation('insert');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: partitionTrans } = useTranslation('partition');
-  const classes = getStyles();
 
   return (
-    <section>
-      <Typography className={classes.tip}>
+    <Box>
+      <Typography
+        sx={theme => ({
+          color: theme.palette.text.primary,
+          fontWeight: 500,
+          mb: 1,
+        })}
+      >
         {insertTrans('targetTip')}
       </Typography>
 
-      <section className={classes.selectors}>
-        <div className="selectorWrapper">
-          <CustomSelector
-            options={collectionOptions}
-            disabled={collectionOptions.length === 0}
-            wrapperClass="selector"
-            labelClass="selectLabel"
-            value={selectedCollection}
-            variant="filled"
-            label={collectionTrans('collection')}
-            onChange={(e: { target: { value: unknown } }) => {
-              const collection = e.target.value;
-              handleCollectionChange &&
-                handleCollectionChange(collection as string);
-            }}
-          />
-          <Divider classes={{ root: 'divider' }} />
-          <CustomSelector
-            options={partitionOptions}
-            disabled={partitionOptions.length === 0}
-            wrapperClass="selector"
-            labelClass="selectLabel"
-            value={selectedPartition}
-            variant="filled"
-            label={partitionTrans('partition')}
-            onChange={(e: { target: { value: unknown } }) => {
-              const partition = e.target.value;
-              handlePartitionChange(partition as string);
-            }}
+      <Box sx={{ mb: 3 }}>
+        <Box
+          sx={{
+            display: 'flex',
+            justifyContent: 'space-between',
+            alignItems: 'center',
+            mb: 3,
+          }}
+        >
+          <Box sx={{ flexBasis: '40%' }}>
+            <CustomSelector
+              options={collectionOptions}
+              disabled={collectionOptions.length === 0}
+              wrapperClass="selector"
+              labelClass="selectLabel"
+              value={selectedCollection}
+              variant="filled"
+              label={collectionTrans('collection')}
+              onChange={(e: { target: { value: unknown } }) => {
+                const collection = e.target.value;
+                handleCollectionChange &&
+                  handleCollectionChange(collection as string);
+              }}
+              sx={{
+                width: '100%',
+                minWidth: 256,
+              }}
+            />
+          </Box>
+          <Divider
+            sx={theme => ({
+              width: 20,
+              mx: 4,
+              bgcolor: theme.palette.text.secondary,
+            })}
           />
-        </div>
-      </section>
-
-      <div className={classes.uploadWrapper}>
-        <Typography className="text file" variant="body1">
+          <Box sx={{ flexBasis: '40%' }}>
+            <CustomSelector
+              options={partitionOptions}
+              disabled={partitionOptions.length === 0}
+              wrapperClass="selector"
+              labelClass="selectLabel"
+              value={selectedPartition}
+              variant="filled"
+              label={partitionTrans('partition')}
+              onChange={(e: { target: { value: unknown } }) => {
+                const partition = e.target.value;
+                handlePartitionChange(partition as string);
+              }}
+              sx={{
+                width: '100%',
+                minWidth: 256,
+              }}
+            />
+          </Box>
+        </Box>
+      </Box>
+
+      <Box
+        sx={theme => ({
+          mt: 3,
+          p: 1,
+          bgcolor: theme.palette.background.default,
+        })}
+      >
+        <Typography
+          sx={theme => ({ color: theme.palette.text.secondary, mb: 1 })}
+          variant="body1"
+        >
           {insertTrans('file')}
         </Typography>
-        <div className="uploaderWrapper">
+        <Box
+          sx={theme => ({
+            display: 'flex',
+            alignItems: 'center',
+            border: `1px solid ${theme.palette.divider}`,
+            p: 1,
+            gap: 1,
+            bgcolor: theme.palette.background.paper,
+          })}
+        >
           <Uploader
             btnClass="uploader"
             label={insertTrans('uploaderLabel')}
             accept=".csv,.json"
-            // selected collection will affect schema, which is required for uploaded data validation check
-            // so upload file should be disabled until user select one collection
             disabled={!selectedCollection}
             disableTooltip={insertTrans('uploadFileDisableTooltip')}
             setFileName={setFileName}
@@ -171,23 +131,28 @@ const InsertImport: FC<InsertImportProps> = ({
             })}
             handleUploadFileChange={handleUploadFileChange}
           />
-          <Typography className="text">
+          <Typography sx={theme => ({ color: theme.palette.text.secondary })}>
             {fileName || insertTrans('fileNamePlaceHolder')}
           </Typography>
-        </div>
+        </Box>
 
-        <Typography variant="body2" className="text title">
+        <Typography
+          variant="body2"
+          sx={theme => ({ color: theme.palette.text.secondary, mt: 1 })}
+        >
           {insertTrans('noteTitle')}
         </Typography>
-        <ul className="noteList">
-          {insertTrans('notes').map((note: string) => (
-            <li key={note} className="text noteItem">
-              <Typography>{note}</Typography>
-            </li>
-          ))}
-        </ul>
-      </div>
-    </section>
+        <Box component="ul" sx={theme => ({ mt: 1, pl: 3 })}>
+          {(insertTrans('notes', { returnObjects: true }) as string[]).map(
+            (note: string) => (
+              <Box component="li" key={note} sx={{ maxWidth: 560 }}>
+                <Typography>{note}</Typography>
+              </Box>
+            )
+          )}
+        </Box>
+      </Box>
+    </Box>
   );
 };
 

+ 60 - 95
client/src/pages/dialogs/insert/Preview.tsx

@@ -1,5 +1,5 @@
 import { FC, useCallback, useMemo } from 'react';
-import { Theme, Typography } from '@mui/material';
+import { Typography, Box } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { InsertPreviewProps } from './Types';
 import CustomSelector from '@/components/customSelector/CustomSelector';
@@ -7,73 +7,9 @@ import AttuGrid from '@/components/grid/Grid';
 import { transferCsvArrayToTableData } from '@/utils';
 import SimpleMenu from '@/components/menu/SimpleMenu';
 import icons from '@/components/icons/Icons';
-import { makeStyles } from '@mui/styles';
 import type { Option } from '@/components/customSelector/Types';
 import type { ColDefinitionsType } from '@/components/grid/Types';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    width: '75vw',
-  },
-  selectorTip: {
-    color: theme.palette.text.secondary,
-    fontWeight: 500,
-    marginBottom: theme.spacing(1),
-  },
-  selectorWrapper: {
-    '& .selector': {
-      flexBasis: '40%',
-      minWidth: '256px',
-    },
-
-    '& .isContainSelect': {
-      paddingTop: theme.spacing(2),
-      paddingBottom: theme.spacing(2),
-    },
-  },
-  gridWrapper: {
-    height: '320px',
-  },
-  tableTip: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-
-    marginTop: theme.spacing(3),
-    marginBottom: theme.spacing(1),
-
-    '& .text': {
-      color: theme.palette.text.secondary,
-      fontWeight: 500,
-    },
-  },
-  menuLabel: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    minWidth: '160px',
-
-    color: theme.palette.text.secondary,
-    backgroundColor: '#fff',
-
-    '&:hover': {
-      backgroundColor: '#fff',
-    },
-  },
-
-  menuIcon: {
-    color: theme.palette.text.secondary,
-  },
-  menuItem: {
-    fontWeight: 500,
-    fontSize: '12px',
-    lineHeight: '16px',
-    color: theme.palette.text.secondary,
-  },
-  menuActive: {
-    color: theme.palette.primary.main,
-  },
-}));
-
 const getTableData = (
   data: any[],
   isContainFieldNames: number
@@ -91,7 +27,7 @@ const InsertPreview: FC<InsertPreviewProps> = ({
   setTableHeads,
   file,
 }) => {
-  const classes = getStyles();
+  // Styles replaced with Box and sx below
   const { t: insertTrans } = useTranslation('insert');
 
   const ArrowIcon = icons.dropdown;
@@ -117,28 +53,27 @@ const InsertPreview: FC<InsertPreviewProps> = ({
             menuItems={schemaOptions.map(schema => ({
               label: schema.label,
               callback: () => handleTableHeadChange(index, schema.label),
-              wrapperClass: `${classes.menuItem} ${
-                head === schema.label ? classes.menuActive : ''
-              }`,
+              wrapperClass: head === schema.label ? 'menu-active' : 'menu-item',
             }))}
             buttonProps={{
-              className: classes.menuLabel,
-              endIcon: <ArrowIcon classes={{ root: classes.menuIcon }} />,
+              sx: {
+                display: 'flex',
+                justifyContent: 'space-between',
+                minWidth: 160,
+                color: theme => theme.palette.text.secondary,
+                backgroundColor: '#fff',
+                '&:hover': { backgroundColor: '#fff' },
+              },
+              endIcon: (
+                <ArrowIcon
+                  sx={{ color: theme => theme.palette.text.secondary }}
+                />
+              ),
             }}
           ></SimpleMenu>
         ),
       })),
-    [
-      tableHeads,
-      classes.menuLabel,
-      classes.menuIcon,
-      classes.menuItem,
-      classes.menuActive,
-      ArrowIcon,
-      schemaOptions,
-      insertTrans,
-      handleTableHeadChange,
-    ]
+    [tableHeads, ArrowIcon, schemaOptions, insertTrans, handleTableHeadChange]
   );
 
   const isContainedOptions: Option[] = [
@@ -161,13 +96,27 @@ const InsertPreview: FC<InsertPreviewProps> = ({
     }));
 
   return (
-    <section className={classes.wrapper}>
-      <form className={classes.selectorWrapper}>
-        <label>
-          <Typography className={classes.selectorTip}>
-            {insertTrans('isContainFieldNames')}
-          </Typography>
-        </label>
+    <Box sx={{ width: '75vw' }}>
+      <Box
+        sx={{
+          display: 'flex',
+          flexWrap: 'wrap',
+          alignItems: 'center',
+          mb: 2,
+          '& .selector': { flexBasis: '40%', minWidth: 256 },
+          '& .isContainSelect': { pt: 2, pb: 2 },
+        }}
+        component="form"
+      >
+        <Typography
+          sx={{
+            color: theme => theme.palette.text.secondary,
+            fontWeight: 500,
+            mb: 1,
+          }}
+        >
+          {insertTrans('isContainFieldNames')}
+        </Typography>
         <CustomSelector
           options={isContainedOptions}
           wrapperClass="selector"
@@ -178,18 +127,34 @@ const InsertPreview: FC<InsertPreviewProps> = ({
             const isContainedValue = e.target.value;
             handleIsContainedChange(isContainedValue as number);
           }}
+          sx={{
+            width: '100px',
+            ml: 2,
+          }}
         />
-      </form>
-      <div className={classes.tableTip}>
+      </Box>
+      <Box
+        sx={{
+          display: 'flex',
+          justifyContent: 'space-between',
+          alignItems: 'center',
+          mt: 3,
+          mb: 1,
+          '& .text': {
+            color: theme => theme.palette.text.secondary,
+            fontWeight: 500,
+          },
+        }}
+      >
         <Typography className="text">
           {insertTrans('previewTipData')}
         </Typography>
         <Typography className="text">
           {insertTrans('previewTipAction')}
         </Typography>
-      </div>
+      </Box>
       {tableData.length > 0 && (
-        <div className={classes.gridWrapper}>
+        <Box sx={{ height: 320 }}>
           <AttuGrid
             toolbarConfigs={[]}
             colDefinitions={colDefinitions}
@@ -202,9 +167,9 @@ const InsertPreview: FC<InsertPreviewProps> = ({
             editHeads={editHeads}
             tableCellMaxWidth="120px"
           />
-        </div>
+        </Box>
       )}
-    </section>
+    </Box>
   );
 };
 

+ 22 - 32
client/src/pages/dialogs/insert/Status.tsx

@@ -4,39 +4,31 @@ import { useTranslation } from 'react-i18next';
 import type { InsertStatusProps } from './Types';
 import successPath from '@/assets/imgs/insert/success.png';
 import failPath from '@/assets/imgs/insert/fail.png';
-import { makeStyles } from '@mui/styles';
 import { InsertStatusEnum } from './consts';
 
-const getStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    width: '75vw',
-    height: (props: { status: InsertStatusEnum }) =>
-      props.status === InsertStatusEnum.loading ? '288px' : '200px',
+import Box from '@mui/material/Box';
 
+const InsertStatus: FC<InsertStatusProps> = ({ status, failMsg }) => {
+  const { t: insertTrans } = useTranslation('insert');
+
+  const textSx = (theme: Theme) => ({ marginTop: theme.spacing(3) });
+  const loadingTipSx = (theme: Theme) => ({ marginBottom: theme.spacing(6) });
+  const loadingSvgSx = (theme: Theme) => ({
+    color: theme.palette.primary.main,
+  });
+  const wrapperSx = (theme: Theme) => ({
+    width: '75vw',
+    height: status === InsertStatusEnum.loading ? '288px' : '200px',
     display: 'flex',
     flexDirection: 'column',
     alignItems: 'center',
     justifyContent: 'center',
-  },
-  loadingTip: {
-    marginBottom: theme.spacing(6),
-  },
-  loadingSvg: {
-    color: theme.palette.primary.main,
-  },
-  text: {
-    marginTop: theme.spacing(3),
-  },
-}));
-
-const InsertStatus: FC<InsertStatusProps> = ({ status, failMsg }) => {
-  const { t: insertTrans } = useTranslation('insert');
-  const classes = getStyles({ status });
+  });
 
   const InsertSuccess = () => (
     <>
       <img src={successPath} alt="insert success" />
-      <Typography variant="h4" className={classes.text}>
+      <Typography variant="h4" sx={textSx}>
         {insertTrans('statusSuccess')}
       </Typography>
     </>
@@ -44,17 +36,13 @@ const InsertStatus: FC<InsertStatusProps> = ({ status, failMsg }) => {
 
   const InsertLoading = () => (
     <>
-      <CircularProgress
-        size={64}
-        thickness={5}
-        classes={{ svg: classes.loadingSvg }}
-      />
-      <Typography variant="h4" className={classes.text}>
+      <CircularProgress size={64} thickness={5} sx={loadingSvgSx} />
+      <Typography variant="h4" sx={textSx}>
         {insertTrans('statusLoading')}
       </Typography>
       <Typography
         variant="h5"
-        className={`${classes.text} ${classes.loadingTip}`}
+        sx={theme => ({ ...textSx(theme), ...loadingTipSx(theme) })}
       >
         {insertTrans('statusLoadingTip')}
       </Typography>
@@ -63,10 +51,10 @@ const InsertStatus: FC<InsertStatusProps> = ({ status, failMsg }) => {
   const InsertError = () => (
     <>
       <img src={failPath} alt="insert error" />
-      <Typography variant="h4" className={classes.text}>
+      <Typography variant="h4" sx={textSx}>
         {insertTrans('statusError')}
       </Typography>
-      {failMsg && <Typography className={classes.text}>{failMsg}</Typography>}
+      {failMsg && <Typography sx={textSx}>{failMsg}</Typography>}
     </>
   );
 
@@ -83,7 +71,9 @@ const InsertStatus: FC<InsertStatusProps> = ({ status, failMsg }) => {
   };
 
   return (
-    <section className={classes.wrapper}>{generateStatus(status)}</section>
+    <Box component="section" sx={wrapperSx}>
+      {generateStatus(status)}
+    </Box>
   );
 };
 

+ 69 - 47
client/src/pages/home/SysCard.tsx

@@ -1,36 +1,5 @@
-import { Theme, Typography } from '@mui/material';
+import { Theme, Typography, Box } from '@mui/material';
 import { Link } from 'react-router-dom';
-import { makeStyles } from '@mui/styles';
-
-const useStyles = makeStyles((theme: Theme) => ({
-  sysCard: {
-    minWidth: 'auto',
-    gap: theme.spacing(1),
-    backgroundColor: theme.palette.background.paper,
-    padding: theme.spacing(2),
-    border: `1px solid ${theme.palette.divider}`,
-    cursor: 'pointer',
-    borderRadius: 8,
-    '&:hover': {
-      boxShadow: '0px 0px 4px 0px #00000029',
-    },
-
-    '& p': {
-      fontSize: '24px',
-      margin: 0,
-    },
-    '& h3': {
-      margin: 0,
-      fontSize: '14px',
-      lineHeight: 1.5,
-      color: theme.palette.text.secondary,
-    },
-    '& a': {
-      textDecoration: 'none',
-      color: theme.palette.text.primary,
-    },
-  },
-}));
 
 const SysCard = (data: {
   title: string;
@@ -38,22 +7,75 @@ const SysCard = (data: {
   des?: string;
   link?: string;
 }) => {
-  const classes = useStyles();
-
-  const content = (
-    <>
-      <Typography component={'p'}>{data.count}</Typography>
-      <Typography component={'h3'}>{data.title}</Typography>
-      {data.des ? <Typography component={'p'}>{data.des}</Typography> : null}
-    </>
-  );
-
   return (
-    <section className={classes.sysCard}>
-      <section>
-        {data.link ? <Link to={data.link}>{content}</Link> : content}
-      </section>
-    </section>
+    <Box
+      sx={(theme: Theme) => ({
+        minWidth: 'auto',
+        gap: theme.spacing(1),
+        backgroundColor: theme.palette.background.paper,
+        padding: theme.spacing(2),
+        border: `1px solid ${theme.palette.divider}`,
+        cursor: 'pointer',
+        borderRadius: 2,
+        display: 'flex',
+        flexDirection: 'column',
+        alignItems: 'flex-start',
+        transition: 'box-shadow 0.2s',
+        '&:hover': {
+          boxShadow: '0px 0px 4px 0px #00000029',
+        },
+        '& a': {
+          textDecoration: 'none',
+          color: theme.palette.text.primary,
+        },
+      })}
+    >
+      {data.link ? (
+        <Link to={data.link} style={{ textDecoration: 'none' }}>
+          <Typography component="p" sx={{ fontSize: 24, m: 0 }}>
+            {data.count}
+          </Typography>
+          <Typography
+            component="h3"
+            sx={theme => ({
+              m: 0,
+              fontSize: 14,
+              lineHeight: 1.5,
+              color: theme.palette.text.secondary,
+            })}
+          >
+            {data.title}
+          </Typography>
+          {data.des ? (
+            <Typography component="p" sx={{ fontSize: 14, m: 0 }}>
+              {data.des}
+            </Typography>
+          ) : null}
+        </Link>
+      ) : (
+        <>
+          <Typography component="p" sx={{ fontSize: 24, m: 0 }}>
+            {data.count}
+          </Typography>
+          <Typography
+            component="h3"
+            sx={theme => ({
+              m: 0,
+              fontSize: 14,
+              lineHeight: 1.5,
+              color: theme.palette.text.secondary,
+            })}
+          >
+            {data.title}
+          </Typography>
+          {data.des ? (
+            <Typography component="p" sx={{ fontSize: 14, m: 0 }}>
+              {data.des}
+            </Typography>
+          ) : null}
+        </>
+      )}
+    </Box>
   );
 };
 

+ 0 - 1
client/src/pages/search/Constants.ts

@@ -1 +0,0 @@
-export const TOP_K_OPTIONS = [50, 100, 150, 200, 250];

+ 13 - 39
client/src/pages/search/SearchParams.tsx

@@ -1,5 +1,5 @@
-import { Theme } from '@mui/material';
 import { FC, useCallback, useContext, useEffect, useMemo } from 'react';
+import Box from '@mui/material/Box';
 import { useTranslation } from 'react-i18next';
 import CustomInput from '@/components/customInput/CustomInput';
 import { ITextfieldConfig } from '@/components/customInput/Types';
@@ -13,24 +13,6 @@ import { rootContext } from '@/context';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
 import type { SearchParamInputConfig, SearchParamsProps } from './Types';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  selector: {
-    width: '100%',
-    marginBottom: theme.spacing(2),
-  },
-  input: {
-    marginBottom: theme.spacing(2),
-  },
-  inlineInput: {
-    width: 160,
-    '&:nth-child(odd)': {
-      marginRight: theme.spacing(1),
-    },
-  },
-  inlineInputWrapper: {},
-}));
 
 const SearchParams: FC<SearchParamsProps> = ({
   indexType = '',
@@ -42,7 +24,6 @@ const SearchParams: FC<SearchParamsProps> = ({
   wrapperClass = '',
 }) => {
   const { t: warningTrans } = useTranslation('warning');
-  const classes = getStyles();
 
   const { openSnackBar } = useContext(rootContext);
 
@@ -107,7 +88,7 @@ const SearchParams: FC<SearchParamsProps> = ({
         onChange: value => {
           handleChange(value);
         },
-        className: classes.inlineInput,
+        className: 'inline-input',
         variant: 'filled',
         type: type,
         value,
@@ -156,7 +137,7 @@ const SearchParams: FC<SearchParamsProps> = ({
       }
       return config;
     },
-    [classes.inlineInput, warningTrans]
+    [warningTrans]
   );
 
   const getSearchInputConfig = useCallback(
@@ -179,7 +160,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           handleChange: value => {
             handleInputChange('filter', value);
           },
-          className: classes.inlineInput,
+          className: 'inline-input',
         },
         round_decimal: {
           label: 'round',
@@ -193,7 +174,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           handleChange: value => {
             handleInputChange('round_decimal', value);
           },
-          className: classes.inlineInput,
+          className: 'inline-input',
         },
         nprobe: {
           label: 'nprobe',
@@ -206,7 +187,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           handleChange: value => {
             handleInputChange('nprobe', value);
           },
-          className: classes.inlineInput,
+          className: 'inline-input',
         },
         radius: {
           label: 'radius',
@@ -218,7 +199,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           handleChange: value => {
             handleInputChange('radius', value);
           },
-          className: classes.inlineInput,
+          className: 'inline-input',
         },
         range_filter: {
           label: 'range filter',
@@ -230,7 +211,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           handleChange: value => {
             handleInputChange('range_filter', value);
           },
-          className: classes.inlineInput,
+          className: 'inline-input',
         },
         ef: {
           label: 'ef',
@@ -367,14 +348,7 @@ const SearchParams: FC<SearchParamsProps> = ({
       const param = configParamMap[paramKey];
       return getInputConfig(param);
     },
-    [
-      indexParams,
-      searchParamsForm,
-      classes.inlineInput,
-      topK,
-      getInputConfig,
-      handleInputChange,
-    ]
+    [indexParams, searchParamsForm, topK, getInputConfig, handleInputChange]
   );
 
   useEffect(() => {
@@ -401,8 +375,8 @@ const SearchParams: FC<SearchParamsProps> = ({
   }, [disabled, setParamsDisabled]);
 
   return (
-    <div className={wrapperClass}>
-      <div className={classes.inlineInputWrapper}>
+    <Box className={wrapperClass}>
+      <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
         {/* dynamic params, now every type only has one param except metric type */}
         {searchParams.map(param => (
           <CustomInput
@@ -413,8 +387,8 @@ const SearchParams: FC<SearchParamsProps> = ({
             validInfo={validation}
           />
         ))}
-      </div>
-    </div>
+      </Box>
+    </Box>
   );
 };
 

+ 0 - 130
client/src/pages/search/Styles.ts

@@ -1,130 +0,0 @@
-import { Theme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
-
-export const getVectorSearchStyles = makeStyles((theme: Theme) => ({
-  pageContainer: {
-    display: 'flex',
-    flexDirection: 'row',
-    gap: theme.spacing(2),
-  },
-  form: {
-    display: 'flex',
-    flexDirection: 'column',
-    gap: theme.spacing(0),
-    width: 360,
-    flexShrink: 0,
-    backgroundColor: theme.palette.background.paper,
-    border: `1px solid ${theme.palette.divider}`,
-
-    '& textarea': {
-      border: `1px solid ${theme.palette.divider}`,
-      marginTop: theme.spacing(0),
-      marginBottom: theme.spacing(1),
-      overflow: 'scroll',
-      height: '65px',
-      maxWidth: '100%',
-      width: '100%',
-      display: 'block',
-      boxSizing: 'border-box',
-    },
-    '& .text': {
-      marginBottom: theme.spacing(1),
-      fontWeight: '600',
-    },
-    overflow: 'hidden',
-  },
-  s1: {
-    '& .wrapper': {
-      display: 'flex',
-      flexDirection: 'row',
-      gap: theme.spacing(2),
-    },
-
-    '& .MuiSelect-root': {
-      width: '116px',
-    },
-  },
-  s2: {
-    position: 'relative',
-    textAlign: 'right',
-  },
-  s3: {},
-  selector: {
-    width: '50%',
-    marginBottom: theme.spacing(0),
-  },
-  exampleBtn: {
-    marginRight: theme.spacing(1),
-  },
-  paramsWrapper: {
-    display: 'flex',
-    flexDirection: 'column',
-  },
-
-  resultsWrapper: {
-    border: `1px solid ${theme.palette.divider}`,
-    background: '#fff',
-    display: 'flex',
-    flexDirection: 'column',
-    flexGrow: 0,
-    width: `calc(100% - 396px)`,
-    padding: theme.spacing(1),
-  },
-  toolbar: {
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-    background: '#fff',
-    paddingBottom: theme.spacing(1),
-    '& .icon': {
-      fontSize: '16px',
-    },
-
-    '& .left': {
-      display: 'flex',
-      alignItems: 'center',
-
-      '& .text': { fontSize: 12, minWidth: '92px' },
-      '& .MuiButton-root': {
-        marginRight: theme.spacing(1),
-      },
-    },
-    '& .right': {},
-  },
-  menuLabel: {
-    minWidth: '108px',
-
-    padding: theme.spacing(0, 1),
-    margin: theme.spacing(0, 1),
-
-    backgroundColor: theme.palette.background.paper,
-    color: theme.palette.text.secondary,
-  },
-  menuItem: {
-    fontWeight: 500,
-    fontSize: '12px',
-    lineHeight: '16px',
-    color: theme.palette.text.secondary,
-  },
-  error: {
-    display: 'block',
-    marginTop: theme.spacing(-1),
-    padding: '8px 0',
-    color: theme.palette.error.main,
-  },
-
-  vectorTableCell: {
-    display: 'flex',
-    whiteSpace: 'nowrap',
-  },
-
-  filterExpressionInput: {
-    width: '33vw',
-    '& .MuiInput-underline:before': {
-      borderWidth: 1,
-    },
-    '& .MuiInput-underline:after': {
-      borderWidth: 1,
-    },
-  },
-}));

+ 0 - 612
client/src/pages/search/VectorSearch.tsx

@@ -1,612 +0,0 @@
-import { useCallback, useEffect, useMemo, useState, useContext } from 'react';
-import { Typography, CardContent, TextField } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { useLocation } from 'react-router-dom';
-import { ALL_ROUTER_TYPES } from '@/router/consts';
-import { useNavigationHook, useSearchResult, usePaginationHook } from '@/hooks';
-import { dataContext } from '@/context';
-import { saveCsvAs } from '@/utils';
-import CustomSelector from '@/components/customSelector/CustomSelector';
-import { ColDefinitionsType } from '@/components/grid/Types';
-import AttuGrid from '@/components/grid/Grid';
-import EmptyCard from '@/components/cards/EmptyCard';
-import icons from '@/components/icons/Icons';
-import CustomButton from '@/components/customButton/CustomButton';
-import SimpleMenu from '@/components/menu/SimpleMenu';
-import Filter from '@/components/advancedSearch';
-import { DataService } from '@/http';
-import {
-  parseValue,
-  parseLocationSearch,
-  getVectorFieldOptions,
-  cloneObj,
-  generateVector,
-  formatNumber,
-} from '@/utils';
-import {
-  LOADING_STATE,
-  DYNAMIC_FIELD,
-  DataTypeEnum,
-  DataTypeStringEnum,
-} from '@/consts';
-import { getLabelDisplayedRows } from './Utils';
-import SearchParams from './SearchParams';
-import { getVectorSearchStyles } from './Styles';
-import { TOP_K_OPTIONS } from './Constants';
-import { FieldObject, CollectionFullObject } from '@server/types';
-import type { Option } from '@/components/customSelector/Types';
-import type { FieldOption, SearchResultView, VectorSearchParam } from './Types';
-
-const VectorSearch = () => {
-  useNavigationHook(ALL_ROUTER_TYPES.SEARCH);
-  const location = useLocation();
-  const { database, collections } = useContext(dataContext);
-
-  // i18n
-  const { t: searchTrans } = useTranslation('search');
-  const { t: btnTrans } = useTranslation('btn');
-
-  const classes = getVectorSearchStyles();
-
-  // data stored inside the component
-  const [tableLoading, setTableLoading] = useState<boolean>(false);
-  const [selectedCollection, setSelectedCollection] = useState<string>('');
-  const [fieldOptions, setFieldOptions] = useState<FieldOption[]>([]);
-  // fields for advanced filter
-  const [filterFields, setFilterFields] = useState<FieldObject[]>([]);
-  const [selectedField, setSelectedField] = useState<string>('');
-
-  // search params form
-  const [searchParam, setSearchParam] = useState<
-    { [key in string]: number | string }
-  >({});
-  // search params disable state
-  const [paramDisabled, setParamDisabled] = useState<boolean>(true);
-  // use null as init value before search, empty array means no results
-  const [searchResult, setSearchResult] = useState<SearchResultView[] | null>(
-    null
-  );
-  // default topK is 100
-  const [topK, setTopK] = useState<number>(100);
-  const [expression, setExpression] = useState<string>('');
-  const [vectors, setVectors] = useState<string>('');
-
-  // latency
-  const [latency, setLatency] = useState<number>(0);
-
-  const searchResultMemo = useSearchResult(searchResult as any);
-
-  const {
-    pageSize,
-    handlePageSize,
-    currentPage,
-    handleCurrentPage,
-    total,
-    data: result,
-    order,
-    orderBy,
-    handleGridSort,
-  } = usePaginationHook(searchResultMemo || []);
-
-  const outputFields: string[] = useMemo(() => {
-    const s = collections.find(
-      c => c.collection_name === selectedCollection
-    ) as CollectionFullObject;
-
-    if (!s) {
-      return [];
-    }
-
-    const fields = (s.schema && s.schema.fields) || [];
-
-    // vector field can't be output fields
-    const invalidTypes = [
-      'BinaryVector',
-      'FloatVector',
-      'Float16Vector',
-      'BFloat16Vector',
-      'SparseFloatVector',
-    ];
-    const nonVectorFields = fields.filter(
-      field => !invalidTypes.includes(field.data_type)
-    );
-
-    const _outputFields = nonVectorFields.map(f => f.name);
-    if (s.schema?.enable_dynamic_field) {
-      _outputFields.push(DYNAMIC_FIELD);
-    }
-    return _outputFields;
-  }, [selectedCollection, collections]);
-
-  const primaryKeyField = useMemo(() => {
-    const selectedCollectionInfo = collections.find(
-      c => c.collection_name === selectedCollection
-    );
-    const fields = selectedCollectionInfo?.schema?.fields || [];
-    return fields.find(f => f.is_primary_key)?.name;
-  }, [selectedCollection, collections]);
-
-  const orderArray = [primaryKeyField, 'id', 'score', ...outputFields];
-
-  const colDefinitions: ColDefinitionsType[] = useMemo(() => {
-    /**
-     * id represents primary key, score represents distance
-     * since we transfer score to distance in the view, and original field which is primary key has already in the table
-     * we filter 'id' and 'score' to avoid redundant data
-     */
-    return searchResult && searchResult.length > 0
-      ? Object.keys(searchResult[0])
-          .sort((a, b) => {
-            const indexA = orderArray.indexOf(a);
-            const indexB = orderArray.indexOf(b);
-            return indexA - indexB;
-          })
-          .filter(item => {
-            // if primary key field name is id, don't filter it
-            const invalidItems = primaryKeyField === 'id' ? [] : ['id'];
-            return !invalidItems.includes(item);
-          })
-          .map(key => ({
-            id: key,
-            align: 'left',
-            disablePadding: false,
-            label: key === DYNAMIC_FIELD ? searchTrans('dynamicFields') : key,
-            needCopy: key !== 'score',
-          }))
-      : [];
-  }, [searchResult, primaryKeyField, orderArray]);
-
-  const [selectedMetricType, setSelectedMetricType] = useState<string>('');
-  const [selectedConsistencyLevel, setSelectedConsistencyLevel] =
-    useState<string>('');
-
-  const { indexType, indexParams, fieldType, selectedFieldDimension } =
-    useMemo(() => {
-      if (selectedField !== '') {
-        // field options must contain selected field, so selectedFieldInfo will never undefined
-        const field = fieldOptions.find(f => f.value === selectedField)?.field;
-        const metric = field?.index.metricType || '';
-        setSelectedMetricType(metric);
-
-        return {
-          metricType: metric,
-          indexType: field?.index.indexType,
-          indexParams: field?.index.indexParameterPairs || [],
-          fieldType: field?.dataType,
-          selectedFieldDimension: field?.dimension || 0,
-        };
-      }
-
-      return {
-        indexType: '',
-        indexParams: [],
-        fieldType: DataTypeEnum.FloatVector,
-        selectedFieldDimension: 0,
-      };
-    }, [selectedField, fieldOptions]);
-
-  /**
-   * vector value validation
-   * @return whether is valid
-   */
-  const vectorValueValid = useMemo(() => {
-    // if user hasn't input value or not select field, don't trigger validation check
-    if (
-      vectors === '' ||
-      selectedFieldDimension === 0 ||
-      fieldType === DataTypeEnum.SparseFloatVector
-    ) {
-      return true;
-    }
-    const dim =
-      fieldType === DataTypeEnum.BinaryVector
-        ? selectedFieldDimension / 8
-        : selectedFieldDimension;
-
-    const value = parseValue(vectors);
-    const isArray = Array.isArray(value);
-    return isArray && value.length === dim;
-  }, [vectors, selectedFieldDimension, fieldType]);
-
-  const searchDisabled = useMemo(() => {
-    /**
-     * before search, user must:
-     * 1. enter vector value, it should be an array and length should be equal to selected field dimension
-     * 2. choose collection and field
-     * 3. set extra search params
-     */
-    const isInvalid =
-      vectors === '' ||
-      selectedCollection === '' ||
-      selectedField === '' ||
-      paramDisabled ||
-      !vectorValueValid;
-    return isInvalid;
-  }, [
-    paramDisabled,
-    selectedField,
-    selectedCollection,
-    vectors,
-    vectorValueValid,
-  ]);
-
-  // fetch data
-  const loadedCollections = collections.filter(
-    c => c.status === LOADING_STATE.LOADED
-  ) as CollectionFullObject[];
-  // sort by rowCounts
-  loadedCollections.sort((a, b) => b.rowCount - a.rowCount);
-
-  const collectionOptions: Option[] = useMemo(
-    () =>
-      loadedCollections.map(c => ({
-        label: `${c.collection_name}(${formatNumber(c.rowCount)})`,
-        value: c.collection_name,
-      })),
-    [loadedCollections]
-  );
-
-  const fetchFieldsWithIndex = useCallback(
-    async (collectionName: string, collections: CollectionFullObject[]) => {
-      const col = collections.find(c => c.collection_name === collectionName);
-
-      // only vector type fields can be select
-      const fieldOptions =
-        col && col.schema
-          ? getVectorFieldOptions(col?.schema.vectorFields!)
-          : [];
-      setFieldOptions(fieldOptions);
-      if (fieldOptions.length > 0) {
-        // set first option value as default field value
-        const [{ value: defaultFieldValue }] = fieldOptions;
-        setSelectedField(defaultFieldValue as string);
-      }
-
-      if (col?.schema) {
-        setFilterFields(col?.schema.scalarFields!);
-      }
-    },
-    [collections]
-  );
-
-  // clear selection if database is changed
-  useEffect(() => {
-    setSelectedCollection('');
-    setVectors('');
-    setSearchResult(null);
-  }, [database]);
-
-  // get field options with index when selected collection changed
-  useEffect(() => {
-    if (selectedCollection !== '') {
-      fetchFieldsWithIndex(
-        selectedCollection,
-        collections as CollectionFullObject[]
-      );
-    }
-    const level = collections.find(c => c.collection_name == selectedCollection)
-      ?.consistency_level!;
-    setSelectedConsistencyLevel(level);
-  }, [selectedCollection, collections, fetchFieldsWithIndex]);
-
-  // set default collection value if is from overview page
-  useEffect(() => {
-    if (location.search && collections.length > 0) {
-      const { collectionName } = parseLocationSearch(location.search);
-      // collection name validation
-      const isNameValid = collections
-        .map(c => c.collection_name)
-        .includes(collectionName);
-      isNameValid && setSelectedCollection(collectionName);
-    }
-  }, [location, collections]);
-
-  // icons
-  const VectorSearchIcon = icons.vectorSearch;
-  const ResetIcon = icons.refresh;
-  const ArrowIcon = icons.dropdown;
-  const ExportIcon = icons.download;
-
-  // methods
-  const handlePageChange = (e: any, page: number) => {
-    handleCurrentPage(page);
-  };
-  const handleReset = () => {
-    /**
-     * reset search includes:
-     * 1. reset vectors
-     * 2. reset selected collection and field
-     * 3. reset search params
-     * 4. reset advanced filter expression
-     * 5. clear search result
-     */
-    setVectors('');
-    setSelectedField('');
-    setSelectedCollection('');
-    setSearchResult(null);
-    setFilterFields([]);
-    setExpression('');
-  };
-
-  const handleSearch = async (topK: number, expr = expression) => {
-    const clonedSearchParams = cloneObj(searchParam);
-    delete clonedSearchParams.round_decimal;
-    const searchParamPairs = {
-      params: JSON.stringify(clonedSearchParams),
-      anns_field: selectedField,
-      topk: topK,
-      metric_type: selectedMetricType,
-      round_decimal: searchParam.round_decimal,
-    };
-
-    const params: VectorSearchParam = {
-      output_fields: outputFields,
-      expr,
-      search_params: searchParamPairs,
-      vectors: [parseValue(vectors)],
-      vector_type: fieldType as DataTypeEnum,
-      consistency_level:
-        selectedConsistencyLevel ||
-        collections.find(c => c.collection_name == selectedCollection)
-          ?.consistency_level!,
-    };
-
-    setTableLoading(true);
-    try {
-      const res = await DataService.vectorSearchData(
-        selectedCollection,
-        params
-      );
-      setTableLoading(false);
-      setSearchResult(res.results);
-      setLatency(res.latency);
-    } catch (err) {
-      setTableLoading(false);
-    }
-  };
-  const handleAdvancedFilterChange = (expression: string) => {
-    setExpression(expression);
-    if (!searchDisabled) {
-      handleSearch(topK, expression);
-    }
-  };
-
-  const handleVectorChange = (value: string) => {
-    setVectors(value);
-  };
-
-  const fillWithExampleVector = (
-    selectedFieldDimension: number,
-    field: FieldObject
-  ) => {
-    const isSparse = field.data_type === DataTypeStringEnum.SparseFloatVector;
-    if (isSparse) {
-      setVectors(
-        JSON.stringify({ [Math.floor(Math.random() * 10)]: Math.random() })
-      );
-    } else {
-      const v = generateVector(selectedFieldDimension);
-      setVectors(`[${v}]`);
-    }
-  };
-
-  return (
-    <section className={`page-wrapper ${classes.pageContainer}`}>
-      <section className={classes.form}>
-        <CardContent className={classes.s1}>
-          <div className="wrapper">
-            <CustomSelector
-              options={collectionOptions}
-              wrapperClass={classes.selector}
-              variant="filled"
-              label={searchTrans('collection')}
-              disabled={collectionOptions.length === 0}
-              value={selectedCollection}
-              onChange={(e: { target: { value: unknown } }) => {
-                const collection = e.target.value as string;
-
-                setSelectedCollection(collection);
-                // every time selected collection changed, reset field
-                setSelectedField('');
-                setSearchResult([]);
-                // update location search
-                const search = `/#/search?collectionName=${collection}`;
-                window.history.pushState({}, '', search);
-              }}
-            />
-            <CustomSelector
-              options={fieldOptions}
-              // readOnly can't avoid all events, so we use disabled instead
-              disabled={selectedCollection === ''}
-              wrapperClass={classes.selector}
-              variant="filled"
-              label={searchTrans('field')}
-              value={selectedField}
-              onChange={(e: { target: { value: unknown } }) => {
-                const field = e.target.value as string;
-                setSelectedField(field);
-              }}
-            />
-          </div>
-        </CardContent>
-
-        <CardContent className={classes.s2}>
-          <textarea
-            className="textarea"
-            placeholder={searchTrans('vectorPlaceholder')}
-            value={vectors}
-            onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
-              handleVectorChange(e.target.value as string);
-            }}
-          />
-          {/* validation */}
-          {!vectorValueValid && (
-            <Typography variant="caption" className={classes.error}>
-              {searchTrans('vectorValueWarning', {
-                dimension:
-                  fieldType === DataTypeEnum.BinaryVector
-                    ? selectedFieldDimension / 8
-                    : selectedFieldDimension,
-              })}
-            </Typography>
-          )}
-
-          {selectedFieldDimension !== 0 ? (
-            <CustomButton
-              className={classes.exampleBtn}
-              onClick={() => {
-                const dim =
-                  fieldType === DataTypeEnum.BinaryVector
-                    ? selectedFieldDimension / 8
-                    : selectedFieldDimension;
-                fillWithExampleVector(
-                  dim,
-                  fieldOptions.find(f => f.value === selectedField)!.field
-                );
-              }}
-            >
-              {btnTrans('example')}
-            </CustomButton>
-          ) : null}
-          <CustomButton
-            variant="contained"
-            disabled={searchDisabled}
-            onClick={() => handleSearch(topK)}
-          >
-            {btnTrans('search')}
-          </CustomButton>
-        </CardContent>
-
-        <CardContent className={classes.s3}>
-          <Typography className="text">
-            {searchTrans('thirdTip', {
-              metricType: `${
-                selectedMetricType ? `(${selectedMetricType})` : ''
-              }`,
-            })}
-          </Typography>
-          <SearchParams
-            wrapperClass={classes.paramsWrapper}
-            consistency_level={selectedConsistencyLevel}
-            handleConsistencyChange={(level: string) => {
-              setSelectedConsistencyLevel(level);
-            }}
-            indexType={indexType!}
-            indexParams={indexParams!}
-            searchParamsForm={searchParam}
-            handleFormChange={setSearchParam}
-            topK={topK}
-            setParamsDisabled={setParamDisabled}
-          />
-        </CardContent>
-      </section>
-
-      <section className={classes.resultsWrapper}>
-        <section className={classes.toolbar}>
-          <div className="left">
-            <Typography variant="h5" className="text">
-              {`${searchTrans('result')}: `}
-            </Typography>
-            {/* topK selector */}
-            <SimpleMenu
-              label={searchTrans('topK', { number: topK })}
-              menuItems={TOP_K_OPTIONS.map(item => ({
-                label: item.toString(),
-                callback: () => {
-                  setTopK(item);
-                  if (!searchDisabled) {
-                    handleSearch(item);
-                  }
-                },
-                wrapperClass: classes.menuItem,
-              }))}
-              buttonProps={{
-                className: classes.menuLabel,
-                endIcon: <ArrowIcon />,
-              }}
-              menuItemWidth="108px"
-            />
-
-            <TextField
-              className={classes.filterExpressionInput}
-              value={expression}
-              onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
-                setExpression(e.target.value as string);
-              }}
-              disabled={selectedField === '' || selectedCollection === ''}
-              InputLabelProps={{ shrink: true }}
-              placeholder={searchTrans('filterExpr')}
-              onKeyDown={e => {
-                if (e.key === 'Enter') {
-                  handleAdvancedFilterChange(expression);
-                  e.preventDefault();
-                }
-              }}
-            />
-
-            <Filter
-              title={searchTrans('exprHelper')}
-              fields={filterFields}
-              filterDisabled={selectedField === '' || selectedCollection === ''}
-              onSubmit={handleAdvancedFilterChange}
-              showTooltip={false}
-            />
-            <CustomButton
-              className="btn"
-              disabled={result.length === 0}
-              onClick={() => {
-                saveCsvAs(searchResult, `search_result_${selectedCollection}`);
-              }}
-              startIcon={<ExportIcon classes={{ root: 'icon' }}></ExportIcon>}
-            >
-              {btnTrans('export')}
-            </CustomButton>
-          </div>
-          <div className="right">
-            <CustomButton
-              className="btn"
-              onClick={handleReset}
-              startIcon={<ResetIcon classes={{ root: 'icon' }} />}
-            >
-              {btnTrans('reset')}
-            </CustomButton>
-          </div>
-        </section>
-
-        {/* search result table section */}
-        {(searchResult && searchResult.length > 0) || tableLoading ? (
-          <AttuGrid
-            toolbarConfigs={[]}
-            colDefinitions={colDefinitions}
-            rows={result}
-            rowCount={total}
-            primaryKey="rank"
-            page={currentPage}
-            rowHeight={39}
-            onPageChange={handlePageChange}
-            rowsPerPage={pageSize}
-            setRowsPerPage={handlePageSize}
-            openCheckBox={false}
-            isLoading={tableLoading}
-            orderBy={orderBy}
-            order={order}
-            labelDisplayedRows={getLabelDisplayedRows(`(${latency} ms)`)}
-            handleSort={handleGridSort}
-          />
-        ) : (
-          <EmptyCard
-            wrapperClass={`page-empty-card`}
-            icon={<VectorSearchIcon />}
-            text={
-              searchResult !== null
-                ? searchTrans('empty')
-                : searchTrans('startTip')
-            }
-          />
-        )}
-      </section>
-    </section>
-  );
-};
-
-export default VectorSearch;

+ 85 - 62
client/src/pages/system/BaseCard.tsx

@@ -1,76 +1,99 @@
 import { FC } from 'react';
-import { SvgIcon, Theme } from '@mui/material';
+import { SvgIcon, Box, useTheme } from '@mui/material';
 import { BaseCardProps } from './Types';
 import pic from '../../assets/imgs/pic.svg?react';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    backgroundColor: theme.palette.background.paper,
-    boxSizing: 'border-box',
-    height: '150px',
-    padding: theme.spacing(2),
-  },
-  title: {
-    color: theme.palette.text.secondary,
-    fontSize: '14px',
-    marginBottom: '5px',
-    textTransform: 'capitalize',
-  },
-  content: {
-    color: theme.palette.text.primary,
-    fontSize: '20px',
-    fontWeight: 600,
-    lineHeight: '36px',
-  },
-  desc: {
-    color: theme.palette.text.secondary,
-    fontSize: '14px',
-    lineHeight: '36px',
-    marginLeft: theme.spacing(1),
-  },
-  emptyRoot: {
-    alignItems: 'center',
-    display: 'flex',
-    flexDirection: 'column',
-    justifyContent: 'flex-start',
-
-    '& > svg': {
-      marginTop: '10px',
-      width: '100%',
-    },
-  },
-  emptyTitle: {
-    fontSize: '14px',
-    marginTop: '14px',
-    textTransform: 'capitalize',
-  },
-  emptyDesc: {
-    fontSize: '10px',
-    color: theme.palette.text.secondary,
-    marginTop: theme.spacing(1),
-  },
-}));
 
 const BaseCard: FC<BaseCardProps> = props => {
-  const classes = getStyles();
+  const theme = useTheme();
   const { children, title, content, desc } = props;
   return (
-    <div className={classes.root}>
-      <div className={classes.title}>{title}</div>
-      {content && <span className={classes.content}>{content}</span>}
-      {desc && <span className={classes.desc}>{desc}</span>}
+    <Box
+      sx={{
+        backgroundColor: theme.palette.background.paper,
+        boxSizing: 'border-box',
+        height: '150px',
+        padding: theme.spacing(2),
+        borderRadius: 1,
+        display: 'flex',
+        flexDirection: 'column',
+        justifyContent: 'flex-start',
+      }}
+    >
+      <Box
+        sx={{
+          color: theme.palette.text.secondary,
+          fontSize: '14px',
+          marginBottom: '5px',
+          textTransform: 'capitalize',
+        }}
+      >
+        {title}
+      </Box>
+      {content && (
+        <Box
+          component="span"
+          sx={{
+            color: theme.palette.text.primary,
+            fontSize: '20px',
+            fontWeight: 600,
+            lineHeight: '36px',
+          }}
+        >
+          {content}
+        </Box>
+      )}
+      {desc && (
+        <Box
+          component="span"
+          sx={{
+            color: theme.palette.text.secondary,
+            fontSize: '14px',
+            lineHeight: '36px',
+            marginLeft: theme.spacing(1),
+          }}
+        >
+          {desc}
+        </Box>
+      )}
       {!content && !desc && (
-        <div className={classes.emptyRoot}>
-          <SvgIcon viewBox="0 0 101 26" component={pic} {...props} />
-          <span className={classes.emptyTitle}>no data available</span>
-          <span className={classes.emptyDesc}>
+        <Box
+          sx={{
+            alignItems: 'center',
+            display: 'flex',
+            flexDirection: 'column',
+            justifyContent: 'flex-start',
+            width: '100%',
+            mt: 1,
+          }}
+        >
+          <SvgIcon
+            viewBox="0 0 101 26"
+            component={pic}
+            sx={{ mt: '10px', width: '100%' }}
+            {...props}
+          />
+          <Box
+            sx={{
+              fontSize: '14px',
+              marginTop: '14px',
+              textTransform: 'capitalize',
+            }}
+          >
+            no data available
+          </Box>
+          <Box
+            sx={{
+              fontSize: '10px',
+              color: theme.palette.text.secondary,
+              marginTop: theme.spacing(1),
+            }}
+          >
             There is no data to show you right now.
-          </span>
-        </div>
+          </Box>
+        </Box>
       )}
       {children}
-    </div>
+    </Box>
   );
 };
 

+ 112 - 111
client/src/pages/system/DataCard.tsx

@@ -1,137 +1,103 @@
 import { FC } from 'react';
 import { useTranslation } from 'react-i18next';
-import { useTheme, Theme } from '@mui/material';
+import { useTheme, Box } from '@mui/material';
 import Progress from './Progress';
 import { formatByteSize, formatSystemTime, getByteString } from '@/utils';
 import { DataProgressProps, DataSectionProps, DataCardProps } from './Types';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    backgroundColor: theme.palette.background.default,
-    padding: theme.spacing(1.5, 2),
-    boxSizing: 'border-box',
-    flexGrow: 1,
-    height: '100%',
-  },
-
-  title: {
-    display: 'flex',
-    justifyContent: 'space-between',
-  },
-
-  content: {
-    color: theme.palette.text.primary,
-    fontSize: '20px',
-    fontWeight: 600,
-    lineHeight: '36px',
-  },
-
-  desc: {
-    color: theme.palette.text.secondary,
-    fontSize: '14px',
-    lineHeight: '36px',
-    marginLeft: theme.spacing(1),
-  },
-
-  rootName: {
-    color: theme.palette.text.secondary,
-    fontSize: 20,
-  },
-
-  childName: {
-    color: theme.palette.primary.main,
-    fontSize: 20,
-  },
-
-  ip: {
-    color: theme.palette.text.primary,
-    fontSize: 11,
-    lineHeight: 2,
-  },
-
-  sectionRoot: {
-    borderSpacing: '0 1px',
-    display: 'table',
-    marginTop: theme.spacing(0.5),
-    width: '100%',
-  },
-
-  sectionRow: {
-    display: 'table-row',
-  },
-
-  sectionHeaderCell: {
-    display: 'table-cell',
-    color: theme.palette.text.secondary,
-    fontSize: '12px',
-    lineHeight: '24px',
-    padding: theme.spacing(0.5, 1),
-    textTransform: 'uppercase',
-    width: '50%',
-  },
-
-  sectionCell: {
-    backgroundColor: theme.palette.background.paper,
-    color: theme.palette.text.primary,
-    display: 'table-cell',
-    fontSize: 13,
-    padding: theme.spacing(1),
-    textTransform: 'capitalize',
-    verticalAlign: 'middle',
-    width: '50%',
-  },
-  progressTitle: {
-    fontSize: '14px',
-    color: theme.palette.text.primary,
-    display: 'flex',
-    justifyContent: 'space-between',
-  },
-}));
 
 const DataSection: FC<DataSectionProps> = props => {
-  const classes = getStyles();
+  const theme = useTheme();
   const { titles, contents } = props;
 
   return (
-    <div className={classes.sectionRoot}>
-      <div className={classes.sectionRow}>
+    <Box
+      sx={{
+        borderSpacing: '0 1px',
+        display: 'table',
+        marginTop: theme.spacing(0.5),
+        width: '100%',
+      }}
+    >
+      <Box sx={{ display: 'table-row' }}>
         {titles.map(titleEntry => (
-          <div key={titleEntry} className={classes.sectionHeaderCell}>
+          <Box
+            key={titleEntry}
+            sx={{
+              display: 'table-cell',
+              color: theme.palette.text.secondary,
+              fontSize: '12px',
+              lineHeight: '24px',
+              padding: theme.spacing(0.5, 1),
+              textTransform: 'uppercase',
+              width: '50%',
+            }}
+          >
             {titleEntry}
-          </div>
+          </Box>
         ))}
-      </div>
+      </Box>
       {contents.map(contentEntry => {
         return (
-          <div key={contentEntry.label} className={classes.sectionRow}>
-            <div className={classes.sectionCell}>{contentEntry.label}</div>
-            <div className={classes.sectionCell}>{contentEntry.value}</div>
-          </div>
+          <Box key={contentEntry.label} sx={{ display: 'table-row' }}>
+            <Box
+              sx={{
+                backgroundColor: theme.palette.background.paper,
+                color: theme.palette.text.primary,
+                display: 'table-cell',
+                fontSize: 13,
+                padding: theme.spacing(1),
+                textTransform: 'capitalize',
+                verticalAlign: 'middle',
+                width: '50%',
+              }}
+            >
+              {contentEntry.label}
+            </Box>
+            <Box
+              sx={{
+                backgroundColor: theme.palette.background.paper,
+                color: theme.palette.text.primary,
+                display: 'table-cell',
+                fontSize: 13,
+                padding: theme.spacing(1),
+                textTransform: 'capitalize',
+                verticalAlign: 'middle',
+                width: '50%',
+              }}
+            >
+              {contentEntry.value}
+            </Box>
+          </Box>
         );
       })}
-    </div>
+    </Box>
   );
 };
 
 const DataProgress: FC<DataProgressProps> = ({ percent = 0, desc = '' }) => {
-  const classes = getStyles();
   const theme = useTheme();
   return (
-    <div>
-      <div className={classes.progressTitle}>
+    <Box>
+      <Box
+        sx={{
+          fontSize: '14px',
+          color: theme.palette.text.primary,
+          display: 'flex',
+          justifyContent: 'space-between',
+        }}
+      >
         <span>{`${Number(percent * 100).toFixed(2)}%`}</span>
         <span>{desc}</span>
-      </div>
+      </Box>
       <Progress percent={percent} color={theme.palette.primary.main} />
-    </div>
+    </Box>
   );
 };
 
 const DataCard: FC<
   DataCardProps & React.HTMLAttributes<HTMLDivElement>
 > = props => {
-  const classes = getStyles();
+  const theme = useTheme();
   const { t } = useTranslation('systemView');
   const { t: commonTrans } = useTranslation();
   const capacityTrans: { [key in string]: string } = commonTrans(
@@ -206,14 +172,49 @@ const DataCard: FC<
   });
 
   return (
-    <div className={classes.root}>
-      <div className={classes.title}>
-        <div>
-          <span className={classes.rootName}>Milvus / </span>
-          <span className={classes.childName}>{node?.infos?.name}</span>
-        </div>
-        <div className={classes.ip}>{`${t('thIP')}:${infos?.ip || ''}`}</div>
-      </div>
+    <Box
+      sx={{
+        backgroundColor: theme.palette.background.default,
+        padding: theme.spacing(1.5, 2),
+        boxSizing: 'border-box',
+        flexGrow: 1,
+        height: '100%',
+      }}
+    >
+      <Box
+        sx={{
+          display: 'flex',
+          justifyContent: 'space-between',
+        }}
+      >
+        <Box>
+          <Box
+            component="span"
+            sx={{
+              color: theme.palette.text.secondary,
+              fontSize: 20,
+            }}
+          >
+            Milvus /{' '}
+          </Box>
+          <Box
+            component="span"
+            sx={{
+              color: theme.palette.primary.main,
+              fontSize: 20,
+            }}
+          >
+            {node?.infos?.name}
+          </Box>
+        </Box>
+        <Box
+          sx={{
+            color: theme.palette.text.primary,
+            fontSize: 11,
+            lineHeight: 2,
+          }}
+        >{`${t('thIP')}:${infos?.ip || ''}`}</Box>
+      </Box>
       <DataSection titles={systemTitle} contents={systemContent} />
       {extend && (
         <DataSection titles={hardwareTitle} contents={hardwareContent} />
@@ -222,7 +223,7 @@ const DataCard: FC<
       {systemConfig.length ? (
         <DataSection titles={configTitle} contents={systemConfig} />
       ) : null}
-    </div>
+    </Box>
   );
 };
 

+ 0 - 171
client/src/pages/system/LineChartCard.tsx

@@ -1,171 +0,0 @@
-import { FC, useState, useEffect, useRef } from 'react';
-import { useTheme, Theme } from '@mui/material';
-import BaseCard from './BaseCard';
-import { makeStyles } from '@mui/styles';
-import type { LineChartCardProps, LinceChartNode } from './Types';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    transform: 'scaleY(-1)',
-    maxWidth: '90%',
-  },
-  ycoord: {
-    cursor: 'pointer',
-    '&:hover, &:focus': {
-      '& line': {
-        transition: 'all .25s',
-        opacity: 1,
-      },
-    },
-
-    '&:hover': {
-      '& circle': {
-        fill: theme.palette.primary.main,
-      },
-    },
-
-    '&:focus': {
-      outline: 'none',
-
-      '& circle': {
-        fill: '#06F3AF',
-      },
-    },
-  },
-}));
-
-const LineChartCard: FC<LineChartCardProps> = props => {
-  const theme = useTheme();
-  const FULL_HEIGHT = 60;
-  const FULL_WIDTH = 300;
-  const ROUND = 5;
-  const STEP = 25;
-
-  const classes = getStyles();
-  const { title, value } = props;
-  const [displayNodes, setDisplayNodes] = useState<LinceChartNode[]>([]);
-  const [currentNode, setCurrentNode] = useState<LinceChartNode>({
-    percent: 0,
-    value: 0,
-    timestamp: Date.now(),
-  });
-
-  const max = useRef(1);
-  const isHover = useRef(false);
-  const nodes = useRef<LinceChartNode[]>([]);
-
-  useEffect(() => {
-    // show at most 10 nodes. so remove the earliest node when nodes exceed 10
-    if (nodes.current.length > 9) {
-      nodes.current.shift();
-    }
-
-    if (value && max.current) {
-      // calculate the y-axis max scale
-      let currentMax = max.current;
-      if (value > max.current) {
-        const pow = Math.ceil(Math.log10(value));
-        currentMax = Math.pow(10, pow);
-        max.current = currentMax;
-      }
-
-      // generate a new node and save in ref
-      if (nodes.current) {
-        const newNodes = nodes.current.slice(0);
-        const newNode = {
-          percent: (value / currentMax) * 100,
-          value,
-          timestamp: Date.now(),
-        };
-        newNodes.push(newNode);
-        nodes.current = newNodes;
-
-        // refresh nodes for display when mouse is not hovering on the chart
-        if (!isHover.current) {
-          setDisplayNodes(newNodes);
-          setCurrentNode(newNode);
-        }
-      }
-    }
-  }, [value]);
-
-  return nodes.current.length ? (
-    <BaseCard
-      title={title}
-      content={`${Math.round(currentNode.value)}ms`}
-      desc={new Date(currentNode.timestamp).toLocaleString()}
-    >
-      <svg
-        className={classes.root}
-        onMouseEnter={() => (isHover.current = true)}
-        onMouseLeave={() => (isHover.current = false)}
-        width={FULL_WIDTH}
-        height={FULL_HEIGHT}
-        viewBox={`0 5 ${FULL_WIDTH} ${FULL_HEIGHT}`}
-        fill={theme.palette.background.paper}
-        xmlns="http://www.w3.org/2000/svg"
-      >
-        {displayNodes.map((node, index) => {
-          const x1 = FULL_WIDTH - (displayNodes.length - index + 1) * STEP;
-          const y1 = node.percent * 0.5 + ROUND * 2;
-
-          let line = null;
-          if (index < displayNodes.length - 1) {
-            const x2 = FULL_WIDTH - (displayNodes.length - index) * STEP;
-            const y2 = displayNodes[index + 1]['percent'] * 0.5 + ROUND * 2;
-            line = (
-              <line
-                x1={x1}
-                y1={y1}
-                x2={x2}
-                y2={y2}
-                stroke={theme.palette.primary.main}
-              />
-            );
-          }
-          return (
-            <g key={`${node.value}${index}`}>
-              {line}
-              <g
-                className={classes.ycoord}
-                onMouseOver={() => {
-                  setCurrentNode(node);
-                }}
-              >
-                <circle
-                  cx={x1}
-                  cy={y1}
-                  r={ROUND}
-                  fill={theme.palette.background.paper}
-                  stroke={theme.palette.primary.main}
-                />
-                <rect
-                  opacity="0"
-                  x={x1 - ROUND}
-                  y={0}
-                  width={ROUND * 2}
-                  height={FULL_HEIGHT}
-                  fill="#E9E9ED"
-                />
-                <line
-                  opacity="0"
-                  x1={x1}
-                  y1={0}
-                  x2={x1}
-                  y2={FULL_WIDTH}
-                  strokeWidth="2"
-                  stroke={theme.palette.primary.main}
-                  strokeDasharray="2.5"
-                />
-              </g>
-            </g>
-          );
-        })}
-      </svg>
-    </BaseCard>
-  ) : (
-    <BaseCard title={title} />
-  );
-};
-
-export default LineChartCard;

+ 53 - 57
client/src/pages/system/NodeListView.tsx

@@ -1,6 +1,6 @@
 import { FC, useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
-import { Theme } from '@mui/material';
+import { Box, useTheme } from '@mui/material';
 import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType } from '@/components/grid/Types';
@@ -12,44 +12,6 @@ import DataCard from './DataCard';
 import { usePaginationHook } from '@/hooks';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { NodeListViewProps, Node } from './Types';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    overflow: 'hidden',
-    padding: '0 16px',
-    display: 'flex',
-    flexDirection: 'column',
-    border: `1px solid ${theme.palette.divider}`,
-  },
-  childCloseBtnContainer: {
-    border: 0,
-    backgroundColor: theme.palette.background.paper,
-    cursor: 'pointer',
-    width: '100%',
-    height: '28px',
-  },
-  childCloseBtn: {
-    border: 0,
-    backgroundColor: theme.palette.background.paper,
-    color: theme.palette.text.primary,
-    width: '100%',
-  },
-  gridContainer: {
-    height: `calc(100vh - 120px)`,
-    display: 'flex',
-    gap: 8,
-  },
-  leftContainer: {
-    height: '100%',
-    width: '70%',
-  },
-  rightContainer: {
-    width: '30%',
-    overflow: 'scroll',
-  },
-  dataCard: {},
-}));
 
 type GridNode = {
   id: string;
@@ -71,7 +33,7 @@ const NodeListView: FC<NodeListViewProps> = props => {
     'capacity'
   ) as any;
 
-  const classes = getStyles();
+  const theme = useTheme();
   const [selectedChildNode, setSelectedChildNode] = useState<GridNode[]>([]);
   const [rows, setRows] = useState<any[]>([]);
   const { selectedCord, childNodes, setCord, setShowChildView } = props;
@@ -195,15 +157,48 @@ const NodeListView: FC<NodeListViewProps> = props => {
   const infoNode = selectedChildNode[0] && selectedChildNode[0].node;
 
   return (
-    <div className={classes.root}>
-      <button
-        className={classes.childCloseBtnContainer}
+    <Box
+      sx={{
+        overflow: 'hidden',
+        padding: '0 16px',
+        display: 'flex',
+        flexDirection: 'column',
+        border: `1px solid ${theme.palette.divider}`,
+      }}
+    >
+      <Box
+        component="button"
+        sx={{
+          border: 0,
+          backgroundColor: theme.palette.background.paper,
+          cursor: 'pointer',
+          width: '100%',
+          height: '28px',
+        }}
         onClick={() => setShowChildView(false)}
       >
-        <KeyboardArrowDown className={classes.childCloseBtn} />
-      </button>
-      <div className={classes.gridContainer}>
-        <div className={classes.leftContainer}>
+        <KeyboardArrowDown
+          sx={{
+            border: 0,
+            backgroundColor: theme.palette.background.paper,
+            color: theme.palette.text.primary,
+            width: '100%',
+          }}
+        />
+      </Box>
+      <Box
+        sx={{
+          height: `calc(100vh - 120px)`,
+          display: 'flex',
+          gap: 1, // theme.spacing(1) is 8px by default, so 1 means 8px
+        }}
+      >
+        <Box
+          sx={{
+            height: '100%',
+            width: '70%',
+          }}
+        >
           <AttuGrid
             toolbarConfigs={[]}
             colDefinitions={colDefinitions}
@@ -225,8 +220,13 @@ const NodeListView: FC<NodeListViewProps> = props => {
               commonTrans(data.length > 1 ? 'grid.nodes' : 'grid.node')
             )}
           />
-        </div>
-        <div className={classes.rightContainer}>
+        </Box>
+        <Box
+          sx={{
+            width: '30%',
+            overflow: 'scroll',
+          }}
+        >
           {infoNode && (
             <>
               <MiniTopo
@@ -235,16 +235,12 @@ const NodeListView: FC<NodeListViewProps> = props => {
                 selectedChildNode={infoNode}
                 setShowChildView={setShowChildView}
               />
-              <DataCard
-                className={classes.dataCard}
-                node={infoNode}
-                extend={true}
-              />
+              <DataCard node={infoNode} extend={true} />
             </>
           )}
-        </div>
-      </div>
-    </div>
+        </Box>
+      </Box>
+    </Box>
   );
 };
 

+ 7 - 15
client/src/pages/system/Progress.tsx

@@ -1,33 +1,23 @@
 import { FC } from 'react';
 import { useTheme } from '@mui/material';
-import { makeStyles } from '@mui/styles';
 import type { ProgressProps } from './Types';
 
-const getStyles = makeStyles(() => ({
-  root: {
-    height: 'auto',
-    transform: 'scaleY(-1)',
-    width: '100%',
-
-    '& line': {
-      transformOrigin: '10px 15px',
-    },
-  },
-}));
-
 const Progress: FC<ProgressProps> = props => {
-  const classes = getStyles();
   const theme = useTheme();
   const { percent = 0, color = '#06F3AF' } = props;
 
   return (
     <svg
-      className={classes.root}
       width="300"
       height="30"
       viewBox="0 0 300 30"
       fill="none"
       xmlns="http://www.w3.org/2000/svg"
+      style={{
+        height: 'auto',
+        transform: 'scaleY(-1)',
+        width: '100%',
+      }}
     >
       <line
         x1={10}
@@ -38,6 +28,7 @@ const Progress: FC<ProgressProps> = props => {
         strokeWidth="12"
         stroke={theme.palette.text.disabled}
         strokeLinecap="round"
+        style={{ transformOrigin: '10px 15px' }}
       />
       <line
         x1={10}
@@ -49,6 +40,7 @@ const Progress: FC<ProgressProps> = props => {
         strokeWidth="12"
         stroke={color}
         strokeLinecap="round"
+        style={{ transformOrigin: '10px 15px' }}
       />
     </svg>
   );

+ 0 - 32
client/src/pages/system/ProgressCard.tsx

@@ -1,32 +0,0 @@
-import { FC } from 'react';
-import { useTranslation } from 'react-i18next';
-import BaseCard from './BaseCard';
-import Progress from './Progress';
-import { getByteString } from '@/utils';
-import type { ProgressCardProps } from './Types';
-
-const color1 = '#06F3AF';
-const color2 = '#635DCE';
-
-const ProgressCard: FC<ProgressCardProps> = props => {
-  const { title, total, usage } = props;
-  const { t } = useTranslation('systemView');
-  const { t: commonTrans } = useTranslation();
-  const capacityTrans: { [key in string]: string } = commonTrans(
-    'capacity'
-  ) as any;
-
-  const color = title === t('diskTitle') ? color1 : color2;
-  const percent = usage && total ? usage / total : 0;
-
-  return (
-    <BaseCard
-      title={title}
-      content={`${getByteString(usage, total, capacityTrans)} (${Math.floor(percent * 100)}%)`}
-    >
-      <Progress percent={percent} color={color} />
-    </BaseCard>
-  );
-};
-
-export default ProgressCard;

+ 46 - 67
client/src/pages/system/SystemView.tsx

@@ -1,69 +1,17 @@
 import { useState, useEffect, useRef } from 'react';
-import { Theme } from '@mui/material';
-import clsx from 'clsx';
+import { Box } from '@mui/material';
 import { useNavigationHook, useInterval } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/consts';
 import { MilvusService } from '@/http';
 import { parseJson } from '@/utils';
 import Topo from './Topology';
 import NodeListView from './NodeListView';
-// import LineChartCard from './LineChartCard';
-// import ProgressCard from './ProgressCard';
 import DataCard from './DataCard';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    margin: '16px',
-    position: 'relative',
-    display: 'flex',
-    height: 'calc(100vh - 80px)',
-    overflow: 'hidden',
-    border: `1px solid ${theme.palette.divider}`,
-    borderRadius: 8,
-    boxShadow: '0 0 10px 0 rgba(0,0,0,0.1)',
-  },
-  transparent: {
-    opacity: 0,
-    transition: 'opacity .5s',
-  },
-  contentContainer: {
-    display: 'flex',
-    borderRadius: 8,
-    gap: 8,
-    width: '100%',
-  },
-  left: {
-    width: '70%',
-    background: theme.palette.background.paper,
-    borderRadius: 8,
-    boxShadow: '0 0 10px 0 rgba(0,0,0,0.1)',
-  },
-  right: { width: '30%', borderRadius: 8 },
-  childView: {
-    height: '100%',
-    width: '100%',
-    transition: 'all .25s',
-    position: 'absolute',
-    // zIndex: 1000,
-    backgroundColor: theme.palette.background.paper,
-    borderRadius: 8,
-  },
-  showChildView: {
-    top: 0,
-    opacity: 1,
-  },
-  hideChildView: {
-    top: 1600,
-    opacity: 0,
-  },
-}));
 
 const SystemView: any = () => {
   useNavigationHook(ALL_ROUTER_TYPES.SYSTEM);
   // const { t } = useTranslation('systemView');
 
-  const classes = getStyles();
   const INTERVAL = 60000;
 
   const [data, setData] = useState<{
@@ -96,9 +44,34 @@ const SystemView: any = () => {
   const childView = useRef<HTMLInputElement>(null);
 
   return (
-    <div className={classes.root}>
-      <div className={classes.contentContainer}>
-        <div className={classes.left}>
+    <Box
+      sx={theme => ({
+        margin: '16px',
+        position: 'relative',
+        display: 'flex',
+        height: 'calc(100vh - 80px)',
+        overflow: 'hidden',
+        border: `1px solid ${theme.palette.divider}`,
+        borderRadius: 2,
+        boxShadow: '0 0 10px 0 rgba(0,0,0,0.1)',
+      })}
+    >
+      <Box
+        sx={{
+          display: 'flex',
+          borderRadius: 2,
+          gap: 1,
+          width: '100%',
+        }}
+      >
+        <Box
+          sx={theme => ({
+            width: '70%',
+            background: theme.palette.background.paper,
+            borderRadius: 2,
+            boxShadow: '0 0 10px 0 rgba(0,0,0,0.1)',
+          })}
+        >
           <Topo
             nodes={nodes}
             childNodes={childNodes}
@@ -106,18 +79,24 @@ const SystemView: any = () => {
             setCord={setCord}
             setShowChildView={setShowChildView}
           />
-        </div>
-        <div className={classes.right}>
+        </Box>
+        <Box sx={{ width: '30%', borderRadius: 2 }}>
           <DataCard node={selectedNode} extend={true} />
-        </div>
-      </div>
+        </Box>
+      </Box>
 
-      <div
+      <Box
         ref={childView}
-        className={clsx(
-          classes.childView,
-          showChildView ? classes.showChildView : classes.hideChildView
-        )}
+        sx={theme => ({
+          height: '100%',
+          width: '100%',
+          transition: 'all .25s',
+          position: 'absolute',
+          backgroundColor: theme.palette.background.paper,
+          borderRadius: 2,
+          top: showChildView ? 0 : 1600,
+          opacity: showChildView ? 1 : 0,
+        })}
       >
         {selectedCord && (
           <NodeListView
@@ -127,8 +106,8 @@ const SystemView: any = () => {
             setShowChildView={setShowChildView}
           />
         )}
-      </div>
-    </div>
+      </Box>
+    </Box>
   );
 };
 

+ 35 - 52
client/src/pages/system/Types.ts

@@ -1,79 +1,62 @@
-import { ReactNode } from "react";
+import { ReactNode } from 'react';
 
 export interface Node {
   infos: {
-    hardware_infos: any,
-    system_info: any,
-    name: string,
-    created_time: string,
-    updated_time: string,
-    system_configurations: any,
-    type: string,
-  },
+    hardware_infos: any;
+    system_info: any;
+    name: string;
+    created_time: string;
+    updated_time: string;
+    system_configurations: any;
+    type: string;
+  };
   connected: {
-    connected_identifier: number,
-    target_type: string,
-    type: string,
-  }[],
-  identifier: number,
+    connected_identifier: number;
+    target_type: string;
+    type: string;
+  }[];
+  identifier: number;
 }
 
 export interface ProgressProps {
-  percent: number,
-  color: string,
-}
-
-export interface ProgressCardProps {
-  title: string,
-  total: number,
-  usage: number,
+  percent: number;
+  color: string;
 }
 
 export interface BaseCardProps {
-  children?: ReactNode,
-  title: string,
-  content?: string,
-  desc?: string,
-}
-
-export interface LineChartCardProps {
-  title: string,
-  value: number,
+  children?: ReactNode;
+  title: string;
+  content?: string;
+  desc?: string;
 }
 
 export interface DataProgressProps {
-  percent: number,
-  desc?: string,
+  percent: number;
+  desc?: string;
 }
 
 export interface DataSectionProps {
-  titles: string[],
-  contents: { label: string, value: string }[],
+  titles: string[];
+  contents: { label: string; value: string }[];
 }
 
 export interface DataCardProps {
-  node?: Node,
-  extend?: boolean
-}
-
-export interface LinceChartNode {
-  percent: number,
-  value: number,
-  timestamp: number,
+  node?: Node;
+  extend?: boolean;
 }
 
 type SetCord = (arg1: Node | null) => void;
 
 export interface MiniTopoProps {
-  selectedCord: Node,
-  selectedChildNode: Node | undefined,
-  setCord: SetCord,
-  setShowChildView: (arg1: boolean) => void,
+  selectedCord: Node;
+  selectedChildNode: Node | undefined;
+  setCord: SetCord;
+  setShowChildView: (arg1: boolean) => void;
 }
 
 export interface NodeListViewProps {
-  selectedCord: Node,
-  childNodes: Node[],
-  setCord: SetCord,
-  setShowChildView: (arg1: boolean) => void,
-}
+  selectedCord: Node;
+  childNodes: Node[];
+  setCord: SetCord;
+  setShowChildView: (arg1: boolean) => void;
+}

+ 0 - 204
client/src/pages/systemHealthy/HealthyIndexDetailView.tsx

@@ -1,204 +0,0 @@
-import { Theme } from '@mui/material';
-import { CHART_WIDTH, LINE_CHART_SMALL_HEIGHT } from './consts';
-import HealthyIndexRow from './HealthyIndexRow';
-import LineChartSmall from './LineChartSmall';
-import { ENodeService, INodeTreeStructure, IThreshold } from './Types';
-import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
-import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
-import { Dispatch, SetStateAction, useState } from 'react';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  mainView: {
-    width: '100%',
-    marginTop: '16px',
-  },
-  healthyIndexItem: {
-    display: 'flex',
-    marginTop: '8px',
-    justifyContent: 'space-between',
-  },
-  healthyIndexLabel: {
-    fontWeight: 500,
-    fontSize: '12px',
-    color: '#444',
-    display: 'flex',
-    alignItems: 'center',
-    cursor: 'pointer',
-  },
-  healthyIndexLabelText: {},
-  healthyIndexRow: {
-    width: `${CHART_WIDTH}px`,
-  },
-  chartItem: {
-    margin: '8px 0',
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'center',
-  },
-  chartLabel: {
-    width: `50px`,
-    paddingLeft: '20px',
-    fontSize: '12px',
-    fontWeight: 500,
-    color: '#444',
-  },
-  chart: {
-    height: `${LINE_CHART_SMALL_HEIGHT}px`,
-    width: `${CHART_WIDTH}px`,
-  },
-}));
-
-const HealthyIndexTreeItem = ({
-  node,
-  threshold,
-}: {
-  node: INodeTreeStructure;
-  threshold: IThreshold;
-}) => {
-  const classes = getStyles();
-  const [open, setOpen] = useState(false);
-  return (
-    <>
-      <div
-        key={`${node.service}-${node.type}-${node.label}`}
-        className={classes.healthyIndexItem}
-      >
-        <div
-          className={classes.healthyIndexLabel}
-          onClick={() => setOpen(!open)}
-        >
-          {open ? (
-            <KeyboardArrowDownIcon fontSize="small" />
-          ) : (
-            <KeyboardArrowRightIcon fontSize="small" />
-          )}
-          <div className={classes.healthyIndexLabelText}>{`${
-            node.type
-          }-${node.label.slice(-5)}`}</div>
-        </div>
-        <div className={classes.healthyIndexRow}>
-          <HealthyIndexRow statusList={node.healthyStatus} />
-        </div>
-      </div>
-      {open && (
-        <>
-          <div className={classes.chartItem}>
-            <div className={classes.chartLabel}>cpu</div>
-            <div className={classes.chart}>
-              <LineChartSmall
-                data={node.cpu || []}
-                format={(v: number) => v.toFixed(3)}
-                unit={'Core'}
-                threshold={threshold.cpu}
-              />
-            </div>
-          </div>
-          <div className={classes.chartItem}>
-            <div className={classes.chartLabel}>memory</div>
-            <div className={classes.chart}>
-              <LineChartSmall
-                data={node.memory || []}
-                format={(v: number) => (v / 1024 / 1024 / 1024).toFixed(1)}
-                unit={'GB'}
-                threshold={threshold.memory}
-              />
-            </div>
-          </div>
-        </>
-      )}
-    </>
-  );
-};
-
-const HealthyIndexWithTree = ({
-  nodeTree,
-  setSelectedService,
-  threshold,
-}: {
-  nodeTree: INodeTreeStructure;
-  setSelectedService: Dispatch<SetStateAction<ENodeService>>;
-  threshold: IThreshold;
-}) => {
-  const classes = getStyles();
-  return (
-    <div className={classes.mainView}>
-      {!!nodeTree && (
-        <div className={classes.healthyIndexItem}>
-          <div
-            className={classes.healthyIndexLabel}
-            onClick={() => setSelectedService(ENodeService.root)}
-          >
-            {nodeTree.label}
-          </div>
-          <div className={classes.healthyIndexRow}>
-            <HealthyIndexRow statusList={nodeTree?.healthyStatus || []} />
-          </div>
-        </div>
-      )}
-      {!!nodeTree &&
-        nodeTree.children.map(node => (
-          <HealthyIndexTreeItem
-            key={node.label}
-            node={node}
-            threshold={threshold}
-          />
-        ))}
-    </div>
-  );
-};
-
-const HealthyIndexWithoutTree = ({
-  nodeTree,
-  setSelectedService,
-}: {
-  nodeTree: INodeTreeStructure;
-  setSelectedService: Dispatch<SetStateAction<ENodeService>>;
-}) => {
-  const classes = getStyles();
-  return (
-    <div className={classes.mainView}>
-      {nodeTree.children.map(node => (
-        <div
-          key={`${node.service}-${node.type}`}
-          className={classes.healthyIndexItem}
-        >
-          <div
-            className={classes.healthyIndexLabel}
-            onClick={() => setSelectedService(node.service)}
-          >
-            {node.label}
-          </div>
-          <div className={classes.healthyIndexRow}>
-            <HealthyIndexRow statusList={node.healthyStatus} />
-          </div>
-        </div>
-      ))}
-    </div>
-  );
-};
-
-const HealthyIndexDetailView = ({
-  nodeTree,
-  setSelectedService,
-  threshold,
-}: {
-  nodeTree: INodeTreeStructure;
-  setSelectedService: Dispatch<SetStateAction<ENodeService>>;
-  threshold: IThreshold;
-}) => {
-  return nodeTree.service === ENodeService.milvus ? (
-    <HealthyIndexWithoutTree
-      nodeTree={nodeTree}
-      setSelectedService={setSelectedService}
-    />
-  ) : (
-    <HealthyIndexWithTree
-      nodeTree={nodeTree}
-      setSelectedService={setSelectedService}
-      threshold={threshold}
-    />
-  );
-};
-
-export default HealthyIndexDetailView;

+ 0 - 58
client/src/pages/systemHealthy/HealthyIndexLegend.tsx

@@ -1,58 +0,0 @@
-import { Theme } from '@mui/material';
-import { HEALTHY_INDEX_ROW_HEIGHT, HEALTHY_STATUS_COLORS } from './consts';
-import { EHealthyStatus } from './Types';
-import { makeStyles } from '@mui/styles';
-
-const legendData = [
-  {
-    label: 'NoData',
-    value: EHealthyStatus.noData,
-  },
-  {
-    label: 'Healthy',
-    value: EHealthyStatus.healthy,
-  },
-  {
-    label: 'Warning',
-    value: EHealthyStatus.warning,
-  },
-  {
-    label: 'Failed',
-    value: EHealthyStatus.failed,
-  },
-];
-
-const getStyles = makeStyles((theme: Theme) => ({
-  legendItem: {
-    display: 'flex',
-    marginLeft: '12px',
-    fontSize: '10px',
-    alignItems: 'flex-end',
-  },
-  legendIcon: {
-    width: '16px',
-  },
-  legendText: { marginLeft: '8px', fontWeight: 500, color: '#666' },
-}));
-
-const HealthyIndexLegend = () => {
-  const classes = getStyles();
-  return (
-    <>
-      {legendData.map(legend => (
-        <div key={legend.label} className={classes.legendItem}>
-          <div
-            className={classes.legendIcon}
-            style={{
-              background: HEALTHY_STATUS_COLORS[legend.value],
-              height: `${HEALTHY_INDEX_ROW_HEIGHT * 0.8}px`,
-            }}
-          ></div>
-          <div className={classes.legendText}>{legend.label}</div>
-        </div>
-      ))}
-    </>
-  );
-};
-
-export default HealthyIndexLegend;

+ 0 - 142
client/src/pages/systemHealthy/HealthyIndexOverview.tsx

@@ -1,142 +0,0 @@
-import { Theme } from '@mui/material';
-import { Dispatch, SetStateAction } from 'react';
-import {
-  CHART_WIDTH,
-  LINE_CHART_LARGE_HEIGHT,
-  MAIN_VIEW_WIDTH,
-} from './consts';
-import HealthyIndexDetailView from './HealthyIndexDetailView';
-import HealthyIndexLegend from './HealthyIndexLegend';
-import LineChartLarge from './LineChartLarge';
-import ThresholdSetting from './ThresholdSetting';
-import TimeRangeTabs from './TimeRangeTabs';
-import {
-  ENodeService,
-  ILineChartData,
-  INodeTreeStructure,
-  IThreshold,
-  ITimeRangeOption,
-} from './Types';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    width: `${MAIN_VIEW_WIDTH}px`,
-    overflow: 'auto',
-    padding: '8px 56px 60px 24px',
-    fontSize: '14px',
-  },
-  headerContent: {
-    display: 'grid',
-    gridTemplateColumns: '1fr auto',
-  },
-  titleContainer: {},
-  title: {
-    display: 'flex',
-    alignItems: 'flex-end',
-  },
-  titleMain: { fontSize: '18px', fontWeight: 500, cursor: 'pointer' },
-  titleExt: { fontSize: '18px', fontWeight: 500, marginLeft: '8px' },
-  timeRangeTabs: {
-    fontSize: '12px',
-  },
-  legendContainer: {
-    display: 'flex',
-    alignItems: 'flex-end',
-  },
-  settingIcon: { marginLeft: '12px', display: 'flex', alignItems: 'flex-end' },
-
-  chartView: { width: '100%', marginTop: '24px' },
-  chartItem: {
-    margin: '16px 0',
-    display: 'flex',
-    justifyContent: 'space-between',
-    alignItems: 'flex-end',
-  },
-  chartLabel: {
-    width: `50px`,
-    fontWeight: 500,
-    color: '#444',
-  },
-  chart: {
-    height: `${LINE_CHART_LARGE_HEIGHT}px`,
-    width: `${CHART_WIDTH}px`,
-  },
-}));
-
-const HealthyIndexOverview = ({
-  selectedNode,
-  lineChartsData,
-  threshold,
-  setThreshold,
-  timeRange,
-  setTimeRange,
-  setSelectedService,
-}: {
-  selectedNode: INodeTreeStructure;
-  lineChartsData: ILineChartData[];
-  threshold: IThreshold;
-  setThreshold: (threshold: IThreshold) => void;
-  timeRange: ITimeRangeOption;
-  setTimeRange: Dispatch<SetStateAction<ITimeRangeOption>>;
-  setSelectedService: Dispatch<SetStateAction<ENodeService>>;
-}) => {
-  const classes = getStyles();
-  return (
-    <div className={classes.root}>
-      <div className={classes.headerContent}>
-        <div className={classes.titleContainer}>
-          <div className={classes.title}>
-            <div
-              className={classes.titleMain}
-              onClick={() => setSelectedService(ENodeService.root)}
-            >
-              Healthy Status
-            </div>
-            {selectedNode.service !== ENodeService.milvus && (
-              <div className={classes.titleExt}>
-                {`> ${selectedNode.service}`}
-              </div>
-            )}
-          </div>
-          <div className={classes.timeRangeTabs}>
-            <TimeRangeTabs timeRange={timeRange} setTimeRange={setTimeRange} />
-          </div>
-        </div>
-        <div className={classes.legendContainer}>
-          <HealthyIndexLegend />
-          <div className={classes.settingIcon}>
-            <ThresholdSetting
-              threshold={threshold}
-              setThreshold={setThreshold}
-            />
-          </div>
-        </div>
-      </div>
-      <HealthyIndexDetailView
-        nodeTree={selectedNode}
-        setSelectedService={setSelectedService}
-        threshold={threshold}
-      />
-      {selectedNode.service === ENodeService.milvus && (
-        <div className={classes.chartView}>
-          <div className={classes.titleMain}>Search Query History</div>
-          {lineChartsData.map(chartData => (
-            <div key={chartData.label} className={classes.chartItem}>
-              <div className={classes.chartLabel}>{chartData.label}</div>
-              <div className={classes.chart}>
-                <LineChartLarge
-                  data={chartData.data}
-                  format={chartData.format}
-                  unit={chartData.unit}
-                />
-              </div>
-            </div>
-          ))}
-        </div>
-      )}
-    </div>
-  );
-};
-
-export default HealthyIndexOverview;

+ 0 - 32
client/src/pages/systemHealthy/HealthyIndexRow.tsx

@@ -1,32 +0,0 @@
-import {
-  CHART_WIDTH,
-  HEALTHY_INDEX_ROW_GAP_RATIO,
-  HEALTHY_INDEX_ROW_HEIGHT,
-  HEALTHY_STATUS_COLORS,
-} from './consts';
-import { EHealthyStatus } from './Types';
-
-const HealthyIndexRow = ({ statusList }: { statusList: EHealthyStatus[] }) => {
-  const length = statusList.length;
-  const stautsItemWidth = length === 0 ? 0 : CHART_WIDTH / length;
-  const statusBlockGap = stautsItemWidth * HEALTHY_INDEX_ROW_GAP_RATIO;
-  const statusBlockWidth = stautsItemWidth * (1 - HEALTHY_INDEX_ROW_GAP_RATIO);
-  return (
-    <svg width={CHART_WIDTH} height={HEALTHY_INDEX_ROW_HEIGHT}>
-      {statusList.map((status, i) => (
-        <rect
-          key={`status-${i}`}
-          x={i * stautsItemWidth + statusBlockGap / 2}
-          y={0}
-          rx={1}
-          ry={1}
-          width={statusBlockWidth}
-          height={HEALTHY_INDEX_ROW_HEIGHT}
-          fill={HEALTHY_STATUS_COLORS[status]}
-        />
-      ))}
-    </svg>
-  );
-};
-
-export default HealthyIndexRow;

+ 0 - 95
client/src/pages/systemHealthy/LineChartLarge.tsx

@@ -1,95 +0,0 @@
-import * as d3 from 'd3';
-import {
-  CHART_WIDTH,
-  LINE_CHART_LARGE_HEIGHT,
-  LINE_COLOR,
-  LINE_LABEL_FONT_SIZE,
-  LINE_LABEL_Y_PADDING,
-  LINE_WIDTH,
-} from './consts';
-
-const LineChartLarge = ({
-  data,
-  format = d => d,
-  unit = '',
-}: {
-  data: number[];
-  format?: (d: any) => string;
-  unit?: string;
-}) => {
-  const length = data.length;
-  const width = CHART_WIDTH;
-  const height = LINE_CHART_LARGE_HEIGHT - 3;
-  const fontSize = LINE_LABEL_FONT_SIZE;
-
-  const xDomain = [0, length];
-  const xRange = [0, CHART_WIDTH];
-  let maxData = d3.max(data, d => d) as number;
-  maxData = maxData === 0 ? 1 : maxData;
-
-  const yDomain = [0, maxData * 1.1];
-  const yRange = [height, 0];
-
-  const xScale = d3.scaleLinear(xDomain, xRange);
-  const yScale = d3.scaleLinear(yDomain, yRange);
-
-  const nodes = data
-    .map((d, i) => (d >= 0 ? [xScale(i + 0.5), yScale(d)] : undefined))
-    .filter(a => a) as [number, number][];
-
-  const line = d3
-    .line()
-    .curve(d3.curveBumpX)
-    .x(d => d[0])
-    .y(d => d[1]);
-
-  return (
-    <svg
-      width={width}
-      height={height}
-      style={{ overflow: 'visible' }}
-      fontSize={fontSize}
-      fontWeight={500}
-    >
-      <g className="x-axis">
-        <line x1={0} y1={height} x2={width} y2={height} stroke="#666" />
-      </g>
-      <g className="y-axis">
-        <line x1={0} y1={0} x2={0} y2={height} stroke="#666" />
-        <text x={-LINE_LABEL_Y_PADDING} y={height} textAnchor="end" fill="#555">
-          {0}
-        </text>
-        <text
-          x={-LINE_LABEL_Y_PADDING}
-          y={fontSize}
-          textAnchor="end"
-          fill="#555"
-        >
-          {format(maxData)}
-        </text>
-        {unit && (
-          <text
-            x={-LINE_LABEL_Y_PADDING}
-            y={fontSize * 2}
-            textAnchor="end"
-            fill={'#666'}
-            fontSize={fontSize - 2}
-          >
-            ({unit})
-          </text>
-        )}
-      </g>
-      <g className="line">
-        <path
-          d={line(nodes) as any}
-          fill="none"
-          stroke={`${LINE_COLOR}`}
-          strokeWidth={LINE_WIDTH}
-          opacity={0.8}
-          strokeLinecap="round"
-        />
-      </g>
-    </svg>
-  );
-};
-export default LineChartLarge;

+ 0 - 125
client/src/pages/systemHealthy/LineChartSmall.tsx

@@ -1,125 +0,0 @@
-import * as d3 from 'd3';
-import {
-  CHART_WIDTH,
-  HEALTHY_STATUS_COLORS,
-  LINE_CHART_LARGE_HEIGHT,
-  LINE_CHART_SMALL_HEIGHT,
-  LINE_COLOR,
-  LINE_LABEL_FONT_SIZE,
-  LINE_LABEL_Y_PADDING,
-  LINE_SMALL_LABEL_FONT_SIZE,
-  LINE_WIDTH,
-} from './consts';
-import { EHealthyStatus } from './Types';
-
-const LineChartSmall = ({
-  data,
-  format = d => d,
-  unit = '',
-  threshold,
-}: {
-  data: number[];
-  format?: (d: any) => string;
-  unit?: string;
-  threshold: number;
-}) => {
-  const length = data.length;
-  const width = CHART_WIDTH;
-  const height = LINE_CHART_SMALL_HEIGHT - 3;
-  const fontSize = LINE_SMALL_LABEL_FONT_SIZE;
-
-  const xDomain = [0, length];
-  const xRange = [0, CHART_WIDTH];
-  let maxData = d3.max(data, d => d) as number;
-  maxData = maxData === 0 ? 1 : maxData;
-
-  const yDomain = [0, maxData * 1.1];
-  const yRange = [height, 0];
-
-  const xScale = d3.scaleLinear(xDomain, xRange);
-  const yScale = d3.scaleLinear(yDomain, yRange);
-
-  const nodes = data
-    .map((d, i) => (d >= 0 ? [xScale(i + 0.5), yScale(d)] : undefined))
-    .filter(a => a) as [number, number][];
-
-  const line = d3
-    .line()
-    .curve(d3.curveBumpX)
-    .x(d => d[0])
-    .y(d => d[1]);
-
-  return (
-    <svg
-      width={width}
-      height={height}
-      style={{ overflow: 'visible' }}
-      fontSize={fontSize}
-      fontWeight={500}
-    >
-      <g className="x-axis">
-        <line x1={0} y1={height} x2={width} y2={height} stroke="#666" />
-        <line
-          x1={width - LINE_LABEL_Y_PADDING}
-          y1={yScale(maxData)}
-          x2={width}
-          y2={yScale(maxData)}
-          stroke="#666"
-          strokeWidth="2"
-        />
-      </g>
-      <g className="y-axis">
-        <text
-          x={width + LINE_LABEL_Y_PADDING}
-          y={height}
-          textAnchor="start"
-          fill="#555"
-        >
-          {0}
-        </text>
-        <text
-          x={width + LINE_LABEL_Y_PADDING}
-          y={yScale(maxData) + 3}
-          textAnchor="start"
-          fill="#555"
-        >
-          {format(maxData)}
-        </text>
-        {unit && (
-          <text
-            x={width + LINE_LABEL_Y_PADDING}
-            y={yScale(maxData) + 3 + fontSize}
-            textAnchor="start"
-            fontSize={fontSize - 2}
-            fill="#555"
-          >
-            ({unit})
-          </text>
-        )}
-      </g>
-      <g className="line">
-        {maxData >= threshold && (
-          <line
-            x1={xScale(0.5)}
-            y1={yScale(threshold)}
-            x2={xScale(data.length - 0.5)}
-            y2={yScale(threshold)}
-            stroke={HEALTHY_STATUS_COLORS[EHealthyStatus.warning]}
-            strokeWidth={LINE_WIDTH + 1}
-            strokeLinecap="round"
-            strokeDasharray={"6 8"}
-          />
-        )}
-        <path
-          d={line(nodes) as any}
-          fill="none"
-          stroke={LINE_COLOR}
-          strokeWidth={LINE_WIDTH}
-          opacity={0.8}
-          strokeLinecap="round"
-        />
-      </g>
-    </svg>
-  );
-};
-export default LineChartSmall;

+ 0 - 151
client/src/pages/systemHealthy/SystemHealthyView.tsx

@@ -1,151 +0,0 @@
-import { useEffect, useMemo, useState } from 'react';
-import { Theme } from '@mui/material';
-import { useTranslation } from 'react-i18next';
-import { useNavigationHook, useInterval } from '@/hooks';
-import { ALL_ROUTER_TYPES } from '@/router/consts';
-import {
-  LAST_TIME_HEALTHY_THRESHOLD_CPU,
-  LAST_TIME_HEALTHY_THRESHOLD_MEMORY,
-  DEFAULT_HEALTHY_THRESHOLD_CPU,
-  DEFAULT_HEALTHY_THRESHOLD_MEMORY,
-} from '@/consts';
-import {
-  ENodeService,
-  ILineChartData,
-  INodeTreeStructure,
-  IPrometheusAllData,
-  IThreshold,
-  ITimeRangeOption,
-} from './Types';
-import Topology from './Topology';
-import { reconNodeTree } from './dataHandler';
-import HealthyIndexOverview from './HealthyIndexOverview';
-import { timeRangeOptions } from './consts';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    margin: '16px 40px',
-    position: 'relative',
-    height: '88%',
-    display: 'flex',
-    flexDirection: 'column',
-  },
-  mainView: {
-    boxShadow: '3px 3px 10px rgba(0, 0, 0, 0.05)',
-    display: 'grid',
-    gridTemplateColumns: '1fr auto',
-    height: '100%',
-  },
-}));
-
-const SystemHealthyView = () => {
-  useNavigationHook(ALL_ROUTER_TYPES.SYSTEM);
-
-  const classes = getStyles();
-
-  const INTERVAL = 60000;
-  const [timeRange, setTimeRange] = useState<ITimeRangeOption>(
-    timeRangeOptions[2]
-  );
-
-  const [threshold, setThreshold] = useState<IThreshold>({
-    cpu: +(
-      window.localStorage.getItem(LAST_TIME_HEALTHY_THRESHOLD_CPU) ||
-      DEFAULT_HEALTHY_THRESHOLD_CPU
-    ),
-    memory: +(
-      window.localStorage.getItem(LAST_TIME_HEALTHY_THRESHOLD_MEMORY) ||
-      DEFAULT_HEALTHY_THRESHOLD_MEMORY
-    ),
-  });
-  const changeThreshold = (threshold: IThreshold) => {
-    window.localStorage.setItem(
-      LAST_TIME_HEALTHY_THRESHOLD_CPU,
-      `${threshold.cpu}`
-    );
-    window.localStorage.setItem(
-      LAST_TIME_HEALTHY_THRESHOLD_MEMORY,
-      `${threshold.memory}`
-    );
-    setThreshold(threshold);
-  };
-  const [prometheusData, setPrometheusData] = useState<IPrometheusAllData>();
-  const nodeTree = useMemo<INodeTreeStructure>(
-    () =>
-      prometheusData
-        ? reconNodeTree(prometheusData, threshold)
-        : ({
-            children: [] as INodeTreeStructure[],
-          } as INodeTreeStructure),
-    [prometheusData, threshold]
-  );
-
-  const { t: prometheusTrans } = useTranslation('prometheus');
-  const lineChartsData = useMemo<ILineChartData[]>(
-    () =>
-      prometheusData
-        ? [
-            {
-              label: prometheusTrans('totalCount'),
-              data: prometheusData.totalVectorsCount,
-            },
-            {
-              label: prometheusTrans('searchCount'),
-              data: prometheusData.searchVectorsCount,
-            },
-            {
-              label: prometheusTrans('searchLatency'),
-              data: prometheusData.sqLatency,
-              format: d => d.toFixed(0),
-              unit: 'ms',
-            },
-          ]
-        : [],
-    [prometheusData]
-  );
-
-  const hasDetailServices = [
-    ENodeService.index,
-    ENodeService.query,
-    ENodeService.data,
-  ];
-
-  const [selectedService, setSelectedService] = useState<ENodeService>(
-    ENodeService.root
-  );
-
-  const selectedNode = useMemo<INodeTreeStructure>(() => {
-    if (hasDetailServices.indexOf(selectedService) >= 0) {
-      return nodeTree.children.find(
-        node => node.service === selectedService
-      ) as INodeTreeStructure;
-    } else return nodeTree;
-  }, [selectedService, nodeTree]);
-
-  return (
-    <div className={classes.root}>
-      {!!prometheusData && (
-        <div className={classes.mainView}>
-          <Topology
-            nodeTree={nodeTree}
-            selectedService={selectedService}
-            onClick={setSelectedService}
-          />
-
-          <HealthyIndexOverview
-            selectedNode={selectedNode}
-            lineChartsData={lineChartsData}
-            threshold={threshold}
-            setThreshold={changeThreshold}
-            timeRange={timeRange}
-            setTimeRange={setTimeRange}
-            setSelectedService={setSelectedService}
-          />
-        </div>
-      )}
-    </div>
-  );
-};
-
-export default SystemHealthyView;

+ 0 - 169
client/src/pages/systemHealthy/ThresholdSetting.tsx

@@ -1,169 +0,0 @@
-import { Dialog, Theme } from '@mui/material';
-import { useMemo, useState } from 'react';
-import CustomInput from '@/components/customInput/CustomInput';
-import { ITextfieldConfig } from '@/components/customInput/Types';
-import { useFormValidation } from '@/hooks';
-import { formatForm } from '@/utils';
-import { HEALTHY_STATUS_COLORS } from './consts';
-import { EHealthyStatus, IThreshold } from './Types';
-import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
-import CustomButton from '@/components/customButton/CustomButton';
-import { makeStyles } from '@mui/styles';
-
-export interface SimpleDialogProps {
-  open: boolean;
-  selectedValue: string;
-  onClose: (value: string) => void;
-}
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    padding: '24px 32px',
-    width: '360px',
-    display: 'flex',
-    flexDirection: 'column',
-  },
-  note: {
-    fontWeight: 500,
-    color: '#444',
-    fontSize: '14px',
-    margin: theme.spacing(1, 0, 2),
-  },
-  input: {
-    margin: theme.spacing(0.5, 0, 0),
-  },
-  button: {
-    alignSelf: 'flex-end',
-  },
-}));
-
-function ThresholdSettingDialog({
-  onClose,
-  open,
-  threshold,
-  setThreshold,
-}: {
-  open: boolean;
-  onClose: () => void;
-  threshold: IThreshold;
-  setThreshold: (threshold: IThreshold) => void;
-}) {
-  const classes = getStyles();
-  const handleClose = () => {
-    setThreshold({ ...form, memory: form.memory * 1024 * 1024 * 1024 });
-    onClose();
-  };
-
-  const [form, setForm] = useState<IThreshold>({
-    cpu: threshold.cpu,
-    memory: threshold.memory / 1024 / 1024 / 1024,
-  });
-
-  const handleFormChange = (key: 'cpu' | 'memory', value: number) => {
-    setForm(v => ({ ...v, [key]: value }));
-  };
-  const checkedForm = useMemo(() => {
-    return formatForm(form);
-  }, [form]);
-  const { validation, checkIsValid } = useFormValidation(checkedForm);
-
-  const inputConfigs: ITextfieldConfig[] = useMemo(
-    () => [
-      {
-        label: `CPU (Core)`,
-        key: 'prometheus_address',
-        onChange: (v: string) => handleFormChange('cpu', +v),
-        variant: 'filled',
-        className: classes.input,
-        placeholder: 'CPU',
-        fullWidth: true,
-
-        defaultValue: form.cpu,
-      },
-      {
-        label: `Memory (GB)`,
-        key: 'prometheus_address',
-        onChange: (v: string) => handleFormChange('memory', +v),
-        variant: 'filled',
-        className: classes.input,
-        placeholder: 'Memory',
-        fullWidth: true,
-
-        defaultValue: form.memory,
-      },
-    ],
-    [form]
-  );
-
-  return (
-    <Dialog onClose={handleClose} open={open}>
-      <div className={classes.root}>
-        <div className={classes.note}>
-          {`Exceeding any threshold will result in a `}
-          <span
-            style={{
-              color: HEALTHY_STATUS_COLORS[EHealthyStatus.warning],
-              fontWeight: 600,
-              fontSize: 18,
-            }}
-          >
-            warning
-          </span>
-          .
-        </div>
-        {inputConfigs.map(v => (
-          <CustomInput
-            type="text"
-            textConfig={v}
-            key={v.label}
-            checkValid={checkIsValid}
-            validInfo={validation}
-          />
-        ))}
-        <div className={classes.button}>
-          <CustomButton variant="contained" onClick={handleClose}>
-            Confirm
-          </CustomButton>
-        </div>
-      </div>
-    </Dialog>
-  );
-}
-
-const ThresholdSetting = ({
-  threshold,
-  setThreshold,
-}: {
-  threshold: IThreshold;
-  setThreshold: (threshold: IThreshold) => void;
-}) => {
-  const [open, setOpen] = useState(false);
-
-  const handleClickOpen = () => {
-    setOpen(true);
-  };
-
-  const handleClose = () => {
-    setOpen(false);
-  };
-
-  return (
-    <>
-      <SettingsOutlinedIcon
-        onClick={handleClickOpen}
-        style={{
-          cursor: 'pointer',
-          opacity: 0.8,
-        }}
-      />
-      <ThresholdSettingDialog
-        threshold={threshold}
-        setThreshold={setThreshold}
-        open={open}
-        onClose={handleClose}
-      />
-    </>
-  );
-};
-
-export default ThresholdSetting;

+ 0 - 60
client/src/pages/systemHealthy/TimeRangeTabs.tsx

@@ -1,60 +0,0 @@
-import { Theme } from '@mui/material';
-import { Dispatch, Fragment, SetStateAction } from 'react';
-import { timeRangeOptions } from './consts';
-import { ITimeRangeOption } from './Types';
-import clsx from 'clsx';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    fontSize: '14px',
-    display: 'flex',
-    alignItems: 'flex-end',
-    color: '#999',
-    fontWeight: 500,
-  },
-  divider: {
-    margin: '0 4px',
-  },
-  label: {
-    cursor: 'pointer',
-    '&:hover': {
-      fontSize: '16px',
-    },
-  },
-  active: {
-    fontWeight: 600,
-    fontSize: '16px',
-    color: '#222',
-  },
-}));
-
-const TimeRangeTabs = ({
-  timeRange,
-  setTimeRange,
-}: {
-  timeRange: ITimeRangeOption;
-  setTimeRange: Dispatch<SetStateAction<ITimeRangeOption>>;
-}) => {
-  const classes = getStyles();
-  return (
-    <div className={classes.root}>
-      {timeRangeOptions.map((timeRangeOption, i: number) => (
-        <Fragment key={timeRangeOption.label}>
-          {i > 0 && <div className={classes.divider}>{'/'}</div>}
-          <div
-            className={clsx(
-              classes.label,
-              timeRangeOption.value === timeRange.value && classes.active
-            )}
-            onClick={() => setTimeRange(timeRangeOption)}
-          >
-            {timeRangeOption.label}
-          </div>
-        </Fragment>
-      ))}
-    </div>
-  );
-};
-
-export default TimeRangeTabs;

+ 0 - 259
client/src/pages/systemHealthy/Topology.tsx

@@ -1,259 +0,0 @@
-import { Theme, useTheme } from '@mui/material';
-import {
-  TOPO_HEIGHT,
-  TOPO_LINK_LENGTH,
-  TOPO_NODE_R,
-  TOPO_WIDTH,
-} from './consts';
-import { getIcon } from './getIcon';
-import { ENodeService, ENodeType, INodeTreeStructure } from './Types';
-import clsx from 'clsx';
-import { makeStyles } from '@mui/styles';
-
-const getStyles = makeStyles((theme: Theme) => ({
-  root: {
-    borderTopLeftRadius: '8px',
-    borderBottomLeftRadius: '8px',
-    overflow: 'auto',
-    backgroundColor: theme.palette.background.paper,
-    position: 'relative',
-  },
-  svg: {
-    overflow: 'visible',
-    position: 'absolute',
-    left: 0,
-    top: 0,
-    bottom: 0,
-    right: 0,
-    margin: 'auto',
-  },
-  prometheusInfoContainer: {
-    position: 'absolute',
-    display: 'flex',
-    fontSize: '12px',
-    padding: '4px 8px',
-    flexWrap: 'wrap',
-  },
-  prometheusInfoItem: {
-    marginRight: '20px',
-    display: 'flex',
-  },
-  prometheusInfoItemLabel: {
-    marginRight: '8px',
-    fontWeight: 600,
-    color: '#333',
-  },
-  prometheusInfoItemText: {
-    fontWeight: 500,
-    color: '#666',
-  },
-  node: {
-    transition: 'all .25s',
-    cursor: 'pointer',
-    transformOrigin: '50% 50%',
-    transformBox: 'fill-box',
-
-    '& circle': {
-      transition: 'all .25s',
-    },
-
-    '& text': {
-      transition: 'all .25s',
-    },
-
-    '&:hover': {
-      transform: 'scale(1.1)',
-      filter: 'drop-shadow(3px 3px 5px rgba(0, 0, 0, .2))',
-      outline: 'none',
-    },
-  },
-  selected: {
-    '& svg path': {
-      fill: theme.palette.background.paper,
-    },
-
-    '& circle': {
-      fill: theme.palette.primary.main,
-      stroke: theme.palette.primary.main,
-    },
-
-    '& text': {
-      fill: theme.palette.background.paper,
-    },
-  },
-}));
-
-const randomList = Array(10)
-  .fill(0)
-  .map(_ => Math.random());
-
-const nodesLayout = (
-  nodes: INodeTreeStructure[],
-  width: number,
-  height: number
-) => {
-  const rootNode =
-    nodes.find(node => node.service === ENodeService.root) ||
-    (nodes.find(node => node.type === ENodeType.coord) as INodeTreeStructure);
-  const childrenNodes = nodes.filter(node => node !== rootNode);
-
-  const rootPos = [248, height * 0.45];
-  const angleStep = (2 * Math.PI) / Math.max(childrenNodes.length, 3);
-  const angleBias = angleStep * 0.4;
-  const childrenPos = childrenNodes.map((node, i) => [
-    rootPos[0] + Math.cos(angleStep * i) * TOPO_LINK_LENGTH[0],
-    rootPos[1] + Math.sin(angleStep * i) * TOPO_LINK_LENGTH[0],
-  ]);
-  const subChildrenPos = childrenNodes.map((node, i) => {
-    const angle = angleStep * i + (randomList[i] - 0.5) * angleBias;
-    return [
-      rootPos[0] + Math.cos(angle) * TOPO_LINK_LENGTH[1],
-      rootPos[1] + Math.sin(angle) * TOPO_LINK_LENGTH[1],
-    ];
-  });
-  return { rootNode, childrenNodes, rootPos, childrenPos, subChildrenPos };
-};
-
-const Topology = ({
-  nodeTree,
-  onClick,
-  selectedService,
-}: {
-  nodeTree: INodeTreeStructure;
-  onClick: (service: ENodeService) => void;
-  selectedService: ENodeService;
-}) => {
-  const width = TOPO_WIDTH;
-  const height = TOPO_HEIGHT;
-
-  const classes = getStyles();
-  const theme = useTheme();
-
-  const { rootNode, childrenNodes, rootPos, childrenPos, subChildrenPos } =
-    nodesLayout(nodeTree.children, width, height);
-
-  return (
-    <div className={classes.root}>
-      <svg
-        className={classes.svg}
-        width={width}
-        height={height}
-        style={{ overflow: 'visible' }}
-      >
-        {childrenNodes.map((node, i) => {
-          const childPos = childrenPos[i];
-          const subChildPos = subChildrenPos[i];
-
-          return (
-            <g key={node.label}>
-              {node.children.length > 0 && (
-                <g
-                  className={classes.node}
-                  onClick={() => onClick(node.service)}
-                >
-                  <line
-                    x1={childPos[0]}
-                    y1={childPos[1]}
-                    x2={subChildPos[0]}
-                    y2={subChildPos[1]}
-                    stroke={theme.palette.primary.main}
-                  />
-                  <circle
-                    cx={subChildPos[0]}
-                    cy={subChildPos[1]}
-                    r={TOPO_NODE_R[2]}
-                    fill="#fff"
-                    stroke={theme.palette.primary.main}
-                  />
-                  {getIcon(
-                    node.children[0],
-                    theme,
-                    subChildPos[0] - 30,
-                    subChildPos[1] - 30
-                  )}
-
-                  <text
-                    textAnchor="middle"
-                    fill={theme.palette.text.primary}
-                    fontSize="12"
-                    x={subChildPos[0]}
-                    y={subChildPos[1] + 50}
-                  >{`${node.children.length - 1} Node(s)`}</text>
-                </g>
-              )}
-              <g
-                className={clsx(
-                  classes.node,
-                  node.service === selectedService && classes.selected
-                )}
-                onClick={() => onClick(node.service)}
-              >
-                <line
-                  x1={rootPos[0]}
-                  y1={rootPos[1]}
-                  x2={childPos[0]}
-                  y2={childPos[1]}
-                  stroke={theme.palette.primary.main}
-                />
-                <circle
-                  cx={childPos[0]}
-                  cy={childPos[1]}
-                  r={TOPO_NODE_R[1]}
-                  fill="#fff"
-                  stroke={theme.palette.primary.main}
-                />
-
-                {node.type === ENodeType.overview &&
-                  getIcon(node, theme, childPos[0] - 12, childPos[1] - 20)}
-
-                <text
-                  textAnchor="middle"
-                  fill={theme.palette.primary.main}
-                  fontWeight="700"
-                  fontSize="12"
-                  x={childPos[0]}
-                  y={childPos[1] + (node.type === ENodeType.overview ? 18 : 6)}
-                >
-                  {node.type === ENodeType.overview
-                    ? node.label
-                    : `${node.type}-${node.label.slice(-5)}`}
-                </text>
-              </g>
-
-              <g
-                onClick={() => onClick(rootNode.service)}
-                className={clsx(
-                  classes.node,
-                  rootNode.service === selectedService && classes.selected
-                )}
-              >
-                <circle
-                  cx={rootPos[0]}
-                  cy={rootPos[1]}
-                  r={TOPO_NODE_R[0]}
-                  fill="#fff"
-                  stroke={theme.palette.primary.main}
-                />
-                <text
-                  textAnchor="middle"
-                  alignmentBaseline="middle"
-                  fill={theme.palette.primary.main}
-                  fontWeight="700"
-                  fontSize="24"
-                  x={`${rootPos[0]}`}
-                  y={`${rootPos[1]}`}
-                >
-                  {rootNode.type === ENodeType.overview
-                    ? rootNode.label
-                    : `${rootNode.service}`}
-                </text>
-              </g>
-            </g>
-          );
-        })}
-      </svg>
-    </div>
-  );
-};
-
-export default Topology;

+ 0 - 79
client/src/pages/systemHealthy/Types.ts

@@ -1,79 +0,0 @@
-export interface ITimeRangeOption {
-  label: string;
-  value: number;
-  step: number;
-}
-
-export enum ENodeType {
-  overview = 0,
-  coord = 'coord',
-  node = 'node',
-}
-
-export enum ENodeService {
-  milvus = 0,
-  meta = 'Meta',
-  msgstream = 'MsgStream',
-  objstorage = 'ObjStorage',
-  root = 'Root',
-  query = 'Query',
-  index = 'Index',
-  data = 'Data',
-}
-
-export interface ILineChartData {
-  label: string;
-  data: number[];
-  format?: (d: number) => string;
-  unit?: string;
-}
-
-export interface INodeTreeStructure {
-  service: ENodeService;
-  type: ENodeType;
-  label: string;
-  healthyStatus: EHealthyStatus[];
-  cpu?: number[];
-  memory?: number[];
-  children: INodeTreeStructure[];
-}
-
-export enum EPrometheusDataStatus {
-  noData = -1,
-  failed = -2,
-}
-
-export enum EHealthyStatus {
-  noData = 0,
-  healthy,
-  warning,
-  failed,
-}
-
-export interface IPrometheusNode {
-  type: string;
-  pod: string;
-  cpu: number[];
-  memory: number[];
-}
-
-export interface IPrometheusAllData {
-  totalVectorsCount: number[];
-  searchVectorsCount: number[];
-  searchFailedVectorsCount?: number[];
-  sqLatency: number[];
-
-  meta: number[];
-  msgstream: number[];
-  objstorage: number[];
-
-  rootNodes: IPrometheusNode[];
-  queryNodes: IPrometheusNode[];
-  indexNodes: IPrometheusNode[];
-  dataNodes: IPrometheusNode[];
-}
-
-export interface IThreshold {
-  cpu: number;
-  memory: number;
-}

+ 0 - 48
client/src/pages/systemHealthy/consts.ts

@@ -1,48 +0,0 @@
-import { EHealthyStatus, ITimeRangeOption } from './Types';
-
-export const TOPO_WIDTH = 600;
-export const TOPO_HEIGHT = 580;
-export const TOPO_NODE_R = [68, 45, 30];
-export const TOPO_LINK_LENGTH = [160, 270];
-
-export const MAIN_VIEW_WIDTH = 560;
-export const CHART_WIDTH = 450;
-export const HEALTHY_INDEX_ROW_HEIGHT = 20;
-export const HEALTHY_INDEX_ROW_GAP_RATIO = 0.3;
-export const HEALTHY_STATUS_COLORS = {
-  [EHealthyStatus.noData]: '#ccc',
-  [EHealthyStatus.healthy]: '#6CD676',
-  [EHealthyStatus.warning]: '#F4DD0E',
-  [EHealthyStatus.failed]: '#F16415',
-};
-
-export const LINE_CHART_LARGE_HEIGHT = 60;
-export const LINE_CHART_SMALL_HEIGHT = 48;
-// export const LINE_COLOR = '#394E97';
-export const LINE_COLOR = 'rgb(6, 175, 242)';
-export const LINE_WIDTH = 1;
-export const LINE_LABEL_Y_PADDING = 6;
-export const LINE_LABEL_FONT_SIZE = 14;
-export const LINE_SMALL_LABEL_FONT_SIZE = 12;
-export const timeRangeOptions: ITimeRangeOption[] = [
-  {
-    label: '1h',
-    value: 60 * 60 * 1000,
-    step: 3 * 60 * 1000,
-  },
-  {
-    label: '24h',
-    value: 24 * 60 * 60 * 1000,
-    step: 60 * 60 * 1000,
-  },
-  {
-    label: '7d',
-    value: 7 * 24 * 60 * 60 * 1000,
-    step: 8 * 60 * 60 * 1000,
-  },
-  {
-    label: '30d',
-    value: 30 * 24 * 60 * 60 * 1000,
-    step: 24 * 60 * 60 * 1000,
-  },
-];

+ 0 - 154
client/src/pages/systemHealthy/dataHandler.ts

@@ -1,154 +0,0 @@
-import * as d3 from 'd3';
-import {
-  EHealthyStatus,
-  ENodeService,
-  ENodeType,
-  EPrometheusDataStatus,
-  INodeTreeStructure,
-  IPrometheusAllData,
-  IPrometheusNode,
-  IThreshold,
-} from './Types';
-
-export const getInternalNode = (
-  prometheusNodes: IPrometheusNode[],
-  service: ENodeService,
-  label: string,
-  threshold: IThreshold
-) => {
-  const length = prometheusNodes[0].cpu.length;
-  const nodes = prometheusNodes
-    .map(node => {
-      const healthyStatus = d3.range(length).map((_, i: number) => {
-        const cpu = node.cpu[i];
-        const memory = node.memory[i];
-        if (cpu === EPrometheusDataStatus.noData) return EHealthyStatus.noData;
-        if (cpu === EPrometheusDataStatus.failed) return EHealthyStatus.failed;
-        return cpu >= threshold.cpu || memory >= threshold.memory
-          ? EHealthyStatus.warning
-          : EHealthyStatus.healthy;
-      });
-      return {
-        service: service,
-        type: node.type === 'coord' ? ENodeType.coord : ENodeType.node,
-        label: node.pod,
-        healthyStatus,
-        cpu: node.cpu,
-        memory: node.memory,
-        children: [],
-      };
-    })
-    .sort((a, b) => {
-      const failedCountA = a.healthyStatus.filter(
-        s => s === EHealthyStatus.failed
-      ).length;
-      const failedCountB = b.healthyStatus.filter(
-        s => s === EHealthyStatus.failed
-      ).length;
-      if (failedCountA !== failedCountB) return failedCountB - failedCountA;
-      const warningCountA = a.healthyStatus.filter(
-        s => s === EHealthyStatus.warning
-      ).length;
-      const warningCountB = b.healthyStatus.filter(
-        s => s === EHealthyStatus.warning
-      ).length;
-      return warningCountB - warningCountA;
-    });
-  const overviewHealthyStatus = d3.range(length).map((_, i: number) => {
-    if (nodes.find(node => node.healthyStatus[i] === EHealthyStatus.failed))
-      return EHealthyStatus.failed;
-    if (nodes.find(node => node.healthyStatus[i] === EHealthyStatus.warning))
-      return EHealthyStatus.warning;
-    if (nodes.find(node => node.healthyStatus[i] === EHealthyStatus.healthy))
-      return EHealthyStatus.healthy;
-    return EHealthyStatus.noData;
-  });
-  const overviewNode = {
-    service,
-    type: ENodeType.overview,
-    label: label,
-    healthyStatus: overviewHealthyStatus,
-    children: nodes,
-  };
-  return overviewNode;
-};
-
-export const reconNodeTree = (
-  prometheusData: IPrometheusAllData,
-  threshold: IThreshold
-) => {
-  // third party
-  const metaNode: INodeTreeStructure = {
-    service: ENodeService.meta,
-    type: ENodeType.overview,
-    label: 'Meta',
-    healthyStatus: rateList2healthyStatus(prometheusData.meta),
-    children: [],
-  };
-  const msgstreamNode: INodeTreeStructure = {
-    service: ENodeService.msgstream,
-    type: ENodeType.overview,
-    label: 'MsgStream',
-    healthyStatus: rateList2healthyStatus(prometheusData.msgstream),
-    children: [],
-  };
-  const objstorageNode: INodeTreeStructure = {
-    service: ENodeService.objstorage,
-    type: ENodeType.overview,
-    label: 'ObjStorage',
-    healthyStatus: rateList2healthyStatus(prometheusData.objstorage),
-    children: [],
-  };
-
-  // internal
-  const rootNode = getInternalNode(
-    prometheusData.rootNodes,
-    ENodeService.root,
-    'Root',
-    threshold
-  );
-  const indexNode = getInternalNode(
-    prometheusData.indexNodes,
-    ENodeService.index,
-    'Index',
-    threshold
-  );
-  const queryNode = getInternalNode(
-    prometheusData.queryNodes,
-    ENodeService.query,
-    'Query',
-    threshold
-  );
-  const dataNode = getInternalNode(
-    prometheusData.dataNodes,
-    ENodeService.data,
-    'Data',
-    threshold
-  );
-
-  return {
-    service: ENodeService.milvus,
-    type: ENodeType.overview,
-    label: 'Overview',
-    healthyStatus: [],
-    children: [
-      rootNode,
-      indexNode,
-      queryNode,
-      dataNode,
-      metaNode,
-      msgstreamNode,
-      objstorageNode,
-    ],
-  } as INodeTreeStructure;
-};
-
-export const THIRD_PARTY_SERVICE_HEALTHY_THRESHOLD = 0.95;
-export const getThirdPartyServiceHealthyStatus = (rate: number) => {
-  if (rate === -1) return EHealthyStatus.noData;
-  if (rate > THIRD_PARTY_SERVICE_HEALTHY_THRESHOLD)
-    return EHealthyStatus.healthy;
-  return EHealthyStatus.failed;
-};
-export const rateList2healthyStatus = (rateList: number[]) =>
-  rateList.map((rate: number) => getThirdPartyServiceHealthyStatus(rate));

File diff suppressed because it is too large
+ 0 - 15
client/src/pages/systemHealthy/getIcon.tsx


+ 0 - 198
client/src/pages/systemHealthy/prometheusDataCase.json

@@ -1,198 +0,0 @@
-{
-  "totalVectorsCount": [
-    10100, 10100, 10100, 10100, 10100, 10100, 20200, 20200, 20200, 20200, 20200,
-    20200, 20200, 20200, 20200, 20200, 20200, 20200, 20200, 20200, 20200
-  ],
-  "searchVectorsCount": [
-    51, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-  ],
-  "sqLatency": [
-    0, 1995.776, 0, 0, 509.44000000000005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0
-  ],
-  "meta": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
-  "msgstream": [
-    0.9999987192278481, 1.000001241850357, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1
-  ],
-  "objstorage": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
-  "rootNodes": [
-    {
-      "type": "coord",
-      "pod": "tianmin-milvus-rootcoord-66bb975f6d-jxx6s",
-      "cpu": [
-        0.012685416666666671, 0.012543402777777778, 0.012973958333333353,
-        0.01245138888888887, 0.014343055555555553, 0.01412222222222223,
-        0.014353472222222226, 0.01409826388888888, 0.013789930555555574,
-        0.01384513888888888, 0.013060416666666647, 0.013064236111111112,
-        0.012952777777777808, 0.013211805555555555, 0.013215277777777791,
-        0.013023958333333275, 0.013395138888888911, 0.01334201388888889,
-        0.012905902777777796, 0.013392708333333303, 0.013308333333333356
-      ],
-      "memory": [
-        256786432, 256786432, 256163840, 256147456, 256143360, 256143360,
-        256139264, 234467328, 227409920, 226013184, 226013184, 226013184,
-        224165888, 224165888, 224063488, 224063488, 223633408, 223633408,
-        223633408, 223633408, 223592448
-      ]
-    }
-  ],
-  "queryNodes": [
-    {
-      "type": "coord",
-      "pod": "tianmin-milvus-querycoord-d779dfdd4-njjp5",
-      "cpu": [
-        0.004101041666666663, 0.003982291666666669, 0.004053125,
-        0.004022916666666671, 0.004190624999999994, 0.004188888888888892,
-        0.004214930555555551, 0.004066666666666663, 0.004054513888888888,
-        0.004250694444444447, 0.004367361111111118, 0.004352083333333338,
-        0.004473263888888886, 0.004397569444444448, 0.004364236111111097,
-        0.00445347222222223, 0.004335069444444441, 0.004356944444444445,
-        0.004137847222222225, 0.004144444444444449, 0.004073263888888887
-      ],
-      "memory": [
-        196358144, 199794688, 196919296, 196612096, 196096000, 195846144,
-        200585216, 199811072, 199643136, 200392704, 195579904, 197345280,
-        199356416, 199155712, 200818688, 201146368, 198569984, 199335936,
-        198352896, 201076736, 196235264
-      ]
-    },
-    {
-      "type": "node",
-      "pod": "tianmin-milvus-querynode-68866999f4-t8wtz",
-      "cpu": [
-        0.007112152777777775, 0.006854513888888884, 0.0073892361111111094,
-        0.006928472222222221, 0.008007291666666671, 0.008076736111111116,
-        0.008597916666666663, 0.008150694444444436, 0.007825694444444448,
-        0.00805312500000001, 0.007207638888888887, 0.007342013888888882,
-        0.00695937500000001, 0.007777430555555548, 0.0076819444444444366,
-        0.007389583333333355, 0.007785763888888873, 0.00781284722222223,
-        0.007146874999999998, 0.007248263888888889, 0.007239236111111103
-      ],
-      "memory": [
-        226652160, 224366592, 222994432, 227840000, 228249600, 226996224,
-        274440192, 251985920, 244887552, 256131072, 255901696, 255942656,
-        253829120, 255168512, 255004672, 261414912, 261603328, 261533696,
-        260169728, 260403200, 260911104
-      ]
-    },
-    {
-      "type": "node",
-      "pod": "tianmin-milvus-querynode-68866999f4-ftmql",
-      "cpu": [
-        0.006090277777777781, 0.0058100694444444415, 0.006048611111111121,
-        0.005899652777777773, 0.006252430555555561, 0.0062645833333333355,
-        0.006651388888888887, 0.0061711805555555565, 0.005942708333333336,
-        0.006573958333333331, 0.0064229166666666514, 0.006414583333333357,
-        0.006329513888888888, 0.006902083333333324, 0.006721180555555545,
-        0.00663472222222222, 0.006575694444444448, 0.006790277777777792,
-        0.006086458333333332, 0.006110763888888882, 0.005955902777777769
-      ],
-      "memory": [
-        241979392, 237637632, 237838336, 245350400, 245428224, 244047872,
-        294604800, 293490688, 293425152, 293871616, 293416960, 293269504,
-        293273600, 294273024, 294141952, 294559744, 294559744, 294158336,
-        293429248, 293548032, 293933056
-      ]
-    }
-  ],
-  "indexNodes": [
-    {
-      "type": "coord",
-      "pod": "tianmin-milvus-indexcoord-864d49b47f-t7w4k",
-      "cpu": [
-        0.0009246527777777776, 0.0009281249999999986, 0.0009041666666666674,
-        0.0009208333333333327, 0.0009611111111111114, 0.0009899305555555553,
-        0.0009916666666666687, 0.0009406249999999971, 0.0009343750000000028,
-        0.0009451388888888859, 0.0009118055555555591, 0.0009263888888888872,
-        0.0009142361111111125, 0.0009312499999999978, 0.0009114583333333333,
-        0.0009156250000000002, 0.0009107638888888896, 0.0009145833333333344,
-        0.001040624999999997, 0.0010041666666666693, 0.0010395833333333314
-      ],
-      "memory": [
-        179036160, 179437568, 178999296, 179400704, 179265536, 179404800,
-        179617792, 179322880, 179195904, 179769344, 179130368, 179163136,
-        179671040, 179466240, 179105792, 179281920, 179163136, 179449856,
-        179531776, 179519488, 179597312
-      ]
-    },
-    {
-      "type": "node",
-      "pod": "tianmin-milvus-indexnode-6cdc5f745b-j5ttn",
-      "cpu": [
-        0.0006562499999999992, 0.0006225694444444447, 0.000621180555555555,
-        0.000625, 0.0006256944444444458, 0.0006343749999999994,
-        0.0008763888888888892, 0.0006246527777777781, 0.0006236111111111104,
-        0.0006934027777777787, 0.0007628472222222212, 0.0007402777777777775,
-        0.0007593750000000002, 0.0007652777777777785, 0.0007510416666666665,
-        0.0007649305555555546, 0.0007270833333333352, 0.0007420138888888891,
-        0.0006767361111111115, 0.0006892361111111079, 0.0006680555555555559
-      ],
-      "memory": [
-        1075818496, 1075908608, 1076088832, 1075875840, 1076109312, 1076006912,
-        1401651200, 1401458688, 1401626624, 1401880576, 1401569280, 1401815040,
-        1401688064, 1401462784, 1401683968, 1401614336, 1401364480, 1401761792,
-        1401597952, 1401376768, 1401483264
-      ]
-    }
-  ],
-  "dataNodes": [
-    {
-      "type": "coord",
-      "pod": "tianmin-milvus-datacoord-bb57486b-w29cx",
-      "cpu": [
-        0.0034170138888888917, 0.0033708333333333307, 0.003458333333333338,
-        0.003440277777777775, 0.0035854166666666664, 0.0036586805555555517,
-        0.003824305555555559, 0.0035819444444444393, 0.0035041666666666693,
-        0.003850694444444448, 0.0038048611111111087, 0.0037906250000000023,
-        0.0037343750000000064, 0.004230902777777774, 0.004072916666666657,
-        0.0041246527777777766, 0.00406284722222223, 0.004109374999999997,
-        0.003622916666666672, 0.0035673611111111036, 0.003509027777777776
-      ],
-      "memory": [
-        207458304, 205180928, 205942784, 203255808, 205172736, 204587008,
-        204673024, 207888384, 207896576, 208162816, 206340096, 207425536,
-        207859712, 208457728, 207384576, 206680064, 207507456, 204734464,
-        206397440, 206385152, 204709888
-      ]
-    },
-    {
-      "type": "node",
-      "pod": "tianmin-milvus-datanode-7b759b9697-snqqx",
-      "cpu": [
-        0.005009374999999999, 0.005132291666666664, 0.005037152777777784,
-        0.0051555555555555565, 0.005372916666666659, 0.005334722222222218,
-        0.005506250000000013, 0.005367013888888879, 0.005113541666666666,
-        0.005218402777777777, 0.005051388888888889, 0.00507847222222223,
-        0.0049961805555555515, 0.0053750000000000065, 0.005430902777777789,
-        0.005427083333333308, 0.005367013888888911, 0.005399305555555556,
-        0.005404861111111106, 0.0053663194444444505, 0.005429861111111083
-      ],
-      "memory": [
-        233619456, 232636416, 232497152, 232665088, 232919040, 232497152,
-        260325376, 259923968, 260128768, 266887168, 266567680, 266571776,
-        266575872, 267214848, 267214848, 267214848, 267223040, 267223040,
-        266584064, 266481664, 266694656
-      ]
-    },
-    {
-      "type": "node",
-      "pod": "tianmin-milvus-datanode-7b759b9697-hzd9w",
-      "cpu": [
-        0.005274652777777773, 0.0052645833333333295, 0.005237500000000005,
-        0.005409027777777784, 0.00546284722222222, 0.0052670138888888905,
-        0.005463541666666663, 0.0053881944444444385, 0.005268055555555564,
-        0.00593715277777777, 0.006545833333333332, 0.006434374999999998,
-        0.006494444444444443, 0.006991666666666687, 0.006936111111111087,
-        0.007105902777777797, 0.006855208333333312, 0.006859375000000006,
-        0.005905555555555553, 0.005819791666666687, 0.0057913194444444436
-      ],
-      "memory": [
-        223993856, 223170560, 223100928, 223793152, 223834112, 222822400,
-        257933312, 257347584, 257466368, 257581056, 257581056, 257318912,
-        257318912, 257642496, 257642496, 257642496, 257966080, 258170880,
-        257781760, 257511424, 257298432
-      ]
-    }
-  ]
-}

+ 6 - 15
client/src/pages/user/PrivilegeGroups.tsx

@@ -1,5 +1,5 @@
 import { useContext, useEffect, useState } from 'react';
-import { Theme, Chip } from '@mui/material';
+import { Chip } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { UserService } from '@/http';
 import { rootContext } from '@/context';
@@ -10,23 +10,11 @@ import Wrapper from '@/components/layout/Wrapper';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import { ALL_ROUTER_TYPES } from '@/router/consts';
 import UpdatePrivilegeGroupDialog from './dialogs/UpdatePrivilegeGroupDialog';
-import { makeStyles } from '@mui/styles';
 import { PrivilegeGroup } from '@server/types';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    height: `calc(100vh - 160px)`,
-  },
-  chip: {
-    marginRight: theme.spacing(0.5),
-  },
-}));
-
 const PrivilegeGroups = () => {
   useNavigationHook(ALL_ROUTER_TYPES.USER);
-  // styles
-  const classes = useStyles();
 
   // ui states
   const [groups, setGroups] = useState<PrivilegeGroup[]>([]);
@@ -191,7 +179,7 @@ const PrivilegeGroups = () => {
               <Chip
                 key={index}
                 label={privilege.name}
-                className={classes.chip}
+                sx={{ mr: 0.5 }}
                 color="primary"
                 variant="filled"
               />
@@ -208,7 +196,10 @@ const PrivilegeGroups = () => {
   };
 
   return (
-    <Wrapper className={classes.wrapper} hasPermission={hasPermission}>
+    <Wrapper
+      sx={{ height: 'calc(100vh - 160px)' }}
+      hasPermission={hasPermission}
+    >
       <AttuGrid
         toolbarConfigs={[]}
         colDefinitions={colDefinitions}

+ 24 - 38
client/src/pages/user/Roles.tsx

@@ -9,48 +9,14 @@ import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import UpdateRoleDialog from './dialogs/UpdateRoleDialog';
 import { ALL_ROUTER_TYPES } from '@/router/consts';
 import CustomToolBar from '@/components/grid/ToolBar';
-import { makeStyles } from '@mui/styles';
 import type { ToolBarConfig } from '@/components/grid/Types';
 import Wrapper from '@/components/layout/Wrapper';
 import type { DeleteRoleParams, CreateRoleParams } from './Types';
 import type { RolesWithPrivileges, RBACOptions } from '@server/types';
 import D3PrivilegeTree from './D3PrivilegeTree';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
-    flexDirection: 'column',
-    overflow: 'auto',
-  },
-  list: {
-    border: `1px solid ${theme.palette.divider}`,
-    borderRadius: '8px',
-    backgroundColor: theme.palette.background.light,
-    width: '16%',
-    height: 'calc(100vh - 200px)',
-    overflow: 'auto',
-    color: theme.palette.text.primary,
-    boxShadow: theme.shadows[1],
-    minWidth: '200px',
-  },
-  tree: {
-    overflow: 'auto',
-    width: 'calc(84% - 16px)',
-  },
-  chip: {
-    marginBottom: theme.spacing(0.5),
-  },
-  groupChip: {
-    marginBottom: theme.spacing(0.5),
-    backgroundColor: theme.palette.primary.dark,
-    color: theme.palette.primary.light,
-  },
-}));
-
 const Roles = () => {
   useNavigationHook(ALL_ROUTER_TYPES.USER);
-  // styles
-  const classes = useStyles();
   // context
   const { database } = useContext(dataContext);
   const { setDialog, handleCloseDialog, openSnackBar } =
@@ -247,11 +213,26 @@ const Roles = () => {
   };
 
   return (
-    <Wrapper className={classes.wrapper} hasPermission={hasPermission}>
+    <Wrapper
+      sx={{ display: 'flex', flexDirection: 'column', overflow: 'auto' }}
+      hasPermission={hasPermission}
+    >
       <CustomToolBar toolbarConfigs={toolbarConfigs} />
 
       <Box sx={{ display: 'flex', flexDirection: 'row', gap: '16px' }}>
-        <Box className={classes.list}>
+        <Box
+          sx={theme => ({
+            border: `1px solid ${(theme as any).palette.divider}`,
+            borderRadius: '8px',
+            backgroundColor: (theme as any).palette.background.light,
+            width: '16%',
+            height: 'calc(100vh - 200px)',
+            overflow: 'auto',
+            color: (theme as any).palette.text.primary,
+            boxShadow: (theme as any).shadows[1],
+            minWidth: '200px',
+          })}
+        >
           <List>
             {roles.map(role => (
               <ListItemButton
@@ -267,13 +248,18 @@ const Roles = () => {
             ))}
           </List>
         </Box>
-        <div className={classes.tree}>
+        <Box
+          sx={{
+            overflow: 'auto',
+            width: 'calc(84% - 16px)',
+          }}
+        >
           <D3PrivilegeTree
             privileges={selectedRole[0]?.privileges}
             role={selectedRole[0]?.roleName}
             rbacOptions={rbacOptions}
           />
-        </div>
+        </Box>
       </Box>
     </Wrapper>
   );

+ 4 - 11
client/src/pages/user/User.tsx

@@ -1,5 +1,4 @@
 import { useContext, useEffect, useState } from 'react';
-import { Theme } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { UserService } from '@/http';
 import AttuGrid from '@/components/grid/Grid';
@@ -17,19 +16,10 @@ import CreateUser from './dialogs/CreateUserDialog';
 import UpdateUserRole from './dialogs/UpdateUserRole';
 import UpdateUser from './dialogs/UpdateUserPassDialog';
 import { ALL_ROUTER_TYPES } from '@/router/consts';
-import { makeStyles } from '@mui/styles';
 import type { UserWithRoles } from '@server/types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    height: `calc(100vh - 160px)`,
-  },
-}));
-
 const Users = () => {
   useNavigationHook(ALL_ROUTER_TYPES.USER);
-  // styles
-  const classes = useStyles();
 
   // ui states
   const [users, setUsers] = useState<UserWithRoles[]>([]);
@@ -257,7 +247,10 @@ const Users = () => {
   };
 
   return (
-    <Wrapper className={classes.wrapper} hasPermission={hasPermission}>
+    <Wrapper
+      sx={{ height: 'calc(100vh - 160px)' }}
+      hasPermission={hasPermission}
+    >
       <AttuGrid
         toolbarConfigs={toolbarConfigs}
         colDefinitions={colDefinitions}

+ 16 - 33
client/src/pages/user/UsersAndRoles.tsx

@@ -1,40 +1,15 @@
 import { useLocation } from 'react-router-dom';
 import { useTranslation } from 'react-i18next';
-import { Theme } from '@mui/material';
 import { useNavigationHook } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/consts';
 import RouteTabList from '@/components/customTabList/RouteTabList';
 import User from './User';
 import Roles from './Roles';
 import PrivilegeGroups from './PrivilegeGroups';
-import { makeStyles } from '@mui/styles';
+import Box from '@mui/material/Box';
 import type { ITab } from '@/components/customTabList/Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    flexDirection: 'row',
-    background: theme.palette.background.paper,
-    padding: theme.spacing(0.5, 2),
-    boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.1)',
-    borderRadius: 8,
-    border: `1px solid ${theme.palette.divider}`,
-  },
-  card: {
-    boxShadow: 'none',
-    flexBasis: theme.spacing(28),
-    width: theme.spacing(28),
-    flexGrow: 0,
-    flexShrink: 0,
-  },
-  tab: {
-    flexGrow: 1,
-    flexShrink: 1,
-    overflowX: 'auto',
-  },
-}));
-
 const Users = () => {
-  const classes = useStyles();
   useNavigationHook(ALL_ROUTER_TYPES.USER);
 
   const location = useLocation();
@@ -63,13 +38,21 @@ const Users = () => {
   const activeTabIndex = tabs.findIndex(t => t.path === currentPath);
 
   return (
-    <section className={`page-wrapper ${classes.wrapper}`}>
-      <RouteTabList
-        tabs={tabs}
-        wrapperClass={classes.tab}
-        activeIndex={activeTabIndex}
-      />
-    </section>
+    <Box
+      component="section"
+      className="page-wrapper"
+      sx={theme => ({
+        display: 'flex',
+        flexDirection: 'row',
+        background: theme.palette.background.paper,
+        padding: theme.spacing(0.5, 2),
+        boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.1)',
+        borderRadius: 2,
+        border: `1px solid ${theme.palette.divider}`,
+      })}
+    >
+      <RouteTabList tabs={tabs} activeIndex={activeTabIndex} />
+    </Box>
   );
 };
 

+ 9 - 20
client/src/pages/user/dialogs/CreateUserDialog.tsx

@@ -1,5 +1,4 @@
 import {
-  Theme,
   Checkbox,
   FormGroup,
   FormControlLabel,
@@ -12,7 +11,6 @@ import CustomInput from '@/components/customInput/CustomInput';
 import { authContext } from '@/context';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
-import { makeStyles } from '@mui/styles';
 import type { CreateUserProps, CreateUserParams } from '../Types';
 import type { Option as RoleOption } from '@/components/customSelector/Types';
 import type {
@@ -20,18 +18,6 @@ import type {
   IValidation,
 } from '@/components/customInput/Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  input: {
-    margin: theme.spacing(1, 0, 0.5),
-  },
-  dialogWrapper: {
-    maxWidth: theme.spacing(70),
-    '& .MuiFormControlLabel-root': {
-      width: theme.spacing(20),
-    },
-  },
-}));
-
 const CreateUser: FC<CreateUserProps> = ({
   handleCreate,
   handleClose,
@@ -59,9 +45,6 @@ const CreateUser: FC<CreateUserProps> = ({
 
   const { validation, checkIsValid, disabled } = useFormValidation(checkedForm);
 
-  // styles
-  const classes = useStyles();
-
   // UI handlers
   const handleInputChange = (key: 'username' | 'password', value: string) => {
     setForm(v => ({ ...v, [key]: value }));
@@ -107,13 +90,14 @@ const CreateUser: FC<CreateUserProps> = ({
     },
   ];
 
+  const inputSx = { mt: 1, mb: 0.5 };
   const createConfigs: ITextfieldConfig[] = [
     {
       label: commonTrans('attu.username'),
       key: 'username',
       onChange: (value: string) => handleInputChange('username', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: commonTrans('attu.username'),
       fullWidth: true,
       validations: [
@@ -135,7 +119,7 @@ const CreateUser: FC<CreateUserProps> = ({
       key: 'password',
       onChange: (value: string) => handleInputChange('password', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: commonTrans('attu.password'),
       fullWidth: true,
       type: 'password',
@@ -155,7 +139,12 @@ const CreateUser: FC<CreateUserProps> = ({
       confirmLabel={btnTrans('create')}
       handleConfirm={handleCreateUser}
       confirmDisabled={disabled}
-      dialogClass={classes.dialogWrapper}
+      sx={{
+        maxWidth: theme => (theme as any).spacing(70),
+        '& .MuiFormControlLabel-root': {
+          width: (theme: any) => theme.spacing(20),
+        },
+      }}
     >
       <>
         {createConfigs.map(v => (

+ 3 - 25
client/src/pages/user/dialogs/PrivilegeGroupOptions.tsx

@@ -1,37 +1,15 @@
-import {
-  Theme,
-  Typography,
-  Checkbox,
-  FormGroup,
-  FormControlLabel,
-} from '@mui/material';
+import { Checkbox, FormGroup, FormControlLabel } from '@mui/material';
 import { FC } from 'react';
 import { PrivilegeGrpOptionsProps } from '../Types';
-import { makeStyles } from '@mui/styles';
-
-const useStyles = makeStyles((theme: Theme) => ({
-  checkBox: {
-    width: theme.spacing(24),
-  },
-  formGrp: {
-    marginBottom: theme.spacing(2),
-  },
-  subTitle: {
-    marginBottom: theme.spacing(0.5),
-  },
-}));
 
 const PrivilegeGroupOptions: FC<PrivilegeGrpOptionsProps> = ({
   options,
   selection,
   onChange,
-  group_name,
 }) => {
-  const classes = useStyles();
-
   return (
     <>
-      <FormGroup row className={classes.formGrp}>
+      <FormGroup row sx={{ mb: 2 }}>
         {options.map((r: string) => (
           <FormControlLabel
             control={
@@ -56,7 +34,7 @@ const PrivilegeGroupOptions: FC<PrivilegeGrpOptionsProps> = ({
             label={r}
             value={r}
             checked={selection.filter(s => s === r).length > 0 ? true : false}
-            className={classes.checkBox}
+            sx={{ width: theme => (theme as any).spacing(24) }}
           />
         ))}
       </FormGroup>

+ 5 - 22
client/src/pages/user/dialogs/UpdateRoleDialog.tsx

@@ -1,4 +1,3 @@
-import { Theme } from '@mui/material';
 import { FC, useMemo, useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
@@ -6,25 +5,11 @@ import CustomInput from '@/components/customInput/CustomInput';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
 import { UserService, DatabaseService } from '@/http';
-import { makeStyles } from '@mui/styles';
 import DBCollectionSelector from './DBCollectionSelector';
 import type { ITextfieldConfig } from '@/components/customInput/Types';
 import type { CreateRoleProps, CreateRoleParams, DBOption } from '../Types';
 import type { DBCollectionsPrivileges, RBACOptions } from '@server/types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  input: {
-    margin: theme.spacing(1, 0, 0.5),
-  },
-  dialogWrapper: {
-    width: '66vw',
-    maxWidth: '66vw',
-  },
-  subTitle: {
-    marginBottom: theme.spacing(0.5),
-  },
-}));
-
 const DEFAULT_DB_Privileges: DBCollectionsPrivileges = {
   '*': {
     collections: {
@@ -132,17 +117,15 @@ const UpdateRoleDialog: FC<CreateRoleProps> = props => {
     }
   };
 
-  // styles
-  const classes = useStyles();
-
   // Input configurations
+  const inputSx = { mt: 1, mb: 0.5 };
   const createConfigs: ITextfieldConfig[] = [
     {
       label: userTrans('role'),
       key: 'roleName',
       onChange: (value: string) => handleInputChange('roleName', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: userTrans('role'),
       fullWidth: true,
       validations: [
@@ -164,14 +147,14 @@ const UpdateRoleDialog: FC<CreateRoleProps> = props => {
         isEditing
           ? 'updateRolePrivilegeTitle'
           : sameAs
-          ? 'dupicateRoleTitle'
-          : 'createRoleTitle'
+            ? 'dupicateRoleTitle'
+            : 'createRoleTitle'
       )}
       handleClose={handleClose}
       confirmLabel={btnTrans(isEditing ? 'update' : 'create')}
       handleConfirm={handleCreateRole}
       confirmDisabled={disabled}
-      dialogClass={classes.dialogWrapper}
+      sx={{ width: '66vw', maxWidth: '66vw' }}
     >
       <>
         {createConfigs.map(v => (

+ 5 - 18
client/src/pages/user/dialogs/UpdateUserPassDialog.tsx

@@ -1,32 +1,18 @@
-import { Theme } from '@mui/material';
 import { FC, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import CustomInput from '@/components/customInput/CustomInput';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
-import { makeStyles } from '@mui/styles';
 import { UserService } from '@/http';
 import type { UpdateUserParams, UpdateUserProps } from '../Types';
 import type { ITextfieldConfig } from '@/components/customInput/Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  root: {
-    maxWidth: 480,
-  },
-  input: {
-    margin: theme.spacing(1, 0, 0.5),
-  },
-}));
-
 const UpdateUser: FC<UpdateUserProps> = ({
   handleClose,
   onUpdate,
   username,
 }) => {
-  // styles
-  const classes = useStyles();
-
   // i18n
   const { t: userTrans } = useTranslation('user');
   const { t: btnTrans } = useTranslation('btn');
@@ -65,13 +51,14 @@ const UpdateUser: FC<UpdateUserProps> = ({
   };
 
   // UI configs
+  const inputSx = { mt: 1, mb: 0.5 };
   const createConfigs: ITextfieldConfig[] = [
     {
       label: userTrans('oldPassword'),
       key: 'oldPassword',
       onChange: (value: string) => handleInputChange('oldPassword', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: userTrans('oldPassword'),
       fullWidth: true,
       validations: [
@@ -90,7 +77,7 @@ const UpdateUser: FC<UpdateUserProps> = ({
       key: 'newPassword',
       onChange: (value: string) => handleInputChange('newPassword', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: userTrans('newPassword'),
       fullWidth: true,
       validations: [
@@ -109,7 +96,7 @@ const UpdateUser: FC<UpdateUserProps> = ({
       key: 'confirmPassword',
       onChange: (value: string) => handleInputChange('confirmPassword', value),
       variant: 'filled',
-      className: classes.input,
+      sx: inputSx,
       placeholder: userTrans('confirmPassword'),
       fullWidth: true,
       validations: [
@@ -134,7 +121,7 @@ const UpdateUser: FC<UpdateUserProps> = ({
 
   return (
     <DialogTemplate
-      dialogClass={classes.root}
+      sx={{ maxWidth: 480 }}
       title={userTrans('updateUserPassTitle', { username })}
       handleClose={handleClose}
       confirmLabel={btnTrans('update')}

+ 7 - 17
client/src/pages/user/dialogs/UpdateUserRole.tsx

@@ -1,23 +1,10 @@
-import { Theme, Checkbox, FormGroup, FormControlLabel } from '@mui/material';
+import { Checkbox, FormGroup, FormControlLabel } from '@mui/material';
 import { FC, useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import { UserService } from '@/http';
-import { makeStyles } from '@mui/styles';
 import type { UpdateUserRoleProps, UpdateUserRoleParams } from '../Types';
 
-const useStyles = makeStyles((theme: Theme) => ({
-  input: {
-    margin: theme.spacing(1, 0, 0.5),
-  },
-  dialogWrapper: {
-    maxWidth: theme.spacing(70),
-    '& .MuiFormControlLabel-root': {
-      width: theme.spacing(20),
-    },
-  },
-}));
-
 const UpdateUserRole: FC<UpdateUserRoleProps> = ({
   onUpdate,
   handleClose,
@@ -33,8 +20,6 @@ const UpdateUserRole: FC<UpdateUserRoleProps> = ({
     roles: roles,
   });
 
-  const classes = useStyles();
-
   const handleUpdate = async () => {
     await UserService.updateUserRole(form);
     onUpdate(form);
@@ -57,7 +42,12 @@ const UpdateUserRole: FC<UpdateUserRoleProps> = ({
       confirmLabel={btnTrans('update')}
       handleConfirm={handleUpdate}
       confirmDisabled={false}
-      dialogClass={classes.dialogWrapper}
+      sx={{
+        maxWidth: theme => (theme as any).spacing(70),
+        '& .MuiFormControlLabel-root': {
+          width: (theme: any) => theme.spacing(20),
+        },
+      }}
     >
       <>
         <FormGroup row>

+ 0 - 4
client/src/router/Router.tsx

@@ -5,9 +5,7 @@ import Databases from '@/pages/databases/Databases';
 import Connect from '@/pages/connect/Connect';
 import Users from '@/pages/user/UsersAndRoles';
 import Index from '@/pages/index';
-import Search from '@/pages/search/VectorSearch';
 import System from '@/pages/system/SystemView';
-import SystemHealthy from '@/pages/systemHealthy/SystemHealthyView';
 import Play from '@/pages/play/Play';
 
 const RouterComponent = () => {
@@ -31,8 +29,6 @@ const RouterComponent = () => {
             element={<Databases />}
           />
 
-          <Route path="search" element={<Search />} />
-          <Route path="system_healthy" element={<SystemHealthy />} />
           {enableManageUsers && (
             <>
               <Route path="users" element={<Users />} />

+ 1 - 102
client/src/utils/search.ts

@@ -1,6 +1,6 @@
 import { VectorStrToObject } from '@/utils';
 import type { FieldOption } from '../types/SearchTypes';
-import type { FieldObject, CollectionFullObject } from '@server/types';
+import type { FieldObject } from '@server/types';
 import type { SearchParams } from '@/pages/databases/types';
 
 export const getVectorFieldOptions = (fields: FieldObject[]): FieldOption[] => {
@@ -72,104 +72,3 @@ export const buildSearchParams = (searchParams: SearchParams) => {
 
   return params;
 };
-
-export const buildSearchCode = (
-  searchParams: SearchParams,
-  collection: CollectionFullObject
-) => {
-  const params = buildSearchParams(searchParams);
-  const isMultiple = params.data.length > 1;
-
-  return {
-    python: isMultiple
-      ? buildMultipleSearchPythonCode(params, collection)
-      : buildSingleSearchPythonCode(params, collection),
-    ['node.js']: buildNodeSearchCode(params, collection),
-  };
-};
-
-export const buildSingleSearchPythonCode = (
-  params: any,
-  collection: CollectionFullObject
-) => {
-  const code = `# python search code
-res = client.search(
-  collection_name="${collection.collection_name}", # Collection name
-  data=query_vector, # Replace with your query vector
-  search_params={
-    "metric_type": "${collection.schema.vectorFields[0].index.metricType}",
-    "params": ${JSON.stringify(params.data[0].params)}, # Search parameters
-  }, # Search parameters
-  limit=${params.limit}, # Max. number of search results to return
-  output_fields=${JSON.stringify(
-    params.output_fields.filter((f: string) => f !== '$meta')
-  )}, # Fields to return in the search results
-  consistency_level="${params.consistency_level}"
-)
-`;
-
-  return code;
-};
-
-export const buildMultipleSearchPythonCode = (
-  params: any,
-  collection: CollectionFullObject
-) => {
-  const code = `from pymilvus import AnnSearchRequest, RRFRanker, WeightedRanker`;
-
-  // build request
-  const data = params.data.map((d: any, i: number) => {
-    const req = `search_param_${i} = {
-    "data": query_vector, # Query vector
-    "anns_field": "${d.anns_field}", # Vector field name
-    "param": {
-        "metric_type": "${
-          collection.schema.vectorFields[i].index.metricType
-        }", # This parameter value must be identical to the one used in the collection schema
-        "params": ${JSON.stringify(d.params)}, # Search parameters
-    }
-  }`;
-    return `${req}\nrequest_${i} = AnnSearchRequest(**search_param_${i})`;
-  });
-
-  let reranker = '';
-  // reranks
-  if (params.rerank) {
-    if (params.rerank.strategy === 'rrf') {
-      reranker = `# Rerank by RRF strategy\nrerank = RRFRanker(k=${params.rerank.params.k})`;
-    }
-    if (params.rerank.strategy === 'weighted') {
-      reranker = `# Rerank by Weighted strategy\nrerank = WeightedRanker(${JSON.stringify(
-        params.rerank.params.weights
-      )})`;
-    }
-  }
-
-  return `${code}\n\n${data.join('\n\n')}\n\nreqs = [${Array.from(
-    { length: params.data.length },
-    (_, i) => `request_${i}`
-  ).join(
-    ', '
-  )}]\n${reranker}\n\n# Perform search\nres = client.search(reqs, rerank, limit=${
-    params.limit
-  }, output_fields=${JSON.stringify(
-    params.output_fields
-  )}, consistency_level="${params.consistency_level}")`;
-};
-
-export const buildNodeSearchCode = (
-  params: any,
-  collection: CollectionFullObject
-) => {
-  // remove data.data
-  params.data.forEach((d: any) => {
-    d.data = `YOUR_QUERY_VECTOR`;
-  });
-
-  return `// nodejs search code
-const params = ${JSON.stringify(
-    { collection_name: collection.collection_name, ...params },
-    null,
-    2
-  )};\nawait milvusClient.search(params)`;
-};

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

@@ -736,7 +736,6 @@ export class CollectionsService {
         [];
 
       index.indexParameterPairs = [
-        ...metricTypePair,
         ...indexParams,
         ...params,
       ];

Some files were not shown because too many files changed in this diff