diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..d7e0a43 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "singleQuote": true, + "useTabs": false, + "tabWidth": 2, + "semi": true, + "jsxSingleQuote": true, + "arrowParens": "avoid", + "endOfLine": "auto", + "printWidth": 120 +} diff --git a/package-lock.json b/package-lock.json index 96ff73e..b8e1f4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12527,6 +12527,14 @@ "scheduler": "^0.20.2" } }, + "react-error-boundary": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.3.tgz", + "integrity": "sha512-A+F9HHy9fvt9t8SNDlonq01prnU8AmkjvGKV4kk8seB9kU3xMEO8J/PQlLVmoOIDODl5U2kufSBs4vrWIqhsAA==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, "react-error-overlay": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", diff --git a/package.json b/package.json index d1d27b3..b3bd5d6 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "mobx-react": "^7.2.0", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-error-boundary": "^3.1.3", "react-helmet": "^6.1.0", "react-hook-form": "^7.12.2", "react-icons": "^3.11.0", diff --git a/src/App.tsx b/src/App.tsx index e6c102f..fadae32 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,42 +1,34 @@ import React from 'react'; +import { observer } from 'mobx-react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import PrivateRoute from './components/PrivateRoute/PrivateRoute'; -import { useRootStore } from './hooks/useRootStore'; -import { observer } from 'mobx-react'; +import ErrorFallback from './components/ErrorFallback'; /** * Pages */ import MainPage from './pages/main'; +import CoursesPage from './pages/courses'; +import MessagesPage from './pages/messages'; +import ProfilePage from './pages/profile'; import AuthPage from './pages/auth'; import NotFoundPage from './pages/404'; -import { useToast } from '@chakra-ui/react'; -import { autorun } from 'mobx'; +import { ErrorBoundary } from 'react-error-boundary'; const App = () => { - const rootStore = useRootStore(); - const toast = useToast(); - - React.useEffect(() => { - autorun(() => { - if (rootStore.networkApiError !== undefined) { - toast({ - title: `Network error while connecting to the server.`, - description: 'Try your request later.', - status: 'error', - }); - } - }); - }); - return ( - - - - - - - + + + + + + + + + + + + ); }; diff --git a/src/components/Auth/AuthForm.tsx b/src/components/Auth/AuthForm.tsx index df965ce..75ce643 100644 --- a/src/components/Auth/AuthForm.tsx +++ b/src/components/Auth/AuthForm.tsx @@ -1,18 +1,11 @@ import React from 'react'; import { MdAccountCircle, MdVpnKey } from 'react-icons/md'; -import { - FormControl, - FormLabel, - InputGroup, - Input, - InputLeftElement, - Button, - Icon, -} from '@chakra-ui/react'; +import { FormControl, FormLabel, InputGroup, Input, InputLeftElement, Button, Icon, useToast } from '@chakra-ui/react'; import { useRootStore } from '../../hooks/useRootStore'; import { useHistory } from 'react-router-dom'; import { observer } from 'mobx-react'; import { SubmitHandler, useForm } from 'react-hook-form'; +import { useErrorHandler } from 'react-error-boundary'; type AuthInputs = { login: string; @@ -23,13 +16,35 @@ const AuthForm: React.FC = () => { const { register, reset, handleSubmit } = useForm(); const { authStore } = useRootStore(); const history = useHistory(); + const handleError = useErrorHandler(); + const toast = useToast(); - const onSubmit: SubmitHandler = async (data) => { + const onSubmit: SubmitHandler = async data => { try { await authStore.login(data.login, data.password); history.push('/'); + toast({ + title: `👋 Welcome back!`, + description: 'You are successfully logged in.', + isClosable: true, + status: 'success', + }); } catch (error) { - console.log('error while log in '); + if (error.response) { + switch (error.response.status) { + case 404: + toast({ title: 'User not found in a system.', status: 'error' }); + break; + case 401: + toast({ title: 'Invalid password.', status: 'error' }); + break; + default: + handleError(error); + break; + } + } else { + handleError(error); + } } finally { reset(); } @@ -41,22 +56,14 @@ const AuthForm: React.FC = () => { Login } /> - + Password } /> - + diff --git a/src/components/ColorModeSwitcher/ColorModeSwitcher.tsx b/src/components/ColorModeSwitcher/ColorModeSwitcher.tsx index 2862482..4560b32 100644 --- a/src/components/ColorModeSwitcher/ColorModeSwitcher.tsx +++ b/src/components/ColorModeSwitcher/ColorModeSwitcher.tsx @@ -1,26 +1,21 @@ -import * as React from "react"; -import { - useColorMode, - useColorModeValue, - IconButton, - IconButtonProps, -} from "@chakra-ui/react"; -import { FaMoon, FaSun } from "react-icons/fa"; +import * as React from 'react'; +import { useColorMode, useColorModeValue, IconButton, IconButtonProps } from '@chakra-ui/react'; +import { FaMoon, FaSun } from 'react-icons/fa'; -type ColorModeSwitcherProps = Omit; +type ColorModeSwitcherProps = Omit; -export const ColorModeSwitcher: React.FC = (props) => { +export const ColorModeSwitcher: React.FC = props => { const { toggleColorMode } = useColorMode(); - const text = useColorModeValue("dark", "light"); + const text = useColorModeValue('dark', 'light'); const SwitchIcon = useColorModeValue(FaMoon, FaSun); return ( } aria-label={`Switch to ${text} mode`} diff --git a/src/components/ErrorFallback.tsx b/src/components/ErrorFallback.tsx new file mode 100644 index 0000000..e0a66b2 --- /dev/null +++ b/src/components/ErrorFallback.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { useToast, Flex, Box, Heading, Text } from '@chakra-ui/react'; +import { MdError } from 'react-icons/md'; + +const ErrorFallback: React.FC<{ error: any }> = props => { + const { error } = props; + const toast = useToast(); + + React.useEffect(() => { + toast({ title: `${error.message}`, duration: 10000, isClosable: true, status: 'error' }); + }); + + return ( + + + + + + Something went wrong + + Sorry, something went wrong. Reload page and try your request again later. + + ); +}; + +export default ErrorFallback; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index bb6e3fe..bf2cddd 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -16,14 +16,16 @@ const Header: React.FC = () => { borderBottomWidth={1} width='100%' height='4rem' - zIndex={4}> + zIndex={4} + > + paddingRight='1.5rem' + > diff --git a/src/components/Header/User/UserInfo.tsx b/src/components/Header/User/UserInfo.tsx index c99f886..fd14f1c 100644 --- a/src/components/Header/User/UserInfo.tsx +++ b/src/components/Header/User/UserInfo.tsx @@ -12,6 +12,7 @@ import { Avatar, Text, Link, + useToast, } from '@chakra-ui/react'; import { MdSettings, MdExitToApp } from 'react-icons/md'; import { observer } from 'mobx-react'; @@ -21,13 +22,13 @@ import { useHistory } from 'react-router-dom'; const UserInfo: React.FC = () => { const { authStore } = useRootStore(); const history = useHistory(); + const toast = useToast(); - const onLogoutHandler = async ( - e: React.MouseEvent - ) => { + const onLogoutHandler = async (e: React.MouseEvent) => { try { await authStore.logout(); history.push('/auth'); + toast({ title: 'You are logged out.', isClosable: true, status: 'info' }); } catch (error) { console.error(error); } diff --git a/src/components/Header/User/UserNotifications.tsx b/src/components/Header/User/UserNotifications.tsx index a547239..65aa9a8 100644 --- a/src/components/Header/User/UserNotifications.tsx +++ b/src/components/Header/User/UserNotifications.tsx @@ -22,7 +22,8 @@ const UserNotifications: React.FC = () => { width: '15px', height: '15px', borderRadius: '100%', - }}> + }} + > ); diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index d4040d0..f19262d 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Flex, Box } from '@chakra-ui/react'; import Header from '../Header/Header'; +import Nav from '../Nav/Nav'; /** * @@ -11,17 +12,10 @@ const Layout: React.FC = ({ children }) => { return ( <>
+