4 + промежуточная 5
This commit is contained in:
parent
056bab6b14
commit
a8937930eb
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
<div class="navbar-collapse collapse justify-content-end" id="navbarNav">
|
||||
<div class="navbar-nav">
|
||||
<a class="btn custom-btn" href="personalAccountLogin.html">Войти</a>
|
||||
<a class="btn custom-btn" href="personalAccountLogin.html">Администратор</a>
|
||||
<a class="btn btn-warning" href="basket.html">Корзина</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -39,9 +39,11 @@
|
||||
</header>
|
||||
<main class="container-fluid p-2">
|
||||
<h1 class="text-warning text-center font-weight-bold">Панель администратора</h1>
|
||||
<div class="text-center">
|
||||
<div class="btn-group" role="group">
|
||||
<a class="btn btn-warning" href="/page-edit.html">Добавить товар</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-warning text-center font-weight-bold" style="padding-top: 10px;">Таблица данных</h2>
|
||||
<table id="items-table" class="table table-striped">
|
||||
|
File diff suppressed because one or more lines are too long
@ -74,6 +74,7 @@
|
||||
class="fw-bold text-body"><u>Регистрация</u></a></p>
|
||||
|
||||
<p class="text-center"><a class="fw-bold text-body" href="Administrator.html">Администратор</a></p>
|
||||
<p class="text-center"><a class="fw-bold text-body" href="AdminDop.html">Администратор2</a></p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
19
lab4/data.json
Normal file
19
lab4/data.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"posts": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "json-server",
|
||||
"author": "typicode"
|
||||
}
|
||||
],
|
||||
"comments": [
|
||||
{
|
||||
"id": 1,
|
||||
"body": "some comment",
|
||||
"postId": 1
|
||||
}
|
||||
],
|
||||
"profile": {
|
||||
"name": "typicode"
|
||||
}
|
||||
}
|
1714
lab4/package-lock.json
generated
1714
lab4/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,33 +1,38 @@
|
||||
{
|
||||
"name": "lab4",
|
||||
"name": "lec4",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
"rest": "json-server data.json",
|
||||
"vite": "vite",
|
||||
"dev": "npm-run-all --parallel rest vite",
|
||||
"prod": "npm-run-all lint 'vite build' --parallel rest 'vite preview'"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.2",
|
||||
"mdb-react-ui-kit": "^7.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"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",
|
||||
"react-bootstrap-icons": "^1.10.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.18.0"
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"json-server": "^0.17.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"vite": "^4.4.5"
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col } from 'react-bootstrap';
|
||||
import BasketCard from '../components/card/BasketCard';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Basket = () => {
|
||||
return (
|
||||
@ -24,7 +25,9 @@ const Basket = () => {
|
||||
Сумма ₽
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn btn-warning" style={{ marginLeft: '25px', marginBottom: '10px' }} href="makingAnOrder">К оплате</a>
|
||||
<Link to='/MakingAnOrder' className="btn btn-warning" style={{ marginLeft: '25px', marginBottom: '10px' }}>
|
||||
К оплате
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ const Contacts = () => {
|
||||
<iframe
|
||||
src="https://yandex.ru/map-widget/v1/?um=constructor%3A0643c92cbdf3809080e5dfb2804b473ea00af31cfabe6fee08676c59d8675f01&source=constructor"
|
||||
className="img-fluid"
|
||||
style={{ width: '100%', height: '720px' }}
|
||||
style={{ width: '100%', height: '500px' }}
|
||||
title="Yandex Map"
|
||||
></iframe>
|
||||
</Col>
|
||||
|
@ -35,7 +35,7 @@ const MakingAnOrder = () => {
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-4" controlId="name">
|
||||
<Form.Label htmlFor="name">Ваше имя</Form.Label>
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PasswordRecovery = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -39,15 +40,11 @@ const PasswordRecovery = () => {
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
<a href="personalAccountLogin" className="fw-bold text-body">
|
||||
<u>Войти</u>
|
||||
</a>
|
||||
<Link className="fw-bold text-body" to='/PersonalAccountLogin' style={{ color: 'black', textDecoration: 'none' }}><u>Войти</u></Link>
|
||||
</p>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
<a href="personalAccountRegister" className="fw-bold text-body">
|
||||
<u>Регистрация</u>
|
||||
</a>
|
||||
<Link className="fw-bold text-body" to='/PersonalAccountRegister' style={{ color: 'black', textDecoration: 'none' }}><u>Регистрация</u></Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccount = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -24,14 +25,14 @@ const PersonalAccount = () => {
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-4" controlId="name">
|
||||
<Form.Label htmlFor="name">Ваше имя</Form.Label>
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-4" controlId="surname">
|
||||
<Form.Label htmlFor="surname">Ваша фамилия</Form.Label>
|
||||
<Form.Label>Ваша фамилия</Form.Label>
|
||||
<Form.Control type="text" required/>
|
||||
<Form.Control.Feedback>Фамилия заполнена</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Фамилия не заполнена</Form.Control.Feedback>
|
||||
@ -65,9 +66,9 @@ const PersonalAccount = () => {
|
||||
</div>
|
||||
|
||||
<div className="d-flex justify-content-center mt-2">
|
||||
<a className="btn btn-outline-danger" type="button" href="/">
|
||||
<Link to='/' className="btn btn-outline-danger" type="button">
|
||||
Выйти
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccountLogin = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -43,28 +44,28 @@ const PersonalAccountLogin = () => {
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
Забыли пароль?{' '}
|
||||
<a href="PasswordRecovery" className="fw-bold text-body">
|
||||
<Link to='/PasswordRecovery' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Восстановление пароля</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className="d-flex justify-content-center">
|
||||
<Button variant="submit" className="btn-block btn-warning text-body mb-0" href="PersonalAccount">
|
||||
<Button as={Link} to='/PersonalAccount' variant="submit" className="btn-block btn-warning text-body mb-0">
|
||||
Вход
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
У вас нет аккаунта?{' '}
|
||||
<a href="personalAccountRegister" className="fw-bold text-body">
|
||||
<Link to='/personalAccountRegister' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Регистрация</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<p className="text-center">
|
||||
<a className="fw-bold text-body" href="Administrator">
|
||||
<Link to='/Administrator' className="fw-bold text-body" style={{ color: 'black'}}>
|
||||
Администратор
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccountRegister = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -27,7 +28,7 @@ const PersonalAccountRegister = () => {
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
<Form.Label htmlFor="name">Ваше имя</Form.Label>
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-4">
|
||||
@ -63,16 +64,18 @@ const PersonalAccountRegister = () => {
|
||||
</Form.Group>
|
||||
|
||||
<div className="d-flex justify-content-center">
|
||||
<Button variant="success" type="button" className="btn-block btn-warning text-body mb-0" href="PersonalAccount">
|
||||
<Link to='/PersonalAccount' style={{color: 'black', textDecoration: 'none'}}>
|
||||
<Button variant="success" type="button" className="btn-block btn-warning text-body mb-0">
|
||||
Регистрация
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
У вас уже есть учетная запись?{' '}
|
||||
<a href="personalAccountLogin" className="fw-bold text-body">
|
||||
<Link to='/PersonalAccountLogin' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Войдите здесь</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
581
lab5/data.json
581
lab5/data.json
File diff suppressed because one or more lines are too long
26
lab5/package-lock.json
generated
26
lab5/package-lock.json
generated
@ -16,7 +16,7 @@
|
||||
"react-bootstrap-icons": "^1.10.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-router-dom": "^6.18.0"
|
||||
"react-router-dom": "^6.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
@ -956,9 +956,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz",
|
||||
"integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==",
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.0.tgz",
|
||||
"integrity": "sha512-5dMOnVnefRsl4uRnAdoWjtVTdh8e6aZqgM4puy9nmEADH72ck+uXwzpJLEKE9Q6F8ZljNewLgmTfkxUrBdv4WA==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
@ -4802,11 +4802,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.18.0.tgz",
|
||||
"integrity": "sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==",
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.0.tgz",
|
||||
"integrity": "sha512-pVvzsSsgUxxtuNfTHC4IxjATs10UaAtvLGVSA1tbUE4GDaOSU1Esu2xF5nWLz7KPiMuW8BJWuPFdlGYJ7/rW0w==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.11.0"
|
||||
"@remix-run/router": "1.13.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@ -4816,12 +4816,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.18.0.tgz",
|
||||
"integrity": "sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==",
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.0.tgz",
|
||||
"integrity": "sha512-CbcKjEyiSVpA6UtCHOIYLUYn/UJfwzp55va4yEfpk7JBN3GPqWfHrdLkAvNCcpXr8QoihcDMuk0dzWZxtlB/mQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.11.0",
|
||||
"react-router": "6.18.0"
|
||||
"@remix-run/router": "1.13.0",
|
||||
"react-router": "6.20.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -12,6 +12,7 @@ const LinesForm = ({ id }) => {
|
||||
validated,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
handleStockChange,
|
||||
} = useLinesItemForm(id);
|
||||
|
||||
const onBack = () => {
|
||||
@ -27,7 +28,7 @@ const LinesForm = ({ id }) => {
|
||||
return (
|
||||
<>
|
||||
<Form className='m-0 p-2' noValidate validated={validated} onSubmit={onSubmit}>
|
||||
<LinesItemForm item={item} handleChange={handleChange} />
|
||||
<LinesItemForm item={item} handleChange={handleChange} handleStockChange={handleStockChange} />
|
||||
<Form.Group className='row justify-content-center m-0 mt-3'>
|
||||
<Button className='col-5 col-lg-2 m-0 me-2' variant='secondary' onClick={() => onBack()}>
|
||||
Назад
|
||||
|
@ -3,23 +3,23 @@ import imgPlaceholder from '../../../Images/200.png';
|
||||
import Input from '../../input/Input.jsx';
|
||||
import Select from '../../input/Select.jsx';
|
||||
import useTypes from '../../types/hooks/TypesHook';
|
||||
import useStocks from '../../types/hooks/StockHook';
|
||||
import './LinesItemForm.css';
|
||||
|
||||
const LinesItemForm = ({ item, handleChange }) => {
|
||||
const LinesItemForm = ({ item, handleChange, handleStockChange }) => {
|
||||
const { types } = useTypes();
|
||||
|
||||
const { stocks } = useStocks();
|
||||
return (
|
||||
<>
|
||||
<div className='text-center'>
|
||||
<img id='image-preview' className='rounded' alt='placeholder'
|
||||
src={item.image || imgPlaceholder} />
|
||||
</div>
|
||||
<Select values={types} name='typeId' label='Товары' value={item.typeId} onChange={handleChange}
|
||||
<Select values={types} name='typeId' label='Товары' value={item.typeId.toString()} onChange={handleChange}
|
||||
required />
|
||||
<Input name='price' label='Цена' value={item.price} onChange={handleChange}
|
||||
type='number' min='1000.0' step='0.50' required />
|
||||
<Input name='stock' label='Акция' value={item.stock} onChange={handleChange}
|
||||
type='number' min='0' step='1' />
|
||||
type='number' min='1' step='1' required />
|
||||
<Select values={stocks} name='stock' label='Акция' value={item.stock} onChange={handleStockChange} />
|
||||
<Input name='count' label='Количество' value={item.count} onChange={handleChange}
|
||||
type='number' min='1' step='1' required />
|
||||
<Input name='image' label='Изображение' onChange={handleChange}
|
||||
@ -30,7 +30,9 @@ const LinesItemForm = ({ item, handleChange }) => {
|
||||
|
||||
LinesItemForm.propTypes = {
|
||||
item: PropTypes.object,
|
||||
|
||||
handleChange: PropTypes.func,
|
||||
handleStockChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default LinesItemForm;
|
||||
|
48
lab5/src/components/lines/form/StocksForm.jsx
Normal file
48
lab5/src/components/lines/form/StocksForm.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import useStocksItemForm from '../hooks/StocksItemFormHook.js';
|
||||
import StocksItemForm from './StocksItemForm.jsx';
|
||||
|
||||
const StocksForm = ({ id }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
stocks,
|
||||
validated,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
} = useStocksItemForm(id);
|
||||
|
||||
const onBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const onSubmit = async (event) => {
|
||||
if (await handleSubmit(event)) {
|
||||
onBack();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form className='m-0 p-2' noValidate validated={validated} onSubmit={onSubmit}>
|
||||
<StocksItemForm item={stocks} handleChange={handleChange} />
|
||||
<Form.Group className='row justify-content-center m-0 mt-3'>
|
||||
<Button className='col-5 col-lg-2 m-0 me-2' variant='secondary' onClick={() => onBack()}>
|
||||
Назад
|
||||
</Button>
|
||||
<Button className='col-5 col-lg-2 m-0 ms-2' type='submit' variant='primary'>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
StocksForm.propTypes = {
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
export default StocksForm;
|
19
lab5/src/components/lines/form/StocksItemForm.jsx
Normal file
19
lab5/src/components/lines/form/StocksItemForm.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import Input from '../../input/Input.jsx';
|
||||
|
||||
const StocksItemForm = ({ item, handleChange }) => {
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-warning text-center font-weight-bold">Добавление акции</h1>
|
||||
<Input name='name' label='Наименование акции ' value={item.name} onChange={handleChange} required />
|
||||
<Input name='value' label='Значение акции' value={item.value} max='100' onChange={handleChange} required />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
StocksItemForm.propTypes = {
|
||||
item: PropTypes.object,
|
||||
handleChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default StocksItemForm;
|
@ -1,11 +1,24 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import getBase64FromFile from '../../utils/Base64';
|
||||
import LinesApiService from '../service/LinesApiService';
|
||||
import StocksApiService from '../service/StocksApiService';
|
||||
import useLinesItem from './LinesItemHook';
|
||||
|
||||
const useLinesItemForm = (id, linesChangeHandle) => {
|
||||
const { item, setItem } = useLinesItem(id);
|
||||
const [stockData, setStockData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (item.stock) {
|
||||
StocksApiService.getById(item.stock)
|
||||
.then(response => setStockData(response))
|
||||
.catch(error => {
|
||||
console.error('Ошибка при получении данных акции:', error);
|
||||
setStockData(null);
|
||||
});
|
||||
}
|
||||
}, [item.stock]);
|
||||
|
||||
const [validated, setValidated] = useState(false);
|
||||
|
||||
@ -16,10 +29,11 @@ const useLinesItemForm = (id, linesChangeHandle) => {
|
||||
const getLineObject = (formData) => {
|
||||
const typeId = parseInt(formData.typeId, 10);
|
||||
const price = parseFloat(formData.price).toFixed(2);
|
||||
const stock = formData.stock !== '' ? parseInt(formData.stock, 10) : null;
|
||||
const stock = stockData.value;
|
||||
const count = parseInt(formData.count, 10);
|
||||
let sum;
|
||||
if (stock !== null) {
|
||||
|
||||
if (stockData !== null) {
|
||||
sum = parseFloat((price * ((100 - stock) / 100)) * count).toFixed(2);
|
||||
} else {
|
||||
sum = parseFloat(price * count).toFixed(2);
|
||||
@ -29,7 +43,7 @@ const useLinesItemForm = (id, linesChangeHandle) => {
|
||||
return {
|
||||
typeId: typeId.toString(),
|
||||
price: price.toString(),
|
||||
stock: stock !== null ? stock.toString() : '',
|
||||
stock: stock,
|
||||
count: count.toString(),
|
||||
sum: sum.toString(),
|
||||
image,
|
||||
@ -45,11 +59,22 @@ const useLinesItemForm = (id, linesChangeHandle) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleStockChange = (event) => {
|
||||
setItem({
|
||||
...item,
|
||||
stock: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
if (event.target.type === 'file') {
|
||||
handleImageChange(event);
|
||||
return;
|
||||
}
|
||||
if (event.target.name === 'stock') {
|
||||
handleStockChange(event);
|
||||
return;
|
||||
}
|
||||
const inputName = event.target.name;
|
||||
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
setItem({
|
||||
@ -82,6 +107,7 @@ const useLinesItemForm = (id, linesChangeHandle) => {
|
||||
validated,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
handleStockChange,
|
||||
resetValidity,
|
||||
};
|
||||
};
|
||||
|
@ -5,9 +5,9 @@ const useLinesItem = (id) => {
|
||||
const emptyItem = {
|
||||
id: '',
|
||||
typeId: '',
|
||||
price: '0',
|
||||
price: '',
|
||||
stock: '',
|
||||
count: '0',
|
||||
count: '',
|
||||
image: '',
|
||||
};
|
||||
const [item, setItem] = useState({ ...emptyItem });
|
||||
|
34
lab5/src/components/lines/hooks/StocksDeleteModalHook.js
Normal file
34
lab5/src/components/lines/hooks/StocksDeleteModalHook.js
Normal file
@ -0,0 +1,34 @@
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import useModal from '../../modal/ModalHook';
|
||||
import StocksApiService from '../service/StocksApiService';
|
||||
|
||||
const useStocksDeleteModal = (linesChangeHandle) => {
|
||||
const { isModalShow, showModal, hideModal } = useModal();
|
||||
const [currentId, setCurrentId] = useState(0);
|
||||
|
||||
const showModalDialogStock = (id) => {
|
||||
showModal();
|
||||
setCurrentId(id);
|
||||
};
|
||||
|
||||
const onCloseStock = () => {
|
||||
hideModal();
|
||||
};
|
||||
|
||||
const onDeleteStock = async () => {
|
||||
await StocksApiService.delete(currentId);
|
||||
linesChangeHandle();
|
||||
toast.success('Акция успешно удалена', { id: 'StocksTable' });
|
||||
onCloseStock();
|
||||
};
|
||||
|
||||
return {
|
||||
isDeleteModalShowStock: isModalShow,
|
||||
showDeleteModalStock: showModalDialogStock,
|
||||
handleDeleteConfirmStock: onDeleteStock,
|
||||
handleDeleteCancelStock: onCloseStock,
|
||||
};
|
||||
};
|
||||
|
||||
export default useStocksDeleteModal;
|
25
lab5/src/components/lines/hooks/StocksHook.js
Normal file
25
lab5/src/components/lines/hooks/StocksHook.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import StocksApiService from '../service/StocksApiService';
|
||||
|
||||
const useStocks = () => {
|
||||
const [stocksRefresh, setStocksRefresh] = useState(false);
|
||||
const [stocks, setStocks] = useState([]);
|
||||
const handleStocksChange = () => setStocksRefresh(!stocksRefresh);
|
||||
|
||||
const getStocks = async () => {
|
||||
let expand = '?_expand=';
|
||||
const data = await StocksApiService.getAll(expand);
|
||||
setStocks(data ?? []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getStocks();
|
||||
}, [stocksRefresh]);
|
||||
|
||||
return {
|
||||
stocks,
|
||||
handleStocksChange,
|
||||
};
|
||||
};
|
||||
|
||||
export default useStocks;
|
62
lab5/src/components/lines/hooks/StocksItemFormHook.js
Normal file
62
lab5/src/components/lines/hooks/StocksItemFormHook.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import StocksApiService from '../service/StocksApiService';
|
||||
import useStocksItem from './StocksItemHook.js';
|
||||
|
||||
const useStocksItemForm = (id, linesChangeHandle) => {
|
||||
const { stocks, setStock } = useStocksItem(id);
|
||||
const [validated, setValidated] = useState(null);
|
||||
|
||||
const resetValidity = () => {
|
||||
setValidated(false);
|
||||
};
|
||||
|
||||
const getStockObject = (formData) => {
|
||||
const name = formData.name;
|
||||
const value = parseInt(formData.value, 10);
|
||||
return {
|
||||
name: name.toString(),
|
||||
value: value.toString(),
|
||||
};
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const inputName = event.target.name;
|
||||
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
setStock({
|
||||
...stocks,
|
||||
[inputName]: inputValue,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
const form = event.currentTarget;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const body = getStockObject(stocks);
|
||||
|
||||
if (form.checkValidity()) {
|
||||
if (id === undefined) {
|
||||
await StocksApiService.create(body);
|
||||
} else {
|
||||
await StocksApiService.update(id, body);
|
||||
}
|
||||
if (linesChangeHandle) linesChangeHandle();
|
||||
toast.success('Акция успешно сохранена', { id: 'StocksTable' });
|
||||
return true;
|
||||
}
|
||||
|
||||
setValidated(true);
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
stocks,
|
||||
validated,
|
||||
handleSubmit,
|
||||
handleChange,
|
||||
resetValidity,
|
||||
};
|
||||
};
|
||||
|
||||
export default useStocksItemForm;
|
32
lab5/src/components/lines/hooks/StocksItemHook.js
Normal file
32
lab5/src/components/lines/hooks/StocksItemHook.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TypesApiService from '../service/StocksApiService';
|
||||
|
||||
const useStocksItem = (id) => {
|
||||
const emptyStocks = {
|
||||
id: '',
|
||||
name: '',
|
||||
stock: '',
|
||||
};
|
||||
const [stocks, setStock] = useState({ ...emptyStocks });
|
||||
|
||||
const getStock = async (stockId = undefined) => {
|
||||
if (stockId && stockId > 0) {
|
||||
const data = await TypesApiService.get(stockId);
|
||||
setStock(data);
|
||||
} else {
|
||||
setStock({ ...emptyStocks });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getStock(id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
return {
|
||||
stocks,
|
||||
setStock,
|
||||
};
|
||||
};
|
||||
|
||||
export default useStocksItem;
|
11
lab5/src/components/lines/service/StocksApiService.js
Normal file
11
lab5/src/components/lines/service/StocksApiService.js
Normal file
@ -0,0 +1,11 @@
|
||||
import ApiService from '../../api/ApiService';
|
||||
|
||||
class StocksApiService extends ApiService {
|
||||
async getById(id) {
|
||||
return this.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
const TypesApiService = new StocksApiService('stocks');
|
||||
|
||||
export default TypesApiService;
|
@ -3,15 +3,27 @@ import { Link, useNavigate } from 'react-router-dom';
|
||||
import Select from '../../input/Select.jsx';
|
||||
import ModalConfirm from '../../modal/ModalConfirm.jsx';
|
||||
import useLinesDeleteModal from '../hooks/LinesDeleteModalHook';
|
||||
import useStocksDeleteModal from '../hooks/StocksDeleteModalHook';
|
||||
import useTypeFilter from '../hooks/LinesFilterHook';
|
||||
import useLines from '../hooks/LinesHook';
|
||||
import LinesTable from './LinesTable.jsx';
|
||||
import LinesTableRow from './LinesTableRow.jsx';
|
||||
import StocksTable from './StocksTable.jsx';
|
||||
import StocksTableRow from './StocksTableRow.jsx';
|
||||
import useStocks from '../hooks/StocksHook.js';
|
||||
|
||||
const Lines = () => {
|
||||
const { types, currentFilter, handleFilterChange } = useTypeFilter();
|
||||
const { lines, handleLinesChange } = useLines(currentFilter);
|
||||
const navigate = useNavigate();
|
||||
const { stocks, handleStocksChange } = useStocks();
|
||||
|
||||
const {
|
||||
isDeleteModalShowStock,
|
||||
showDeleteModalStock,
|
||||
handleDeleteConfirmStock,
|
||||
handleDeleteCancelStock,
|
||||
} = useStocksDeleteModal(handleStocksChange);
|
||||
|
||||
const {
|
||||
isDeleteModalShow,
|
||||
@ -28,6 +40,14 @@ const Lines = () => {
|
||||
showDeleteModal(id);
|
||||
};
|
||||
|
||||
const showEditStocksPage = (id) => {
|
||||
navigate(`/PageEditStocks/${id}`);
|
||||
};
|
||||
|
||||
const handleDeleteStock = (id) => {
|
||||
showDeleteModalStock(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-warning text-center font-weight-bold">Панель администратора</h1>
|
||||
@ -36,6 +56,9 @@ const Lines = () => {
|
||||
<Button as={Link} to='/pageEdit' variant='warning'>
|
||||
Добавить товар
|
||||
</Button>
|
||||
<Button as={Link} to='/PageEditStocks' variant='warning'>
|
||||
Добавить акцию
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<Select className='mt-2' values={types} label='Фильтр по товарам'
|
||||
@ -51,9 +74,25 @@ const Lines = () => {
|
||||
/>)
|
||||
}
|
||||
</LinesTable>
|
||||
|
||||
<h2 className="text-warning text-center font-weight-bold mt-2">Таблица акций</h2>
|
||||
<StocksTable>
|
||||
{
|
||||
stocks.map((stock, index) =>
|
||||
<StocksTableRow key={stock.id}
|
||||
index={index} stock={stock}
|
||||
onDelete={() => handleDeleteStock(stock.id)}
|
||||
onEdit={() => showEditStocksPage(stock.id)}
|
||||
/>)
|
||||
}
|
||||
</StocksTable>
|
||||
|
||||
<ModalConfirm show={isDeleteModalShow}
|
||||
onConfirm={handleDeleteConfirm} onClose={handleDeleteCancel}
|
||||
title='Удаление' message='Удалить элемент?' />
|
||||
<ModalConfirm show={isDeleteModalShowStock}
|
||||
onConfirm={handleDeleteConfirmStock} onClose={handleDeleteCancelStock}
|
||||
title='Удаление' message='Удалить акцию?' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
27
lab5/src/components/lines/table/StocksTable.jsx
Normal file
27
lab5/src/components/lines/table/StocksTable.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Table } from 'react-bootstrap';
|
||||
|
||||
const StocksTable = ({ children }) => {
|
||||
return (
|
||||
<Table className='mt-2' striped responsive>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">№</th>
|
||||
<th scope="col" className="w-25">Имя акции</th>
|
||||
<th scope="col" className="w-75">Значение акции</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{children}
|
||||
</tbody >
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
StocksTable.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default StocksTable;
|
30
lab5/src/components/lines/table/StocksTableRow.jsx
Normal file
30
lab5/src/components/lines/table/StocksTableRow.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PencilSquare, Trash3 } from 'react-bootstrap-icons';
|
||||
|
||||
const StocksTableRow = ({
|
||||
index, stock, onDelete, onEdit
|
||||
}) => {
|
||||
const handleAnchorClick = (event, action) => {
|
||||
event.preventDefault();
|
||||
action();
|
||||
};
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<th scope="row">{index + 1}</th>
|
||||
<td>{stock.name}</td>
|
||||
<td>{stock.value}</td>
|
||||
<td><a href="#" onClick={(event) => handleAnchorClick(event, onEdit)}><PencilSquare /></a></td>
|
||||
<td><a href="#" onClick={(event) => handleAnchorClick(event, onDelete)}><Trash3 /></a></td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
StocksTableRow.propTypes = {
|
||||
index: PropTypes.number,
|
||||
stock: PropTypes.object,
|
||||
onDelete: PropTypes.func,
|
||||
onEdit: PropTypes.func,
|
||||
};
|
||||
|
||||
export default StocksTableRow;
|
21
lab5/src/components/types/hooks/StockHook.js
Normal file
21
lab5/src/components/types/hooks/StockHook.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import StocksApiService from '../../lines/service/StocksApiService';
|
||||
|
||||
const useStocks = () => {
|
||||
const [stocks, setStocks] = useState([]);
|
||||
|
||||
const getStocks = async () => {
|
||||
const data = await StocksApiService.getAll();
|
||||
setStocks(data ?? []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getStocks();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
stocks,
|
||||
};
|
||||
};
|
||||
|
||||
export default useStocks;
|
@ -16,6 +16,7 @@ import Administrator from './pages/Administrator.jsx';
|
||||
import Basket from './pages/Basket.jsx';
|
||||
import MakingAnOrder from './pages/MakingAnOrder.jsx';
|
||||
import PageEdit from './pages/PageEdit.jsx';
|
||||
import PageEditStocks from './pages/PageEditStocs.jsx';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -66,6 +67,10 @@ const routes = [
|
||||
path: '/PageEdit/:id?',
|
||||
element: <PageEdit/>,
|
||||
},
|
||||
{
|
||||
path: '/PageEditStocks/:id?',
|
||||
element: <PageEditStocks/>,
|
||||
}
|
||||
];
|
||||
|
||||
const router = createBrowserRouter([
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col } from 'react-bootstrap';
|
||||
import BasketCard from '../components/card/BasketCard';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Basket = () => {
|
||||
return (
|
||||
@ -24,7 +25,9 @@ const Basket = () => {
|
||||
Сумма ₽
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn btn-warning" style={{ marginLeft: '25px', marginBottom: '10px' }} href="makingAnOrder">К оплате</a>
|
||||
<Link to='/MakingAnOrder' className="btn btn-warning" style={{ marginLeft: '25px', marginBottom: '10px' }}>
|
||||
К оплате
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ const Contacts = () => {
|
||||
<iframe
|
||||
src="https://yandex.ru/map-widget/v1/?um=constructor%3A0643c92cbdf3809080e5dfb2804b473ea00af31cfabe6fee08676c59d8675f01&source=constructor"
|
||||
className="img-fluid"
|
||||
style={{ width: '100%', height: '720px' }}
|
||||
style={{ width: '100%', height: '500px' }}
|
||||
title="Yandex Map"
|
||||
></iframe>
|
||||
</Col>
|
||||
|
@ -35,7 +35,7 @@ const MakingAnOrder = () => {
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className="mb-4" controlId="name">
|
||||
<Form.Label htmlFor="name">Ваше имя</Form.Label>
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
|
@ -1,174 +1,7 @@
|
||||
//import { useState } from 'react';
|
||||
//import { Container, Col, Row, Form, Button } from 'react-bootstrap';
|
||||
|
||||
import { useParams } from 'react-router-dom';
|
||||
import LinesForm from '../components/lines/form/LinesForm.jsx';
|
||||
|
||||
const PageEdit = () => {
|
||||
/*const [validated, setValidated] = useState(false);
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const form = event.currentTarget;
|
||||
if (form.checkValidity() === false) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState('');
|
||||
const [price, setPrice] = useState(0.00);
|
||||
const [stock, setStock] = useState(0);
|
||||
const [count, setCount] = useState(0);
|
||||
const [imagePreview, setImagePreview] = useState('https://via.placeholder.com/200');
|
||||
|
||||
const handleItemChange = (e) => {
|
||||
setSelectedItem(e.target.value);
|
||||
};
|
||||
|
||||
const handlePriceChange = (e) => {
|
||||
setPrice(parseFloat(e.target.value));
|
||||
};
|
||||
|
||||
const handleStockChange = (e) => {
|
||||
setStock(parseInt(e.target.value, 10));
|
||||
};
|
||||
|
||||
const handleCountChange = (e) => {
|
||||
setCount(parseInt(e.target.value, 10));
|
||||
};
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onloadend = () => {
|
||||
const img = new Image();
|
||||
img.src = reader.result;
|
||||
|
||||
img.onload = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
const maxWidth = 200;
|
||||
const maxHeight = 200;
|
||||
let width = img.width;
|
||||
let height = img.height;
|
||||
|
||||
if (width > height) {
|
||||
if (width > maxWidth) {
|
||||
height *= maxWidth / width;
|
||||
width = maxWidth;
|
||||
}
|
||||
} else {
|
||||
if (height > maxHeight) {
|
||||
width *= maxHeight / height;
|
||||
height = maxHeight;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
setImagePreview(canvas.toDataURL('image/png'));
|
||||
};
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container fluid className="p-2">
|
||||
<h1 className="text-warning text-center font-weight-bold">Добавление товара</h1>
|
||||
<Row className="text-center">
|
||||
<Col>
|
||||
<img id="image-preview" src={imagePreview} className="rounded rounded-circle" alt="placeholder" />
|
||||
</Col>
|
||||
</Row>
|
||||
<Form
|
||||
id="items-form"
|
||||
className="needs-validation"
|
||||
noValidate
|
||||
validated={validated}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form.Group controlId="item" className="mb-2">
|
||||
<Form.Label>Товары</Form.Label>
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="selected"
|
||||
onChange={handleItemChange}
|
||||
value={selectedItem}
|
||||
required
|
||||
>
|
||||
</Form.Control>
|
||||
<Form.Control.Feedback type="invalid">Пожалуйста, выберите товар.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="price" className="mb-2">
|
||||
<Form.Label>Цена</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
name="price"
|
||||
value={price}
|
||||
min="1000.00"
|
||||
step="0.50"
|
||||
onChange={handlePriceChange}
|
||||
required
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">Пожалуйста, введите цену.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="stock" className="mb-2">
|
||||
<Form.Label>Акция</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
name="price"
|
||||
value={stock}
|
||||
min="0"
|
||||
step="1"
|
||||
onChange={handleStockChange}
|
||||
required
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">Пожалуйста, введите акцию.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="count" className="mb-2">
|
||||
<Form.Label>Количество</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
name="count"
|
||||
value={count}
|
||||
min="1"
|
||||
step="1"
|
||||
onChange={handleCountChange}
|
||||
required
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">Пожалуйста, введите количество.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="image" className="mb-2">
|
||||
<Form.Label>Изображение</Form.Label>
|
||||
<Form.Control
|
||||
type="file"
|
||||
name="image"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">Пожалуйста, выберите изображение.</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Row className="mb-2">
|
||||
<Col>
|
||||
<Button href="Administrator" className="btn btn-secondary">Назад</Button>
|
||||
<Button type="submit" className="btn btn-primary">Сохранить</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Container>
|
||||
);*/
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
return (
|
||||
|
12
lab5/src/pages/PageEditStocs.jsx
Normal file
12
lab5/src/pages/PageEditStocs.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import StocksForm from '../components/lines/form/StocksForm';
|
||||
|
||||
const PageEditStocks = () => {
|
||||
const { id } = useParams();
|
||||
|
||||
return (
|
||||
<StocksForm id={id} />
|
||||
);
|
||||
};
|
||||
|
||||
export default PageEditStocks;
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PasswordRecovery = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -39,15 +40,11 @@ const PasswordRecovery = () => {
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
<a href="personalAccountLogin" className="fw-bold text-body">
|
||||
<u>Войти</u>
|
||||
</a>
|
||||
<Link className="fw-bold text-body" to='/PersonalAccountLogin' style={{ color: 'black', textDecoration: 'none' }}><u>Войти</u></Link>
|
||||
</p>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
<a href="personalAccountRegister" className="fw-bold text-body">
|
||||
<u>Регистрация</u>
|
||||
</a>
|
||||
<Link className="fw-bold text-body" to='/PersonalAccountRegister' style={{ color: 'black', textDecoration: 'none' }}><u>Регистрация</u></Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,36 +1,18 @@
|
||||
/* eslint-disable linebreak-style */
|
||||
/* eslint-disable object-curly-newline */
|
||||
/* eslint-disable linebreak-style */
|
||||
import { useState } from 'react';
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccount = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
lastname: '',
|
||||
firstname: '',
|
||||
email: '',
|
||||
date: '',
|
||||
tel: '',
|
||||
});
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const form = event.currentTarget;
|
||||
if (form.checkValidity() === false) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (form.checkValidity() !== false) {
|
||||
console.log(formData);
|
||||
}
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const inputName = event.target.name;
|
||||
const inputValue = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
setFormData({
|
||||
...formData,
|
||||
[inputName]: inputValue,
|
||||
});
|
||||
setValidated(true);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -42,42 +24,37 @@ const PersonalAccount = () => {
|
||||
<h2 className="text-uppercase text-center mb-5">Личный кабинет</h2>
|
||||
|
||||
<Form noValidate validated={validated} onSubmit={handleSubmit}>
|
||||
<Form.Group className='mb-2' controlId='firstname'>
|
||||
<Form.Label>Имя</Form.Label>
|
||||
<Form.Control type='text' name='firstname' required
|
||||
value={formData.firstname} onChange={handleChange} />
|
||||
<Form.Group className="mb-4" controlId="name">
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type='invalid'>Имя не заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-4' controlId='lastname'>
|
||||
<Form.Label>Фамилия</Form.Label>
|
||||
<Form.Control type='text' name='lastname' required
|
||||
value={formData.lastname} onChange={handleChange} />
|
||||
<Form.Group className="mb-4" controlId="surname">
|
||||
<Form.Label>Ваша фамилия</Form.Label>
|
||||
<Form.Control type="text" required/>
|
||||
<Form.Control.Feedback>Фамилия заполнена</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type='invalid'>Фамилия не заполнена</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Фамилия не заполнена</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-4' controlId='email'>
|
||||
<Form.Label>Ваш адрес электронной почты</Form.Label>
|
||||
<Form.Control type='email' name='email' placeholder='name@example.ru' required
|
||||
value={formData.email} onChange={handleChange} />
|
||||
<Form.Control.Feedback>E-mail заполнен</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type='invalid'>E-mail не заполнен</Form.Control.Feedback>
|
||||
<Form.Group className="mb-4">
|
||||
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
|
||||
<Form.Control type="email" id="form3Example3cg" required />
|
||||
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-4">
|
||||
<Form.Label htmlFor="form3Example4cg">Дата рождения</Form.Label>
|
||||
<Form.Control type="date" id="form3Example4cg" required
|
||||
value={formData.date} onChange={handleChange} />
|
||||
<Form.Control type="date" id="form3Example4cg" required />
|
||||
<Form.Control.Feedback>Дата рождения заполнена</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Дата рождения не заполнена</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-2">
|
||||
<Form.Label htmlFor="form3Example5cg">Номер телефона</Form.Label>
|
||||
<Form.Control type="tel" id="form3Example5cg" required
|
||||
value={formData.tel} onChange={handleChange} />
|
||||
<Form.Control type="tel" id="form3Example5cg" required />
|
||||
<Form.Control.Feedback>Номер телефона заполнен</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Номер телефона не заполнен</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
@ -89,9 +66,9 @@ const PersonalAccount = () => {
|
||||
</div>
|
||||
|
||||
<div className="d-flex justify-content-center mt-2">
|
||||
<a className="btn btn-outline-danger" type="button" href="/">
|
||||
<Link to='/' className="btn btn-outline-danger" type="button">
|
||||
Выйти
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccountLogin = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -43,28 +44,28 @@ const PersonalAccountLogin = () => {
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
Забыли пароль?{' '}
|
||||
<a href="PasswordRecovery" className="fw-bold text-body">
|
||||
<Link to='/PasswordRecovery' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Восстановление пароля</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<div className="d-flex justify-content-center">
|
||||
<Button variant="submit" className="btn-block btn-warning text-body mb-0" href="PersonalAccount">
|
||||
<Button as={Link} to='/PersonalAccount' variant="submit" className="btn-block btn-warning text-body mb-0">
|
||||
Вход
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
У вас нет аккаунта?{' '}
|
||||
<a href="personalAccountRegister" className="fw-bold text-body">
|
||||
<Link to='/personalAccountRegister' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Регистрация</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<p className="text-center">
|
||||
<a className="fw-bold text-body" href="Administrator">
|
||||
<Link to='/Administrator' className="fw-bold text-body" style={{ color: 'black'}}>
|
||||
Администратор
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const PersonalAccountRegister = () => {
|
||||
const [validated, setValidated] = useState(false);
|
||||
@ -27,7 +28,7 @@ const PersonalAccountRegister = () => {
|
||||
<Form.Control type="text" required />
|
||||
<Form.Control.Feedback>Имя заполнено</Form.Control.Feedback>
|
||||
<Form.Control.Feedback type="invalid">Имя не заполнено</Form.Control.Feedback>
|
||||
<Form.Label htmlFor="name">Ваше имя</Form.Label>
|
||||
<Form.Label>Ваше имя</Form.Label>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-4">
|
||||
@ -63,16 +64,18 @@ const PersonalAccountRegister = () => {
|
||||
</Form.Group>
|
||||
|
||||
<div className="d-flex justify-content-center">
|
||||
<Button variant="success" type="button" className="btn-block btn-warning text-body mb-0" href="PersonalAccount">
|
||||
<Link to='/PersonalAccount' style={{color: 'black', textDecoration: 'none'}}>
|
||||
<Button variant="success" type="button" className="btn-block btn-warning text-body mb-0">
|
||||
Регистрация
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted mb-0">
|
||||
У вас уже есть учетная запись?{' '}
|
||||
<a href="personalAccountLogin" className="fw-bold text-body">
|
||||
<Link to='/PersonalAccountLogin' className="fw-bold text-body" style={{ color: 'black', textDecoration: 'none' }}>
|
||||
<u>Войдите здесь</u>
|
||||
</a>
|
||||
</Link>
|
||||
</p>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
|
BIN
Отчёты/~$тчёт 5.docx
Normal file
BIN
Отчёты/~$тчёт 5.docx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user