рабочий вход и выход

This commit is contained in:
goblinrf 2023-12-15 17:31:52 +03:00
parent 5ce820f4e9
commit a4056aaf25
11 changed files with 323 additions and 87 deletions

View File

@ -5,6 +5,26 @@
"name": "Новость" "name": "Новость"
} }
], ],
"users": [
{
"id": 1,
"login": "user1",
"password": "password1",
"status": "user"
},
{
"id": 2,
"login": "user2",
"password": "password2",
"status": "admin"
},
{
"id": 3,
"login": "user3",
"password": "password3",
"status": "user"
}
],
"lines": [ "lines": [
{ {
"typeId": "1", "typeId": "1",
@ -29,14 +49,6 @@
"sum": "14.12.2023", "sum": "14.12.2023",
"image": "", "image": "",
"id": 12 "id": 12
},
{
"typeId": "1",
"price": "jhkh",
"count": "kjh",
"sum": "14.12.2023",
"image": "",
"id": 13
} }
] ]
} }

103
Lab5/package-lock.json generated
View File

@ -8,15 +8,19 @@
"name": "lec4", "name": "lec4",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"axios": "^1.6.1", "axios": "^1.6.2",
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"fs": "^0.0.1-security",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.9.1",
"react-bootstrap-icons": "^1.10.3", "react-bootstrap-icons": "^1.10.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.49.2",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-router-dom": "^6.18.0" "react-redux": "^9.0.4",
"react-router-dom": "^6.21.0",
"redux": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.15", "@types/react": "^18.2.15",
@ -956,9 +960,9 @@
} }
}, },
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.11.0", "version": "1.14.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.0.tgz",
"integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==", "integrity": "sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A==",
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
} }
@ -1094,6 +1098,11 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz",
"integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA=="
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@types/warning": { "node_modules/@types/warning": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
@ -1355,9 +1364,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.6.1", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
"integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@ -2849,6 +2858,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -4750,6 +4764,22 @@
"react": "^18.2.0" "react": "^18.2.0"
} }
}, },
"node_modules/react-hook-form": {
"version": "7.49.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.2.tgz",
"integrity": "sha512-TZcnSc17+LPPVpMRIDNVITY6w20deMdNi6iehTFLV1x8SqThXGwu93HjlUVU09pzFgZH7qZOvLMM7UYf2ShAHA==",
"engines": {
"node": ">=18",
"pnpm": "8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-hot-toast": { "node_modules/react-hot-toast": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
@ -4775,6 +4805,32 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"node_modules/react-redux": {
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.0.4.tgz",
"integrity": "sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA==",
"dependencies": {
"@types/use-sync-external-store": "^0.0.3",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^18.2.25",
"react": "^18.0",
"react-native": ">=0.69",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"react-native": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@ -4785,11 +4841,11 @@
} }
}, },
"node_modules/react-router": { "node_modules/react-router": {
"version": "6.18.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.18.0.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.0.tgz",
"integrity": "sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==", "integrity": "sha512-hGZ0HXbwz3zw52pLZV3j3+ec+m/PQ9cTpBvqjFQmy2XVUWGn5MD+31oXHb6dVTxYzmAeaiUBYjkoNz66n3RGCg==",
"dependencies": { "dependencies": {
"@remix-run/router": "1.11.0" "@remix-run/router": "1.14.0"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
@ -4799,12 +4855,12 @@
} }
}, },
"node_modules/react-router-dom": { "node_modules/react-router-dom": {
"version": "6.18.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.18.0.tgz", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.0.tgz",
"integrity": "sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==", "integrity": "sha512-1dUdVj3cwc1npzJaf23gulB562ESNvxf7E4x8upNJycqyUm5BRRZ6dd3LrlzhtLaMrwOCO8R0zoiYxdaJx4LlQ==",
"dependencies": { "dependencies": {
"@remix-run/router": "1.11.0", "@remix-run/router": "1.14.0",
"react-router": "6.18.0" "react-router": "6.21.0"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
@ -4843,6 +4899,11 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/redux": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.0.tgz",
"integrity": "sha512-blLIYmYetpZMET6Q6uCY7Jtl/Im5OBldy+vNPauA8vvsdqyt66oep4EUpAMWNHauTC6xa9JuRPhRB72rY82QGA=="
},
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz",
@ -5640,6 +5701,14 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

View File

@ -11,15 +11,19 @@
"prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'" "prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'"
}, },
"dependencies": { "dependencies": {
"react": "^18.2.0", "axios": "^1.6.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.18.0",
"react-hot-toast": "^2.4.1",
"axios": "^1.6.1",
"bootstrap": "^5.3.2", "bootstrap": "^5.3.2",
"fs": "^0.0.1-security",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1", "react-bootstrap": "^2.9.1",
"react-bootstrap-icons": "^1.10.3", "react-bootstrap-icons": "^1.10.3",
"prop-types": "^15.8.1" "react-dom": "^18.2.0",
"react-hook-form": "^7.49.2",
"react-hot-toast": "^2.4.1",
"react-redux": "^9.0.4",
"react-router-dom": "^6.21.0",
"redux": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.15", "@types/react": "^18.2.15",

View File

@ -2,18 +2,31 @@ import PropTypes from 'prop-types';
import { Container } from 'react-bootstrap'; import { Container } from 'react-bootstrap';
import { Toaster } from 'react-hot-toast'; import { Toaster } from 'react-hot-toast';
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { Provider } from 'react-redux';
import React, { useEffect } from 'react';
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 { BrowserRouter as Router } from 'react-router-dom';
import store from './Reducer/store'; // Путь к вашему store
const App = ({ routes }) => { const App = ({ routes }) => {
useEffect(() => {
const storedUser = localStorage.getItem('user');
if (storedUser) {
store.dispatch({ type: 'SET_USER', payload: JSON.parse(storedUser) });
}
}, []);
return ( return (
<> <>
<Provider store={store}>
<Navigation routes={routes}></Navigation> <Navigation routes={routes}></Navigation>
<Container className='p-2' as='main' fluid> <Container className='p-2' as='main' fluid>
<Outlet /> <Outlet />
</Container> </Container>
<Footer /> <Footer />
<Toaster position='top-center' reverseOrder={true} /> <Toaster position='top-center' reverseOrder={true} />
</Provider>
</> </>
); );
}; };

View File

@ -0,0 +1,7 @@
// store.js
import { createStore } from 'redux';
import userReducer from './userReducer';
const store = createStore(userReducer);
export default store;

View File

@ -0,0 +1,23 @@
// userReducer.js
const initialState = {
user: null,
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_USER':
return {
...state,
user: action.payload,
};
case 'LOGOUT':
return {
...state,
user: null,
};
default:
return state;
}
};
export default userReducer;

View File

@ -0,0 +1,21 @@
const axios = require('axios');
const fs = require('fs');
// Чтение содержимого файла db.json
const dbContent = fs.readFileSync('./db.json', 'utf8');
// Адрес вашего JSON Server
const baseURL = 'http://localhost:8081/';
// Отправка данных на сервер
axios({
method: 'post',
url: `${baseURL}db`,
data: JSON.parse(dbContent),
})
.then((response) => {
console.log('Данные успешно загружены на сервер JSON Server.');
})
.catch((error) => {
console.error('Ошибка загрузки данных:', error);
});

View File

@ -1,41 +1,61 @@
import PropTypes from 'prop-types'; import React from 'react';
import { Container, Nav, Navbar } from 'react-bootstrap'; import { useSelector, useDispatch } from 'react-redux';
import { Cart2 } from 'react-bootstrap-icons';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { Container, Nav, Navbar } from 'react-bootstrap';
import { logoutUser } from './js/userActions';
import PropTypes from 'prop-types';
import logo from '../../assets/logo.png'; import logo from '../../assets/logo.png';
import './Navigation.css'; import './Navigation.css';
const Navigation = ({ routes }) => { const Navigation = ({ routes }) => {
const location = useLocation(); const location = useLocation();
const indexPageLink = routes.filter((route) => route.index === false).shift(); const indexPageLink = routes.filter((route) => route.index === false).shift();
const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title')); const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title'));
const user = useSelector((state) => state.user); // Получение пользователя из хранилища Redux
const dispatch = useDispatch();
return ( const handleLogout = () => {
<header> dispatch(logoutUser()); // Действие для выхода пользователя
<Navbar expand='md' className='my-navbar'> };
<Container fluid>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}> return (
<img src={logo} alt="Мой логотип" width="280" height="60" /> <header>
</Navbar.Brand> <Navbar expand='md' className='my-navbar'>
<Navbar.Toggle aria-controls='main-navbar' /> <Container fluid>
<Navbar.Collapse id='main-navbar'> <Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<Nav className='me-auto link' activeKey={location.pathname}> <img src={logo} alt="Мой логотип" width="280" height="60" />
{ </Navbar.Brand>
pages.map((page) => <Navbar.Toggle aria-controls='main-navbar' />
<Nav.Link as={Link} key={page.path} eventKey={page.path} to={page.path ?? '/'}> <Navbar.Collapse id='main-navbar'>
{page.title} <Nav className='me-auto link' activeKey={location.pathname}>
</Nav.Link>) {pages.map((page) => (
} <Nav.Link
</Nav> as={Link}
</Navbar.Collapse> key={page.path}
</Container> eventKey={page.path}
</Navbar > to={page.path ?? '/'}
</header> >
); {page.title}
</Nav.Link>
))}
</Nav>
<div className='user-info'>
{user ? (
<div className='user-welcome'>
<span>Добро пожаловать, {user.username}</span>
<button onClick={handleLogout}>Выход</button>
</div>
) : null}
</div>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
);
}; };
Navigation.propTypes = { Navigation.propTypes = {
routes: PropTypes.array, routes: PropTypes.array,
}; };
export default Navigation; export default Navigation;

