Pārlūkot izejas kodu

Refactor overview page to schema page (#700)

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 6 mēneši atpakaļ
vecāks
revīzija
44859fe49b

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

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

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

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

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

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

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

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

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

@@ -67,6 +67,7 @@ const collectionTrans = {
   loadContent:
   loadContent:
     'All search and query operations within Milvus are executed in memory, only loaded collection can be searched.',
     'All search and query operations within Milvus are executed in memory, only loaded collection can be searched.',
   loadConfirmLabel: 'Load',
   loadConfirmLabel: 'Load',
+  replica: 'Replica',
   replicaNum: 'Replica number',
   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.`,
   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`,
   enableRepica: `Enable in-memory replica`,
@@ -134,7 +135,7 @@ const collectionTrans = {
   clickToLoad: 'Click to load the collection.',
   clickToLoad: 'Click to load the collection.',
   clickToRelease: 'Click to release the collection.',
   clickToRelease: 'Click to release the collection.',
   clickToSearch: 'Click to execute vector search.',
   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...',
   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 DatabaseTree from '@/pages/databases/tree';
 import { ITab } from '@/components/customTabList/Types';
 import { ITab } from '@/components/customTabList/Types';
 import Partitions from './collections/partitions/Partitions';
 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 Data from './collections/data/CollectionData';
 import Segments from './collections/segments/Segments';
 import Segments from './collections/segments/Segments';
 import Properties from './collections/properties/Properties';
 import Properties from './collections/properties/Properties';
@@ -391,9 +391,9 @@ const CollectionTabs = (props: {
   // collection tabs
   // collection tabs
   const collectionTabs: ITab[] = [
   const collectionTabs: ITab[] = [
     {
     {
-      label: collectionTrans('overviewTab'),
-      component: <Overview />,
-      path: `overview`,
+      label: collectionTrans('schemaTab'),
+      component: <Schema />,
+      path: `schema`,
     },
     },
     {
     {
       label: collectionTrans('searchTab'),
       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 ReleaseCollectionDialog from '@/pages/dialogs/ReleaseCollectionDialog';
 import { makeStyles } from '@mui/styles';
 import { makeStyles } from '@mui/styles';
 
 
+// Define styles using MUI's makeStyles
 const useStyles = makeStyles((theme: Theme) => ({
 const useStyles = makeStyles((theme: Theme) => ({
   root: {
   root: {
     display: 'flex',
     display: 'flex',
@@ -19,15 +20,13 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
   },
   chip: {
   chip: {
     border: 'none',
     border: 'none',
-    background: `rgba(0, 0, 0, 0.04)`,
     marginRight: theme.spacing(0.5),
     marginRight: theme.spacing(0.5),
     paddingLeft: theme.spacing(0.5),
     paddingLeft: theme.spacing(0.5),
   },
   },
   circle: {
   circle: {
-    width: '8px',
-    height: '8px',
+    width: 8,
+    height: 8,
     borderRadius: '50%',
     borderRadius: '50%',
-    backgroundColor: theme.palette.primary.main,
   },
   },
   loaded: {
   loaded: {
     border: `1px solid ${theme.palette.primary.main}`,
     border: `1px solid ${theme.palette.primary.main}`,
@@ -41,68 +40,53 @@ const useStyles = makeStyles((theme: Theme) => ({
     border: `1px solid ${theme.palette.text.disabled}`,
     border: `1px solid ${theme.palette.text.disabled}`,
     backgroundColor: '#fff',
     backgroundColor: '#fff',
   },
   },
-
   loading: {
   loading: {
-    marginRight: '10px',
-  },
-  icon: {
-    marginTop: theme.spacing(0.5),
-  },
-  flash: {
-    animation: '$bgColorChange 1.5s infinite',
+    marginRight: theme.spacing(1.25),
   },
   },
   extraBtn: {
   extraBtn: {
     height: 24,
     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 StatusAction: FC<StatusActionType> = props => {
+  const {
+    status,
+    percentage = 0,
+    collection,
+    action = () => {},
+    showExtraAction,
+    createIndexElement,
+  } = props;
+
+  // Theme and styles
   const theme = useTheme();
   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: commonTrans } = useTranslation();
   const { t: collectionTrans } = useTranslation('collection');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
   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) {
     switch (status) {
       case LOADING_STATE.UNLOADED:
       case LOADING_STATE.UNLOADED:
         return {
         return {
           label: statusTrans.unloaded,
           label: statusTrans.unloaded,
-          icon: <div className={`${classes.circle} ${classes.unloaded}`}></div>,
           tooltip: collectionTrans('clickToLoad'),
           tooltip: collectionTrans('clickToLoad'),
-          deleteIcon: <LoadIcon />,
+          icon: <div className={`${classes.circle} ${classes.unloaded}`}></div>,
+          deleteIcon: <Icons.load />,
         };
         };
-
       case LOADING_STATE.LOADED:
       case LOADING_STATE.LOADED:
         return {
         return {
           label: statusTrans.loaded,
           label: statusTrans.loaded,
-          icon: <div className={`${classes.circle} ${classes.loaded}`}></div>,
           tooltip: collectionTrans('clickToRelease'),
           tooltip: collectionTrans('clickToRelease'),
-          deleteIcon: <ReleaseIcon />,
+          icon: <div className={`${classes.circle} ${classes.loaded}`}></div>,
+          deleteIcon: <Icons.release />,
         };
         };
       case LOADING_STATE.LOADING:
       case LOADING_STATE.LOADING:
         return {
         return {
@@ -114,58 +98,57 @@ const StatusAction: FC<StatusActionType> = props => {
               className={classes.loading}
               className={classes.loading}
             />
             />
           ),
           ),
+          deleteIcon: null, // No delete icon during loading
         };
         };
-
       default:
       default:
         return {
         return {
           label: status,
           label: status,
-          icon: <span></span>,
           tooltip: '',
           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 noIndex = collection.schema && !collection.schema.hasVectorIndex;
-  const noVectorIndexTooltip = collectionTrans('noVectorIndexTooltip');
   const noIndexIcon = (
   const noIndexIcon = (
     <div className={`${classes.circle} ${classes.noIndex}`}></div>
     <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 (
   return (
     <div className={classes.root}>
     <div className={classes.root}>
-      <CustomToolTip
-        title={noIndex ? noVectorIndexTooltip : tooltip}
-        placement={'top'}
-      >
+      <CustomToolTip title={noIndex ? noIndexTooltip : tooltip} placement="top">
         <Chip
         <Chip
           className={classes.chip}
           className={classes.chip}
-          variant="outlined"
           label={<Typography>{label}</Typography>}
           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}
           disabled={noIndex}
-          deleteIcon={deleteIcon}
+          deleteIcon={<Icons.release />}
           size="small"
           size="small"
           icon={noIndex ? noIndexIcon : icon}
           icon={noIndex ? noIndexIcon : icon}
         />
         />
       </CustomToolTip>
       </CustomToolTip>
 
 
-      {props.showExtraAction && collection.schema && (
+      {showExtraAction && collection.schema && (
         <>
         <>
           {status === LOADING_STATE.LOADED && (
           {status === LOADING_STATE.LOADED && (
             <CustomButton
             <CustomButton
@@ -173,19 +156,17 @@ const StatusAction: FC<StatusActionType> = props => {
               className={classes.extraBtn}
               className={classes.extraBtn}
               tooltip={collectionTrans('clickToSearch')}
               tooltip={collectionTrans('clickToSearch')}
               onClick={() => {
               onClick={() => {
-                const currentHash = window.location.hash;
-                const newHash = currentHash.replace('overview', 'search');
-
+                const newHash = window.location.hash.replace(
+                  'schema',
+                  'search'
+                );
                 window.location.hash = newHash;
                 window.location.hash = newHash;
               }}
               }}
             >
             >
               {btnTrans('vectorSearch')}
               {btnTrans('vectorSearch')}
             </CustomButton>
             </CustomButton>
           )}
           )}
-
-          {collection.schema &&
-            !collection.schema.hasVectorIndex &&
-            props.createIndexElement}
+          {!collection.schema.hasVectorIndex && createIndexElement}
         </>
         </>
       )}
       )}
     </div>
     </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: {
   chip: {
-    background: `rgba(0, 0, 0, 0.04)`,
     padding: theme.spacing(0.5),
     padding: theme.spacing(0.5),
 
 
     '& .icon': {
     '& .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 { useContext } from 'react';
 import { useParams } from 'react-router-dom';
 import { useParams } from 'react-router-dom';
 import AttuGrid from '@/components/grid/Grid';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType } from '@/components/grid/Types';
 import { ColDefinitionsType } from '@/components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import Icons from '@/components/icons/Icons';
 import Icons from '@/components/icons/Icons';
-import { formatFieldType } from '@/utils';
+import { formatFieldType, formatNumber } from '@/utils';
 import { dataContext } from '@/context';
 import { dataContext } from '@/context';
 import IndexTypeElement from './IndexTypeElement';
 import IndexTypeElement from './IndexTypeElement';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import { getLabelDisplayedRows } from '@/pages/search/Utils';
 import StatusAction from '@/pages/databases/collections/StatusAction';
 import StatusAction from '@/pages/databases/collections/StatusAction';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { FieldObject } from '@server/types';
 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 Overview = () => {
   const { fetchCollection, collections, loading } = useContext(dataContext);
   const { fetchCollection, collections, loading } = useContext(dataContext);
@@ -157,16 +60,10 @@ const Overview = () => {
                 className={classes.chip}
                 className={classes.chip}
                 size="small"
                 size="small"
                 label="Partition key"
                 label="Partition key"
-                variant="outlined"
               />
               />
             ) : null}
             ) : null}
             {f.autoID ? (
             {f.autoID ? (
-              <Chip
-                className={classes.chip}
-                size="small"
-                label="auto id"
-                variant="outlined"
-              />
+              <Chip className={classes.chip} size="small" label="auto id" />
             ) : null}
             ) : null}
           </div>
           </div>
         );
         );
@@ -279,101 +176,102 @@ const Overview = () => {
     <section className={classes.wrapper}>
     <section className={classes.wrapper}>
       {collection && (
       {collection && (
         <section className={classes.infoWrapper}>
         <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="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>
             </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>
           </div>
         </section>
         </section>
       )}
       )}
 
 
       <section className={classes.gridWrapper}>
       <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
         <AttuGrid
           toolbarConfigs={[]}
           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'
       node.type === 'db'
         ? `/databases/${database}/${params.databasePage || 'collections'}`
         ? `/databases/${database}/${params.databasePage || 'collections'}`
         : `/databases/${database}/${node.name}/${
         : `/databases/${database}/${node.name}/${
-            params.collectionPage || 'overview'
+            params.collectionPage || 'schema'
           }`
           }`
     );
     );
     // close context menu
     // close context menu

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

@@ -19,7 +19,7 @@ const getCommonThemes = (mode: PaletteMode) => ({
     mode,
     mode,
     primary: {
     primary: {
       main: '#0ACE82',
       main: '#0ACE82',
-      light: mode === 'light' ? '#65BA74' : '#4caf9f',
+      light: mode === 'light' ? '#f0fdf4' : '#1b4332',
       dark: mode === 'light' ? '#08a568' : '#078b63',
       dark: mode === 'light' ? '#08a568' : '#078b63',
     },
     },
     secondary: {
     secondary: {
@@ -35,6 +35,7 @@ const getCommonThemes = (mode: PaletteMode) => ({
     background: {
     background: {
       default: mode === 'light' ? '#f5f5f5' : '#121212',
       default: mode === 'light' ? '#f5f5f5' : '#121212',
       paper: mode === 'light' ? '#ffffff' : '#1e1e1e',
       paper: mode === 'light' ? '#ffffff' : '#1e1e1e',
+      light: mode === 'light' ? '#f5f5f5' : '#121212',
     },
     },
   },
   },
   spacing: (factor: number) => `${8 * factor}px`,
   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 { Option } from '@/components/customSelector/Types';
 import { METRIC_TYPES_VALUES, DataTypeEnum } from '@/consts';
 import { METRIC_TYPES_VALUES, DataTypeEnum } from '@/consts';
 import { IForm } from '@/hooks';
 import { IForm } from '@/hooks';
-import { IndexType } from '@/pages/databases/collections/overview/Types';
+import { IndexType } from '@/pages/databases/collections/schema/Types';
 
 
 interface IInfo {
 interface IInfo {
   [key: string]: any;
   [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 {
 export interface CreateIndexCodeParam {
   collectionName: string;
   collectionName: string;
   fieldName: string;
   fieldName: string;

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

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