стадия торг

This commit is contained in:
Вячеслав Иванов 2023-11-15 23:17:59 +04:00
parent 231d8fea1f
commit f1d991fb7c
24 changed files with 762 additions and 188 deletions

View File

@ -2,12 +2,12 @@
<html lang="ru">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/logo.png" href="src/Images/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Каталог</title>
</head>
<body>
<div id="root"></div>
<div id="root" class="h-100 d-flex flex-column"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

93
lab4/package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"bootstrap": "^5.3.2",
"mdb-react-ui-kit": "^7.0.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
@ -388,6 +389,21 @@
"node": ">=6.9.0"
}
},
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
"optional": true,
"dependencies": {
"@emotion/memoize": "0.7.4"
}
},
"node_modules/@emotion/memoize": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@ -1072,7 +1088,6 @@
"version": "18.2.15",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz",
"integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
@ -1455,6 +1470,14 @@
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"node_modules/clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -2294,6 +2317,29 @@
"is-callable": "^1.1.3"
}
},
"node_modules/framer-motion": {
"version": "10.16.5",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.5.tgz",
"integrity": "sha512-GEzVjOYP2MIpV9bT/GbhcsBNoImG3/2X3O/xVNWmktkv9MdJ7P/44zELm/7Fjb+O3v39SmKFnoDQB32giThzpg==",
"dependencies": {
"tslib": "^2.4.0"
},
"optionalDependencies": {
"@emotion/is-prop-valid": "^0.8.2"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3084,6 +3130,32 @@
"yallist": "^3.0.2"
}
},
"node_modules/mdb-react-ui-kit": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/mdb-react-ui-kit/-/mdb-react-ui-kit-7.0.0.tgz",
"integrity": "sha512-ke23u6Ux63nrmi3PFa7H1YlmQe76POT5rNpxuet0MB4+8OjCzw+WviL4EJtzDlNHXhnw9mfU9a5MF8q0swO+wA==",
"dependencies": {
"@popperjs/core": "2.11.5",
"clsx": "1.1.1",
"framer-motion": "^10.16.4",
"react-popper": "2.3.0"
},
"peerDependencies": {
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.3",
"react": "^18.1.0",
"react-dom": "^18.1.0"
}
},
"node_modules/mdb-react-ui-kit/node_modules/@popperjs/core": {
"version": "2.11.5",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
"integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3518,6 +3590,11 @@
"react": "^18.2.0"
}
},
"node_modules/react-fast-compare": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -3528,6 +3605,20 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-popper": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
"integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
"dependencies": {
"react-fast-compare": "^3.0.1",
"warning": "^4.0.2"
},
"peerDependencies": {
"@popperjs/core": "^2.0.0",
"react": "^16.8.0 || ^17 || ^18",
"react-dom": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",

View File

@ -10,13 +10,14 @@
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.18.0",
"bootstrap": "^5.3.2",
"mdb-react-ui-kit": "^7.0.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-bootstrap-icons": "^1.10.3",
"prop-types": "^15.8.1"
"react-dom": "^18.2.0",
"react-router-dom": "^6.18.0"
},
"devDependencies": {
"@types/react": "^18.2.15",

View File

@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@ -7,13 +7,13 @@ import Navigation from './components/navigation/Navigation.jsx';
const App = ({ routes }) => {
return (
<>
<div className="d-flex flex-column min-vh-100">
<Navigation routes={routes}></Navigation>
<Container className='p-2' as="main" fluid>
<Container className="flex-grow-1 p-2" as="main" fluid>
<Outlet />
</Container>
<Footer />
</>
</div>
);
};

View File

@ -0,0 +1,4 @@
.btn-opaque {
background-color: rgba(255, 255, 255, 0.8); /* You can adjust the alpha value for opacity */
/* You can also set other styles to make the buttons more visible */
}

View File

@ -0,0 +1,41 @@
import { useState } from 'react';
import { Card, Col, Row, Form } from 'react-bootstrap';
const BasketCard = () => {
const [quantity, setQuantity] = useState(2);
const handleQuantityChange = (event) => {
setQuantity(event.target.value);
};
return (
<Card className="rounded-3 mb-4">
<Card.Body className="p-4">
<Row className="d-flex justify-content-between align-items-center">
<Col md={2} lg={2} xl={2}>
<img src="./src/Images/pizza.png" className="img-fluid rounded-3" alt="Cotton T-shirt" />
</Col>
<Col md={3} lg={3} xl={3}>
<p className="lead fw-normal mb-2">Название пиццы</p>
</Col>
<Col md={3} lg={3} xl={2} className="d-flex align-items-center">
<Form.Control
id="form1"
min="0"
name="quantity"
value={quantity}
type="number"
className="form-control form-control-sm text-center"
onChange={handleQuantityChange}
/>
</Col>
<Col md={3} lg={2} xl={2} offset-lg={1}>
<h5 className="mb-0">Цена </h5>
</Col>
</Row>
</Card.Body>
</Card>
);
};
export default BasketCard;

View File

View File

@ -0,0 +1,20 @@
import { Col, Card, Button } from 'react-bootstrap';
const ProductCard = () => {
return (
<Col lg={3} md={4} sm={6} xs={12} className="mb-4">
<Card>
<Card.Img src="./src/Images/pizza.png" alt="Product Image" className="card-img-top" />
<Card.Body>
<Card.Title>Название</Card.Title>
</Card.Body>
<Card.Footer>
<div className="text-warning font-weight-bold">Цена </div>
<Button variant="warning">В корзину</Button>
</Card.Footer>
</Card>
</Col>
);
};
export default ProductCard;

View File

@ -0,0 +1,21 @@
import { Col, Card } from 'react-bootstrap';
const StockCard = () => {
return (
<Col lg={4} md={6} sm={12} className="mb-4">
<Card>
<Card.Img src="./src/Images/stock.png" alt="Stock Image" className="card-img-top" />
<Card.Body>
<Card.Title>Дарим кибер-призы</Card.Title>
<Card.Text>
Вот так ачивка! Закажите Кибер-комбо и получите доступ к играм от MY.GAMES, а еще кокосовый батончик и
шоколадное печенье «Cyber» от Bite. А также станьте автоматическим участником розыгрыша игровых ключей и
больших пицц 29 июня.
</Card.Text>
</Card.Body>
</Card>
</Col>
);
};
export default StockCard;

View File

@ -1,3 +1,4 @@
.my-footer {
background-color: #D9D9D9;
height: 32px;
}

View File

@ -2,14 +2,8 @@
background-color: #D9D9D9;
}
@media (min-width: 768px) {
.my-navbar {
height: 100px;
}
}
.my-navbar {
text-decoration: underline;
.my-navbar .nav-link {
text-decoration: none;
}
@media (max-width: 576px) {
@ -20,7 +14,7 @@
@media (min-width: 577px) and (max-width: 992px) {
.my-navbar img {
margin-left: 25px;
margin-left: 25px
}
}

View File

@ -1,43 +1,45 @@
import PropTypes from 'prop-types';
import { Container, Nav, Navbar, Button } from 'react-bootstrap';
import { Link, useLocation } from 'react-router-dom';
import Image from 'react-bootstrap/Image';
import './Navigation.css';
const Navigation = ({ routes }) => {
const location = useLocation();
const indexPageLink = routes.filter((route) => route.index === false).shift();
const indexPageLink = routes.find((route) => route.index === true);
const pages = routes.filter((route) => Object.prototype.hasOwnProperty.call(route, 'title'));
return (
<header>
<Navbar expand='md' bg='dark' data-bs-theme='dark' className='my-navbar'>
<Navbar expand='md' className='my-navbar'>
<Container fluid>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<img src="./Images/logo.png" alt="logo" width="128" />
</Navbar.Brand>
<Navbar.Brand as={Link} to={indexPageLink?.path ?? '/'}>
<Image src="./src/Images/logo.png" width="128" alt="Logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls='main-navbar' />
<Navbar.Collapse id='main-navbar'>
<Nav className='me-auto link' activeKey={location.pathname}>
{
pages.map((page) =>
<Nav.Link as={Link} key={page.path} eventKey={page.path} to={page.path ?? '/'}>
{page.title}
</Nav.Link>)
}
{pages.map((page) => (
<Nav.Link as={Link} key={page.path} eventKey={page.path} to={page.path ?? '/'}>
{page.title}
</Nav.Link>
))}
</Nav>
<Nav>
<Button className="custom-btn" as={Link} to="personalAccountLogin.html">Войти</Button>
<Button variant="warning" as={Link} to="basket.html">Корзина</Button>
<Button className="custom-btn" as={Link} to="/personalAccountLogin">
Войти
</Button>
<Button variant="warning" as={Link} to="/Basket">
Корзина
</Button>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
);
};
Navigation.propTypes = {
routes: PropTypes.array,
routes: PropTypes.array,
};
export default Navigation;

View File

@ -1,69 +1,18 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
.btn {
height: 35px;
width: 176px;
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
margin-right: 10px;
margin-top: 10px;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
.custom-btn {
color: black;
border: none;
background-color: #FFA800;
}
.btn.custom-btn:hover {
color: black;
background-color: #FFA800 !important;
}

View File

@ -5,34 +5,57 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import App from './App.jsx';
import './index.css';
import ErrorPage from './pages/ErrorPage.jsx';
import Index from './pages/index.jsx';
import Index from './pages/Index.jsx';
import Stock from './pages/Stock.jsx';
import Contacts from './pages/Contacts.jsx';
import PersonalAccountLogin from './pages/PersonalAccountLogin.jsx';
import PersonalAccount from './pages/PersonalAccount.jsx';
import PersonalAccountRegister from './pages/PersonalAccountRegister.jsx';
import PasswordRecovery from './pages/PasswordRecovery.jsx';
import Administrator from './pages/Administrator.jsx';
import Basket from './pages/Basket.jsx';
const routes = [
{
index: true,
path: '/',
element: <Index />,
title: 'Главная страница',
title: 'Каталог',
},
{
path: '/page2',
element: <Index />,
title: 'Вторая страница',
path: '/stock',
element: <Stock />,
title: 'Акции',
},
{
path: '/page3',
element: <Index />,
title: 'Третья страница',
path: '/contacts',
element: <Contacts />,
title: 'Контакты',
},
{
path: '/page4',
element: <Index />,
title: 'Четвертая страница',
path: '/personalAccountLogin',
element: <PersonalAccountLogin />,
},
{
path: '/page-edit',
element: <Index />,
path: '/personalAccount',
element: <PersonalAccount />,
},
{
path: '/personalAccountRegister',
element: <PersonalAccountRegister/>,
},
{
path: '/PasswordRecovery',
element: <PasswordRecovery/>,
},
{
path: '/Administrator',
element: <Administrator/>,
},
{
path: '/Basket',
element: <Basket/>,
}
];
const router = createBrowserRouter([

View File

@ -0,0 +1,50 @@
import { Container, Row, Col, Button, Table } from 'react-bootstrap';
const Administrator = () => {
return (
<Container>
<Row>
<Col>
<h1 className="text-warning text-center font-weight-bold">Панель администратора</h1>
<div className="btn-group" role="group">
<Button variant="warning" href="/page-edit.html">
Добавить товар
</Button>
</div>
</Col>
</Row>
<Row>
<Col>
<h2 className="text-warning text-center font-weight-bold" style={{ paddingTop: '10px' }}>
Таблица данных
</h2>
<Table id="items-table" striped bordered>
<thead>
<tr>
<th scope="col"></th>
<th scope="col" className="w-25">
Товар
</th>
<th scope="col" className="w-25">
Цена
</th>
<th scope="col" className="w-10">
Акция
</th>
<th scope="col" className="w-25">
Количество
</th>
<th scope="col" className="w-25">
Сумма
</th>
</tr>
</thead>
<tbody></tbody>
</Table>
</Col>
</Row>
</Container>
);
};
export default Administrator;

32
lab4/src/pages/Basket.jsx Normal file
View File

@ -0,0 +1,32 @@
import { Container, Row, Col } from 'react-bootstrap';
import BasketCard from '../components/card/BasketCard';
const Basket = () => {
return (
<Container>
<Row>
<Col className="d-flex justify-content-center align-items-center">
<h1 className="text-warning font-weight-bold">Корзина</h1>
</Col>
</Row>
<Row>
<Col xs={10}>
<BasketCard />
<BasketCard />
<BasketCard />
</Col>
</Row>
<div className="d-flex justify-content-start align-items-center">
<div className="text-dark font-weight-bold" style={{ fontSize: '24px' }}>
Сумма заказа:
</div>
<div className="text-end" style={{ color: '#F7D22D', fontSize: '24px' }}>
Сумма
</div>
</div>
<a className="btn btn-warning" style={{ marginLeft: '25px', marginBottom: '10px' }} href="makingAnOrder.html">К оплате</a>
</Container>
);
};
export default Basket;

View File

@ -0,0 +1,36 @@
import { Container, Row, Col } from 'react-bootstrap';
const Contacts = () => {
return (
<Container>
<Row>
<Col className="d-flex justify-content-center align-items-center">
<h1 className="text-warning font-weight-bold">Контакты</h1>
</Col>
</Row>
<Row>
<Col className="d-flex justify-content-center">
<iframe
src="https://yandex.ru/map-widget/v1/?um=constructor%3A0643c92cbdf3809080e5dfb2804b473ea00af31cfabe6fee08676c59d8675f01&amp;source=constructor"
className="img-fluid"
style={{ width: '100%', height: '720px' }}
title="Yandex Map"
></iframe>
</Col>
</Row>
<Row>
<Col>
<a href="tel:71112223344" className="text-warning font-weight-bold" style={{ fontSize: '35px', marginLeft: '10px', marginTop: '10px' }}>
7 111 222 33 44
</a>
<h2 className="font-weight-bold" style={{ fontSize: '35px', marginLeft: '10px', marginTop: '10px' }}>
ул. Северный венец 32
</h2>
<p style={{ fontSize: '25px', marginLeft: '10px', marginTop: '10px' }}>Доставка и самовывоз 10:00 23:00</p>
</Col>
</Row>
</Container>
);
};
export default Contacts;

View File

@ -0,0 +1,61 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
import { useState } from 'react';
const PasswordRecovery = () => {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
return (
<Container className="h-100">
<Row className="d-flex justify-content-center align-items-center h-100">
<Col xs={12} md={9} lg={7} xl={6}>
<Card style={{ borderRadius: '15px', borderColor: 'gold' }}>
<Card.Body className="p-5">
<h2 className="text-uppercase text-center mb-5">Восстановление пароля</h2>
<h4 className="text-black text-center mb-5">
Введите свой адрес электронной почты, и мы вышлем вам электронное письмо с инструкциями по сбросу вашего пароля
</h4>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
</Form.Group>
<div className="d-flex justify-content-center">
<Button type="submit" className="btn-block btn-warning text-body mb-0">
Сбросить пароль
</Button>
</div>
<p className="text-center text-muted mb-0">
<a href="personalAccountLogin" className="fw-bold text-body">
<u>Войти</u>
</a>
</p>
<p className="text-center text-muted mb-0">
<a href="personalAccountRegister" className="fw-bold text-body">
<u>Регистрация</u>
</a>
</p>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
export default PasswordRecovery;

View File

@ -0,0 +1,81 @@
import { useState } from 'react';
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
const PersonalAccount = () => {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
return (
<Container className="h-100">
<Row className="justify-content-center align-items-center h-100">
<Col xs={12} md={9} lg={7} xl={6}>
<Card style={{ borderRadius: '15px', borderColor: 'gold' }}>
<Card.Body className="p-5">
<h2 className="text-uppercase text-center mb-5">Личный кабинет</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name">
<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.Group>
<Form.Group className="mb-4" controlId="surname">
<Form.Control type="text" required/>
<Form.Control.Feedback>Фамилия заполнена</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Фамилия не заполнена</Form.Control.Feedback>
<Form.Label htmlFor="surname">Ваша фамилия</Form.Label>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control type="date" id="form3Example4cg" required />
<Form.Control.Feedback>Дата рождения заполнена</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Дата рождения не заполнена</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Дата рождения</Form.Label>
</Form.Group>
<Form.Group className="mb-2">
<Form.Control type="tel" id="form3Example5cg" required />
<Form.Control.Feedback>Номер телефона заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Номер телефона не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example5cg">Номер телефона</Form.Label>
</Form.Group>
<div className="d-flex justify-content-center">
<Button variant="warning" type="submit" id="saveButton">
Сохранить
</Button>
</div>
<div className="d-flex justify-content-center mt-2">
<a className="btn btn-outline-danger" type="button" href="Index">
Выйти
</a>
</div>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
export default PersonalAccount;

View File

@ -0,0 +1,78 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
import { useState } from 'react';
const PersonalAccountLogin = () => {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
return (
<Container>
<Row className="d-flex justify-content-center align-items-center h-100">
<Col xs={12} md={9} lg={7} xl={6}>
<Card style={{ borderRadius: '15px', borderColor: 'gold' }}>
<Card.Body className="p-5">
<h2 className="text-uppercase text-center mb-5">Войти</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control type="password" id="form3Example4cg" size="lg" required />
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Пароль</Form.Label>
</Form.Group>
<Form.Group className="mb-2" controlId="form2Example3cg">
<Form.Check type="checkbox" label="Запомнить меня" />
</Form.Group>
<p className="text-center text-muted mb-0">
Забыли пароль?{' '}
<a href="PasswordRecovery" className="fw-bold text-body">
<u>Восстановление пароля</u>
</a>
</p>
<div className="d-flex justify-content-center">
<Button variant="submit" className="btn-block btn-warning text-body mb-0" href="PersonalAccount">
Вход
</Button>
</div>
<p className="text-center text-muted mb-0">
У вас нет аккаунта?{' '}
<a href="personalAccountRegister" className="fw-bold text-body">
<u>Регистрация</u>
</a>
</p>
<p className="text-center">
<a className="fw-bold text-body" href="Administrator">
Администратор
</a>
</p>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
export default PersonalAccountLogin;

View File

@ -0,0 +1,86 @@
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap';
import { useState } from 'react';
const PersonalAccountRegister = () => {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
return (
<Container className="h-100">
<Row className="justify-content-center align-items-center h-100">
<Col xs={12} md={9} lg={7} xl={6}>
<Card style={{ borderRadius: '15px', borderColor: 'gold' }}>
<Card.Body className="p-5">
<h2 className="text-uppercase text-center mb-5">Создать учетную запись</h2>
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Form.Group className="mb-4" controlId="name">
<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.Group>
<Form.Group className="mb-4">
<Form.Control type="email" id="form3Example3cg" required />
<Form.Control.Feedback>Email заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Email не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example3cg">Ваш адрес электронной почты</Form.Label>
</Form.Group>
<Form.Group className="mb-4">
<Form.Control type="password" id="form3Example4cg" size="lg" required />
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label htmlFor="form3Example4cg">Пароль</Form.Label>
</Form.Group>
<Form.Group controlId="formRepeatPassword" className="mb-4">
<Form.Control type="password"required/>
<Form.Control.Feedback>Пароль заполнен</Form.Control.Feedback>
<Form.Control.Feedback type="invalid">Пароль не заполнен</Form.Control.Feedback>
<Form.Label>Повторите свой пароль</Form.Label>
</Form.Group>
<Form.Group controlId="formCheckbox" className="d-flex justify-content-center mb-5">
<Form.Check type="checkbox" label={
<span>
Я согласен со всеми утверждениями в{' '}
<a href="#!" className="text-body">
<u>Условиях обслуживания</u>
</a>
</span>
} />
</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">
Регистрация
</Button>
</div>
<p className="text-center text-muted mb-0">
У вас уже есть учетная запись?{' '}
<a href="personalAccountLogin" className="fw-bold text-body">
<u>Войдите здесь</u>
</a>
</p>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
export default PersonalAccountRegister;

37
lab4/src/pages/Stock.jsx Normal file
View File

@ -0,0 +1,37 @@
import { Container, Row, Col } from 'react-bootstrap';
import StockCard from '../components/card/StockCard';
const Stock = () => {
return (
<Container>
<Row>
<Col className="d-flex justify-content-center align-items-center">
<h1 className="text-warning font-weight-bold">Акции</h1>
</Col>
</Row>
<Row>
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
<StockCard />
</Row>
</Container>
);
};
export default Stock;

View File

@ -1,33 +1,41 @@
import { Link } from 'react-router-dom';
import { Container, Row, Col } from 'react-bootstrap';
import ProductCard from '../components/card/ProductCard';
const Index = () => {
return (
<>
<>
<h1>Пример web-страницы.</h1>
<h2>1. Структурные элементы</h2>
<p><b>Полужирное начертание <i>курсив</i></b></p>
<p>Абзац 2 <Link to="/Index">Ссылка</Link></p>
<h3>1.1. Списки</h3>
<p>
Список маркированный:
</p>
<ul>
<li><a href="/Index" target="_blank">
Элемент списка 1</a></li>
<li>Элемент списка 2</li>
<li>...</li>
</ul>
<p>
Список нумерованный:
</p>
<ol>
<li>Элемент списка 1</li>
<li>Элемент списка 2</li>
<li>...</li>
</ol>
</>
</>
<Container>
<Row>
<Col className="d-flex justify-content-center align-items-center">
<h1 className="text-warning font-weight-bold">Каталог</h1>
</Col>
</Row>
<Row>
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
<ProductCard />
</Row>
</Container>
);
};