2
0
Эх сурвалжийг харах

Refactor overview page to schema page (#700)

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 6 сар өмнө
parent
commit
44859fe49b

+ 1 - 1
client/src/context/Data.tsx

@@ -18,7 +18,7 @@ import {
 import {
   IndexCreateParam,
   IndexManageParam,
-} from '@/pages/databases/collections/overview/Types';
+} from '@/pages/databases/collections/schema/Types';
 import { DataContextType } from './Types';
 import {
   CollectionObject,

+ 1 - 1
client/src/context/Types.ts

@@ -9,7 +9,7 @@ import { NavInfo } from '@/router/Types';
 import {
   IndexCreateParam,
   IndexManageParam,
-} from '@/pages/databases/collections/overview/Types';
+} from '@/pages/databases/collections/schema/Types';
 import { AuthObject } from '@server/types';
 
 export type RootContextType = {

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

@@ -15,7 +15,7 @@ import { ManageRequestMethods } from '../types/Common';
 import {
   IndexCreateParam,
   IndexManageParam,
-} from '@/pages/databases/collections/overview/Types';
+} from '@/pages/databases/collections/schema/Types';
 
 export class CollectionService extends BaseModel {
   static getCollections(data?: {

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

@@ -65,6 +65,7 @@ const collectionTrans = {
   loadContent:
     'Milvus中的所有搜索和查询操作都在内存中执行,只有加载的Collection可以被搜索。',
   loadConfirmLabel: '加载',
+  replica: '副本',
   replicaNum: '副本数量',
   replicaDes: `有了内存副本,Milvus可以在多个查询节点上加载相同的段。副本数量不能超过查询节点数量。`,
   enableRepica: `启用内存副本`,

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

@@ -67,6 +67,7 @@ const collectionTrans = {
   loadContent:
     'All search and query operations within Milvus are executed in memory, only loaded collection can be searched.',
   loadConfirmLabel: 'Load',
+  replica: 'Replica',
   replicaNum: 'Replica number',
   replicaDes: `With in-memory replicas, Milvus can load the same segment on multiple query nodes. The replica number can not exceed query node count.`,
   enableRepica: `Enable in-memory replica`,
@@ -134,7 +135,7 @@ const collectionTrans = {
   clickToLoad: 'Click to load the collection.',
   clickToRelease: 'Click to release the collection.',
   clickToSearch: 'Click to execute vector search.',
-  clickToCreateVectorIndex: 'Click to create an vector index.',
+  clickToCreateVectorIndex: 'Click to create the vector index.',
   collectionIsLoading: 'The collection is loading...',
 };
 

+ 4 - 4
client/src/pages/databases/Databases.tsx

@@ -8,7 +8,7 @@ import RouteTabList from '@/components/customTabList/RouteTabList';
 import DatabaseTree from '@/pages/databases/tree';
 import { ITab } from '@/components/customTabList/Types';
 import Partitions from './collections/partitions/Partitions';
-import Overview from './collections/overview/Overview';
+import Schema from './collections/schema/Schema';
 import Data from './collections/data/CollectionData';
 import Segments from './collections/segments/Segments';
 import Properties from './collections/properties/Properties';
@@ -391,9 +391,9 @@ const CollectionTabs = (props: {
   // collection tabs
   const collectionTabs: ITab[] = [
     {
-      label: collectionTrans('overviewTab'),
-      component: <Overview />,
-      path: `overview`,
+      label: collectionTrans('schemaTab'),
+      component: <Schema />,
+      path: `schema`,
     },
     {
       label: collectionTrans('searchTab'),

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

@@ -12,6 +12,7 @@ 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',
@@ -19,15 +20,13 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
   chip: {
     border: 'none',
-    background: `rgba(0, 0, 0, 0.04)`,
     marginRight: theme.spacing(0.5),
     paddingLeft: theme.spacing(0.5),
   },
   circle: {
-    width: '8px',
-    height: '8px',
+    width: 8,
+    height: 8,
     borderRadius: '50%',
-    backgroundColor: theme.palette.primary.main,
   },
   loaded: {
     border: `1px solid ${theme.palette.primary.main}`,
@@ -41,68 +40,53 @@ const useStyles = makeStyles((theme: Theme) => ({
     border: `1px solid ${theme.palette.text.disabled}`,
     backgroundColor: '#fff',
   },
-
   loading: {
-    marginRight: '10px',
-  },
-  icon: {
-    marginTop: theme.spacing(0.5),
-  },
-  flash: {
-    animation: '$bgColorChange 1.5s infinite',
+    marginRight: theme.spacing(1.25),
   },
   extraBtn: {
     height: 24,
   },
-
-  '@keyframes bgColorChange': {
-    '0%': {
-      backgroundColor: (props: any) => props.color,
-    },
-    '50%': {
-      backgroundColor: 'transparent',
-    },
-    '100%': {
-      backgroundColor: (props: any) => props.color,
-    },
-  },
 }));
 
 const StatusAction: FC<StatusActionType> = props => {
+  const {
+    status,
+    percentage = 0,
+    collection,
+    action = () => {},
+    showExtraAction,
+    createIndexElement,
+  } = props;
+
+  // Theme and styles
   const theme = useTheme();
-  const { setDialog } = useContext(rootContext);
+  const classes = useStyles();
 
-  const classes = useStyles({ color: theme.palette.primary.main });
-  const ReleaseIcon = Icons.release;
-  const LoadIcon = Icons.load;
+  // Context
+  const { setDialog } = useContext(rootContext);
 
-  const { status, percentage = 0, collection, action = () => {} } = props;
+  // Translations
   const { t: commonTrans } = useTranslation();
   const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
 
-  const statusTrans = commonTrans('status');
-  const {
-    label,
-    tooltip,
-    icon = <span></span>,
-    deleteIcon = <ReleaseIcon />,
-  } = useMemo(() => {
+  // Determine status-related labels and icons
+  const { label, tooltip, icon, deleteIcon } = useMemo(() => {
+    const statusTrans = commonTrans('status');
     switch (status) {
       case LOADING_STATE.UNLOADED:
         return {
           label: statusTrans.unloaded,
-          icon: <div className={`${classes.circle} ${classes.unloaded}`}></div>,
           tooltip: collectionTrans('clickToLoad'),
-          deleteIcon: <LoadIcon />,
+          icon: <div className={`${classes.circle} ${classes.unloaded}`}></div>,
+          deleteIcon: <Icons.load />,
         };
-
       case LOADING_STATE.LOADED:
         return {
           label: statusTrans.loaded,
-          icon: <div className={`${classes.circle} ${classes.loaded}`}></div>,
           tooltip: collectionTrans('clickToRelease'),
-          deleteIcon: <ReleaseIcon />,
+          icon: <div className={`${classes.circle} ${classes.loaded}`}></div>,
+          deleteIcon: <Icons.release />,
         };
       case LOADING_STATE.LOADING:
         return {
@@ -114,58 +98,57 @@ const StatusAction: FC<StatusActionType> = props => {
               className={classes.loading}
             />
           ),
+          deleteIcon: null, // No delete icon during loading
         };
-
       default:
         return {
           label: status,
-          icon: <span></span>,
           tooltip: '',
-          deleteIcon: <ReleaseIcon />,
+          icon: <span></span>,
+          deleteIcon: <Icons.release />,
         };
     }
-  }, [status, statusTrans, percentage]);
+  }, [status, percentage, classes, commonTrans, collectionTrans]);
 
+  // Handle missing vector index
   const noIndex = collection.schema && !collection.schema.hasVectorIndex;
-  const noVectorIndexTooltip = collectionTrans('noVectorIndexTooltip');
   const noIndexIcon = (
     <div className={`${classes.circle} ${classes.noIndex}`}></div>
   );
+  const noIndexTooltip = collectionTrans('noVectorIndexTooltip');
+
+  // Handle chip click
+  const handleChipClick = (e: MouseEvent<HTMLDivElement>) => {
+    e.stopPropagation();
+    setDialog({
+      open: true,
+      type: 'custom',
+      params: {
+        component:
+          status === LOADING_STATE.UNLOADED ? (
+            <LoadCollectionDialog collection={collection} />
+          ) : (
+            <ReleaseCollectionDialog collection={collection} />
+          ),
+      },
+    });
+  };
 
   return (
     <div className={classes.root}>
-      <CustomToolTip
-        title={noIndex ? noVectorIndexTooltip : tooltip}
-        placement={'top'}
-      >
+      <CustomToolTip title={noIndex ? noIndexTooltip : tooltip} placement="top">
         <Chip
           className={classes.chip}
-          variant="outlined"
           label={<Typography>{label}</Typography>}
-          onDelete={() => action()}
-          onClick={(e: MouseEvent<HTMLDivElement>) => {
-            e.stopPropagation();
-            setDialog({
-              open: true,
-              type: 'custom',
-              params: {
-                component:
-                  collection.status === LOADING_STATE.UNLOADED ? (
-                    <LoadCollectionDialog collection={collection} />
-                  ) : (
-                    <ReleaseCollectionDialog collection={collection} />
-                  ),
-              },
-            });
-          }}
+          onClick={handleChipClick}
           disabled={noIndex}
-          deleteIcon={deleteIcon}
+          deleteIcon={<Icons.release />}
           size="small"
           icon={noIndex ? noIndexIcon : icon}
         />
       </CustomToolTip>
 
-      {props.showExtraAction && collection.schema && (
+      {showExtraAction && collection.schema && (
         <>
           {status === LOADING_STATE.LOADED && (
             <CustomButton
@@ -173,19 +156,17 @@ const StatusAction: FC<StatusActionType> = props => {
               className={classes.extraBtn}
               tooltip={collectionTrans('clickToSearch')}
               onClick={() => {
-                const currentHash = window.location.hash;
-                const newHash = currentHash.replace('overview', 'search');
-
+                const newHash = window.location.hash.replace(
+                  'schema',
+                  'search'
+                );
                 window.location.hash = newHash;
               }}
             >
               {btnTrans('vectorSearch')}
             </CustomButton>
           )}
-
-          {collection.schema &&
-            !collection.schema.hasVectorIndex &&
-            props.createIndexElement}
+          {!collection.schema.hasVectorIndex && createIndexElement}
         </>
       )}
     </div>

+ 0 - 0
client/src/pages/databases/collections/overview/CreateForm.tsx → client/src/pages/databases/collections/schema/CreateForm.tsx


+ 0 - 0
client/src/pages/databases/collections/overview/CreateIndexDialog.tsx → client/src/pages/databases/collections/schema/CreateIndexDialog.tsx


+ 0 - 1
client/src/pages/databases/collections/overview/IndexTypeElement.tsx → client/src/pages/databases/collections/schema/IndexTypeElement.tsx

@@ -45,7 +45,6 @@ const useStyles = makeStyles((theme: Theme) => ({
     },
   },
   chip: {
-    background: `rgba(0, 0, 0, 0.04)`,
     padding: theme.spacing(0.5),
 
     '& .icon': {

+ 90 - 192
client/src/pages/databases/collections/overview/Overview.tsx → client/src/pages/databases/collections/schema/Schema.tsx

@@ -1,115 +1,18 @@
-import { Theme, Typography, Chip, Tooltip } from '@mui/material';
+import { Typography, Chip, Tooltip } from '@mui/material';
 import { useContext } from 'react';
 import { useParams } from 'react-router-dom';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType } from '@/components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import Icons from '@/components/icons/Icons';
-import { formatFieldType } from '@/utils';
+import { formatFieldType, formatNumber } from '@/utils';
 import { dataContext } from '@/context';
 import IndexTypeElement from './IndexTypeElement';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import StatusAction from '@/pages/databases/collections/StatusAction';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { FieldObject } from '@server/types';
-import { makeStyles } from '@mui/styles';
-
-const useStyles = makeStyles((theme: Theme) => ({
-  wrapper: {
-    display: 'flex',
-    flexDirection: 'column',
-    flexGrow: 1,
-    height: `100%`,
-    overflow: 'auto',
-    '& h5': {
-      color: theme.palette.text.secondary,
-      marginBottom: theme.spacing(0.5),
-      fontSize: 13,
-      fontWeight: 400,
-    },
-  },
-  infoWrapper: {
-    marginBottom: theme.spacing(2),
-    paddingTop: theme.spacing(0.5),
-  },
-  horizonalBlock: {
-    display: 'flex',
-    flexWrap: 'wrap',
-    '& >div': {
-      marginRight: theme.spacing(2),
-    },
-  },
-  block: {
-    '& *': {
-      fontSize: '14px',
-      lineHeight: 1.5,
-    },
-    color: theme.palette.text.primary,
-    paddingBottom: theme.spacing(2),
-  },
-  icon: {
-    fontSize: '20px',
-    marginLeft: theme.spacing(0.5),
-  },
-  extraBtn: {
-    height: 24,
-  },
-  questionIcon: {
-    position: 'relative',
-    top: '2px',
-    right: '-2px',
-  },
-  primaryKeyChip: {
-    fontSize: '8px',
-    position: 'relative',
-    top: '3px',
-    color: 'grey',
-  },
-  chip: {
-    marginLeft: theme.spacing(0.5),
-    marginRight: theme.spacing(0.5),
-    fontSize: '12px',
-    background: 'rgba(0, 0, 0, 0.04)',
-    border: 'none',
-  },
-  featureChip: {
-    marginLeft: 0,
-    border: 'none',
-  },
-  nameWrapper: {
-    display: 'flex',
-    alignItems: 'center',
-
-    '& .key': {
-      width: '16px',
-      height: '16px',
-      marginLeft: theme.spacing(0.5),
-    },
-  },
-
-  paramWrapper: {
-    // set min width to prevent other table cell stretching
-    minWidth: 180,
-
-    '& .param': {
-      marginRight: theme.spacing(2),
-
-      '& .key': {
-        color: theme.palette.text.secondary,
-        display: 'inline-block',
-        marginRight: theme.spacing(0.5),
-      },
-
-      '& .value': {
-        color: theme.palette.text.primary,
-      },
-    },
-  },
-
-  gridWrapper: {
-    paddingBottom: theme.spacing(2),
-  },
-}));
+import { useStyles } from './Styles';
 
 const Overview = () => {
   const { fetchCollection, collections, loading } = useContext(dataContext);
@@ -157,16 +60,10 @@ const Overview = () => {
                 className={classes.chip}
                 size="small"
                 label="Partition key"
-                variant="outlined"
               />
             ) : null}
             {f.autoID ? (
-              <Chip
-                className={classes.chip}
-                size="small"
-                label="auto id"
-                variant="outlined"
-              />
+              <Chip className={classes.chip} size="small" label="auto id" />
             ) : null}
           </div>
         );
@@ -279,101 +176,102 @@ const Overview = () => {
     <section className={classes.wrapper}>
       {collection && (
         <section className={classes.infoWrapper}>
-          <div className={classes.block}>
-            <Typography variant="h5">
-              {collectionTrans('description')}
-            </Typography>
-            <Typography variant="h6">
-              {collection?.description || '--'}
-            </Typography>
-          </div>
+          <div className={classes.cardWrapper}>
+            <div className={classes.card}>
+              <Typography variant="h5">{collectionTrans('name')}</Typography>
+              <Typography variant="h6">{collection.collection_name}</Typography>
+              <Typography variant="h5">
+                {collectionTrans('description')}
+              </Typography>
+              <Typography variant="h6">
+                {collection?.description || '--'}
+              </Typography>
+              <Typography variant="h5">
+                {collectionTrans('createdTime')}
+              </Typography>
+              <Typography variant="h6">
+                {new Date(collection.createdTime).toLocaleString()}
+              </Typography>
+            </div>
 
-          <section className={classes.horizonalBlock}>
-            <div className={classes.block}>
+            <div className={classes.card}>
               <Typography variant="h5">{collectionTrans('status')}</Typography>
+              <Typography variant="h6">
+                <StatusAction
+                  status={collection.status}
+                  percentage={collection.loadedPercentage}
+                  collection={collection}
+                  showExtraAction={true}
+                  createIndexElement={CreateIndexElement}
+                />
+              </Typography>
+              <Typography variant="h5">{collectionTrans('replica')}</Typography>
+              <Typography variant="h6">
+                {collection.replicas?.length}
+              </Typography>
 
-              <StatusAction
-                status={collection.status}
-                percentage={collection.loadedPercentage}
-                collection={collection}
-                showExtraAction={true}
-                createIndexElement={CreateIndexElement}
-              />
+              <Typography variant="h5">
+                {collection.loaded ? (
+                  collectionTrans('count')
+                ) : (
+                  <>
+                    {collectionTrans('rowCount')}
+                    <CustomToolTip title={collectionTrans('entityCountInfo')}>
+                      <Icons.question
+                        classes={{ root: classes.questionIcon }}
+                      />
+                    </CustomToolTip>
+                  </>
+                )}
+              </Typography>
+              <Typography variant="h6">
+                {formatNumber(Number(collection?.rowCount || '0'))}
+              </Typography>
             </div>
-          </section>
-
-          <div className={classes.block}>
-            <Typography variant="h5">
-              {collection.loaded ? (
-                collectionTrans('count')
-              ) : (
-                <>
-                  {collectionTrans('rowCount')}
-                  <CustomToolTip title={collectionTrans('entityCountInfo')}>
-                    <Icons.question classes={{ root: classes.questionIcon }} />
-                  </CustomToolTip>
-                </>
-              )}
-            </Typography>
-            <Typography variant="h6">{collection?.rowCount || '0'}</Typography>
-          </div>
+            <div className={classes.card}>
+              <Typography variant="h5">
+                {collectionTrans('features')}
+              </Typography>
+              <Typography variant="h6">
+                <Tooltip
+                  title={
+                    consistencyTooltipsMap[collection.consistency_level!] || ''
+                  }
+                  placement="top"
+                  arrow
+                >
+                  <Chip
+                    className={`${classes.chip} ${classes.featureChip}`}
+                    label={`${collectionTrans('consistency')}: ${
+                      collection.consistency_level
+                    }`}
+                    size="small"
+                  />
+                </Tooltip>
 
-          <div className={classes.block}>
-            <Typography variant="h5">
-              {collectionTrans('createdTime')}
-            </Typography>
-            <Typography variant="h6">
-              {new Date(collection.createdTime).toLocaleString()}
-            </Typography>
-          </div>
-
-          <div className={classes.block}>
-            <Typography variant="h5">
-              {collectionTrans('consistency')}
-              <CustomToolTip title={collectionTrans('consistencyLevelInfo')}>
-                <Icons.question classes={{ root: classes.questionIcon }} />
-              </CustomToolTip>
-            </Typography>
-            <Typography variant="h6">
-              <Tooltip
-                title={
-                  consistencyTooltipsMap[collection.consistency_level!] || ''
-                }
-                placement="top"
-                arrow
-              >
-                <Chip
-                  className={`${classes.chip} ${classes.featureChip}`}
-                  label={collection.consistency_level}
-                  variant="outlined"
-                  size="small"
-                />
-              </Tooltip>
-            </Typography>
+                {collection &&
+                collection.schema &&
+                collection.schema.enable_dynamic_field ? (
+                  <Tooltip
+                    title={collectionTrans('dynamicSchemaTooltip')}
+                    placement="top"
+                    arrow
+                  >
+                    <Chip
+                      className={`${classes.chip}`}
+                      label={collectionTrans('dynamicSchema')}
+                      size="small"
+                    />
+                  </Tooltip>
+                ) : null}
+              </Typography>
+            </div>
           </div>
         </section>
       )}
 
       <section className={classes.gridWrapper}>
-        <Typography variant="h5">
-          {collectionTrans('schema')}
-          {collection &&
-          collection.schema &&
-          collection.schema.enable_dynamic_field ? (
-            <Tooltip
-              title={collectionTrans('dynamicSchemaTooltip')}
-              placement="top"
-              arrow
-            >
-              <Chip
-                className={`${classes.chip}`}
-                label={collectionTrans('dynamicSchema')}
-                size="small"
-                icon={<Icons.check />}
-              />
-            </Tooltip>
-          ) : null}
-        </Typography>
+        {/* <Typography variant="h5">{collectionTrans('schema')}</Typography> */}
 
         <AttuGrid
           toolbarConfigs={[]}

+ 104 - 0
client/src/pages/databases/collections/schema/Styles.tsx

@@ -0,0 +1,104 @@
+import { makeStyles } from '@mui/styles';
+import { Theme } from '@mui/material';
+
+export const useStyles = makeStyles((theme: Theme) => ({
+  wrapper: {
+    display: 'flex',
+    flexDirection: 'column',
+    flexGrow: 1,
+    height: `100%`,
+    overflow: 'auto',
+    '& h5': {
+      color: theme.palette.text.secondary,
+      marginBottom: theme.spacing(0.5),
+      fontSize: 13,
+      fontWeight: 400,
+    },
+    '& h6': {
+      color: theme.palette.text.primary,
+      marginBottom: theme.spacing(1),
+    },
+  },
+  infoWrapper: {
+    marginBottom: theme.spacing(1.5),
+    paddingTop: theme.spacing(0.5),
+  },
+  cardWrapper: {
+    display: 'flex',
+    flexWrap: 'wrap',
+    '& > div:not(:last-child)': { marginRight: theme.spacing(1.5) },
+    height: 200,
+  },
+  card: {
+    backgroundColor: theme.palette.background.default,
+    borderRadius: 8,
+    padding: theme.spacing(1.5, 2),
+    boxSizing: 'border-box',
+    flexGrow: 1,
+    flexShrink: 1,
+    flexBasis: 'calc(33.333% - 12px)',
+    height: '100%',
+    minWidth: 200,
+  },
+  icon: {
+    fontSize: '20px',
+    marginLeft: theme.spacing(0.5),
+  },
+  extraBtn: {
+    height: 24,
+  },
+  questionIcon: {
+    width: 14,
+    position: 'relative',
+    top: '6px',
+    right: '-2px',
+  },
+  primaryKeyChip: {
+    fontSize: '8px',
+    position: 'relative',
+    top: '3px',
+    color: 'grey',
+  },
+  chip: {
+    fontSize: '12px',
+    color: theme.palette.text.primary,
+    border: 'none',
+  },
+  featureChip: {
+    marginRight: 4,
+    border: 'none',
+  },
+  nameWrapper: {
+    display: 'flex',
+    alignItems: 'center',
+
+    '& .key': {
+      width: '16px',
+      height: '16px',
+      marginLeft: theme.spacing(0.5),
+    },
+  },
+
+  paramWrapper: {
+    // set min width to prevent other table cell stretching
+    minWidth: 180,
+
+    '& .param': {
+      marginRight: theme.spacing(2),
+
+      '& .key': {
+        color: theme.palette.text.secondary,
+        display: 'inline-block',
+        marginRight: theme.spacing(0.5),
+      },
+
+      '& .value': {
+        color: theme.palette.text.primary,
+      },
+    },
+  },
+
+  gridWrapper: {
+    paddingBottom: theme.spacing(2),
+  },
+}));

+ 0 - 0
client/src/pages/databases/collections/overview/Types.ts → client/src/pages/databases/collections/schema/Types.ts


+ 1 - 1
client/src/pages/databases/tree/index.tsx

@@ -111,7 +111,7 @@ const DatabaseTree: React.FC<DatabaseToolProps> = props => {
       node.type === 'db'
         ? `/databases/${database}/${params.databasePage || 'collections'}`
         : `/databases/${database}/${node.name}/${
-            params.collectionPage || 'overview'
+            params.collectionPage || 'schema'
           }`
     );
     // close context menu

+ 27 - 1
client/src/styles/theme.ts

@@ -19,7 +19,7 @@ const getCommonThemes = (mode: PaletteMode) => ({
     mode,
     primary: {
       main: '#0ACE82',
-      light: mode === 'light' ? '#65BA74' : '#4caf9f',
+      light: mode === 'light' ? '#f0fdf4' : '#1b4332',
       dark: mode === 'light' ? '#08a568' : '#078b63',
     },
     secondary: {
@@ -35,6 +35,7 @@ const getCommonThemes = (mode: PaletteMode) => ({
     background: {
       default: mode === 'light' ? '#f5f5f5' : '#121212',
       paper: mode === 'light' ? '#ffffff' : '#1e1e1e',
+      light: mode === 'light' ? '#f5f5f5' : '#121212',
     },
   },
   spacing: (factor: number) => `${8 * factor}px`,
@@ -176,6 +177,31 @@ export const getAttuTheme = (mode: PaletteMode) => {
           },
         },
       },
+      MuiChip: {
+        styleOverrides: {
+          root: {
+            backgroundColor: mode === 'light' ? '#f0fdf4' : '#1b4332',
+            color: mode === 'light' ? '#2a6f4e' : '#d8f3dc',
+            '& .MuiChip-label': {
+              fontWeight: 500,
+            },
+          },
+          outlined: {
+            borderColor: mode === 'light' ? '#94d2bd' : '#74c69d',
+          },
+          clickable: {
+            '&:hover': {
+              backgroundColor: mode === 'light' ? '#d1fae5' : '#2d6a4f',
+            },
+          },
+          deleteIcon: {
+            color: mode === 'light' ? '#40916c' : '#b7e4c7',
+            '&:hover': {
+              color: mode === 'light' ? '#1b4332' : '#95d5b2',
+            },
+          },
+        },
+      },
     },
   };
 };

+ 1 - 1
client/src/utils/Form.ts

@@ -1,7 +1,7 @@
 import { Option } from '@/components/customSelector/Types';
 import { METRIC_TYPES_VALUES, DataTypeEnum } from '@/consts';
 import { IForm } from '@/hooks';
-import { IndexType } from '@/pages/databases/collections/overview/Types';
+import { IndexType } from '@/pages/databases/collections/schema/Types';
 
 interface IInfo {
   [key: string]: any;

+ 1 - 1
client/src/utils/code/Types.ts

@@ -1,4 +1,4 @@
-import { IndexExtraParam } from '../../pages/databases/collections/overview/Types';
+import { IndexExtraParam } from '../../pages/databases/collections/schema/Types';
 export interface CreateIndexCodeParam {
   collectionName: string;
   fieldName: string;

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

@@ -469,7 +469,7 @@ export class CollectionsService {
       id: collectionInfo.collectionID,
       loadedPercentage,
       consistency_level: collectionInfo.consistency_level,
-      replicas: replicas && replicas.replicas,
+      replicas: replicas && replicas.replicas || [],
       loaded: status === LOADING_STATE.LOADED,
       status,
       properties: collectionInfo.properties,