Skip to content

Commit

Permalink
Add registration
Browse files Browse the repository at this point in the history
  • Loading branch information
AxeRicin committed Dec 12, 2023
1 parent abce412 commit 3970cb1
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 3 deletions.
2 changes: 2 additions & 0 deletions frontend/src/Components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AuthProvider from '../hoc/AuthProvider.js';
import ChatPage from '../page/ChatPage.js';
import store from '../store/store.js';
import ApiProvider from '../hoc/ApiProvider.js';
import RegistrationPage from '../page/RegistrationPage.js';

const App = () => (
<div className="d-flex flex-column h-100">
Expand All @@ -20,6 +21,7 @@ const App = () => (
<Route index element={<ChatPage />} />
<Route path="*" element={<NotfoundPage />} />
<Route path={getRoutes.loginpage()} element={<LoginPage />} />
<Route path={getRoutes.signuppage()} element={<RegistrationPage />} />
</Route>
</Routes>
</BrowserRouter>
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/Components/Layout.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import {
Link, Outlet, useLocation, Navigate,
Link, Outlet, useLocation, Navigate, useNavigate,
} from 'react-router-dom';
import { Button } from 'react-bootstrap';
import { useContext } from 'react';
import getRoutes from '../routes';
import useAuth from '../hook/useAuth';
import { AuthContext } from '../hoc/AuthProvider';

const Layout = () => {
const location = useLocation();
const { userToken } = useAuth();
const { signOut } = useContext(AuthContext);
const navigate = useNavigate();

const hendlesignOut = () => {
signOut(() => navigate(getRoutes.loginpage()));
};

if (location.pathname === getRoutes.main() && !userToken) {
return <Navigate to="/login" state={{ from: location }} />;
return <Navigate to={getRoutes.loginpage()} state={{ from: location }} />;
}
return (
<>
<nav className="shadow-sm navbar navbar-expand-lg navbar-light bg-white">
<div className="container">
<Link className="navbar-brand" to={getRoutes.main()}>Hexlet Chat</Link>
{userToken && <Button variant="primary" onClick={hendlesignOut}>Выйти</Button>}
</div>
</nav>
<Outlet />
Expand Down
126 changes: 126 additions & 0 deletions frontend/src/Components/RegistrationForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import axios from 'axios';
import { Formik } from 'formik';
import { Button, Form } from 'react-bootstrap';
import * as yup from 'yup';
import {
useContext, useEffect, useRef, useState,
} from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import getRoutes from '../routes.js';
import { AuthContext } from '../hoc/AuthProvider';

const RegistrationForm = () => {
const { signIn } = useContext(AuthContext);
const navigate = useNavigate();
const location = useLocation();
const [isExistingUser, setExistingUser] = useState(false);
const usernameRef = useRef();

const fromPage = location.state?.from?.pathname ?? getRoutes.main();

const registrationSchema = yup.object().shape({
username: yup.string().required('Обязательное поле').min(3, 'От 3 до 20 символов').max(20, 'От 3 до 20 символов'),
password: yup.string().required('Обязательное поле').min(6, 'Не менее 6 символов'),
confirmPassword: yup.string().required('Обязательное поле').oneOf([yup.ref('password')], 'Пароли должны совпадать'),
});

useEffect(() => {
usernameRef.current.select();
}, []);

return (
<Formik
initialValues={{
username: '',
password: '',
confirmPassword: '',
}}
validationSchema={registrationSchema}
onSubmit={async ({ username, password }) => {
try {
setExistingUser(false);
const { data } = await axios.post(getRoutes.signup(), { username, password });
signIn(data, () => navigate(fromPage, { replace: true }));
} catch (err) {
switch (err.response.status) {
case 409:
setExistingUser(true);
break;

default:
console.error('Возникла непредвиденная ошибка');
break;
}
}
}}
>
{(props) => (
<Form className="w-50" onSubmit={props.handleSubmit}>
<h1 className="text-center mb-4">Регистрация</h1>
<Form.Group className="form-floating mb-3" controlId="username">
<Form.Control
ref={usernameRef}
name="username"
autoComplete="username"
placeholder="От 3 до 20 символов"
required
value={props.values.username}
onChange={props.handleChange}
isInvalid={(props.touched.username && props.errors.username) || isExistingUser}
/>
<Form.Label>Имя пользователя</Form.Label>
{
props.touched.username
&& props.errors.username
&& <Form.Control.Feedback type="invalid" tooltip>{props.errors.username}</Form.Control.Feedback>
}
{isExistingUser && <Form.Control.Feedback type="invalid" tooltip>Такой пользователь уже существует</Form.Control.Feedback>}
</Form.Group>
<Form.Group className="form-floating mb-3" controlId="password">
<Form.Control
name="password"
autoComplete="new-password"
placeholder="Не менее 6 символов"
required
type="password"
value={props.values.password}
onChange={props.handleChange}
isInvalid={(props.touched.password && props.errors.password) || isExistingUser}
/>
<Form.Label>Пароль</Form.Label>
{
props.touched.password
&& props.errors.password
&& <Form.Control.Feedback type="invalid" tooltip>{props.errors.password}</Form.Control.Feedback>
}
{isExistingUser && <Form.Control.Feedback type="invalid" tooltip>Такой пользователь уже существует</Form.Control.Feedback>}
</Form.Group>
<Form.Group className="form-floating mb-4" controlId="confirmPassword">
<Form.Control
name="confirmPassword"
autoComplete="new-password"
placeholder="Пароли должны совпадать"
required
type="password"
value={props.values.confirmPassword}
onChange={props.handleChange}
isInvalid={
(props.touched.confirmPassword && props.errors.confirmPassword) || isExistingUser
}
/>
<Form.Label>Подтвердите пароль</Form.Label>
{
props.touched.confirmPassword
&& props.errors.confirmPassword
&& <Form.Control.Feedback type="invalid" tooltip>{props.errors.confirmPassword}</Form.Control.Feedback>
}
{isExistingUser && <Form.Control.Feedback type="invalid" tooltip>Такой пользователь уже существует</Form.Control.Feedback>}
</Form.Group>
<Button className="w-100" variant="outline-primary" type="submit">Зарегистрироваться</Button>
</Form>
)}
</Formik>
);
};

export default RegistrationForm;
File renamed without changes
2 changes: 1 addition & 1 deletion frontend/src/page/LoginPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const LoginPage = () => (
<div className="card-footer p-4">
<div className="text-center">
<span>Нет аккаунта? </span>
<Link to={getRoutes.signup()}>Регистрация</Link>
<Link to={getRoutes.signuppage()}>Регистрация</Link>
</div>
</div>
</div>
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/page/RegistrationPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import avatar1 from '../assets/avatar1.jpg';
import RegistrationForm from '../Components/RegistrationForm';

const RegistrationPage = () => (
<div className="container-fluid h-100">
<div className="row justify-content-center align-content-center h-100">
<div className="col-12 col-md-8 col-xxl-6">
<div className="card shadow-sm">
<div className="card-body d-flex flex-column flex-md-row justify-content-around align-items-center p-5">
<div>
<img className="rounded-circle" src={avatar1} alt="Регистрация" />
</div>
<RegistrationForm />
</div>
</div>
</div>
</div>
</div>
);

export default RegistrationPage;
1 change: 1 addition & 0 deletions frontend/src/routes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
main: () => '/',
loginpage: () => '/login',
signuppage: () => '/signup',
login: () => '/api/v1/login',
signup: () => '/api/v1/signup',
data: () => '/api/v1/data',
Expand Down

0 comments on commit 3970cb1

Please sign in to comment.