View File

@ -0,0 +1,7 @@
// userActions.js
export const logoutUser = () => {
return {
type: 'LOGOUT',
};
};

View File

@ -60,7 +60,7 @@ const Page1 = () => {
</div> </div>
<div className="col-md-4"> <div className="col-md-4">
<div className="p-2"> <div className="p-2">
<img src="./src/assets/image_from_main_1.jpg" alt="Картинка" className="w-100" /> <img src="src/assets/image_from_main_1.jpg" alt="Картинка" className="w-100" />
<img src="../src/assets/image_from_main_2.jpg" alt="Картинка" className=" d-block mt-2 w-100" /> <img src="../src/assets/image_from_main_2.jpg" alt="Картинка" className=" d-block mt-2 w-100" />
<img src="../src/assets/image_from_main_3.jpg" alt="Картинка" className="d-block mt-2 w-100" /> <img src="../src/assets/image_from_main_3.jpg" alt="Картинка" className="d-block mt-2 w-100" />
</div> </div>

View File

@ -1,32 +1,92 @@
/* eslint-disable linebreak-style */ import { useForm } from 'react-hook-form';
import { useState } from 'react';
import axios from 'axios';
import { useDispatch } from 'react-redux';
const Page5 = () => { const Page5 = () => {
return ( const { register, handleSubmit } = useForm();
<> const [errorMessage, setErrorMessage] = useState('');
<div className="container-fluid"> const baseURL = 'http://localhost:8081/';
<div className="row"> const dispatch = useDispatch();
<div className="col-xl-4 col-lg-5 col-md-6 text-center"> const onSubmit = async (data) => {
<div className="d-flex align-items-center min-vh-100"> try {
<div className="container"> const response = await axios.get(`${baseURL}users`, {
<a className="navbar-brand" href="/"> params: {
<img src="./src/assets/logo_cabinet.jpg" alt="Логотип" width="280" height="170" className="text-center mb-4" /> login: data.login,
</a> password: data.password,
<h2 className="text-center"> Вход</h2> },
<h3 className="text-center">Личный кабинет</h3> });
<form>
<div className="mb-3"> if (response.data.length > 0) {
<input type="text" className="form-control" id="login" name="login" placeholder="Логин" required /> console.log('Успешный вход');
</div> const userData = { username: data.login }; // Замените на данные пользователя
<div className="mb-3"> dispatch({ type: 'SET_USER', payload: userData });
<input type="password" className="form-control" id="password" name="password" placeholder="Пароль" required /> localStorage.setItem('user', JSON.stringify(userData));
</div> redirectToHomePage(); // Замените на вашу функцию перехода
<button className="btn btn-purple btn-block" type="submit">Вход</button> } else {
</form> console.error('Неверный логин или пароль');
setErrorMessage('Неверный логин или пароль');
}
} catch (error) {
console.error('Произошла ошибка:', error);
setErrorMessage('Произошла ошибка. Пожалуйста, попробуйте снова.');
}
};
const redirectToHomePage = () => {
window.location.href = 'http://localhost:5173/';
};
return (
<>
<div className="container-fluid">
<div className="row">
<div className="col-xl-4 col-lg-5 col-md-6 text-center">
<div className="d-flex align-items-center min-vh-100">
<div className="container">
<a className="navbar-brand" href="/">
<img
src="./src/assets/logo_cabinet.jpg"
alt="Логотип"
width="280"
height="170"
className="text-center mb-4"
/>
</a>
<h2 className="text-center"> Вход</h2>
<h3 className="text-center">Личный кабинет</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-3">
<input
type="text"
className="form-control"
placeholder="Логин"
{...register('login', { required: true })}
/>
</div>
<div className="mb-3">
<input
type="password"
className="form-control"
placeholder="Пароль"
{...register('password', { required: true })}
/>
</div>
<button className="btn btn-purple btn-block" type="submit">Вход</button>
{errorMessage && <p>{errorMessage}</p>}
</form>
</div>
</div> </div>
</div> </div>
<div className="col-xl-8 col-lg-7 col-md-6 d-none d-md-flex">
<img
src="./src/assets/cabinet_autch.jpg"
alt="Изображение"
style={{ width: '100%', height: 'auto' }}
/>
</div>
</div> </div>
<div id="ams-auth-image" className="col-xl-8 col-lg-7 col-md-6 d-none d-md-flex" style={ { backgroundImage: 'url(./src/assets/cabinet_autch.jpg)' }}></div>
</div> </div>
</div>
</> </>
); );
}; };