This commit is contained in:
Вячеслав Иванов 2023-12-22 09:56:47 +04:00
parent 9cf636b347
commit 00d60a9f14
15 changed files with 569 additions and 133 deletions

155
lab5/.~data.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,10 +5,12 @@ import { Outlet } from 'react-router-dom';
import Footer from './components/footer/Footer.jsx'; import Footer from './components/footer/Footer.jsx';
import Navigation from './components/navigation/Navigation.jsx'; import Navigation from './components/navigation/Navigation.jsx';
import { CartProvider } from './components/card/CartContext.jsx'; import { CartProvider } from './components/card/CartContext.jsx';
import { UserProvider } from './components/users/UserContext.jsx';
const App = ({ routes }) => { const App = ({ routes }) => {
return ( return (
<div className="d-flex flex-column min-vh-100"> <div className="d-flex flex-column min-vh-100">
<UserProvider>
<CartProvider> <CartProvider>
<Navigation routes={routes}></Navigation> <Navigation routes={routes}></Navigation>
<Container className="flex-grow-1 p-2" as="main" fluid> <Container className="flex-grow-1 p-2" as="main" fluid>
@ -17,6 +19,7 @@ const App = ({ routes }) => {
<Footer /> <Footer />
<Toaster position='top-center' reverseOrder={true} /> <Toaster position='top-center' reverseOrder={true} />
</CartProvider> </CartProvider>
</UserProvider>
</div> </div>
); );
}; };

View File

@ -24,6 +24,18 @@ class ApiService {
async delete(id) { async delete(id) {
return ApiClient.delete(`${this.url}/${id}`); return ApiClient.delete(`${this.url}/${id}`);
} }
async getByHandle(handle) {
return ApiClient.get(`${this.url}?handle=${handle}`);
}
async getByEmail(email) {
return ApiClient.get(`${this.url}?email=${email}`);
}
async getAllForUser(userId) {
return ApiClient.get(`${this.url}?userId=${userId}`);
}
} }
export default ApiService; export default ApiService;

View File

@ -12,6 +12,11 @@ const Navigation = ({ routes }) => {
const indexPageLink = routes.find((route) => route.index === true); const indexPageLink = routes.find((route) => route.index === true);
const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title')); const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title'));
const storedUserData = localStorage.getItem('user');
const storedUser = storedUserData ? JSON.parse(storedUserData) : {};
const userName = storedUser && storedUser.handle ? storedUser.handle : '';
const isAdmin = userName.toLowerCase() === 'admin';
return ( return (
<Navbar expand='md' className='my-navbar'> <Navbar expand='md' className='my-navbar'>
<Container fluid> <Container fluid>
@ -28,9 +33,22 @@ const Navigation = ({ routes }) => {
))} ))}
</Nav> </Nav>
<Nav> <Nav>
<Button className="custom-btn" as={Link} to={localStorage.getItem('nameOfUser') ? "/personalAccount" : "/personalAccountLogin"}> {userName ? (
{localStorage.getItem('nameOfUser') ? localStorage.getItem('nameOfUser') : "Войти"} <>
<Button className="custom-btn" as={Link} to="/personalAccount">
{userName}
</Button> </Button>
{isAdmin && (
<Button className="custom-btn" as={Link} to="/administrator">
Панель
</Button>
)}
</>
) : (
<Button className="custom-btn" as={Link} to="/personalAccountLogin">
Профиль
</Button>
)}
<Button variant="warning" as={Link} to="/Basket"> <Button variant="warning" as={Link} to="/Basket">
<Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381; <Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381;
</Button> </Button>

View File

@ -0,0 +1,23 @@
import { createContext, useReducer, useEffect } from 'react';
import { loadUser, saveUser, userReducer } from './userReducer';
import PropTypes from 'prop-types';
export const UserContext = createContext(null);
export const UserProvider = ({ children }) => {
const [user, dispatch] = useReducer(userReducer, null, loadUser);
useEffect(() => {
saveUser(user || null);
}, [user]);
return <UserContext.Provider value = {{ user, dispatch }}>
{children}
</UserContext.Provider>;
};
UserProvider.propTypes = {
children: PropTypes.node,
};
export default UserContext;

View File

@ -0,0 +1,20 @@
import UsersApiService from "./service/UsersApiService";
import useUser from "./userHook";
const useSubmit = () => {
const { userLogin } = useUser();
const onSubmit = async (data) => {
const res1 = await UsersApiService.getByHandle(data.handle);
if (res1.length === 0) {
return 1;
}
if (res1[0].password !== data.password) {
return 2;
}
userLogin(res1[0]);
return 3;
};
return { onSubmit };
};
export default useSubmit;

