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",
|
"bootstrap": "^5.3.2",
|
||||||
"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.2",
|
||||||
"react-bootstrap-icons": "^1.10.3",
|
"react-bootstrap-icons": "^1.10.3",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
@ -4699,9 +4699,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-bootstrap": {
|
"node_modules/react-bootstrap": {
|
||||||
"version": "2.9.1",
|
"version": "2.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.2.tgz",
|
||||||
"integrity": "sha512-ezgmh/ARCYp18LbZEqPp0ppvy+ytCmycDORqc8vXSKYV3cer4VH7OReV8uMOoKXmYzivJTxgzGHalGrHamryHA==",
|
"integrity": "sha512-a36B+EHsAI/aH+ZhXNILBFnqscE3zr10dWmjBmfhIb2QR7KSXJiGzYd6Faf/25G8G7/CP9TCL2B0WhUBOD2UBQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.22.5",
|
"@babel/runtime": "^7.22.5",
|
||||||
"@restart/hooks": "^0.4.9",
|
"@restart/hooks": "^0.4.9",
|
||||||
|
@ -11,15 +11,15 @@
|
|||||||
"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",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.18.0",
|
|
||||||
"react-hot-toast": "^2.4.1",
|
|
||||||
"axios": "^1.6.1",
|
"axios": "^1.6.1",
|
||||||
"bootstrap": "^5.3.2",
|
"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",
|
"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": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.15",
|
||||||
@ -35,4 +35,4 @@
|
|||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"vite": "^4.4.5"
|
"vite": "^4.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Container } from 'react-bootstrap';
|
// import { Container } from 'react-bootstrap';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
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 Sidebar from './components/sidebar/Sidebar.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 }) => {
|
const App = ({ routes }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<AuthProvider>
|
||||||
|
<CartProvider>
|
||||||
|
<body>
|
||||||
<Navigation routes={routes}></Navigation>
|
<Navigation routes={routes}></Navigation>
|
||||||
|
|
||||||
<Container className='flex-container container-fluid' as="main" fluid>
|
<main className='flex-container container-fluid'>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<Container className='' as="main" fluid>
|
<main className='flex-container full-page-div'>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Container>
|
</main>
|
||||||
|
</main>
|
||||||
</Container>
|
|
||||||
<Footer />
|
<Footer />
|
||||||
|
</body>
|
||||||
|
</CartProvider>
|
||||||
|
</AuthProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -29,6 +38,20 @@ App.propTypes = {
|
|||||||
|
|
||||||
export default App;
|
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">
|
{/* <div className="app-container">
|
||||||
<Navigation routes={routes}></Navigation>
|
<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();
|
action();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="col-md-4 mb-4">
|
<div className="col-md-4 mb-4">
|
||||||
<div className="rectNews d-flex flex-column">
|
<div className="card mb-4 shadow-sm">
|
||||||
<img src={item.image} width="100%" alt={item.name} />
|
<img className="card-img-top" src={item.image} width="100%" />
|
||||||
<div className="rectNewsTextBox text-center">
|
<div className="card-body">
|
||||||
<span className="rectNewsText">
|
<div className="container">
|
||||||
<a href="#" onClick={(event) => handleAnchorClick(event, onEdit)}><PencilFill /></a>
|
<div className="row">
|
||||||
<a href="#" onClick={(event) => handleAnchorClick(event, onDelete)}><Trash3 /></a>
|
|
||||||
{item.description}
|
<div className='col-md-4'>
|
||||||
</span>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,11 +85,11 @@ const UpdateVideos = () => {
|
|||||||
<>
|
<>
|
||||||
<Container className="col text-center">
|
<Container className="col text-center">
|
||||||
<span className="mainSt">
|
<span className="mainSt">
|
||||||
<b>Новости</b>
|
<b>Видео</b>
|
||||||
</span>
|
</span>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Button variant='info' onClick={() => showFormModal()}>
|
<Button variant='info' onClick={() => showFormModal()}>
|
||||||
Добавить товар</Button>
|
Добавить видео</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="mainDiv col-md-9 ml-sm-auto col-lg-10 px-">
|
<div className="mainDiv col-md-9 ml-sm-auto col-lg-10 px-">
|
||||||
<div className='mainRow row'>
|
<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 Search from './Search.jsx';
|
||||||
import useSearching from '../hooks/SearchHooks.js';
|
import useSearching from '../hooks/SearchHooks.js';
|
||||||
import Input from '../../input/Input.jsx';
|
import Input from '../../input/Input.jsx';
|
||||||
import useTypeFilter from '../../lines/hooks/LinesFilterHook.js';
|
// import useTypeFilter from '../../lines/hooks/LinesFilterHook.js';
|
||||||
|
|
||||||
const Searching = () => {
|
const Searching = () => {
|
||||||
const { type, currentFilter } = useTypeFilter;
|
// const { type, currentFilter } = useTypeFilter;
|
||||||
const { searching } = useSearching();
|
const { searching } = useSearching();
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const {lines} =useLines(currentFilter);
|
// const {lines} =useLines(currentFilter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -50,7 +50,7 @@ h4 {
|
|||||||
height: 500px;
|
height: 500px;
|
||||||
}*/
|
}*/
|
||||||
/* .container-fluid{
|
/* .container-fluid{
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.full-page-div {
|
.full-page-div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -109,24 +109,10 @@ font-size: 13px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* .sidebar {
|
.sidebar {
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
height: 100vh;
|
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,
|
img,
|
||||||
svg {
|
svg {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
// import React from 'react';
|
// import React from 'react';
|
||||||
import './Sidebar.css';
|
import '../sidebar/Sidebar.css';
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
return (
|
return (
|
||||||
|
<body>
|
||||||
<div className="container-fluid ">
|
<div className="container-fluid ">
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -78,6 +79,7 @@ const Sidebar = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</body>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ const useTypes = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getTypes();
|
getTypes();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
types,
|
types,
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Entry from '../components/logins/login/Entry/Entry.jsx';
|
||||||
|
|
||||||
|
|
||||||
const AdminPage = () => {
|
const AdminPage = () => {
|
||||||
@ -15,6 +16,7 @@ const AdminPage = () => {
|
|||||||
<>
|
<>
|
||||||
<main role="main" className="col-md-9 ml-sm-auto col-lg-10 px-4">
|
<main role="main" className="col-md-9 ml-sm-auto col-lg-10 px-4">
|
||||||
<div className="full-page-div">
|
<div className="full-page-div">
|
||||||
|
<Entry />
|
||||||
<div className="btn-group" role="group">
|
<div className="btn-group" role="group">
|
||||||
<button id="items-add" className="btn btn-info">
|
<button id="items-add" className="btn btn-info">
|
||||||
Добавить видео (диалог)
|
Добавить видео (диалог)
|
||||||
|
@ -16,7 +16,7 @@ html, body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.full-page-div {
|
.full-page-div {
|
||||||
background-color: #ececec; /* Цвет фона */
|
background-color:#ececec; /* Цвет фона #ececec */
|
||||||
padding: 1%; /* Внутренний отступ */
|
padding: 1%; /* Внутренний отступ */
|
||||||
margin: 1%; /* Внешний отступ */
|
margin: 1%; /* Внешний отступ */
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import UpdateVideos from '../components/lines/videos/UpdateVideos.jsx';
|
import UpdateVideos from '../components/lines/videos/UpdateVideos.jsx';
|
||||||
|
import '../pages/Pages.css';
|
||||||
|
|
||||||
const StartPage = () => {
|
const StartPage = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,7 +18,7 @@ const StartPage = () => {
|
|||||||
<div className="p-5 text-bg-dark rounded-3">
|
<div className="p-5 text-bg-dark rounded-3">
|
||||||
<h2>Популярные трансляции</h2>
|
<h2>Популярные трансляции</h2>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<UpdateVideos />
|
<UpdateVideos/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user