mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +03:00
feat: stores
This commit is contained in:
parent
2b655ddd09
commit
73841e8f68
73
package-lock.json
generated
73
package-lock.json
generated
@ -2819,6 +2819,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/history": {
|
||||||
|
"version": "4.7.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
||||||
|
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/html-minifier-terser": {
|
"@types/html-minifier-terser": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz",
|
||||||
@ -2845,6 +2851,12 @@
|
|||||||
"@types/istanbul-lib-report": "*"
|
"@types/istanbul-lib-report": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/js-cookie": {
|
||||||
|
"version": "2.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
|
||||||
|
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
"version": "7.0.9",
|
"version": "7.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
|
||||||
@ -2924,6 +2936,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-helmet": {
|
||||||
|
"version": "6.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.2.tgz",
|
||||||
|
"integrity": "sha512-dcfAZNlWb5JYFbO9CGfrPWLJAyFcT6UeR3u35eBbv8liY2Rg4K7fM1G5+HnwVgot+C+kVwXAZ8pLEn2jsMfTDg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/react-router": {
|
||||||
|
"version": "5.1.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz",
|
||||||
|
"integrity": "sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/history": "*",
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/react-router-dom": {
|
||||||
|
"version": "5.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz",
|
||||||
|
"integrity": "sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/history": "*",
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-router": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/resolve": {
|
"@types/resolve": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
|
||||||
@ -3763,6 +3805,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.2.tgz",
|
||||||
"integrity": "sha512-5LMaDRWm8ZFPAEdzTYmgjjEdj1YnQcpfrVajO/sn/LhbpGp0Y0H64c2hLZI1gRMxfA+w1S71Uc/nHaOXgcCvGg=="
|
"integrity": "sha512-5LMaDRWm8ZFPAEdzTYmgjjEdj1YnQcpfrVajO/sn/LhbpGp0Y0H64c2hLZI1gRMxfA+w1S71Uc/nHaOXgcCvGg=="
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||||
|
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"axobject-query": {
|
"axobject-query": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
||||||
@ -9493,6 +9543,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"js-cookie": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-oUbbplKuH07/XX2YD2+Q+GMiPpnVXaRz8npE7suhBH9QEkJe2W7mQ6rwuMXHue3fpfcftQwzgyvGzIHyfCSngQ=="
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@ -10121,6 +10176,24 @@
|
|||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mobx": {
|
||||||
|
"version": "6.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx/-/mobx-6.3.2.tgz",
|
||||||
|
"integrity": "sha512-xGPM9dIE1qkK9Nrhevp0gzpsmELKU4MFUJRORW/jqxVFIHHWIoQrjDjL8vkwoJYY3C2CeVJqgvl38hgKTalTWg=="
|
||||||
|
},
|
||||||
|
"mobx-react": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-KHUjZ3HBmZlNnPd1M82jcdVsQRDlfym38zJhZEs33VxyVQTvL77hODCArq6+C1P1k/6erEeo2R7rpE7ZeOL7dg==",
|
||||||
|
"requires": {
|
||||||
|
"mobx-react-lite": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mobx-react-lite": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-q5+UHIqYCOpBoFm/PElDuOhbcatvTllgRp3M1s+Hp5j0Z6XNgDbgqxawJ0ZAUEyKM8X1zs70PCuhAIzX1f4Q/g=="
|
||||||
|
},
|
||||||
"move-concurrently": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||||
|
|||||||
@ -12,7 +12,11 @@
|
|||||||
"@types/jest": "^25.0.0",
|
"@types/jest": "^25.0.0",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
"@types/react-dom": "^16.9.0",
|
"@types/react-dom": "^16.9.0",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"framer-motion": "^4.0.0",
|
"framer-motion": "^4.0.0",
|
||||||
|
"js-cookie": "^3.0.0",
|
||||||
|
"mobx": "^6.3.2",
|
||||||
|
"mobx-react": "^7.2.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
@ -47,6 +51,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^16.14.13"
|
"@types/js-cookie": "^2.2.7",
|
||||||
|
"@types/react": "^16.14.13",
|
||||||
|
"@types/react-helmet": "^6.1.2",
|
||||||
|
"@types/react-router-dom": "^5.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/App.tsx
25
src/App.tsx
@ -1,14 +1,21 @@
|
|||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import { ChakraProvider, Box, Text, Grid, theme } from "@chakra-ui/react";
|
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
||||||
import { ColorModeSwitcher } from "./ColorModeSwitcher";
|
import PrivateRoute from './components/PrivateRoute/PrivateRoute';
|
||||||
|
import { ChakraProvider, theme } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pages
|
||||||
|
*/
|
||||||
|
import MainPage from './pages/main';
|
||||||
|
import AuthPage from './pages/auth';
|
||||||
|
|
||||||
export const App = () => (
|
export const App = () => (
|
||||||
<ChakraProvider theme={theme}>
|
<ChakraProvider theme={theme}>
|
||||||
<Box textAlign="center" fontSize="xl">
|
<Router>
|
||||||
<Grid minH="100vh" p={3}>
|
<Switch>
|
||||||
<ColorModeSwitcher justifySelf="flex-end" />
|
<PrivateRoute exact path='/' component={MainPage} title='Main' />
|
||||||
<Text>Appliction</Text>
|
<Route path='/auth' component={AuthPage} />
|
||||||
</Grid>
|
</Switch>
|
||||||
</Box>
|
</Router>
|
||||||
</ChakraProvider>
|
</ChakraProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
10
src/axios/index.ts
Normal file
10
src/axios/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { API_BASE_URL } from '../constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create axios config
|
||||||
|
*/
|
||||||
|
export default axios.create({
|
||||||
|
baseURL: API_BASE_URL,
|
||||||
|
responseType: 'json',
|
||||||
|
});
|
||||||
104
src/components/Auth/Auth.tsx
Normal file
104
src/components/Auth/Auth.tsx
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { MdAccountCircle, MdVpnKey } from 'react-icons/md';
|
||||||
|
import {
|
||||||
|
Flex,
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
InputGroup,
|
||||||
|
Input,
|
||||||
|
InputLeftElement,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useRootStore } from '../../hooks/useRootStore';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
type LoginDataInput = {
|
||||||
|
login: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize user through a form.
|
||||||
|
*/
|
||||||
|
const Auth: React.FC = () => {
|
||||||
|
const [loginData, setLoginData] = useState<LoginDataInput>({
|
||||||
|
login: '',
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
const { authStore } = useRootStore();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const { login, password } = loginData;
|
||||||
|
await authStore.login(login, password);
|
||||||
|
history.push('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex minHeight='100vh' align='center' justifyContent='center'>
|
||||||
|
<Box
|
||||||
|
p={8}
|
||||||
|
my={40}
|
||||||
|
width='full'
|
||||||
|
maxWidth='450px'
|
||||||
|
borderWidth={1}
|
||||||
|
borderRadius={8}>
|
||||||
|
<Box textAlign='center'>
|
||||||
|
<Flex justifyContent='center'>
|
||||||
|
{/* <LogoIcon viewBox='0 0 24 24' boxSize={'64px'} fill='blue.600' /> */}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
<Box my='30px'>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<FormControl isRequired={true}>
|
||||||
|
<FormLabel>Login</FormLabel>
|
||||||
|
<InputGroup>
|
||||||
|
<InputLeftElement children={<Icon as={MdAccountCircle} />} />
|
||||||
|
<Input
|
||||||
|
type='text'
|
||||||
|
name='login'
|
||||||
|
placeholder='email@mail.com'
|
||||||
|
value={loginData.login}
|
||||||
|
onChange={(e) =>
|
||||||
|
setLoginData({ ...loginData, login: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl mt={5} isRequired={true}>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<InputGroup>
|
||||||
|
<InputLeftElement children={<Icon as={MdVpnKey} />} />
|
||||||
|
<Input
|
||||||
|
type='password'
|
||||||
|
name='password'
|
||||||
|
placeholder='********'
|
||||||
|
value={loginData.password}
|
||||||
|
onChange={(e) =>
|
||||||
|
setLoginData({ ...loginData, password: e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
loadingText='Logging in...'
|
||||||
|
type='submit'
|
||||||
|
colorScheme='blue'
|
||||||
|
variant='outline'
|
||||||
|
width='full'
|
||||||
|
mt={4}>
|
||||||
|
Log in
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer(Auth);
|
||||||
29
src/components/Layout/Layout.tsx
Normal file
29
src/components/Layout/Layout.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Flex, Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Layout component
|
||||||
|
* Сomponent for rendering the current page.
|
||||||
|
*/
|
||||||
|
const Layout: React.FC = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<Flex minH='100vh' flexDirection='column'>
|
||||||
|
<Box pl='18rem' mt='4rem' mb='4rem' flex='1 0 auto'>
|
||||||
|
<Box
|
||||||
|
as='main'
|
||||||
|
mr='auto'
|
||||||
|
ml='auto'
|
||||||
|
maxW='85rem'
|
||||||
|
pl={5}
|
||||||
|
pr={5}
|
||||||
|
pt='2rem'
|
||||||
|
pb='2rem'>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
43
src/components/PrivateRoute/PrivateRoute.tsx
Normal file
43
src/components/PrivateRoute/PrivateRoute.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import Layout from '../Layout/Layout';
|
||||||
|
import { Route, Redirect } from 'react-router-dom';
|
||||||
|
import { IPrivateRouteProps } from '../../interfaces';
|
||||||
|
import { useRootStore } from '../../hooks/useRootStore';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user is loggedIn by reactive variable and if not,
|
||||||
|
* then a redirect to "/auth" route.
|
||||||
|
*/
|
||||||
|
const PrivateRoute: React.FC<IPrivateRouteProps> = ({
|
||||||
|
children,
|
||||||
|
component: Component,
|
||||||
|
title,
|
||||||
|
...options
|
||||||
|
}) => {
|
||||||
|
const { authStore } = useRootStore();
|
||||||
|
const isLoggedIn = authStore.isLoggedIn;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
{...options}
|
||||||
|
render={(props) =>
|
||||||
|
isLoggedIn ? (
|
||||||
|
<Layout>
|
||||||
|
<Helmet>
|
||||||
|
<title>{title} • App </title>
|
||||||
|
</Helmet>
|
||||||
|
<Component {...props} title={title} />
|
||||||
|
</Layout>
|
||||||
|
) : (
|
||||||
|
<Redirect
|
||||||
|
to={{ pathname: '/auth', state: { from: props.location } }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer(PrivateRoute);
|
||||||
1
src/constants.ts
Normal file
1
src/constants.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const API_BASE_URL: string = 'http://localhost:3333/api';
|
||||||
6
src/contexts/index.ts
Normal file
6
src/contexts/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import RootStore from '../stores/RootStore';
|
||||||
|
|
||||||
|
export const StoreContext = React.createContext<RootStore | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
11
src/hooks/useRootStore.ts
Normal file
11
src/hooks/useRootStore.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { StoreContext } from '../contexts';
|
||||||
|
|
||||||
|
export const useRootStore = () => {
|
||||||
|
const context = React.useContext(StoreContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useRootStore must be used within RootStoreProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -1,15 +1,18 @@
|
|||||||
import { ColorModeScript } from "@chakra-ui/react";
|
import { ColorModeScript } from '@chakra-ui/react';
|
||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from 'react-dom';
|
||||||
import { App } from "./App";
|
import { App } from './App';
|
||||||
import reportWebVitals from "./reportWebVitals";
|
import { RootStoreProvider } from './providers';
|
||||||
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<>
|
||||||
<ColorModeScript />
|
<ColorModeScript />
|
||||||
<App />
|
<RootStoreProvider>
|
||||||
</React.StrictMode>,
|
<App />
|
||||||
document.getElementById("root")
|
</RootStoreProvider>
|
||||||
|
</>,
|
||||||
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
|||||||
17
src/interfaces/index.ts
Normal file
17
src/interfaces/index.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { RouteProps } from 'react-router-dom';
|
||||||
|
import type { RouteComponentProps } from 'react-router-dom';
|
||||||
|
|
||||||
|
export interface IDataResult {
|
||||||
|
data: any;
|
||||||
|
message: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPageProps extends RouteComponentProps {
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPrivateRouteProps extends RouteProps {
|
||||||
|
component: React.FC<any>;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
30
src/pages/auth.tsx
Normal file
30
src/pages/auth.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import React from 'react';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import Auth from '../components/Auth/Auth';
|
||||||
|
import { useRootStore } from '../hooks/useRootStore';
|
||||||
|
import { IPageProps } from '../interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth page
|
||||||
|
*/
|
||||||
|
const AuthPage: React.FC<IPageProps> = () => {
|
||||||
|
const { authStore } = useRootStore();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
if (authStore.isLoggedIn) {
|
||||||
|
history.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Helmet>
|
||||||
|
<title>Auth</title>
|
||||||
|
</Helmet>
|
||||||
|
<Auth />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default observer(AuthPage);
|
||||||
35
src/pages/main.tsx
Normal file
35
src/pages/main.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Heading,
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
Grid,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { IPageProps } from '../interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main page
|
||||||
|
*/
|
||||||
|
const MainPage: React.FC<IPageProps> = ({ title, history }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Breadcrumb fontWeight='medium' fontSize='sm'>
|
||||||
|
<BreadcrumbItem isCurrentPage>
|
||||||
|
<BreadcrumbLink href='/'>{title}</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</Breadcrumb>
|
||||||
|
<Box marginTop={10}>
|
||||||
|
<Box>
|
||||||
|
<Heading as='h1'>Main page</Heading>
|
||||||
|
<Grid templateColumns='3fr 1fr' mt='25px'>
|
||||||
|
...
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainPage;
|
||||||
13
src/providers/index.tsx
Normal file
13
src/providers/index.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { StoreContext } from '../contexts';
|
||||||
|
import RootStore from '../stores/RootStore';
|
||||||
|
|
||||||
|
let store: RootStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root store provider
|
||||||
|
*/
|
||||||
|
export const RootStoreProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
const root = store ?? new RootStore();
|
||||||
|
return <StoreContext.Provider value={root}>{children}</StoreContext.Provider>;
|
||||||
|
};
|
||||||
24
src/services/AuthService.ts
Normal file
24
src/services/AuthService.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import api from '../axios/index';
|
||||||
|
import { IDataResult } from '../interfaces';
|
||||||
|
|
||||||
|
interface ILoginResult extends IDataResult {
|
||||||
|
data: {
|
||||||
|
token: string;
|
||||||
|
type: string;
|
||||||
|
expires_at: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AuthService {
|
||||||
|
public async requestLogin(
|
||||||
|
login: string,
|
||||||
|
password: string
|
||||||
|
): Promise<ILoginResult> {
|
||||||
|
const result = await api.post('v1/auth/login', {
|
||||||
|
login,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/stores/AuthStore.ts
Normal file
42
src/stores/AuthStore.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { makeAutoObservable } from 'mobx';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
import AuthService from '../services/AuthService';
|
||||||
|
import RootStore from './RootStore';
|
||||||
|
|
||||||
|
export default class AuthStore {
|
||||||
|
public root: RootStore;
|
||||||
|
|
||||||
|
public token: string | null = null;
|
||||||
|
|
||||||
|
public isLoggedIn: boolean = !!Cookies.get('logged_in');
|
||||||
|
|
||||||
|
public authService: AuthService;
|
||||||
|
|
||||||
|
constructor(root: RootStore) {
|
||||||
|
this.root = root;
|
||||||
|
this.authService = new AuthService();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get getToken(): string | null {
|
||||||
|
return this.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setIsLoggedIn(value: boolean) {
|
||||||
|
this.isLoggedIn = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setToken(token: string) {
|
||||||
|
Cookies.set('logged_in', 'true');
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async login(login: string, password: string) {
|
||||||
|
const result = await this.authService.requestLogin(login, password);
|
||||||
|
|
||||||
|
this.setIsLoggedIn(true);
|
||||||
|
this.setToken(result.data.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public logout() {}
|
||||||
|
}
|
||||||
9
src/stores/RootStore.ts
Normal file
9
src/stores/RootStore.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import AuthStore from './AuthStore';
|
||||||
|
|
||||||
|
export default class RootStore {
|
||||||
|
public authStore: AuthStore;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.authStore = new AuthStore(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user