View File

@ -0,0 +1,34 @@
import UsersApiService from "./service/UsersApiService";
import useUser from "./userHook";
const useSubmit = () => {
const { userLogin } = useUser();
const getNewUser = (formData) => {
const emailt = formData.email;
const handlet = formData.handle;
const passwordt = formData.password;
return {
email: emailt,
handle: handlet,
password: passwordt,
};
};
const onSubmit = async (data) => {
console.log(data);
const res1 = await UsersApiService.getByHandle(data.handle);
if (res1.length !== 0) {
return 1;
}
const res2 = await UsersApiService.getByEmail(data.email);
if (res2.length !== 0) {
return 2;
}
const newUser = getNewUser(data);
const curUser = await UsersApiService.create(newUser);
userLogin(curUser);
return 3;
};
return { onSubmit };
};
export default useSubmit;

View File

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

View File

@ -0,0 +1,20 @@
import { useContext } from 'react';
import { userLogin, userLogout } from './userReducer.js';
import UserContext from './UserContext.jsx';
const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser должен использоваться внутри UserProvider');
}
const { dispatch } = useContext(UserContext);
return {
userLogout: () => dispatch(userLogout()),
userLogin: (user) => dispatch(userLogin(user)),
};
};
export default useUser;

View File

@ -0,0 +1,39 @@
const USER_KEY = 'user';
const USER_LOGIN = 'user/login';
const USER_LOGOUT = 'user/logout';
export const saveUser = (user) => {
localStorage.setItem(USER_KEY, JSON.stringify(user));
};
export const loadUser = (initialValue = []) => {
const userData = localStorage.getItem(USER_KEY);
if (userData) {
return JSON.parse(userData);
}
return initialValue;
};
export const userReducer = (state, action) => {
console.log(action);
switch (action.type) {
case USER_LOGOUT: {
return null;
}
case USER_LOGIN: {
return action.user;
}
default: {
throw new Error(`Unknown action: ${action.type}`);
}
}
};
export const userLogout = () => ({
type: USER_LOGOUT,
});
export const userLogin = (user) => ({
type: USER_LOGIN,
user,
});

View File

