лаба 5, сделала авторизацию и регистрацию

This commit is contained in:
a.puchkina 2024-01-12 00:23:31 +04:00
parent 8bf96a862c
commit 7395b0e77b
21 changed files with 498 additions and 220 deletions

View File

@ -1,4 +1,57 @@
{ {
"currentUser": {
"8": ""
},
"users": [
{
"email": "anna",
"name": "anna",
"password": "anna",
"id": 1
},
{
"email": "a1",
"name": "a1",
"password": "a1",
"id": 2
},
{
"email": "qwe",
"name": "qwe",
"password": "qwe",
"id": 3
},
{
"email": "a8",
"name": "a8",
"password": "a8",
"id": 4
},
{
"email": "a8",
"name": "a8",
"password": "a8",
"id": 5
},
{
"email": "a8",
"name": "a8",
"password": "a8",
"id": 6
},
{
"email": "a6",
"name": "a6",
"password": "a6",
"id": 7
},
{
"email": "a7",
"name": "a1",
"password": "a8",
"id": 8
}
],
"types": [ "types": [
{ {
"id": 1, "id": 1,

View File

@ -8,7 +8,7 @@
"name": "lec4", "name": "lec4",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"axios": "^1.6.1", "axios": "^1.6.5",
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
@ -1356,11 +1356,11 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.6.1", "version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.4",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"proxy-from-env": "^1.1.0" "proxy-from-env": "^1.1.0"
} }

View File

@ -11,7 +11,7 @@
"prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'" "prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.1", "axios": "^1.6.5",
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",

View File

@ -4,9 +4,17 @@ import { Toaster } from 'react-hot-toast';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { CartProvider } from './components/cart/CartContext.jsx'; import { CartProvider } from './components/cart/CartContext.jsx';
import Navigation from './components/navigation/Navigation.jsx'; import Navigation from './components/navigation/Navigation.jsx';
import UserContext from './components/users/UserContext.jsx';
import appHook from './components/users/AppHook';
const App = ({ routes }) => { const App = ({ routes }) => {
const {
user, setUser, logged, setLogged, curUser, setCurUser,
} = appHook();
return ( return (
<UserContext.Provider value={{
user, setUser, logged, setLogged, curUser, setCurUser,
}}>
<CartProvider> <CartProvider>
<Navigation routes={routes}></Navigation> <Navigation routes={routes}></Navigation>
<Container className='p-0' as='main' fluid> <Container className='p-0' as='main' fluid>
@ -14,6 +22,7 @@ const App = ({ routes }) => {
</Container> </Container>
<Toaster position='top-center' reverseOrder={true} /> <Toaster position='top-center' reverseOrder={true} />
</CartProvider> </CartProvider>
</UserContext.Provider>
); );
}; };

View File

@ -1,171 +0,0 @@
/* eslint-disable linebreak-style */
import { useRef, useState, useEffect } from 'react';
import axios from './api/axios';
const USER_REGEX = /^[A-z][A-z0-9-_]{3,23}$/;
const PWD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%]).{8,24}$/;
const REGISTER_URL = '/register';
const Register = () => {
const userRef = useRef();
const errRef = useRef();
const [user, setUser] = useState('');
const [validName, setValidName] = useState(false);
const [userFocus, setUserFocus] = useState(false);
const [pwd, setPwd] = useState('');
const [validPwd, setValidPwd] = useState(false);
const [pwdFocus, setPwdFocus] = useState(false);
const [matchPwd, setMatchPwd] = useState('');
const [validMatch, setValidMatch] = useState(false);
const [matchFocus, setMatchFocus] = useState(false);
const [errMsg, setErrMsg] = useState('');
const [success, setSuccess] = useState(false);
useEffect(() => {
userRef.current.focus();
}, []);
useEffect(() => {
setValidName(USER_REGEX.test(user));
}, [user]);
useEffect(() => {
setValidPwd(PWD_REGEX.test(pwd));
setValidMatch(pwd === matchPwd);
}, [pwd, matchPwd]);
useEffect(() => {
setErrMsg('');
}, [user, pwd, matchPwd]);
const handleSubmit = async (e) => {
e.preventDefault();
// if button enabled with JS hack
const v1 = USER_REGEX.test(user);
const v2 = PWD_REGEX.test(pwd);
if (!v1 || !v2) {
setErrMsg('Invalid Entry');
return;
}
try {
const response = await axios.post(
REGISTER_URL,
JSON.stringify({ user, pwd }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true,
},
);
console.log(response?.data);
console.log(response?.accessToken);
console.log(JSON.stringify(response));
setSuccess(true);
setUser('');
setPwd('');
setMatchPwd('');
} catch (err) {
if (!err?.response) {
setErrMsg('No Server Response');
} else if (err.response?.status === 409) {
setErrMsg('Username Taken');
} else {
setErrMsg('Registration Failed');
}
errRef.current.focus();
}
};
return (
<>
{success ? (
<section>
<h1>Success!</h1>
<p>
<a href="#">Sign In</a>
</p>
</section>
) : (
<section>
<p ref={errRef} className={errMsg ? 'errmsg' : 'offscreen'} aria-live="assertive">{errMsg}</p>
<h1>Register</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="username">
Username:
</label>
<input
type="text"
id="username"
ref={userRef}
autoComplete="off"
onChange={(e) => setUser(e.target.value)}
value={user}
required
aria-invalid={validName ? 'false' : 'true'}
aria-describedby="uidnote"
onFocus={() => setUserFocus(true)}
onBlur={() => setUserFocus(false)}
/>
<p id="uidnote" className={userFocus && user && !validName ? 'instructions' : 'offscreen'}>
4 to 24 characters.<br />
Must begin with a letter.<br />
Letters, numbers, underscores, hyphens allowed.
</p>
<label htmlFor="password">
Password:
</label>
<input
type="password"
id="password"
onChange={(e) => setPwd(e.target.value)}
value={pwd}
required
aria-invalid={validPwd ? 'false' : 'true'}
aria-describedby="pwdnote"
onFocus={() => setPwdFocus(true)}
onBlur={() => setPwdFocus(false)}
/>
<p id="pwdnote" className={pwdFocus && !validPwd ? 'instructions' : 'offscreen'}>
Allowed special characters: <span aria-label="exclamation mark">!</span>
<span aria-label="at symbol">@</span> <span aria-label="hashtag">#
</span> <span aria-label="dollar sign">$</span> <span aria-label="percent">%</span>
</p>
<label htmlFor="confirm_pwd">
Confirm Password:
</label>
<input
type="password"
id="confirm_pwd"
onChange={(e) => setMatchPwd(e.target.value)}
value={matchPwd}
required
aria-invalid={validMatch ? 'false' : 'true'}
aria-describedby="confirmnote"
onFocus={() => setMatchFocus(true)}
onBlur={() => setMatchFocus(false)}
/>
<p id="confirmnote" className={matchFocus && !validMatch ? 'instructions' : 'offscreen'}>
Must match the first password input field.
</p>
<button disabled={!!(!validName || !validPwd
|| !validMatch)}>Sign Up</button>
</form>
<p>
Already registered?<br />
<span className="line">
<a href="#">Sign In</a>
</span>
</p>
</section>
)}
</>
);
};
export default Register;

View File

@ -1,5 +0,0 @@
import axios from 'axios';
export default axios.create({
baseURL: 'http://localhost:3500',
});

View File

@ -2,15 +2,46 @@ import PropTypes from 'prop-types';
import { Container, Nav, Navbar } from 'react-bootstrap'; import { Container, Nav, Navbar } from 'react-bootstrap';
import { Journal, Cart2 } from 'react-bootstrap-icons'; import { Journal, Cart2 } from 'react-bootstrap-icons';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { useContext } from 'react';
import UserContext from '../users/UserContext.jsx';
import useCart from '../cart/CartHook'; import useCart from '../cart/CartHook';
import CurrentUserApiService from '../users/CurrenUserApiService';
import './Navigation.css'; import './Navigation.css';
const Navigation = ({ routes }) => { const Navigation = () => {
const { getCartSum } = useCart(); const { getCartSum } = useCart();
const location = useLocation(); const {
const indexPageLink = routes.filter((route) => route.index === false).shift(); logged, setLogged,
const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title')); } = useContext(UserContext);
let first;
let second;
let third;
const onClick = async () => {
localStorage.removeItem('User');
localStorage.removeItem('currentUser');
await CurrentUserApiService.deleteAll();
setLogged(false);
};
if (!logged) {
first = <div className="p-2">
<Nav.Link href="/reg" id='textNav'>Регистрация</Nav.Link>
</div>;
second = <div className="p-2">
<Nav.Link href="/page3" id='textNav'>Аккаунт</Nav.Link>
</div>;
third = <div></div>;
} else {
first = <div className="p-2">
<Nav.Link href="/" id='textNav' onClick={onClick} >Выйти</Nav.Link>
</div>;
second = <div className="p-2">
</div>;
third = <div>
<Navbar.Brand as={Link} to='/cart'>
<Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381;
</Navbar.Brand>
</div>;
}
return ( return (
<header> <header>
<Navbar expand='md' className='my-navbar'> <Navbar expand='md' className='my-navbar'>
@ -18,29 +49,57 @@ const Navigation = ({ routes }) => {
<Navbar.Brand as={Link} to='/page2'> <Navbar.Brand as={Link} to='/page2'>
<Journal className='d-inline-block align-top me-1 logo' /> <Journal className='d-inline-block align-top me-1 logo' />
</Navbar.Brand> </Navbar.Brand>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}> <Navbar.Brand as={Link} to={'/page1'?.path ?? '/'}>
ANNA ANNA
</Navbar.Brand> </Navbar.Brand>
<Navbar.Toggle aria-controls='main-navbar' /> <Navbar.Toggle aria-controls='main-navbar' />
<Navbar.Collapse id='main-navbar' className='justify-content-end'> <Navbar.Collapse id='main-navbar' className='justify-content-end'>
<Nav className='me-auto link' activeKey={location.pathname}> {first}
{ {second}
pages.map((page) => {third}
<Nav.Link as={Link} key={page.path} eventKey={page.path} to={page.path ?? '/'}>
{page.title}
</Nav.Link>)
}
</Nav>
<Nav>
<Navbar.Brand as={Link} to='/cart'>
<Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381;
</Navbar.Brand>
</Nav>
</Navbar.Collapse> </Navbar.Collapse>
</Container> </Container>
</Navbar > </Navbar >
</header> </header>
); );
// const { getCartSum } = useCart();
// const location = useLocation();
// const indexPageLink = routes.filter((route) => route.index === false).shift();
// const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title'));
// return (
// <header>
// <Navbar expand='md' className='my-navbar'>
// <Container fluid>
// <Navbar.Brand as={Link} to='/page2'>
// <Journal className='d-inline-block align-top me-1 logo' />
// </Navbar.Brand>
// <Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
// ANNA
// </Navbar.Brand>
// <Navbar.Toggle aria-controls='main-navbar' />
// <Navbar.Collapse id='main-navbar' className='justify-content-end'>
// <Nav className='me-auto link' activeKey={location.pathname}>
// {
// pages.map((page) =>
// <Nav.Link as={Link} key={page.path}
// eventKey={page.path} to={page.path ?? '/'}>
// {page.title}
// </Nav.Link>)
// }
// </Nav>
// <Nav>
// <Navbar.Brand as={Link} to='/cart'>
// <Cart2 className='d-inline-block align-top
// me-1 logo' /> {getCartSum() ?? ''} &#8381;
// </Navbar.Brand>
// </Nav>
// </Navbar.Collapse>
// </Container>
// </Navbar >
// </header>
// );
}; };
Navigation.propTypes = { Navigation.propTypes = {

View File

@ -1,3 +1,4 @@
/* eslint-disable linebreak-style */
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import TypesApiService from '../service/TypesApiService'; import TypesApiService from '../service/TypesApiService';

View File

@ -0,0 +1,76 @@
/* eslint-disable linebreak-style */
import { useEffect, useState } from 'react';
import UsersApiService from '../service/UsersApiService';
export const useUsers = () => {
const [users, setUsers] = useState([]);
const getUsers = async () => {
const data = await UsersApiService.getAll();
setUsers(data ?? []);
};
useEffect(() => {
getUsers();
}, []);
return {
users,
};
};
export const useUserL = (loginEnter) => {
let login = loginEnter;
const [users, setUser] = useState(null);
const getUser = async () => {
const data = await UsersApiService.getAll(`?login=${login}`);
if (data && data.length > 0) {
setUser(data[0]);
} else {
setUser(null);
}
};
useEffect(() => {
getUser(login);
}, []);
const handlerLoginChanged = (newLogin) => {
login = newLogin;
getUser();
};
return {
users,
handlerLoginChanged,
};
};
export const useUser = (id) => {
const emptyUser = {
id: '',
login: '',
password: '',
};
const [user, setUser] = useState({ ...emptyUser });
const getUser = async (userId = undefined) => {
if (userId && userId > 0) {
const data = await UsersApiService.get(userId);
setUser(data);
} else {
setUser({ ...emptyUser });
}
};
useEffect(() => {
getUser(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]);
return {
user,
setUser,
};
};

View File

@ -1,3 +1,4 @@
/* eslint-disable linebreak-style */
import ApiService from '../../api/ApiService'; import ApiService from '../../api/ApiService';
const TypesApiService = new ApiService('types'); const TypesApiService = new ApiService('types');

View File

@ -0,0 +1,6 @@
/* eslint-disable linebreak-style */
import ApiService from '../../api/ApiService';
const UsersApiService = new ApiService('users');
export default UsersApiService;

View File

@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';
import useUsers from './UsersHook';
const appHook = (id) => {
const { user, setUser } = useUsers(-1);
const [logged, setLogged] = useState(false);
const [curUser, setCurUser] = useState(null);
const load = async () => {
const data = localStorage.getItem('User');
if (localStorage.getItem('User') !== null) {
setCurUser(data);
setLogged(true);
}
};
useEffect(() => {
load();
}, []);
return {
user, setUser, logged, setLogged, curUser, setCurUser
};
};
export default appHook;

View File

@ -0,0 +1,5 @@
import ApiService from '../api/ApiService';
const CurrentUserApiService = new ApiService('currentUser');
export default CurrentUserApiService;

View File

@ -0,0 +1,67 @@
import { useState, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import UsersApiService from './UsersApiService';
import UserContext from './UserContext.jsx';
import CurrentUserApiService from './CurrenUserApiService';
const SignInn = () => {
const [validated, setValidated] = useState(false);
const navigate = useNavigate();
const {
user, setUser, setLogged, setCurUser,
} = useContext(UserContext);
const onMain = () => {
navigate('/page2');
};
const handleChange = (event) => {
const inputName = event.target.name;
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
setUser({
...user,
[inputName]: inputValue,
});
};
const getUserObject = (formData) => {
const email = String(formData.email, 30);
const password = String(formData.password, 30);
return {
email: email.toString(),
password: password.toString(),
};
};
const handleSubmit = async (event) => {
event.preventDefault();
event.stopPropagation();
const body = getUserObject(user);
const data = await UsersApiService.getAll(`?email=${body.email}`);
if (data) {
if (body.password === '' || body.email === '') {
toast.success('Не введены данные');
return false;
}
if (data[0].password === body.password) {
toast.success('Успешный вход', { id: 'User' });
localStorage.setItem('User', JSON.stringify(data[0]));
localStorage.setItem('currentUser', data[0].id);
await CurrentUserApiService.create(data[0].id);
setCurUser(data[0]);
setLogged(true);
setValidated(true);
return true;
}
}
return false;
};
const onSubmit = async (event) => {
if (await handleSubmit(event)) {
onMain();
}
};
return {
user, handleChange, handleSubmit, onSubmit, validated,
};
};
export default SignInn;

View File

@ -0,0 +1,5 @@
import { createContext } from 'react';
const UserContext = createContext(undefined);
export default UserContext;

View File

@ -0,0 +1,5 @@
import ApiService from '../api/ApiService';
const UsersApiService = new ApiService('users');
export default UsersApiService;

View File

@ -0,0 +1,33 @@
import { useEffect, useState } from 'react';
import UsersApiService from './UsersApiService';
const useUsers = (id) => {
const emptyUser = {
id: '',
name: '',
email: '',
password: '',
};
const [user, setUser] = useState({ ...emptyUser });
const getUser = async (userId = undefined) => {
if (userId && userId > 0) {
const data = await UsersApiService.get(userId);
setUser(data);
} else {
setUser({ ...emptyUser });
}
};
useEffect(() => {
getUser(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]);
return {
user,
setUser,
};
};
export default useUsers;

View File

@ -1,3 +1,4 @@
/* eslint-disable linebreak-style */
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
@ -11,6 +12,7 @@ import Page2 from './pages/Page2.jsx';
import Page3 from './pages/Page3.jsx'; import Page3 from './pages/Page3.jsx';
import Page4 from './pages/Page4.jsx'; import Page4 from './pages/Page4.jsx';
import PageEdit from './pages/PageEdit.jsx'; import PageEdit from './pages/PageEdit.jsx';
import Registration from './pages/Registration.jsx';
const routes = [ const routes = [
{ {
@ -27,6 +29,11 @@ const routes = [
element: <Page3 />, element: <Page3 />,
title: 'Аккаунт', title: 'Аккаунт',
}, },
{
path: '/reg',
element: <Registration />,
title: 'регистрация',
},
{ {
path: '/page4', path: '/page4',
element: <Page4 />, element: <Page4 />,

View File

@ -1,15 +1,42 @@
import { Link } from 'react-router-dom'; /* eslint-disable linebreak-style */
import { // import { Link } from 'react-router-dom';
Container, Col, Row, Form, Button, // import {
} from 'react-bootstrap'; // Container, Col, Row, Form, Button,
// } from 'react-bootstrap';
// import Register from '../components/Register'; // import Register from '../components/Register';
// import { useState } from 'react';
// import {
// Button, Form, Container,
// } from 'react-bootstrap';
// import { Link, useNavigate } from 'react-router-dom';
// import { useUserL } from '../components/types/hooks/UsersHook';
import { Container, Form, Button } from 'react-bootstrap';
import SignInn from '../components/users/SignInHook';
import Input from '../components/input/Input.jsx';
import './Page3.css'; import './Page3.css';
const Page3 = () => { const Page3 = () => {
const {
user, handleChange, validated, onSubmit,
} = SignInn();
return ( return (
// <Register /> <div className="accountPage text-center">
<> <Container>
<div className="accountPage"> <Form.Text>
<h1>АВТОРИЗАЦИЯ</h1>
</Form.Text>
<Form id="items-form" noValidate validated={validated} onSubmit={onSubmit}>
<Input name='email' type='email' placeholder='Введите почту' onChange={handleChange} value={user.email}></Input>
<Input name='password' type='password' placeholder='Введите пароль'onChange={handleChange} value={user.password}></Input>
<Button className='my-3' type="submit" variant="outline-secondary">Войти</Button>
</Form>
</Container>
</div>
);
};
/* <div className="accountPage">
<Container> <Container>
<Row> <Row>
<Col className="mx-auto"> <Col className="mx-auto">
@ -51,9 +78,6 @@ const Page3 = () => {
</Col> </Col>
</Row> </Row>
</Container> </Container>
</div> </div> */
</>
);
};
export default Page3; export default Page3;

View File

@ -1,3 +1,4 @@
/* eslint-disable linebreak-style */
import Lines from '../components/lines/table/Lines.jsx'; import Lines from '../components/lines/table/Lines.jsx';
const Page4 = () => { const Page4 = () => {

View File

@ -0,0 +1,78 @@
/* eslint-disable linebreak-style */
import { Container, Form, Button } from 'react-bootstrap';
import { useContext, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import Input from '../components/input/Input.jsx';
import UserContext from '../components/users/UserContext.jsx';
import UsersApiService from '../components/users/UsersApiService';
const Registration = () => {
const [validated, setValidated] = useState(false);
const navigate = useNavigate();
const { user, setUser } = useContext(UserContext);
const toAuto = () => {
navigate('/page3');
};
const handleChange = (event) => {
const inputName = event.target.name;
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
setUser({
...user,
[inputName]: inputValue,
});
};
const getUserObject = (formData) => {
const email = String(formData.email, 10);
const name = String(formData.name, 30);
const password = String(formData.password, 30);
return {
email: email.toString(),
name: name.toString(),
password: password.toString(),
};
};
const handleSubmit = async (event) => {
event.preventDefault();
event.stopPropagation();
const body = getUserObject(user);
if (body.password === '' || body.email === '' || body.name === '') {
toast.success('Введены пустые значения');
return false;
}
if (user.id === '') {
await UsersApiService.create(body);
} else {
await UsersApiService.update(user.id, body);
}
if (body) {
toast.success('Регистрация прошла успешно!', { id: 'User' });
setValidated(true);
return true;
}
return false;
};
const onSubmit = async (event) => {
if (await handleSubmit(event)) {
toAuto();
}
};
return (
<div className='accountPage text-center'>
<Container>
<Form.Text>
<h1>РЕГИСТРАЦИЯ</h1>
</Form.Text>
<Form id="items-form" noValidate validated={validated} onSubmit={onSubmit}>
<Input name='name' type='text' label='Имя' placeholder='Введите nick' required onChange={handleChange} value={user.name}></Input>
<Input name='email' type='email' label='Почта' placeholder='Введите почту' required onChange={handleChange} value={user.email}></Input>
<Input name='password' type='password' label='Пароль' placeholder='Введите пароль' required onChange={handleChange} value={user.password}></Input>
<Button className='my-2' type="submit" variant="outline-secondary">Зарегистрироваться</Button>
</Form>
</Container>
</div>
);
};
export default Registration;