Browse Source

update nav menu

tumao 4 years ago
parent
commit
cc62d64005

+ 6 - 0
client/src/assets/icons/collecion.svg

@@ -0,0 +1,6 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.75 7.8333L6.25 3.5083" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M17.5 13.3333V6.66663C17.4997 6.37435 17.4225 6.0873 17.2763 5.83426C17.13 5.58122 16.9198 5.37109 16.6667 5.22496L10.8333 1.89163C10.58 1.74535 10.2926 1.66833 10 1.66833C9.70744 1.66833 9.42003 1.74535 9.16667 1.89163L3.33333 5.22496C3.08022 5.37109 2.86998 5.58122 2.72372 5.83426C2.57745 6.0873 2.5003 6.37435 2.5 6.66663V13.3333C2.5003 13.6256 2.57745 13.9126 2.72372 14.1657C2.86998 14.4187 3.08022 14.6288 3.33333 14.775L9.16667 18.1083C9.42003 18.2546 9.70744 18.3316 10 18.3316C10.2926 18.3316 10.58 18.2546 10.8333 18.1083L16.6667 14.775C16.9198 14.6288 17.13 14.4187 17.2763 14.1657C17.4225 13.9126 17.4997 13.6256 17.5 13.3333Z" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M2.72461 5.80005L9.99961 10.0084L17.2746 5.80005" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 18.4V10" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 3 - 0
client/src/assets/icons/console.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.667 15.8333V5.83333H3.33366V15.8333H16.667ZM16.667 2.5C17.109 2.5 17.5329 2.67559 17.8455 2.98816C18.1581 3.30072 18.3337 3.72464 18.3337 4.16667V15.8333C18.3337 16.2754 18.1581 16.6993 17.8455 17.0118C17.5329 17.3244 17.109 17.5 16.667 17.5H3.33366C2.89163 17.5 2.46771 17.3244 2.15515 17.0118C1.84259 16.6993 1.66699 16.2754 1.66699 15.8333V4.16667C1.66699 3.72464 1.84259 3.30072 2.15515 2.98816C2.46771 2.67559 2.89163 2.5 3.33366 2.5H16.667ZM10.8337 14.1667V12.5H15.0003V14.1667H10.8337ZM7.98366 10.8333L4.64199 7.5H7.00033L9.75033 10.25C10.0753 10.575 10.0753 11.1083 9.75033 11.4333L7.01699 14.1667H4.65866L7.98366 10.8333Z" fill="#82838E"/>
+</svg>

+ 4 - 0
client/src/assets/icons/overview.svg

@@ -0,0 +1,4 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.5 7.49996L10 1.66663L17.5 7.49996V16.6666C17.5 17.1087 17.3244 17.5326 17.0118 17.8451C16.6993 18.1577 16.2754 18.3333 15.8333 18.3333H4.16667C3.72464 18.3333 3.30072 18.1577 2.98816 17.8451C2.67559 17.5326 2.5 17.1087 2.5 16.6666V7.49996Z" stroke="#06AFF2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7.5 18.3333V10H12.5V18.3333" stroke="#06AFF2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 17 - 0
client/src/components/icons/Icons.tsx

@@ -12,9 +12,14 @@ import AppsIcon from '@material-ui/icons/Apps';
 import MoreVertIcon from '@material-ui/icons/MoreVert';
 import CancelIcon from '@material-ui/icons/Cancel';
 import CheckCircleIcon from '@material-ui/icons/CheckCircle';
+import ExpandLess from '@material-ui/icons/ExpandLess';
+import ExpandMore from '@material-ui/icons/ExpandMore';
 
 import { SvgIcon } from '@material-ui/core';
 import { ReactComponent as MilvusIcon } from '../../assets/icons/milvus.svg';
+import { ReactComponent as OverviewIcon } from '../../assets/icons/overview.svg';
+import { ReactComponent as CollectionIcon } from '../../assets/icons/collecion.svg';
+import { ReactComponent as ConsoleIcon } from '../../assets/icons/console.svg';
 
 const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
   search: (props = {}) => <SearchIcon {...props} />,
