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

View File

@ -24,6 +24,18 @@ class ApiService {
async delete(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;

View File

@ -12,32 +12,50 @@ const Navigation = ({ routes }) => {
const indexPageLink = routes.find((route) => route.index === true);
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 (
<Navbar expand='md' className='my-navbar'>
<Container fluid>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<Image src="./src/Images/logo.png" width="128" alt="Logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls='main-navbar' />
<Navbar.Collapse id='main-navbar'>
<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>
<Button className="custom-btn" as={Link} to={localStorage.getItem('nameOfUser') ? "/personalAccount" : "/personalAccountLogin"}>
{localStorage.getItem('nameOfUser') ? localStorage.getItem('nameOfUser') : "Войти"}
<Navbar expand='md' className='my-navbar'>
<Container fluid>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<Image src="./src/Images/logo.png" width="128" alt="Logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls='main-navbar' />
<Navbar.Collapse id='main-navbar'>
<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>
{userName ? (
<>
<Button className="custom-btn" as={Link} to="/personalAccount">
{userName}
</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">
<Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381;
</Button>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
)}
<Button variant="warning" as={Link} to="/Basket">
<Cart2 className='d-inline-block align-top me-1 logo' /> {getCartSum() ?? ''} &#8381;
</Button>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
);
};

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';
const MakingAnOrder = () => {
@ -15,15 +15,24 @@ const MakingAnOrder = () => {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
setValidated(true);
};
const [name, setName] = useState('');
useEffect(() => {
const storedName = localStorage.getItem('nameOfUser');
if (storedName) {
setName(storedName);
}
}, []);
return (
<Container className="h-100">
@ -36,7 +45,7 @@ const MakingAnOrder = () => {
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name">
<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 type="invalid">Имя не заполнено</Form.Control.Feedback>
</Form.Group>

View File

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

View File

@ -1,18 +1,40 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
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 [validated, setValidated] = useState(false);
const { userLogin } = useUser();
const { onSubmit } = useSubmit();
const navigate = useNavigate();
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
const handleSubmit = async (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
} else {
event.preventDefault();
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 (
@ -24,21 +46,21 @@ const PersonalAccountLogin = () => {
<h2 className="text-uppercase text-center mb-5">Войти</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
<Form.Group className="mb-4">
<Form.Control type="text" id="formHandle" required />
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
<Form.Label htmlFor="formHandle">Имя</Form.Label>
</Form.Group>
<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 type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Пароль</Form.Label>
<Form.Label htmlFor="formPassword">Пароль</Form.Label>
</Form.Group>
<Form.Group className="mb-2" controlId="form2Example3cg">
<Form.Group className="mb-2" controlId="formRememberMe">
<Form.Check type="checkbox" label="Запомнить меня" />
</Form.Group>
@ -50,7 +72,7 @@ const PersonalAccountLogin = () => {
</p>
<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>
</div>
@ -61,12 +83,6 @@ const PersonalAccountLogin = () => {
<u>Регистрация</u>
</Link>
</p>
<p className="text-center">
<Link to='/Administrator' className="fw-bold text-body" style={{ color: 'black'}}>
Администратор
</Link>
</p>
</Form>
</Card.Body>
</Card>

View File

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