diff --git a/package-lock.json b/package-lock.json
index b8e1f4e..aebfe43 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1408,6 +1408,27 @@
"@chakra-ui/utils": "1.8.2"
}
},
+ "@chakra-ui/icons": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-1.0.15.tgz",
+ "integrity": "sha512-MMuPwmeCil9vAXceIN/Fxn6CNHbhkLofFQaKUfs+UaBsviiU2tvS0nqGaxm/9FNzLr5ithPVWpbz3uV7DXc77g==",
+ "requires": {
+ "@chakra-ui/icon": "1.1.11",
+ "@types/react": "^17.0.0"
+ },
+ "dependencies": {
+ "@types/react": {
+ "version": "17.0.19",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.19.tgz",
+ "integrity": "sha512-sX1HisdB1/ZESixMTGnMxH9TDe8Sk709734fEQZzCV/4lSu9kJCPbo2PbTRoZM+53Pp0P10hYVyReUueGwUi4A==",
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ }
+ }
+ },
"@chakra-ui/image": {
"version": "1.0.18",
"resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-1.0.18.tgz",
diff --git a/package.json b/package.json
index b3bd5d6..5ac4519 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@chakra-ui/icons": "^1.0.15",
"@chakra-ui/react": "^1.0.0",
"@emotion/react": "^11.0.0",
"@emotion/styled": "^11.0.0",
diff --git a/src/components/Header/AppName.tsx b/src/components/Header/AppName.tsx
index c255273..b4bf84e 100644
--- a/src/components/Header/AppName.tsx
+++ b/src/components/Header/AppName.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { Flex, Box, Text, Heading } from '@chakra-ui/react';
import { MdSchool } from 'react-icons/md';
+import config from '../../config';
const AppName: React.FC = () => {
return (
@@ -8,11 +9,11 @@ const AppName: React.FC = () => {
-
-
- App name
+
+
+ {config.metaData.appName}
- App description
+ {config.metaData.appDescription}
);
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
index e2a3214..da9fbfe 100644
--- a/src/components/Header/Header.tsx
+++ b/src/components/Header/Header.tsx
@@ -3,8 +3,12 @@ import { Flex, Box } from '@chakra-ui/react';
import AppName from './AppName';
import UserInfo from './User/UserInfo';
import UserNotifications from './User/UserNotifications';
+import useWindowDimensions from '../../hooks/useWindowDimensions';
+import NavMobile from '../Nav/Mobile/Nav';
const Header: React.FC = () => {
+ const { isDesktop } = useWindowDimensions();
+
return (
{
>
-
-
+ {isDesktop ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx
index d19b26e..b82619e 100644
--- a/src/components/Layout/Layout.tsx
+++ b/src/components/Layout/Layout.tsx
@@ -1,11 +1,12 @@
import React from 'react';
import { Flex, Box } from '@chakra-ui/react';
import Header from '../Header/Header';
-import Nav from '../Nav/Nav';
+import NavDesktop from '../Nav/Desktop/Nav';
import { observer } from 'mobx-react';
import { useRootStore } from '../../hooks/useRootStore';
import { useErrorHandler } from 'react-error-boundary';
import { ColorModeSwitcher } from '../ColorModeSwitcher/ColorModeSwitcher';
+import useWindowDimensions from '../../hooks/useWindowDimensions';
/**
*
@@ -13,6 +14,7 @@ import { ColorModeSwitcher } from '../ColorModeSwitcher/ColorModeSwitcher';
* Сomponent for rendering the current page.
*/
const Layout: React.FC = ({ children }) => {
+ const { isDesktop } = useWindowDimensions();
const { authStore, userStore } = useRootStore();
const handleError = useErrorHandler();
@@ -27,10 +29,19 @@ const Layout: React.FC = ({ children }) => {
return (
<>
-
-
+ {isDesktop && }
-
+
{children}
diff --git a/src/components/Nav/Desktop/Nav.tsx b/src/components/Nav/Desktop/Nav.tsx
new file mode 100644
index 0000000..0064995
--- /dev/null
+++ b/src/components/Nav/Desktop/Nav.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import NavLink from './NavLink';
+import config from '../../../config';
+import { Flex, Box } from '@chakra-ui/react';
+
+/**
+ *
+ * Nav component
+ * Component for navigating the application.
+ */
+const Nav: React.FC = () => {
+ return (
+
+
+
+ {config.links.map((link, i) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default Nav;
diff --git a/src/components/Nav/NavLink.tsx b/src/components/Nav/Desktop/NavLink.tsx
similarity index 56%
rename from src/components/Nav/NavLink.tsx
rename to src/components/Nav/Desktop/NavLink.tsx
index fe7c7af..d2a5a48 100644
--- a/src/components/Nav/NavLink.tsx
+++ b/src/components/Nav/Desktop/NavLink.tsx
@@ -1,43 +1,35 @@
import React from 'react';
import { Box, useColorMode } from '@chakra-ui/react';
-import type { History } from 'history';
-import type { LinkType } from '../../interfaces';
-import type { ActiveLinkState } from './Nav';
+import type { LinkType } from '../../../interfaces';
+import { useHistory } from 'react-router-dom';
+
+type ActiveLinkState = {
+ title: string | null;
+};
/**
*
* NavLink component
* Returns the link component for navigation.
- *
*/
-
-interface NavLinkProps {
- handleOnHoverLink: (title: string) => void;
- handleOnLeaveLink: () => void;
- handleRoute: (location: string) => void;
- history: History;
- activeLink: ActiveLinkState;
- link: LinkType;
-}
-
-const NavLink: React.FC = ({
- handleOnHoverLink,
- handleOnLeaveLink,
- handleRoute,
- history,
- activeLink,
- link,
-}) => {
+const NavLink: React.FC<{ link: LinkType; onCloseDrawer?: () => void }> = ({ link, onCloseDrawer }) => {
+ const [activeLink, setActiveLink] = React.useState({ title: null });
const { colorMode } = useColorMode();
- const onMouseEnter = () => handleOnHoverLink(link.title);
- const onClick = () => handleRoute(link.location);
+ const history = useHistory();
+
+ const handleClick = () => {
+ history.push(link.location);
+
+ /* Close mobile drawer on click link */
+ onCloseDrawer && onCloseDrawer();
+ };
return (
setActiveLink({ title: link.title })}
+ onMouseLeave={() => setActiveLink({ title: null })}
+ onClick={handleClick}
display='flex'
margin='3px 0'
width='100%'
diff --git a/src/components/Nav/Mobile/Nav.tsx b/src/components/Nav/Mobile/Nav.tsx
new file mode 100644
index 0000000..3a6f724
--- /dev/null
+++ b/src/components/Nav/Mobile/Nav.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { Flex } from '@chakra-ui/layout';
+import { Drawer, DrawerOverlay, DrawerContent, DrawerHeader, DrawerBody } from '@chakra-ui/modal';
+import { HamburgerIcon } from '@chakra-ui/icons';
+import { useDisclosure, IconButton } from '@chakra-ui/react';
+import config from '../../../config';
+import NavLink from '../Desktop/NavLink';
+
+const NavMobile: React.FC = () => {
+ const { isOpen, onOpen, onClose } = useDisclosure();
+
+ return (
+ <>
+ } onClick={onOpen} />
+
+
+
+ Navigation
+
+
+ {config.links.map((link, i) => (
+
+ ))}
+
+
+
+
+ >
+ );
+};
+
+export default NavMobile;
diff --git a/src/components/Nav/Nav.tsx b/src/components/Nav/Nav.tsx
deleted file mode 100644
index 4bb9f42..0000000
--- a/src/components/Nav/Nav.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import React, { useState } from 'react';
-import { useHistory } from 'react-router-dom';
-import NavLink from './NavLink';
-import config from '../../config';
-import { Flex, Box } from '@chakra-ui/react';
-
-/**
- *
- * Nav component
- * Component for navigating the application.
- */
-export type ActiveLinkState = {
- title: string | null;
-};
-
-const Nav: React.FC = () => {
- const [activeLink, setActiveLink] = useState({ title: null });
- const history = useHistory();
-
- const handleRoute = (location: string): void => history.push(location);
- const handleOnHoverLink = (title: string): void => setActiveLink({ title });
- const handleOnLeaveLink = (): void => setActiveLink({ title: null });
-
- return (
-
-
-
- {config.links.map((link, i) => (
-
- ))}
-
-
-
- );
-};
-
-export default Nav;
diff --git a/src/config.ts b/src/config.ts
index fceb7fb..47c2a88 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -2,6 +2,10 @@ import { MdAccountCircle, MdCollectionsBookmark, MdHome, MdMessage } from 'react
import { IAppConfig } from './interfaces';
const config: IAppConfig = {
+ metaData: {
+ appName: 'Educt',
+ appDescription: 'Learn management system',
+ },
links: [
{ location: '/', title: 'Home', icon: MdHome },
{ location: '/courses', title: 'Courses', icon: MdCollectionsBookmark },
diff --git a/src/hooks/useWindowDimensions.ts b/src/hooks/useWindowDimensions.ts
new file mode 100644
index 0000000..e52e813
--- /dev/null
+++ b/src/hooks/useWindowDimensions.ts
@@ -0,0 +1,28 @@
+import React from 'react';
+
+const useWindowDimensions = () => {
+ const [windowDimensions, setWindowDimensions] = React.useState({
+ width: window.innerWidth,
+ height: window.innerHeight,
+ });
+
+ React.useEffect(() => {
+ /**
+ * Update state on resize
+ */
+ const handleResize = () => setWindowDimensions({ width: window.innerWidth, height: window.innerHeight });
+
+ window.addEventListener('resize', handleResize);
+
+ return () => window.removeEventListener('resize', handleResize);
+ });
+
+ return {
+ windowDimensions,
+ isDesktop: windowDimensions.width > 768,
+ isTablet: windowDimensions.width < 768,
+ isMobile: windowDimensions.width < 568,
+ };
+};
+
+export default useWindowDimensions;
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 9a793d4..41b8ce9 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -9,6 +9,10 @@ export type LinkType = {
};
export interface IAppConfig {
+ metaData: {
+ appName: string;
+ appDescription: string;
+ };
links: LinkType[];
}
diff --git a/src/pages/auth.tsx b/src/pages/auth.tsx
index 0e9da67..648aa1e 100644
--- a/src/pages/auth.tsx
+++ b/src/pages/auth.tsx
@@ -28,7 +28,7 @@ const AuthPage: React.FC = () => {
-
+
diff --git a/src/stores/UserStore.ts b/src/stores/UserStore.ts
index 6fb9346..e6b245d 100644
--- a/src/stores/UserStore.ts
+++ b/src/stores/UserStore.ts
@@ -1,5 +1,5 @@
import { AxiosInstance } from 'axios';
-import { autorun, makeAutoObservable } from 'mobx';
+import { makeAutoObservable, runInAction } from 'mobx';
import { IUserResult } from '../interfaces';
import UserService from '../services/UserService';
import RootStore from './RootStore';
@@ -30,7 +30,7 @@ export default class UserStore {
try {
const result = await this.userService.fetchMe();
- autorun(() => {
+ runInAction(() => {
this.me = result.data;
});