@@ -29,9 +34,21 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
   more: (props = {}) => <MoreVertIcon {...props} />,
   app: (props = {}) => <AppsIcon {...props} />,
   success: (props = {}) => <CheckCircleIcon {...props} />,
+  expandLess: (props = {}) => <ExpandLess {...props} />,
+  expandMore: (props = {}) => <ExpandMore {...props} />,
+
   milvus: (props = {}) => (
     <SvgIcon viewBox="0 0 44 31" component={MilvusIcon} {...props} />
   ),
+  navOverview: (props = {}) => (
+    <SvgIcon viewBox="0 0 20 20" component={OverviewIcon} {...props} />
+  ),
+  navCollection: (props = {}) => (
+    <SvgIcon viewBox="0 0 20 20" component={CollectionIcon} {...props} />
+  ),
+  navConsole: (props = {}) => (
+    <SvgIcon viewBox="0 0 20 20" component={ConsoleIcon} {...props} />
+  ),
 };
 
 export default icons;

+ 6 - 1
client/src/components/icons/Types.ts

@@ -11,4 +11,9 @@ export type IconsType =
   | 'app'
   | 'more'
   | 'success'
-  | 'milvus';
+  | 'milvus'
+  | 'navOverview'
+  | 'navCollection'
+  | 'navConsole'
+  | 'expandLess'
+  | 'expandMore';

+ 44 - 13
client/src/components/layout/Layout.tsx

@@ -3,14 +3,17 @@ import Header from './Header';
 import { makeStyles, Theme, createStyles } from '@material-ui/core';
 import NavMenu from '../menu/NavMenu';
 import { NavMenuItem } from '../menu/Types';
