diff --git a/laba5/data.json b/laba5/data.json index 41534f1..e1abb08 100644 --- a/laba5/data.json +++ b/laba5/data.json @@ -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": [ { "id": 1, diff --git a/laba5/package-lock.json b/laba5/package-lock.json index 53d74c6..aaf5890 100644 --- a/laba5/package-lock.json +++ b/laba5/package-lock.json @@ -8,7 +8,7 @@ "name": "lec4", "version": "0.0.0", "dependencies": { - "axios": "^1.6.1", + "axios": "^1.6.5", "bootstrap": "^5.3.2", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -1356,11 +1356,11 @@ } }, "node_modules/axios": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", - "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } diff --git a/laba5/package.json b/laba5/package.json index 10d47a1..8b27a30 100644 --- a/laba5/package.json +++ b/laba5/package.json @@ -11,7 +11,7 @@ "prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'" }, "dependencies": { - "axios": "^1.6.1", + "axios": "^1.6.5", "bootstrap": "^5.3.2", "prop-types": "^15.8.1", "react": "^18.2.0", diff --git a/laba5/src/App.jsx b/laba5/src/App.jsx index 290fba4..98d233d 100644 --- a/laba5/src/App.jsx +++ b/laba5/src/App.jsx @@ -4,9 +4,17 @@ import { Toaster } from 'react-hot-toast'; import { Outlet } from 'react-router-dom'; import { CartProvider } from './components/cart/CartContext.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 { + user, setUser, logged, setLogged, curUser, setCurUser, + } = appHook(); return ( + @@ -14,6 +22,7 @@ const App = ({ routes }) => { + ); }; diff --git a/laba5/src/components/Register.js b/laba5/src/components/Register.js deleted file mode 100644 index 680ff26..0000000 --- a/laba5/src/components/Register.js +++ /dev/null @@ -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 ? ( -
-

Success!

-

- Sign In -

-
- ) : ( -
-

{errMsg}

-

Register

-
- - setUser(e.target.value)} - value={user} - required - aria-invalid={validName ? 'false' : 'true'} - aria-describedby="uidnote" - onFocus={() => setUserFocus(true)} - onBlur={() => setUserFocus(false)} - /> -

- 4 to 24 characters.
- Must begin with a letter.
- Letters, numbers, underscores, hyphens allowed. -

- - - setPwd(e.target.value)} - value={pwd} - required - aria-invalid={validPwd ? 'false' : 'true'} - aria-describedby="pwdnote" - onFocus={() => setPwdFocus(true)} - onBlur={() => setPwdFocus(false)} - /> -

- Allowed special characters: ! - @ # - $ % -

- - - setMatchPwd(e.target.value)} - value={matchPwd} - required - aria-invalid={validMatch ? 'false' : 'true'} - aria-describedby="confirmnote" - onFocus={() => setMatchFocus(true)} - onBlur={() => setMatchFocus(false)} - /> -

- Must match the first password input field. -

- - -
-

- Already registered?
- - Sign In - -

