diff --git a/client/src/components/customTabList/RouteTabList.tsx b/client/src/components/customTabList/RouteTabList.tsx new file mode 100644 index 00000000..ca955f7b --- /dev/null +++ b/client/src/components/customTabList/RouteTabList.tsx @@ -0,0 +1,116 @@ +import { Box, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'; +import { FC, useState } from 'react'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { ITabListProps, ITabPanel } from './Types'; + +const useStyles = makeStyles((theme: Theme) => ({ + wrapper: { + display: 'flex', + flexDirection: 'column', + flexBasis: 0, + flexGrow: 1, + '& .MuiTab-wrapper': { + textTransform: 'capitalize', + fontWeight: 'bold', + color: '#323232', + }, + }, + tab: { + height: theme.spacing(0.5), + backgroundColor: theme.palette.primary.main, + }, + tabContainer: { + borderBottom: '1px solid #e9e9ed', + }, + tabContent: { + minWidth: 0, + marginRight: theme.spacing(3), + }, + tabPanel: { + flexBasis: 0, + flexGrow: 1, + marginTop: theme.spacing(2), + overflow: 'hidden', + }, +})); + +const TabPanel = (props: ITabPanel) => { + const { children, value, index, className = '', ...other } = props; + + return ( + + ); +}; + +const a11yProps = (index: number) => { + return { + id: `tab-${index}`, + 'aria-controls': `tabpanel-${index}`, + }; +}; + +const RouteTabList: FC = props => { + const { tabs, activeIndex = 0, wrapperClass = '' } = props; + const classes = useStyles(); + const [value, setValue] = useState(activeIndex); + const navigate = useNavigate(); + const location = useLocation(); + + const handleChange = (event: any, newValue: any) => { + setValue(newValue); + const newPath = + location.pathname.split('/').slice(0, -1).join('/') + + '/' + + tabs[newValue].path; + + navigate(`${newPath}`); + }; + + return ( +
+ }} + value={value} + onChange={handleChange} + aria-label="tabs" + > + {tabs.map((tab, index) => ( + + ))} + + + {tabs.map((tab, index) => ( + + {tab.component} + + ))} +
+ ); +}; + +export default RouteTabList; diff --git a/client/src/components/customTabList/Types.ts b/client/src/components/customTabList/Types.ts index 21cb9fcf..fec17720 100644 --- a/client/src/components/customTabList/Types.ts +++ b/client/src/components/customTabList/Types.ts @@ -3,12 +3,13 @@ import { ReactElement } from 'react'; export interface ITab { label: string; component: ReactElement; + path?: string; } export interface ITabListProps { tabs: ITab[]; activeIndex?: number; - handleTabChange?: (index: number) => void; + handleTabChange?:(index:number) => void; wrapperClass?: string; } diff --git a/client/src/pages/collections/Collection.tsx b/client/src/pages/collections/Collection.tsx index b5d16c99..d00c75e2 100644 --- a/client/src/pages/collections/Collection.tsx +++ b/client/src/pages/collections/Collection.tsx @@ -1,19 +1,17 @@ -import { useMemo, useContext } from 'react'; -import { useNavigate, useLocation, useParams } from 'react-router-dom'; +import { useContext } from 'react'; +import { useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { makeStyles, Theme } from '@material-ui/core'; import { authContext } from '@/context'; import { useNavigationHook } from '@/hooks'; import { ALL_ROUTER_TYPES } from '@/router/Types'; -import CustomTabList from '@/components/customTabList/CustomTabList'; +import RouteTabList from '@/components/customTabList/RouteTabList'; import { ITab } from '@/components/customTabList/Types'; import Partitions from '../partitions/Partitions'; -import { parseLocationSearch } from '@/utils'; import Schema from '../schema/Schema'; import Query from '../query/Query'; import Preview from '../preview/Preview'; import Segments from '../segments/Segments'; -import { TAB_ENUM } from './Types'; const useStyles = makeStyles((theme: Theme) => ({ wrapper: { @@ -38,49 +36,40 @@ const Collection = () => { const classes = useStyles(); const { isManaged } = useContext(authContext); - const { collectionName = '' } = useParams<{ + const { collectionName = '', tab = '' } = useParams<{ collectionName: string; + tab: string; }>(); useNavigationHook(ALL_ROUTER_TYPES.COLLECTION_DETAIL, { collectionName }); - const navigate = useNavigate(); - const location = useLocation(); - const { t: collectionTrans } = useTranslation('collection'); - const activeTabIndex = useMemo(() => { - const { activeIndex } = location.search - ? parseLocationSearch(location.search) - : { activeIndex: TAB_ENUM.schema }; - return Number(activeIndex); - }, [location]); - - const handleTabChange = (activeIndex: number) => { - const path = location.pathname; - navigate(`${path}?activeIndex=${activeIndex}`); - }; - const tabs: ITab[] = [ { label: collectionTrans('schemaTab'), - component: , + component: , + path: `schema`, }, { label: collectionTrans('partitionTab'), - component: , + component: , + path: `partitions`, }, { label: collectionTrans('previewTab'), - component: , + component: , + path: `preview`, }, { label: collectionTrans('queryTab'), - component: , + component: , + path: `query`, }, { label: collectionTrans('segmentsTab'), - component: , + component: , + path: `segments`, }, ]; @@ -89,13 +78,14 @@ const Collection = () => { tabs.splice(1, 1); } + const activeTab = tabs.findIndex(t => t.path === tab); + return (
-
); diff --git a/client/src/pages/collections/Collections.tsx b/client/src/pages/collections/Collections.tsx index 44076bc4..1fe8cdf4 100644 --- a/client/src/pages/collections/Collections.tsx +++ b/client/src/pages/collections/Collections.tsx @@ -151,7 +151,7 @@ const Collections = () => { Object.assign(v, { nameElement: ( diff --git a/client/src/pages/overview/collectionCard/CollectionCard.tsx b/client/src/pages/overview/collectionCard/CollectionCard.tsx index d8802f60..63b97e67 100644 --- a/client/src/pages/overview/collectionCard/CollectionCard.tsx +++ b/client/src/pages/overview/collectionCard/CollectionCard.tsx @@ -154,7 +154,7 @@ const CollectionCard: FC = ({
- + {collectionName} diff --git a/client/src/pages/partitions/Partitions.tsx b/client/src/pages/partitions/Partitions.tsx index 0c5c8857..7b736f0f 100644 --- a/client/src/pages/partitions/Partitions.tsx +++ b/client/src/pages/partitions/Partitions.tsx @@ -1,6 +1,6 @@ import { makeStyles, Theme } from '@material-ui/core'; -import { FC, useContext, useEffect, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useContext, useEffect, useState } from 'react'; +import { useSearchParams, useParams } from 'react-router-dom'; import Highlighter from 'react-highlight-words'; import AttuGrid from '@/components/grid/Grid'; import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types'; @@ -31,11 +31,9 @@ const useStyles = makeStyles((theme: Theme) => ({ })); let timer: NodeJS.Timeout | null = null; -// get init search value from url -// const { search = '' } = parseLocationSearch(window.location.search); -const Partitions: FC<{ - collectionName: string; -}> = ({ collectionName }) => { + +const Partitions = () => { + const { collectionName = '' } = useParams<{ collectionName: string }>(); const classes = useStyles(); const { t } = useTranslation('partition'); const { t: successTrans } = useTranslation('success'); @@ -67,8 +65,7 @@ const Partitions: FC<{ handleGridSort, } = usePaginationHook(searchedPartitions); const [loading, setLoading] = useState(true); - const { setDialog, handleCloseDialog, openSnackBar } = - useContext(rootContext); + const { setDialog, openSnackBar } = useContext(rootContext); const fetchPartitions = async (collectionName: string) => { try { diff --git a/client/src/pages/preview/Preview.tsx b/client/src/pages/preview/Preview.tsx index fabfe245..50773603 100644 --- a/client/src/pages/preview/Preview.tsx +++ b/client/src/pages/preview/Preview.tsx @@ -1,6 +1,7 @@ -import { FC, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import AttuGrid from '@/components/grid/Grid'; +import { useParams } from 'react-router-dom'; import { Collection, MilvusIndex } from '@/http'; import { usePaginationHook, useSearchResult } from '@/hooks'; import { generateVector } from '@/utils'; @@ -15,9 +16,8 @@ import { ToolBarConfig } from '@/components/grid/Types'; import CustomToolBar from '@/components/grid/ToolBar'; import { getQueryStyles } from '../query/Styles'; -const Preview: FC<{ - collectionName: string; -}> = ({ collectionName }) => { +const Preview = () => { + const { collectionName } = useParams<{ collectionName: string }>(); const [fields, setFields] = useState([]); const [tableLoading, setTableLoading] = useState(); const [queryResult, setQueryResult] = useState(); @@ -140,7 +140,7 @@ const Preview: FC<{ type: 'button', btnVariant: 'text', onClick: () => { - loadData(collectionName); + loadData(collectionName!); }, label: btnTrans('refresh'), }, diff --git a/client/src/pages/query/Query.tsx b/client/src/pages/query/Query.tsx index 262036d2..d8e939be 100644 --- a/client/src/pages/query/Query.tsx +++ b/client/src/pages/query/Query.tsx @@ -1,6 +1,7 @@ -import { FC, useEffect, useState, useRef, useContext } from 'react'; +import { useEffect, useState, useRef, useContext } from 'react'; import { TextField } from '@material-ui/core'; import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; import { rootContext } from '@/context'; import { Collection, DataService } from '@/http'; import { usePaginationHook, useSearchResult } from '@/hooks'; @@ -22,9 +23,8 @@ import { } from '@/consts'; import CustomSelector from '@/components/customSelector/CustomSelector'; -const Query: FC<{ - collectionName: string; -}> = ({ collectionName }) => { +const Query = () => { + const { collectionName } = useParams<{ collectionName: string }>(); const [fields, setFields] = useState([]); const [expression, setExpression] = useState(''); const [tableLoading, setTableLoading] = useState(); @@ -122,7 +122,7 @@ const Query: FC<{ return; } try { - const res = await Collection.queryData(collectionName, { + const res = await Collection.queryData(collectionName!, { expr: expr, output_fields: fields.map(i => i.name), offset: 0, @@ -145,7 +145,7 @@ const Query: FC<{ }; const handleDelete = async () => { - await DataService.deleteEntities(collectionName, { + await DataService.deleteEntities(collectionName!, { expr: `${primaryKey.value} in [${selectedData .map(v => primaryKey.type === DataTypeStringEnum.VarChar diff --git a/client/src/pages/schema/Schema.tsx b/client/src/pages/schema/Schema.tsx index e31bf0fa..55b99f25 100644 --- a/client/src/pages/schema/Schema.tsx +++ b/client/src/pages/schema/Schema.tsx @@ -1,5 +1,6 @@ import { makeStyles, Theme, Typography, Chip } from '@material-ui/core'; -import { FC, useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } 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'; @@ -58,9 +59,8 @@ const useStyles = makeStyles((theme: Theme) => ({ }, })); -const Schema: FC<{ - collectionName: string; -}> = ({ collectionName }) => { +const Schema = () => { + const { collectionName = '' } = useParams<{ collectionName: string }>(); const classes = useStyles(); const { t: collectionTrans } = useTranslation('collection'); const { t: indexTrans } = useTranslation('index'); @@ -187,7 +187,7 @@ const Schema: FC<{ id: 'indexName', align: 'left', disablePadding: true, - label: indexTrans('indexName') + label: indexTrans('indexName'), }, { id: '_indexTypeElement', diff --git a/client/src/pages/segments/Segments.tsx b/client/src/pages/segments/Segments.tsx index 9d8432a7..30a0f930 100644 --- a/client/src/pages/segments/Segments.tsx +++ b/client/src/pages/segments/Segments.tsx @@ -1,5 +1,6 @@ -import { useEffect, useState, FC, useContext } from 'react'; +import { useEffect, useState, useContext } from 'react'; import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; import { Segement } from '@/http'; import { usePaginationHook } from '@/hooks'; import { rootContext } from '@/context'; @@ -12,9 +13,8 @@ import FlushDialog from '@/pages/dialogs/FlushDialog'; import { getQueryStyles } from '../query/Styles'; import { Segment } from './Types'; -const Segments: FC<{ - collectionName: string; -}> = ({ collectionName }) => { +const Segments = () => { + const { collectionName = '' } = useParams<{ collectionName: string }>(); const classes = getQueryStyles(); const { setDialog } = useContext(rootContext); @@ -58,7 +58,7 @@ const Segments: FC<{ fetchSegments(); }, label: btnTrans('refresh'), - icon: 'refresh' + icon: 'refresh', }, { type: 'button', diff --git a/client/src/pages/user/Users.tsx b/client/src/pages/user/Users.tsx index d30e504f..429591b2 100644 --- a/client/src/pages/user/Users.tsx +++ b/client/src/pages/user/Users.tsx @@ -1,15 +1,12 @@ -import { useMemo } from 'react'; -import { useNavigate, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { makeStyles, Theme } from '@material-ui/core'; import { useNavigationHook } from '@/hooks'; import { ALL_ROUTER_TYPES } from '@/router/Types'; -import CustomTabList from '@/components/customTabList/CustomTabList'; +import RouteTabList from '@/components/customTabList/RouteTabList'; import { ITab } from '@/components/customTabList/Types'; -import { parseLocationSearch } from '@/utils'; import User from './User'; import Roles from './Roles'; -import { TAB_ENUM } from './Types'; const useStyles = makeStyles((theme: Theme) => ({ wrapper: { @@ -34,41 +31,32 @@ const Users = () => { const classes = useStyles(); useNavigationHook(ALL_ROUTER_TYPES.USER); - const navigate = useNavigate(); const location = useLocation(); + const currentPath = location.pathname.slice(1); const { t: userTrans } = useTranslation('user'); - const activeTabIndex = useMemo(() => { - const { activeIndex } = location.search - ? parseLocationSearch(location.search) - : { activeIndex: TAB_ENUM.schema }; - return Number(activeIndex); - }, [location]); - - const handleTabChange = (activeIndex: number) => { - const path = location.pathname; - navigate(`${path}?activeIndex=${activeIndex}`); - }; - const tabs: ITab[] = [ { label: userTrans('users'), component: , + path: 'users', }, { label: userTrans('roles'), component: , + path: 'roles', }, ]; + const activeTabIndex = tabs.findIndex(t => t.path === currentPath); + return (
-
); diff --git a/client/src/router/Router.tsx b/client/src/router/Router.tsx index 82b62354..20941f6d 100644 --- a/client/src/router/Router.tsx +++ b/client/src/router/Router.tsx @@ -22,11 +22,17 @@ const RouterComponent = () => { } /> } /> } /> + } + /> + } /> } /> {!isManaged && ( <> } /> + } /> } /> )}