@ -1,4 +1,4 @@
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap'; import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
const MakingAnOrder = () => { const MakingAnOrder = () => {
@ -25,6 +25,15 @@ const MakingAnOrder = () => {
setValidated(true); setValidated(true);
}; };
const [name, setName] = useState('');
useEffect(() => {
const storedName = localStorage.getItem('nameOfUser');
if (storedName) {
setName(storedName);
}
}, []);
return ( return (
<Container className="h-100"> <Container className="h-100">
<Row className="d-flex justify-content-center align-items-center h-100"> <Row className="d-flex justify-content-center align-items-center h-100">
@ -36,7 +45,7 @@ const MakingAnOrder = () => {
<Form noValidate validated={validated} onSubmit={handleSubmit}> <Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name"> <Form.Group className="mb-4" controlId="name">
<Form.Label>Ваше имя</Form.Label> <Form.Label>Ваше имя</Form.Label>
<Form.Control type="text" required /> <Form.Control type="text" value={name} required onChange={(e) => setName(e.target.value)} />
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback> <Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
</Form.Group> </Form.Group>

View File

@ -4,29 +4,51 @@ import { Link } from 'react-router-dom';
const PersonalAccount = () => { const PersonalAccount = () => {
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const [name, setName] = useState('');
const [surname, setSurname] = useState('');
const [email, setEmail] = useState('');
const [birthdate, setBirthdate] = useState('');
const [phoneNumber, setPhoneNumber] = useState('');
useEffect(() => {
const storedUserData = localStorage.getItem('user');
const storedUser = storedUserData ? JSON.parse(storedUserData) : {};
if (storedUser) {
setName(storedUser.handle || '');
setSurname(storedUser.surname || '');
setEmail(storedUser.email || '');
setBirthdate(storedUser.birthdate || '');
setPhoneNumber(storedUser.phoneNumber || '');
}
}, []);
const handleSubmit = (event) => { const handleSubmit = (event) => {
const form = event.currentTarget; const form = event.currentTarget;
if (form.checkValidity() === false) { if (form.checkValidity() === false) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} else {
const storedUserData = localStorage.getItem('user');
const storedUser = storedUserData ? JSON.parse(storedUserData) : {};
const newUserData = {
...storedUser,
handle: name,
surname: surname,
email: email,
birthdate: birthdate,
phoneNumber: phoneNumber,
};
localStorage.setItem('user', JSON.stringify(newUserData));
} }
setValidated(true); setValidated(true);
}; };
const [name, setName] = useState('');
useEffect(() => {
const storedName = localStorage.getItem('nameOfUser');
if (storedName) {
setName(storedName);
}
}, []);
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem('nameOfUser'); localStorage.removeItem('user');
history.push('/'); history.push('/');
}; };
@ -39,39 +61,29 @@ const PersonalAccount = () => {
<h2 className="text-uppercase text-center mb-5">Личный кабинет</h2> <h2 className="text-uppercase text-center mb-5">Личный кабинет</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}> <Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name"> <Form.Group className="mb-4">
<Form.Label>Ваше имя</Form.Label> <Form.Label>Ваше имя</Form.Label>
<Form.Control type="text" value={name} required onChange={(e) => setName(e.target.value)} /> <Form.Control type="text" value={name} onChange={(e) => setName(e.target.value)} readOnly />
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
</Form.Group> </Form.Group>
<Form.Group className="mb-4" controlId="surname"> <Form.Group className="mb-4">
<Form.Label>Ваша фамилия</Form.Label> <Form.Label>Ваша фамилия</Form.Label>
<Form.Control type="text" required/> <Form.Control type="text" value={surname} onChange={(e) => setSurname(e.target.value)} />
<Form.Control.Feedback>Фамилия заполнена</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Фамилия не заполнена</Form.Control.Feedback>
</Form.Group> </Form.Group>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label> <Form.Label>Ваш адрес электронной почты</Form.Label>
<Form.Control type="email" id="form3Example3cg" required /> <Form.Control type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
</Form.Group> </Form.Group>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Label htmlFor="form3Example4cg">Дата рождения</Form.Label> <Form.Label>Дата рождения</Form.Label>
<Form.Control type="date" id="form3Example4cg" required /> <Form.Control type="date" value={birthdate} onChange={(e) => setBirthdate(e.target.value)} />
<Form.Control.Feedback>Дата рождения заполнена</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Дата рождения не заполнена</Form.Control.Feedback>
</Form.Group> </Form.Group>
<Form.Group className="mb-2"> <Form.Group className="mb-2">
<Form.Label htmlFor="form3Example5cg">Номер телефона</Form.Label> <Form.Label>Номер телефона</Form.Label>
<Form.Control type="tel" id="form3Example5cg" required /> <Form.Control type="tel"value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} />
<Form.Control.Feedback>Номер телефона заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Номер телефона не заполнен</Form.Control.Feedback>
</Form.Group> </Form.Group>
<div className="d-flex justify-content-center"> <div className="d-flex justify-content-center">

View File

@ -1,18 +1,40 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap'; import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
import { useState } from 'react'; import { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import useUser from '../components/users/userHook';
import useSubmit from '../components/users/loginHook';
const PersonalAccountLogin = () => { const PersonalAccountLogin = () => {
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const { userLogin } = useUser();
const { onSubmit } = useSubmit();
const navigate = useNavigate();
const handleSubmit = (event) => { const handleSubmit = async (event) => {
const form = event.currentTarget; const form = event.currentTarget;
if (form.checkValidity() === false) { if (form.checkValidity() === false) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} } else {
event.preventDefault();
setValidated(true); setValidated(true);
const formData = {
handle: form.elements.formHandle.value,
password: form.elements.formPassword.value,
};
const result = await onSubmit(formData);
if (result === 1) {
alert('Пользователь с таким хэндлом не существует');
} else if (result === 2) {
alert('Введен неверный пароль');
} else if (result === 3) {
userLogin(formData);
navigate('/PersonalAccount');
}
}
}; };
return ( return (
@ -25,20 +47,20 @@ const PersonalAccountLogin = () => {
<Form noValidate validated={validated} onSubmit={handleSubmit}> <Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required /> <Form.Control type="text" id="formHandle" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback> <Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label> <Form.Label htmlFor="formHandle">Имя</Form.Label>
</Form.Group> </Form.Group>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Control type="password" id="form3Example4cg" size="lg" required /> <Form.Control type="password" id="formPassword" size="lg" required />
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback> <Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Пароль</Form.Label> <Form.Label htmlFor="formPassword">Пароль</Form.Label>
</Form.Group> </Form.Group>
<Form.Group className="mb-2" controlId="form2Example3cg"> <Form.Group className="mb-2" controlId="formRememberMe">
<Form.Check type="checkbox" label="Запомнить меня" /> <Form.Check type="checkbox" label="Запомнить меня" />
</Form.Group> </Form.Group>
@ -50,7 +72,7 @@ const PersonalAccountLogin = () => {
</p> </p>
<div className="d-flex justify-content-center"> <div className="d-flex justify-content-center">
<Button as={Link} to='/PersonalAccount' variant="submit" className="btn-block btn-warning text-body mb-0"> <Button to='/personalAccount' type="submit" className="btn-block btn-warning text-body mb-0">
Вход Вход
</Button> </Button>
</div> </div>
@ -61,12 +83,6 @@ const PersonalAccountLogin = () => {
<u>Регистрация</u> <u>Регистрация</u>
</Link> </Link>
</p> </p>
<p className="text-center">
<Link to='/Administrator' className="fw-bold text-body" style={{ color: 'black'}}>
Администратор
</Link>
</p>
</Form> </Form>
</Card.Body> </Card.Body>
</Card> </Card>

View File

@ -1,23 +1,50 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap'; import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
import { useState } from 'react'; import { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import UsersApiService from '../components/users/service/UsersApiService';
import useSubmit from '../components/users/regHook';
import useUser from '../components/users/userHook';
const PersonalAccountRegister = () => { const PersonalAccountRegister = () => {
const [validated, setValidated] = useState(false); const [validated, setValidated] = useState(false);
const { userLogin } = useUser();
const { onSubmit } = useSubmit();
const navigate = useNavigate();
const handleSubmit = (event) => { const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (event) => {
const form = event.currentTarget; const form = event.currentTarget;
if (form.checkValidity() === false) { if (form.checkValidity() === false) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} } else {
event.preventDefault();
setValidated(true); setValidated(true);
const formData = {
handle: name,
email: email,
password: password,
}; };
function setNameOfUser(name) { const result = await onSubmit(formData);
localStorage.setItem('nameOfUser', name);
if (result === 1) {
alert('Пользователь с таким хэндлом уже существует');
} else if (result === 2) {
alert('Пользователь с таким email уже существует');
} else if (result === 3) {
await UsersApiService.create(formData);
userLogin(formData);
navigate('/PersonalAccount');
} }
}
};
return ( return (
<Container className="h-100"> <Container className="h-100">
@ -28,34 +55,43 @@ const PersonalAccountRegister = () => {
<h2 className="text-uppercase text-center mb-5">Создать учетную запись</h2> <h2 className="text-uppercase text-center mb-5">Создать учетную запись</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}> <Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name"> <Form.Group className="mb-4">
<Form.Control type="text" required /> <Form.Control
type="text"
id="formHandle"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback> <Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
<Form.Label>Ваше имя</Form.Label> <Form.Label>Ваше имя</Form.Label>
</Form.Group> </Form.Group>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required /> <Form.Control
type="email"
id="formEmail"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback> <Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label> <Form.Label>Ваш адрес электронной почты</Form.Label>
</Form.Group> </Form.Group>
<Form.Group className="mb-4"> <Form.Group className="mb-4">
<Form.Control type="password" id="form3Example4cg" size="lg" required /> <Form.Control
type="password"
id="formPassword"
size="lg"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback> <Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback> <Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Пароль</Form.Label> <Form.Label htmlFor="form3Example4cg">Пароль</Form.Label>
</Form.Group> </Form.Group>
<Form.Group controlId="formRepeatPassword" className="mb-4">
<Form.Control type="password"required/>
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label>Повторите свой пароль</Form.Label>
</Form.Group>
<Form.Group controlId="formCheckbox" className="d-flex justify-content-center mb-5"> <Form.Group controlId="formCheckbox" className="d-flex justify-content-center mb-5">
<Form.Check type="checkbox" label={ <Form.Check type="checkbox" label={
<span> <span>
@ -68,11 +104,9 @@ const PersonalAccountRegister = () => {
</Form.Group> </Form.Group>
<div className="d-flex justify-content-center"> <div className="d-flex justify-content-center">
<Link to='/PersonalAccount' style={{color: 'black', textDecoration: 'none'}}> <Button to='/personalAccount' type="submit" className="btn-block btn-warning text-body mb-0">
<Button variant="success" type="button" className="btn-block btn-warning text-body mb-0" onClick={() => setNameOfUser(document.getElementById('name').value)}> Вход
Регистрация
</Button> </Button>
</Link>
</div> </div>
<p className="text-center text-muted mb-0"> <p className="text-center text-muted mb-0">