-// import { useHistory } from 'react-router';
-// import { useTranslation } from 'react-i18next';
+import { useContext } from 'react';
+import { rootContext } from '../../context/Root';
+import icons from '../icons/Icons';
+import { useTranslation } from 'react-i18next';
+import { useHistory } from 'react-router-dom';
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
     root: {
       minHeight: '100vh',
-      backgroundColor: (props: any) => props.backgroundColor,
+      backgroundColor: '#f5f5f5',
     },
     content: {
       display: 'flex',
@@ -19,20 +22,47 @@ const useStyles = makeStyles((theme: Theme) =>
       flex: 1,
       display: 'flex',
       flexDirection: 'column',
-      height: `100vh`,
+      height: '100vh',
       overflowY: 'scroll',
     },
+    activeConsole: {
+      '& path': {
+        fill: theme.palette.primary.main,
+      },
+    },
+    normalConsole: {
+      '& path': {
+        fill: '#82838e',
+      },
+    },
   })
 );
 
 const Layout = (props: any) => {
-  const path = window.location.hash.slice(2);
-  const greyPaths = ['', 'billing'];
-  const bgColor = greyPaths.includes(path) ? '#f5f5f5' : '#fff';
-  const classes = useStyles({ backgroundColor: bgColor });
+  const history = useHistory();
+  const { isAuth } = useContext(rootContext);
+  const { t } = useTranslation('nav');
+  const classes = useStyles();
 
-  const data: NavMenuItem[] = [];
-  const isAuth = false;
+  const menuItems: NavMenuItem[] = [
+    {
+      icon: icons.navOverview,
+      label: t('overview'),
+      onClick: () => history.push('/'),
+    },
+    {
+      icon: icons.navCollection,
+      label: t('collection'),
+      onClick: () => history.push('/collections'),
+    },
+    {
+      icon: icons.navConsole,
+      label: t('console'),
+      onClick: () => history.push('/console'),
+      iconActiveClass: classes.activeConsole,
+      iconNormalClass: classes.normalConsole,
+    },
+  ];
 
   return (
     <div className={classes.root}>
@@ -41,9 +71,10 @@ const Layout = (props: any) => {
           {isAuth && (
             <NavMenu
               width="200px"
-              data={data}
-              defaultActive="Lock"
-              defaultOpen={{ security: true }}
+              data={menuItems}
+              defaultActive={t('overview')}
+              // used for nested child menu
+              defaultOpen={{ [t('overview')]: true }}
             />
           )}
 

+ 43 - 52
client/src/components/menu/NavMenu.tsx

@@ -1,16 +1,13 @@
-import { useState, FC, useEffect } from 'react';
+import { useState, FC } from 'react';
 import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
 import List from '@material-ui/core/List';
 import ListItem from '@material-ui/core/ListItem';
 import ListItemIcon from '@material-ui/core/ListItemIcon';
 import ListItemText from '@material-ui/core/ListItemText';
 import Collapse from '@material-ui/core/Collapse';
-import ExpandLess from '@material-ui/icons/ExpandLess';
-import ExpandMore from '@material-ui/icons/ExpandMore';
 import { NavMenuItem, NavMenuType } from './Types';
-import { useTranslation } from 'react-i18next';
-import { useLocation } from 'react-router';
 import icons from '../icons/Icons';
+import { useTranslation } from 'react-i18next';
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
@@ -32,39 +29,26 @@ const useStyles = makeStyles((theme: Theme) =>
 
       width: 'initial',
       color: '#82838e',
+    },
+    itemIcon: {
+      minWidth: '20px',
+      marginRight: theme.spacing(1),
 
-      '& svg': {
-        width: '20px',
-        height: '20px',
+      '& .icon': {
+        fill: 'transparent',
 
         '& path': {
           stroke: '#82838e',
         },
-
-        '& .st0': {
-          fill: '#82838e',
-        },
       },
     },
-    itemIcon: {
-      minWidth: '20px',
-      marginRight: theme.spacing(1),
-    },
     active: {
       color: theme.palette.primary.main,
       borderRight: `2px solid ${theme.palette.primary.main}`,
 
-      '& svg': {
+      '& .icon': {
         '& path': {
           stroke: theme.palette.primary.main,
-
-          transition: 'all 0.2s',
-        },
-
-        '& .st0': {
-          fill: theme.palette.primary.main,
-
-          transition: 'all 0.2s',
         },
       },
     },
@@ -75,12 +59,20 @@ const useStyles = makeStyles((theme: Theme) =>
       justifyContent: 'center',
       alignItems: 'center',
 
-      marginTop: '30px',
+      marginTop: theme.spacing(3),
 
-      marginBottom: '65px',
+      marginBottom: theme.spacing(8),
+
+      '& h3': {
+        margin: 0,
+        fontSize: '16px',
+        lineHeight: '19px',
+        letterSpacing: '0.15px',
+        color: '#323232',
+      },
     },
     logo: {
-      width: '150px',
+      marginRight: theme.spacing(1),
     },
 
     feedback: {
@@ -98,7 +90,12 @@ const NavMenu: FC<NavMenuType> = props => {
   const classes = useStyles({ width });
   const [open, setOpen] = useState<{ [x: string]: boolean }>(defaultOpen);
   const [active, setActive] = useState<string>(defaultActive);
-  const location = useLocation();
+
+  const { t } = useTranslation();
+  const milvusTrans: { [key in string]: string } = t('milvus');
+
+  const ExpandLess = icons.expandLess;
+  const ExpandMore = icons.expandMore;
 
   const handleClick = (label: string) => {
     setOpen(v => ({
@@ -107,31 +104,26 @@ const NavMenu: FC<NavMenuType> = props => {
     }));
   };
 
-  const { t } = useTranslation();
-  // const navTrans: { [key in string]: string | object } = t('nav');
-
-  // useEffect(() => {
-  //   const activeLabel = location.pathname.includes('queries')
-  //     ? (navTrans.query as string)
-  //     : (navTrans.database as string);
-  //   setActive(activeLabel);
-  // }, [location.pathname, navTrans.query, navTrans.database]);
-
   const NestList = (props: { data: NavMenuItem[]; className?: string }) => {
     const { className, data } = props;
     return (
       <>
-        {data.map((v: any) => {
+        {data.map((v: NavMenuItem) => {
           const IconComponent = v.icon;
+          const isActive = active === v.label;
+          const iconClass =
+            v.iconActiveClass && v.iconNormalClass
+              ? isActive
+                ? v.iconActiveClass
+                : v.iconNormalClass
+              : 'icon';
           if (v.children) {
             return (
               <div key={v.label}>
                 <ListItem button onClick={() => handleClick(v.label)}>
-                  {v.icon && (
-                    <ListItemIcon>
-                      <IconComponent />
-                    </ListItemIcon>
-                  )}
+                  <ListItemIcon>
+                    <IconComponent classes={{ root: iconClass }} />
+                  </ListItemIcon>
 
                   <ListItemText primary={v.label} />
                   {open[v.label] ? <ExpandLess /> : <ExpandMore />}
@@ -149,18 +141,16 @@ const NavMenu: FC<NavMenuType> = props => {
               button
               key={v.label}
               className={`${className || ''} ${classes.item} ${
-                active === v.label ? classes.active : ''
+                isActive ? classes.active : ''
               }`}
               onClick={() => {
                 setActive(v.label);
                 v.onClick && v.onClick();
               }}
             >
-              {v.icon && (
-                <ListItemIcon className={classes.itemIcon}>
-                  <IconComponent />
-                </ListItemIcon>
-              )}
+              <ListItemIcon className={classes.itemIcon}>
+                <IconComponent classes={{ root: iconClass }} />
+              </ListItemIcon>
 
               <ListItemText primary={v.label} />
             </ListItem>
@@ -177,9 +167,10 @@ const NavMenu: FC<NavMenuType> = props => {
       <div>
         <div className={classes.logoWrapper}>
           <Logo classes={{ root: classes.logo }} />
+          <h3>{milvusTrans.admin}</h3>
         </div>
 
-        {/* <NestList data={data} /> */}
+        <NestList data={data} />
       </div>
     </List>
   );

+ 3 - 1
client/src/components/menu/Types.ts

@@ -12,7 +12,9 @@ export type NavMenuItem = {
   icon: (
     props?: any
   ) => React.ReactElement<any, string | React.JSXElementConstructor<any>>;
-  label: String;
+  iconActiveClass?: string;
+  iconNormalClass?: string;
+  label: string;
   onClick?: () => void;
   children?: NavMenuItem[];
 };

+ 6 - 0
client/src/context/Root.tsx

@@ -34,6 +34,8 @@ export const rootContext = React.createContext<RootContextType>({
   setDialog: params => {},
   handleCloseDialog: () => {},
   setDrawer: (params: any) => {},
+  isAuth: false,
+  setIsAuth: () => {},
 });
 
 const { Provider } = rootContext;
@@ -65,6 +67,8 @@ export const RootProvider = (props: { children: React.ReactNode }) => {
     child: <></>,
   });
 
+  const [isAuth, setIsAuth] = useState<boolean>(false);
+
   const handleSnackBarClose = () => {
     setSnackBar(v => ({ ...v, open: false }));
   };
@@ -114,6 +118,8 @@ export const RootProvider = (props: { children: React.ReactNode }) => {
         setDialog,
         handleCloseDialog,
         setDrawer,
+        isAuth,
+        setIsAuth,
       }}
     >
       <ThemeProvider theme={theme}>

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

@@ -1,4 +1,4 @@
-import { ReactElement } from 'react';
+import { Dispatch, ReactElement, SetStateAction } from 'react';
 
 export type RootContextType = {
   openSnackBar: OpenSnackBarType;
@@ -6,6 +6,8 @@ export type RootContextType = {
   setDialog: (params: DialogType) => void;
   handleCloseDialog: () => void;
   setDrawer: (params: any) => void;
+  isAuth: boolean;
+  setIsAuth: Dispatch<SetStateAction<boolean>>;
 };
 
 // this is for any custom dialog

+ 7 - 0
client/src/i18n/cn/nav.ts

@@ -0,0 +1,7 @@
+const navTrans = {
+  overview: 'Overview',
+  collection: 'Collection',
+  console: 'Search Console',
+};
+
+export default navTrans;

+ 7 - 0
client/src/i18n/en/nav.ts

@@ -0,0 +1,7 @@
+const navTrans = {
+  overview: 'Overview',
+  collection: 'Collection',
+  console: 'Search Console',
+};
+
+export default navTrans;

+ 4 - 0
client/src/i18n/index.ts

@@ -7,17 +7,21 @@ import buttonEn from './en/button';
 import buttonCn from './cn/button';
 import warningCn from './cn/warning';
 import warningEn from './en/warning';
+import navCn from './cn/nav';
+import navEn from './en/nav';
 
 export const resources = {
   cn: {
     translation: commonCn,
     btn: buttonCn,
     warning: warningCn,
+    nav: navCn,
   },
   en: {
     translation: commonEn,
     btn: buttonEn,
     warning: warningEn,
+    nav: navEn,
   },
 };
 

+ 5 - 0
client/src/pages/collections/Collections.tsx

@@ -0,0 +1,5 @@
+const Collections = () => {
+  return <section>collection</section>;
+};
+
+export default Collections;

+ 4 - 3
client/src/pages/connect/Connect.tsx

@@ -4,11 +4,12 @@ import { ITextfieldConfig } from '../../components/customInput/Types';
 import icons from '../../components/icons/Icons';
 import ConnectContainer from './ConnectContainer';
 import CustomInput from '../../components/customInput/CustomInput';
-import { useMemo, useState } from 'react';
+import { useContext, useMemo, useState } from 'react';
 import { formatForm } from '../../utils/Form';
 import { useFormValidation } from '../../hooks/Form';
 import CustomButton from '../../components/customButton/CustomButton';
 import { useHistory } from 'react-router-dom';
+import { rootContext } from '../../context/Root';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -40,7 +41,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 
 const Connect = () => {
   const history = useHistory();
-
+  const { setIsAuth } = useContext(rootContext);
   const classes = useStyles();
   const { t } = useTranslation();
   const { t: warningTrans } = useTranslation('warning');
@@ -63,7 +64,7 @@ const Connect = () => {
   };
 
   const handleConnect = () => {
-    console.log('connect address', form.address);
+    setIsAuth(true);
     history.push('/');
   };
 

+ 5 - 0
client/src/pages/console/Console.tsx

@@ -0,0 +1,5 @@
+const Console = () => {
+  return <section>console</section>;
+};
+
+export default Console;

+ 12 - 0
client/src/router/Config.ts

@@ -1,4 +1,6 @@
+import Collections from '../pages/collections/Collections';
 import Connect from '../pages/connect/Connect';
+import Console from '../pages/console/Console';
 import Overview from '../pages/overview/Overview';
 
 const RouterConfig = [
@@ -12,6 +14,16 @@ const RouterConfig = [
     component: Connect,
     auth: false,
   },
+  {
+    path: '/collections',
+    component: Collections,
+    auth: true,
+  },
+  {
+    path: '/console',
+    component: Console,
+    auth: true,
+  },
 ];
 
 export default RouterConfig;

+ 21 - 1
client/src/router/Router.tsx

@@ -1,18 +1,38 @@
 import { Switch, Route, BrowserRouter, Redirect } from 'react-router-dom';
 import routerConfig from './Config';
 import Layout from '../components/layout/Layout';
+import { useContext } from 'react';
+import { rootContext } from '../context/Root';
 /**
  * Global responsible for global effect
  * Layout responsible for ui view
  *
  */
 const RouterWrapper = () => {
+  const { isAuth } = useContext(rootContext);
+
   return (
     <BrowserRouter>
       <Layout>
         <Switch>
           {routerConfig.map(v => (
-            <Route key={v.path} exact path={v.path} component={v.component} />
+            <Route
+              exact
+              key={v.path}
+              path={v.path}
+              render={() => {
+                const Page = v.component;
+                return isAuth || !v.auth ? (
+                  <Page />
+                ) : (
+                  <Redirect
+                    to={{
+                      pathname: '/connect',
+                    }}
+                  />
+                );
+              }}
+            />
           ))}
 
           <Route