process
This commit is contained in:
parent
dabcb6d568
commit
eea4e8d8c6
8
lab5/package-lock.json
generated
8
lab5/package-lock.json
generated
@ -12,7 +12,7 @@
|
||||
"bootstrap": "^5.3.2",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.9.1",
|
||||
"react-bootstrap": "^2.9.2",
|
||||
"react-bootstrap-icons": "^1.10.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
@ -4699,9 +4699,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-bootstrap": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.1.tgz",
|
||||
"integrity": "sha512-ezgmh/ARCYp18LbZEqPp0ppvy+ytCmycDORqc8vXSKYV3cer4VH7OReV8uMOoKXmYzivJTxgzGHalGrHamryHA==",
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.2.tgz",
|
||||
"integrity": "sha512-a36B+EHsAI/aH+ZhXNILBFnqscE3zr10dWmjBmfhIb2QR7KSXJiGzYd6Faf/25G8G7/CP9TCL2B0WhUBOD2UBQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.22.5",
|
||||
"@restart/hooks": "^0.4.9",
|
||||
|
@ -11,15 +11,15 @@
|
||||
"prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"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",
|
||||
"react-bootstrap": "^2.9.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.9.2",
|
||||
"react-bootstrap-icons": "^1.10.3",
|
||||
"prop-types": "^15.8.1"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-router-dom": "^6.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
@ -35,4 +35,4 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"vite": "^4.4.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,33 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Container } from 'react-bootstrap';
|
||||
// import { Container } from 'react-bootstrap';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import './App.css';
|
||||
import Footer from './components/footer/Footer.jsx';
|
||||
import Navigation from './components/navigation/Navigation.jsx';
|
||||
import Sidebar from './components/sidebar/Sidebar.jsx';
|
||||
import { AuthProvider } from './components/logins/login/context/AuthContext.jsx';
|
||||
import { CartProvider } from './components/cart/CartContext.jsx';
|
||||
|
||||
|
||||
const App = ({ routes }) => {
|
||||
return (
|
||||
<>
|
||||
<AuthProvider>
|
||||
<CartProvider>
|
||||
<body>
|
||||
<Navigation routes={routes}></Navigation>
|
||||
|
||||
<Container className='flex-container container-fluid' as="main" fluid>
|
||||
<Sidebar />
|
||||
<Container className='' as="main" fluid>
|
||||
<Outlet />
|
||||
</Container>
|
||||
|
||||
</Container>
|
||||
<main className='flex-container container-fluid'>
|
||||
<Sidebar />
|
||||
<main className='flex-container full-page-div'>
|
||||
<Outlet />
|
||||
</main>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</body>
|
||||
</CartProvider>
|
||||
</AuthProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -29,6 +38,20 @@ App.propTypes = {
|
||||
|
||||
export default App;
|
||||
|
||||
|
||||
{/* <Navigation routes={routes}></Navigation>
|
||||
|
||||
<Container className='flex-container container-fluid' as="main" fluid>
|
||||
<Sidebar />
|
||||
<Container className='container-fluid full-page-div' as="main" fluid>
|
||||
<Sidebar />
|
||||
<Outlet />
|
||||
</Container>
|
||||
|
||||
</Container>
|
||||
<Footer /> */}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
{/* <div className="app-container">
|
||||
<Navigation routes={routes}></Navigation>
|
||||
|
||||
|
8
lab5/src/components/cart/Cart.css
Normal file
8
lab5/src/components/cart/Cart.css
Normal file
@ -0,0 +1,8 @@
|
||||
.cart-image {
|
||||
width: 3.1rem;
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
height: auto;
|
||||
}
|
59
lab5/src/components/cart/Cart.jsx
Normal file
59
lab5/src/components/cart/Cart.jsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { Button, ButtonGroup, Card } from 'react-bootstrap';
|
||||
import { DashLg, PlusLg, XLg } from 'react-bootstrap-icons';
|
||||
import imgPlaceholder from '../../assets/200.png';
|
||||
import './Cart.css';
|
||||
import useCart from './CartHook';
|
||||
|
||||
const Cart = () => {
|
||||
const {
|
||||
cart,
|
||||
getCartSum,
|
||||
addToCart,
|
||||
removeFromCart,
|
||||
clearCart,
|
||||
} = useCart();
|
||||
|
||||
return (
|
||||
<div className='d-flex flex-column align-items-center'>
|
||||
<div className='mb-2 col-12 col-md-8 col-lg-6 d-flex align-items-center'>
|
||||
<strong className='flex-fill'>Корзина</strong>
|
||||
<Button variant='danger' onClick={() => clearCart()}>
|
||||
<XLg /> Очистить
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
cart.map((cartItem) =>
|
||||
<Card key={cartItem.id} className='mb-2 col-12 col-md-8 col-lg-6'>
|
||||
<Card.Body className='p-2 d-flex flex-column flex-sm-row align-items-center'>
|
||||
<div className='cart-item flex-fill'>
|
||||
<img className='cart-image' src={cartItem.image || imgPlaceholder} alt="Cart Image" />
|
||||
{cartItem.type.name}
|
||||
</div>
|
||||
<div className='cart-item mt-2 mt-sm-0 d-flex flex-column align-items-center align-items-sm-end'>
|
||||
<div>
|
||||
{cartItem.price}
|
||||
{' * '}
|
||||
{cartItem.count}
|
||||
{' = '}
|
||||
{parseFloat(cartItem.price * cartItem.count).toFixed(2)}
|
||||
</div>
|
||||
<ButtonGroup className='mt-2 mt-sm-1' aria-label="Cart counter">
|
||||
<Button variant="primary" onClick={() => addToCart(cartItem)}>
|
||||
<PlusLg />
|
||||
</Button>
|
||||
<Button variant="danger" onClick={() => removeFromCart(cartItem)}>
|
||||
<DashLg />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>)
|
||||
}
|
||||
<div className='mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-end'>
|
||||
<strong>Итого: {getCartSum()} ₽</strong>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cart;
|
29
lab5/src/components/cart/CartContext.jsx
Normal file
29
lab5/src/components/cart/CartContext.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
createContext,
|
||||
useEffect,
|
||||
useReducer,
|
||||
} from 'react';
|
||||
import { cartReducer, loadCart, saveCart } from './CartReducer';
|
||||
|
||||
const CartContext = createContext(null);
|
||||
|
||||
export const CartProvider = ({ children }) => {
|
||||
const [cart, dispatch] = useReducer(cartReducer, [], loadCart);
|
||||
|
||||
useEffect(() => {
|
||||
saveCart(cart || []);
|
||||
}, [cart]);
|
||||
|
||||
return (
|
||||
<CartContext.Provider value={{ cart, dispatch }}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
CartProvider.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default CartContext;
|
26
lab5/src/components/cart/CartHook.js
Normal file
26
lab5/src/components/cart/CartHook.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { useContext } from 'react';
|
||||
import CartContext from './CartContext.jsx';
|
||||
import { cartAdd, cartClear, cartRemove } from './CartReducer';
|
||||
|
||||
const useCart = () => {
|
||||
const { cart, dispatch } = useContext(CartContext);
|
||||
|
||||
const cartSum = () => {
|
||||
return parseFloat(
|
||||
cart?.reduce((sum, cartItem) => {
|
||||
return sum + (cartItem.price * cartItem.count);
|
||||
}, 0)
|
||||
?? 0,
|
||||
).toFixed(2);
|
||||
};
|
||||
|
||||
return {
|
||||
cart,
|
||||
getCartSum: () => cartSum(),
|
||||
addToCart: (item) => dispatch(cartAdd(item)),
|
||||
removeFromCart: (item) => dispatch(cartRemove(item)),
|
||||
clearCart: () => dispatch(cartClear()),
|
||||
};
|
||||
};
|
||||
|
||||
export default useCart;
|
71
lab5/src/components/cart/CartReducer.js
Normal file
71
lab5/src/components/cart/CartReducer.js
Normal file
@ -0,0 +1,71 @@
|
||||
const setCartCount = (cart, item, value) => {
|
||||
return cart.map((cartItem) => {
|
||||
if (cartItem.id === item.id) {
|
||||
return { ...cartItem, count: cartItem.count + value };
|
||||
}
|
||||
return cartItem;
|
||||
});
|
||||
};
|
||||
|
||||
const addToCart = (cart, item) => {
|
||||
const existsItem = cart.find((cartItem) => cartItem.id === item.id);
|
||||
if (existsItem !== undefined) {
|
||||
return setCartCount(cart, item, 1);
|
||||
}
|
||||
return [...cart, { ...item, count: 1 }];
|
||||
};
|
||||
|
||||
const removeFromCart = (cart, item) => {
|
||||
const existsItem = cart.find((cartItem) => cartItem.id === item.id);
|
||||
if (existsItem !== undefined && existsItem.count > 1) {
|
||||
return setCartCount(cart, item, -1);
|
||||
}
|
||||
return cart.filter((cartItem) => cartItem.id !== item.id);
|
||||
};
|
||||
|
||||
const CART_KEY = 'localCart';
|
||||
const CART_ADD = 'cart/add';
|
||||
const CART_REMOVE = 'cart/remove';
|
||||
const CART_CLEAR = 'cart/clear';
|
||||
|
||||
export const saveCart = (cart) => {
|
||||
localStorage.setItem(CART_KEY, JSON.stringify(cart));
|
||||
};
|
||||
|
||||
export const loadCart = (initialValue = []) => {
|
||||
const cartData = localStorage.getItem(CART_KEY);
|
||||
if (cartData) {
|
||||
return JSON.parse(cartData);
|
||||
}
|
||||
return initialValue;
|
||||
};
|
||||
|
||||
export const cartReducer = (cart, action) => {
|
||||
const { item } = action;
|
||||
switch (action.type) {
|
||||
case CART_ADD: {
|
||||
return addToCart(cart, item);
|
||||
}
|
||||
case CART_REMOVE: {
|
||||
return removeFromCart(cart, item);
|
||||
}
|
||||
case CART_CLEAR: {
|
||||
return [];
|
||||
}
|
||||
default: {
|
||||
throw Error(`Unknown action: ${action.type}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const cartAdd = (item) => ({
|
||||
type: CART_ADD, item,
|
||||
});
|
||||
|
||||
export const cartRemove = (item) => ({
|
||||
type: CART_REMOVE, item,
|
||||
});
|
||||
|
||||
export const cartClear = () => ({
|
||||
type: CART_CLEAR,
|
||||
});
|
@ -23,18 +23,35 @@ const UpdateVideo1 = ({
|
||||
action();
|
||||
};
|
||||
return (
|
||||
<div className="col-md-4 mb-4">
|
||||
<div className="rectNews d-flex flex-column">
|
||||
<img src={item.image} width="100%" alt={item.name} />
|
||||
<div className="rectNewsTextBox text-center">
|
||||
<span className="rectNewsText">
|
||||
<a href="#" onClick={(event) => handleAnchorClick(event, onEdit)}><PencilFill /></a>
|
||||
<a href="#" onClick={(event) => handleAnchorClick(event, onDelete)}><Trash3 /></a>
|
||||
{item.description}
|
||||
</span>
|
||||
<div className="col-md-4 mb-4">
|
||||
<div className="card mb-4 shadow-sm">
|
||||
<img className="card-img-top" src={item.image} width="100%" />
|
||||
<div className="card-body">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
|
||||
<div className='col-md-4'>
|
||||
<p className='h-4 m-0 text-nowrap font-italic font-weight-bold'>
|
||||
{item.name}
|
||||
</p>
|
||||
<p className='fs-5 text-nowrap text-secondary'>
|
||||
{item.description}
|
||||
</p>
|
||||
<a href="#" onClick={(event) => handleAnchorClick(event, onEdit)}><PencilFill /></a>
|
||||
<a href="#" onClick={(event) => handleAnchorClick(event, onDelete)}><Trash3 /></a>
|
||||
</div>
|
||||
<div className='col-md-4'>
|
||||
</div>
|
||||
<div className='col-md-4'>
|
||||
<a className='btn btn-secondary ms-auto' href='./channel'></a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -68,11 +85,11 @@ const UpdateVideos = () => {
|
||||
<>
|
||||
<Container className="col text-center">
|
||||
<span className="mainSt">
|
||||
<b>Новости</b>
|
||||
<b>Видео</b>
|
||||
</span>
|
||||
<div className="text-center">
|
||||
<Button variant='info' onClick={() => showFormModal()}>
|
||||
Добавить товар</Button>
|
||||
Добавить видео</Button>
|
||||
</div>
|
||||
<div className="mainDiv col-md-9 ml-sm-auto col-lg-10 px-">
|
||||
<div className='mainRow row'>
|
||||
|
33
lab5/src/components/logins/hooks/DataItemHook.js
Normal file
33
lab5/src/components/logins/hooks/DataItemHook.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import EntrysDataApiService from '../service/EntrysDataApiService';
|
||||
|
||||
const useEntysDataItem = (id) => {
|
||||
const emptyItem = {
|
||||
id: '',
|
||||
login: '',
|
||||
password: '',
|
||||
role: '',
|
||||
};
|
||||
const [item, setItem] = useState({ ...emptyItem });
|
||||
|
||||
const getItem = async (itemId = undefined) => {
|
||||
if (itemId && itemId > 0) {
|
||||
const data = await EntrysDataApiService.get(itemId);
|
||||
setItem(data);
|
||||
} else {
|
||||
setItem({ ...emptyItem });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getItem(id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
return {
|
||||
item,
|
||||
setItem,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEntysDataItem;
|
22
lab5/src/components/logins/hooks/EntrysDataHook.js
Normal file
22
lab5/src/components/logins/hooks/EntrysDataHook.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import EntrysDataApiService from '../service/EntrysDataApiService';
|
||||
|
||||
const useEntrysData = (login, password, expand) => {
|
||||
const [entrys, setEntrys] = useState([]);
|
||||
|
||||
const getEntrysData = async () => {
|
||||
const data = await EntrysDataApiService.getAll(expand);
|
||||
setEntrys(data ?? []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getEntrysData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [login, password]);
|
||||
|
||||
return {
|
||||
entrys,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEntrysData;
|
60
lab5/src/components/logins/hooks/EntrysDataItemHook.js
Normal file
60
lab5/src/components/logins/hooks/EntrysDataItemHook.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { useState } from 'react';
|
||||
import EntrysDataApiService from '../service/EntrysDataApiService';
|
||||
import useEntysDataItem from './DataItemHook';
|
||||
|
||||
const useEntrysItemForm = (id) => {
|
||||
const { item, setItem } = useEntysDataItem(id);
|
||||
|
||||
const [validated, setValidated] = useState(false);
|
||||
|
||||
const resetValidity = () => {
|
||||
setValidated(false);
|
||||
};
|
||||
|
||||
const getLineObject = (formData) => {
|
||||
const Login = formData.login.toString();
|
||||
const Password = formData.password.toString();
|
||||
const Role = 'user';
|
||||
return {
|
||||
login: Login,
|
||||
password: Password,
|
||||
role: Role,
|
||||
};
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const inputName = event.target.name;
|
||||
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
setItem({
|
||||
...item,
|
||||
[inputName]: inputValue,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
const form = event.currentTarget;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const body = getLineObject(item);
|
||||
if (form.checkValidity()) {
|
||||
if (id === undefined) {
|
||||
await EntrysDataApiService.create(body);
|
||||
} else {
|
||||
await EntrysDataApiService.update(id, body);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
setValidated(true);
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
item,
|
||||
validated,
|
||||
UseHandleSubmit: handleSubmit,
|
||||
UseHandleChange: handleChange,
|
||||
resetValidity,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEntrysItemForm;
|
0
lab5/src/components/logins/hooks/Reducer.js
Normal file
0
lab5/src/components/logins/hooks/Reducer.js
Normal file
58
lab5/src/components/logins/login/Entry.jsx
Normal file
58
lab5/src/components/logins/login/Entry.jsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import Input from '../../input/Input.jsx';
|
||||
import useEntrysData from '../hooks/EntrysDataHook';
|
||||
|
||||
const Entry = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [login, setLogin] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const isLoginValid = (value) => /^[a-zA-Z]+$/.test(value);
|
||||
const { entrys } = useEntrysData(login, password);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const form = event.currentTarget;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (form.checkValidity() !== false) {
|
||||
if (isLoginValid(login)) {
|
||||
if (entrys.length === 0) {
|
||||
toast.error('Аккаунт не найден');
|
||||
} else {
|
||||
setValidated(true);
|
||||
toast.success('Был произведён вход');
|
||||
}
|
||||
} else {
|
||||
toast.error('Логин должен быть введён латинскими символами');
|
||||
}
|
||||
}
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<body className="container-fluid text-center">
|
||||
<span className="mainSt">
|
||||
<b>Личный кабинет</b>
|
||||
</span>
|
||||
<div className="rectpage4 d-flex row justify-content-center">
|
||||
<span className="EntrysSt">
|
||||
<b>Вход</b>
|
||||
</span>
|
||||
<Form className="col-md-4 m-0 w-auto" onSubmit={ handleSubmit } noValidate validated={validated}>
|
||||
<label className="form-label"><b>Логин</b></label>
|
||||
<Input name="login" value = { login } onChange={(e) => setLogin(e.target.value)}
|
||||
placeholder="dyctator" type="text" required />
|
||||
<label className="form-label"><b>Пароль</b></label>
|
||||
<Input name="password" value = { password } onChange={(e) => setPassword(e.target.value)}
|
||||
type="password" required />
|
||||
<Button className="btn btn-primary w-auto" type="submit" >Войти</Button>
|
||||
<Button as={Link} to='/registrPage' variant = "danger" className="btn btn-primary w-auto" >Регистрация</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</body>
|
||||
);
|
||||
};
|
||||
|
||||
export default Entry;
|
87
lab5/src/components/logins/login/Entry/Entry.jsx
Normal file
87
lab5/src/components/logins/login/Entry/Entry.jsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import Input from '../../../input/Input.jsx';
|
||||
import useEntrysData from '../../hooks/EntrysDataHook';
|
||||
import { AuthContext } from '../context/AuthContext.jsx';
|
||||
|
||||
const Entry = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [login, setLogin] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const isLoginValid = (value) => /^[a-zA-Z0-9]+$/.test(value);
|
||||
const { entrys } = useEntrysData(login, password, `?login=${login}&password=${password}`);
|
||||
const { state, dispatch } = useContext(AuthContext);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const form = event.currentTarget;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (form.checkValidity() !== false) {
|
||||
if (isLoginValid(login)) {
|
||||
if (entrys.length === 0) {
|
||||
toast.error('Аккаунт не найден');
|
||||
} else {
|
||||
setValidated(true);
|
||||
dispatch({
|
||||
type: 'LOGIN',
|
||||
payload: entrys,
|
||||
});
|
||||
toast.success(`Был произведён вход: ${login}`);
|
||||
}
|
||||
} else {
|
||||
toast.error('Логин должен быть введён латинскими символами');
|
||||
}
|
||||
}
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
const handleLogOut = () => {
|
||||
dispatch({
|
||||
type: 'LOGOUT',
|
||||
payload: entrys,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container-fluid text-center">
|
||||
<span className="mainSt">
|
||||
<b>Личный кабинет</b>
|
||||
</span>
|
||||
<div className="rectpage4 d-flex row justify-content-center">
|
||||
{state.user === null ? (
|
||||
<>
|
||||
<span className="EntrysSt">
|
||||
<b>Вход</b>
|
||||
</span>
|
||||
<Form className="col-md-4 m-0 w-auto" onSubmit={ handleSubmit } noValidate validated={validated}>
|
||||
<label className="form-label"><b>Логин</b></label>
|
||||
<Input name="login" value = { login } onChange={(e) => setLogin(e.target.value)}
|
||||
placeholder="dyctator" type="text" required />
|
||||
<label className="form-label"><b>Пароль</b></label>
|
||||
<Input name="password" value = { password } onChange={(e) => setPassword(e.target.value)}
|
||||
type="password" required />
|
||||
<Button className="btn btn-primary w-auto" type="submit" >Войти</Button>
|
||||
<Button as={Link} to='/registrPage' variant = "danger" className="btn btn-primary w-auto" >Регистрация</Button>
|
||||
</Form>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className = 'EntrysSt'>Welcome, {state.user.map((item) => item.login)}</p>
|
||||
<Button className="btn btn-primary w-25 h-25" variant = "danger" onClick = {handleLogOut}>
|
||||
Выйти
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
Entry.propTypes = {
|
||||
item: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Entry;
|
73
lab5/src/components/logins/login/Registration.jsx
Normal file
73
lab5/src/components/logins/login/Registration.jsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import Input from '../../input/Input.jsx';
|
||||
import useEntrysData from '../hooks/EntrysDataHook';
|
||||
import useEntrysItemForm from '../hooks/EntrysDataItemHook';
|
||||
|
||||
const Entry = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [login, setLogin] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const isLoginValid = (value) => /^[a-zA-Z0-9]+$/.test(value);
|
||||
const { entrys } = useEntrysData(login, password, `?login=${login}`);
|
||||
|
||||
const { UseHandleSubmit, UseHandleChange } = useEntrysItemForm();
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const form = event.currentTarget;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (form.checkValidity() !== false) {
|
||||
if (isLoginValid(login)) {
|
||||
const isLoginExists = entrys.some((item) => item.login === login);
|
||||
if (isLoginExists) {
|
||||
toast.error('Такой аккаунт уже создан');
|
||||
} else {
|
||||
UseHandleSubmit(event);
|
||||
toast.success('Ваш аккаунт успешно создан');
|
||||
}
|
||||
} else {
|
||||
toast.error('Логин должен быть введён латинскими символами');
|
||||
return;
|
||||
}
|
||||
}
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
const handleChangeLogin = (event) => {
|
||||
setLogin(event.target.value);
|
||||
UseHandleChange(event);
|
||||
};
|
||||
|
||||
const handleChangePassword = (event) => {
|
||||
setPassword(event.target.value);
|
||||
UseHandleChange(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container-fluid text-center">
|
||||
<span className="mainSt">
|
||||
<b>Личный кабинет</b>
|
||||
</span>
|
||||
<div className="rectpage4 d-flex row justify-content-center">
|
||||
<span className="EntrysSt">
|
||||
<b>Регистрация</b>
|
||||
</span>
|
||||
<Form className="col-md-4 m-0 w-auto" onSubmit={ handleSubmit } noValidate validated={validated}>
|
||||
<label className="form-label"><b>Логин</b></label>
|
||||
<Input name="login" value = { login } onChange={ handleChangeLogin }
|
||||
placeholder="dyctator" type="text" required />
|
||||
<label className="form-label"><b>Пароль</b></label>
|
||||
<Input name="password" value = { password } onChange={ handleChangePassword }
|
||||
type="password" required />
|
||||
<Button as={Link} to='/page4' className = "btn btn-info">Назад</Button>
|
||||
<Button className="btn btn-primary w-auto" type="submit" >Создать</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Entry;
|
66
lab5/src/components/logins/login/context/AuthContext.jsx
Normal file
66
lab5/src/components/logins/login/context/AuthContext.jsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { createContext, useReducer, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// Функция для сохранения состояния user в localStorage
|
||||
const saveToLocalstorage = (user) => {
|
||||
if (user) {
|
||||
localStorage.setItem('authState', JSON.stringify(user));
|
||||
} else {
|
||||
localStorage.removeItem('authState');
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для восстановления состояния user из localStorage
|
||||
const getInitialState = () => {
|
||||
const userStorage = localStorage.getItem('authState');
|
||||
if (userStorage) {
|
||||
return { user: JSON.parse(userStorage) };
|
||||
}
|
||||
return { user: null };
|
||||
};
|
||||
|
||||
const AuthReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'LOGIN':
|
||||
saveToLocalstorage(action.payload);
|
||||
return {
|
||||
...state,
|
||||
user: action.payload,
|
||||
};
|
||||
case 'LOGOUT':
|
||||
saveToLocalstorage(null);
|
||||
return {
|
||||
...state,
|
||||
user: null,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const AuthContext = createContext();
|
||||
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(AuthReducer, getInitialState, getInitialState);
|
||||
|
||||
// При инициализации компонента проверим localStorage
|
||||
useEffect(() => {
|
||||
const user = localStorage.getItem('authState');
|
||||
if (user) {
|
||||
dispatch({
|
||||
type: 'LOGIN',
|
||||
payload: JSON.parse(user),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ state, dispatch }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
AuthProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
import ApiService from '../../api/ApiService';
|
||||
|
||||
const EntrysDataApiService = new ApiService('entrysData');
|
||||
|
||||
export default EntrysDataApiService;
|
@ -3,13 +3,13 @@ import TableDirect from './TableDirect.jsx';
|
||||
import Search from './Search.jsx';
|
||||
import useSearching from '../hooks/SearchHooks.js';
|
||||
import Input from '../../input/Input.jsx';
|
||||
import useTypeFilter from '../../lines/hooks/LinesFilterHook.js';
|
||||
// import useTypeFilter from '../../lines/hooks/LinesFilterHook.js';
|
||||
|
||||
const Searching = () => {
|
||||
const { type, currentFilter } = useTypeFilter;
|
||||
// const { type, currentFilter } = useTypeFilter;
|
||||
const { searching } = useSearching();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const {lines} =useLines(currentFilter);
|
||||
// const {lines} =useLines(currentFilter);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -50,7 +50,7 @@ h4 {
|
||||
height: 500px;
|
||||
}*/
|
||||
/* .container-fluid{
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
.full-page-div {
|
||||
width: 100%;
|
||||
@ -109,24 +109,10 @@ font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
/* .sidebar {
|
||||
.sidebar {
|
||||
background-color: #f8f9fa;
|
||||
height: 100vh;
|
||||
} */
|
||||
.video-thumbnail {
|
||||
/* width: 1920px;
|
||||
height: 1080px; */
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.embed-responsive{
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 1600px;
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
|
@ -1,8 +1,9 @@
|
||||
// import React from 'react';
|
||||
import './Sidebar.css';
|
||||
import '../sidebar/Sidebar.css';
|
||||
|
||||
const Sidebar = () => {
|
||||
return (
|
||||
<body>
|
||||
<div className="container-fluid ">
|
||||
|
||||
<div className="row">
|
||||
@ -78,6 +79,7 @@ const Sidebar = () => {
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,6 @@ const useTypes = () => {
|
||||
useEffect(() => {
|
||||
getTypes();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
types,
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Entry from '../components/logins/login/Entry/Entry.jsx';
|
||||
|
||||
|
||||
const AdminPage = () => {
|
||||
@ -15,6 +16,7 @@ const AdminPage = () => {
|
||||
<>
|
||||
<main role="main" className="col-md-9 ml-sm-auto col-lg-10 px-4">
|
||||
<div className="full-page-div">
|
||||
<Entry />
|
||||
<div className="btn-group" role="group">
|
||||
<button id="items-add" className="btn btn-info">
|
||||
Добавить видео (диалог)
|
||||
|
@ -16,7 +16,7 @@ html, body {
|
||||
}
|
||||
}
|
||||
.full-page-div {
|
||||
background-color: #ececec; /* Цвет фона */
|
||||
background-color:#ececec; /* Цвет фона #ececec */
|
||||
padding: 1%; /* Внутренний отступ */
|
||||
margin: 1%; /* Внешний отступ */
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import UpdateVideos from '../components/lines/videos/UpdateVideos.jsx';
|
||||
import '../pages/Pages.css';
|
||||
|
||||
const StartPage = () => {
|
||||
return (
|
||||
@ -16,7 +18,7 @@ const StartPage = () => {
|
||||
<div className="p-5 text-bg-dark rounded-3">
|
||||
<h2>Популярные трансляции</h2>
|
||||
<div className="row">
|
||||
<UpdateVideos />
|
||||
<UpdateVideos/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user