-
- )} - - ); -}; - -export default Register; diff --git a/laba5/src/components/api/axios.js b/laba5/src/components/api/axios.js deleted file mode 100644 index e1cdfc9..0000000 --- a/laba5/src/components/api/axios.js +++ /dev/null @@ -1,5 +0,0 @@ -import axios from 'axios'; - -export default axios.create({ - baseURL: 'http://localhost:3500', -}); diff --git a/laba5/src/components/navigation/Navigation.jsx b/laba5/src/components/navigation/Navigation.jsx index afbfaef..654299b 100644 --- a/laba5/src/components/navigation/Navigation.jsx +++ b/laba5/src/components/navigation/Navigation.jsx @@ -2,45 +2,104 @@ import PropTypes from 'prop-types'; import { Container, Nav, Navbar } from 'react-bootstrap'; import { Journal, Cart2 } from 'react-bootstrap-icons'; import { Link, useLocation } from 'react-router-dom'; +import { useContext } from 'react'; +import UserContext from '../users/UserContext.jsx'; import useCart from '../cart/CartHook'; +import CurrentUserApiService from '../users/CurrenUserApiService'; import './Navigation.css'; -const Navigation = ({ routes }) => { +const Navigation = () => { 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')); - + const { + logged, setLogged, + } = 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 =
+ Регистрация +
; + second =
+ Аккаунт +
; + third =
; + } else { + first =
+ Выйти +
; + second =
+
; + third =
+ + {getCartSum() ?? ''} ₽ + +
; + } return (
- - + + - - - - ANNA - - - - - + + + + ANNA + + + + {first} + {second} + {third}
); + + // 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 ( + //
+ // + // + // + // + // + // + // ANNA + // + // + // + // + // + // + // + // + //
+ // ); }; Navigation.propTypes = { diff --git a/laba5/src/components/types/hooks/TypesHook.js b/laba5/src/components/types/hooks/TypesHook.js index 35f605e..a3b1f18 100644 --- a/laba5/src/components/types/hooks/TypesHook.js +++ b/laba5/src/components/types/hooks/TypesHook.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import { useEffect, useState } from 'react'; import TypesApiService from '../service/TypesApiService'; diff --git a/laba5/src/components/types/hooks/UsersHook.js b/laba5/src/components/types/hooks/UsersHook.js new file mode 100644 index 0000000..18ebc49 --- /dev/null +++ b/laba5/src/components/types/hooks/UsersHook.js @@ -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, + }; +}; diff --git a/laba5/src/components/types/service/TypesApiService.js b/laba5/src/components/types/service/TypesApiService.js index 1168c55..a3835df 100644 --- a/laba5/src/components/types/service/TypesApiService.js +++ b/laba5/src/components/types/service/TypesApiService.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import ApiService from '../../api/ApiService'; const TypesApiService = new ApiService('types'); diff --git a/laba5/src/components/types/service/UsersApiService.js b/laba5/src/components/types/service/UsersApiService.js new file mode 100644 index 0000000..3c11d06 --- /dev/null +++ b/laba5/src/components/types/service/UsersApiService.js @@ -0,0 +1,6 @@ +/* eslint-disable linebreak-style */ +import ApiService from '../../api/ApiService'; + +const UsersApiService = new ApiService('users'); + +export default UsersApiService; diff --git a/laba5/src/components/users/AppHook.js b/laba5/src/components/users/AppHook.js new file mode 100644 index 0000000..384fcad --- /dev/null +++ b/laba5/src/components/users/AppHook.js @@ -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; diff --git a/laba5/src/components/users/CurrenUserApiService.js b/laba5/src/components/users/CurrenUserApiService.js new file mode 100644 index 0000000..ad62252 --- /dev/null +++ b/laba5/src/components/users/CurrenUserApiService.js @@ -0,0 +1,5 @@ +import ApiService from '../api/ApiService'; + +const CurrentUserApiService = new ApiService('currentUser'); + +export default CurrentUserApiService; diff --git a/laba5/src/components/users/SignInHook.js b/laba5/src/components/users/SignInHook.js new file mode 100644 index 0000000..c677db7 --- /dev/null +++ b/laba5/src/components/users/SignInHook.js @@ -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; diff --git a/laba5/src/components/users/UserContext.jsx b/laba5/src/components/users/UserContext.jsx new file mode 100644 index 0000000..36b64ea --- /dev/null +++ b/laba5/src/components/users/UserContext.jsx @@ -0,0 +1,5 @@ +import { createContext } from 'react'; + +const UserContext = createContext(undefined); + +export default UserContext; diff --git a/laba5/src/components/users/UsersApiService.js b/laba5/src/components/users/UsersApiService.js new file mode 100644 index 0000000..200c667 --- /dev/null +++ b/laba5/src/components/users/UsersApiService.js @@ -0,0 +1,5 @@ +import ApiService from '../api/ApiService'; + +const UsersApiService = new ApiService('users'); + +export default UsersApiService; diff --git a/laba5/src/components/users/UsersHook.js b/laba5/src/components/users/UsersHook.js new file mode 100644 index 0000000..ccc4d87 --- /dev/null +++ b/laba5/src/components/users/UsersHook.js @@ -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; diff --git a/laba5/src/main.jsx b/laba5/src/main.jsx index 85c5d29..3c01dc3 100644 --- a/laba5/src/main.jsx +++ b/laba5/src/main.jsx @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import 'bootstrap/dist/css/bootstrap.min.css'; import React from 'react'; import ReactDOM from 'react-dom/client'; @@ -11,6 +12,7 @@ import Page2 from './pages/Page2.jsx'; import Page3 from './pages/Page3.jsx'; import Page4 from './pages/Page4.jsx'; import PageEdit from './pages/PageEdit.jsx'; +import Registration from './pages/Registration.jsx'; const routes = [ { @@ -27,6 +29,11 @@ const routes = [ element: , title: 'Аккаунт', }, + { + path: '/reg', + element: , + title: 'регистрация', + }, { path: '/page4', element: , diff --git a/laba5/src/pages/Page3.jsx b/laba5/src/pages/Page3.jsx index 01d0dcf..424eb9c 100644 --- a/laba5/src/pages/Page3.jsx +++ b/laba5/src/pages/Page3.jsx @@ -1,15 +1,42 @@ -import { Link } from 'react-router-dom'; -import { - Container, Col, Row, Form, Button, -} from 'react-bootstrap'; +/* eslint-disable linebreak-style */ +// import { Link } from 'react-router-dom'; +// import { +// Container, Col, Row, Form, Button, +// } from 'react-bootstrap'; // 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'; const Page3 = () => { + const { + user, handleChange, validated, onSubmit, + } = SignInn(); return ( - // - <> -
+
+ + +

АВТОРИЗАЦИЯ

+
+
+ + + +
+
+
+ ); + }; + + /*
@@ -51,9 +78,6 @@ const Page3 = () => { -
- - ); -}; +
*/ export default Page3; diff --git a/laba5/src/pages/Page4.jsx b/laba5/src/pages/Page4.jsx index bfff490..0e51f0e 100644 --- a/laba5/src/pages/Page4.jsx +++ b/laba5/src/pages/Page4.jsx @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import Lines from '../components/lines/table/Lines.jsx'; const Page4 = () => { diff --git a/laba5/src/pages/Registration.jsx b/laba5/src/pages/Registration.jsx new file mode 100644 index 0000000..35edf2b --- /dev/null +++ b/laba5/src/pages/Registration.jsx @@ -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 ( +
+ + +

РЕГИСТРАЦИЯ

+
+
+ + + + +
+
+
+ ); +}; + +export default Registration;