Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baeb13067f |
55
.gitignore
vendored
55
.gitignore
vendored
@@ -1,54 +1 @@
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# Зависимости Node.js
|
||||
/node_modules/
|
||||
|
||||
# Результаты сборки Vite
|
||||
/dist/
|
||||
|
||||
# Логи
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
pnpm-debug.log*
|
||||
*.log
|
||||
|
||||
# Файлы окружения
|
||||
.env
|
||||
.env.*.local
|
||||
|
||||
# Кеши
|
||||
/.cache/
|
||||
.vite/
|
||||
|
||||
# IDE и редакторы
|
||||
.vscode/
|
||||
.idea/
|
||||
/*.sublime-workspace
|
||||
/*.sublime-project
|
||||
|
||||
# Системные файлы
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Отчеты об покрытии тестами
|
||||
/coverage/
|
||||
|
||||
# Порты и артефакты
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Прочее
|
||||
/.DS_Store
|
||||
node_modules
|
||||
|
||||
54
Account.html
Normal file
54
Account.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин: ЛК</title>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="images/logo.jpg" alt="'Название магазина'">
|
||||
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<span>Навигация ▾</span>
|
||||
<ul class="links">
|
||||
<li><a href="Basket.html">Корзина</a></li>
|
||||
<li><a href="Order.html">Заказы</a></li>
|
||||
<li><a href="Favorites.html">Избранное</a></li>
|
||||
<li><a href="Account.html">Личный кабинет</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="account-container">
|
||||
<div class="account">
|
||||
<img src="images/бананы.jpg">
|
||||
<h3>Имя Фамилия</h3>
|
||||
<p>Описание</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="contacts">
|
||||
<p>Помощь:</p>
|
||||
<div class="contact-item">
|
||||
<img src="images/phone.png" alt="Phone">
|
||||
<p>8 (800)-555-35-35</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/vk.png" alt="VK">
|
||||
<p>vk.com</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/telegram.png" alt="Telegram">
|
||||
<p>tg.me</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/gmail.png" alt="Gmail">
|
||||
<p>ozon-zon-zon@mail.joke</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
60
Basket.html
Normal file
60
Basket.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин: Корзина</title>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="images/logo.jpg" alt="'Название магазина'">
|
||||
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<span>Навигация ▾</span>
|
||||
<ul class="links">
|
||||
<li><a href="Basket.html">Корзина</a></li>
|
||||
<li><a href="Order.html">Заказы</a></li>
|
||||
<li><a href="Favorites.html">Избранное</a></li>
|
||||
<li><a href="Account.html">Личный кабинет</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="basket">
|
||||
<h1>Корзина</h1>
|
||||
<ol>
|
||||
<li><img src="images/glasses.jpg">Очки<p>349 руб.</p></li>
|
||||
<li><img src="images/chery.jpg">Chery Tiggo 7 Pro Max 64gb 128mp<p>5 руб.</p></li>
|
||||
<li><img src="images/vanadiy.jpg">Ванадий<p>2099 руб.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="buy">
|
||||
<a href="https://xn----7sbon6aucai8a.xn--p1ai/wa-data/public/shop/products/98/27/2798/images/6060/6060.970.jpg" target="_blank">
|
||||
Оплатить
|
||||
</a>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="contacts">
|
||||
<p>Помощь:</p>
|
||||
<div class="contact-item">
|
||||
<img src="images/phone.png" alt="Phone">
|
||||
<p>8 (800)-555-35-35</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/vk.png" alt="VK">
|
||||
<p>vk.com</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/telegram.png" alt="Telegram">
|
||||
<p>tg.me</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/gmail.png" alt="Gmail">
|
||||
<p>ozon-zon-zon@mail.joke</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
54
Favorites.html
Normal file
54
Favorites.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин: Избранное</title>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="images/logo.jpg" alt="'Название магазина'">
|
||||
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<span>Навигация ▾</span>
|
||||
<ul class="links">
|
||||
<li><a href="Basket.html">Корзина</a></li>
|
||||
<li><a href="Order.html">Заказы</a></li>
|
||||
<li><a href="Favorites.html">Избранное</a></li>
|
||||
<li><a href="Account.html">Личный кабинет</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="favourites">
|
||||
<h2>Избранное</h2>
|
||||
<ol>
|
||||
<li><img src ="images/masha.jpg">Женщина<p>бесценна</p></li>
|
||||
<li><img src ="images/screwdriver.jpg">Отвертка<p>219 руб</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="contacts">
|
||||
<p>Помощь:</p>
|
||||
<div class="contact-item">
|
||||
<img src="images/phone.png" alt="Phone">
|
||||
<p>8 (800)-555-35-35</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/vk.png" alt="VK">
|
||||
<p>vk.com</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/telegram.png" alt="Telegram">
|
||||
<p>tg.me</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/gmail.png" alt="Gmail">
|
||||
<p>ozon-zon-zon@mail.joke</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
65
Order.html
Normal file
65
Order.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин: Заказы</title>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="images/logo.jpg" alt="'Название магазина'">
|
||||
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<span>Навигация ▾</span>
|
||||
<ul class="links">
|
||||
<li><a href="Basket.html">Корзина</a></li>
|
||||
<li><a href="Order.html">Заказы</a></li>
|
||||
<li><a href="Favorites.html">Избранное</a></li>
|
||||
<li><a href="Account.html">Личный кабинет</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="orders"><h1>Заказы</h1></div>
|
||||
<div class="typeOfOrder">
|
||||
<div class="inProcess">
|
||||
<h2>В процессе</h2>
|
||||
<ol>
|
||||
<li><img src="images/spoon.jpg">Ложка</li>
|
||||
<li><img src="images/fork.jpg">Вилка</li>
|
||||
<li><img src="images/knife.jpg">Нож</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="done">
|
||||
<h2>Завершённые</h2>
|
||||
<ol>
|
||||
<li><img src="images/iron.jpg">Утюг</li>
|
||||
<li><img src="images/bananas.jpg">Бананы</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="contacts">
|
||||
<p>Помощь:</p>
|
||||
<div class="contact-item">
|
||||
<img src="images/phone.png" alt="Phone">
|
||||
<p>8 (800)-555-35-35</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/vk.png" alt="VK">
|
||||
<p>vk.com</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/telegram.png" alt="Telegram">
|
||||
<p>tg.me</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/gmail.png" alt="Gmail">
|
||||
<p>ozon-zon-zon@mail.joke</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
387
css/style.css
Normal file
387
css/style.css
Normal file
@@ -0,0 +1,387 @@
|
||||
/*Общие стили*/
|
||||
body {
|
||||
background-color: #edf7f6;
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 180px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/*header*/
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
header img {
|
||||
width: 200px;
|
||||
height: auto;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
header h1 a{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/*навигация*/
|
||||
navbar {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
right: 20px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
span{
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.dropdown > span {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: 2px solid black;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
/*background: #edf7f6;*/
|
||||
background: white;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.dropdown:hover .links{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
padding-right: 30px;
|
||||
padding-top: 50px;
|
||||
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: -115px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: none;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.links li {
|
||||
list-style: none;
|
||||
color: black;
|
||||
width: 140px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/*Основной контент*/
|
||||
.goods {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 70px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.goods p {
|
||||
text-align: center;
|
||||
width: 270px;
|
||||
height: 285px;
|
||||
border: 2px solid black;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.goods img {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/*Заказы*/
|
||||
.typeOfOrder {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.typeOfOrder h2{
|
||||
border: 2px solid black;
|
||||
border-radius: 5px;
|
||||
width: 180px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.inProcess h2{
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.done h2{
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
/*Корзина и Избранное*/
|
||||
.basket,
|
||||
.favourites {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.basket h1,
|
||||
.orders h1 {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
border: 2px solid black;
|
||||
border-radius: 10px;
|
||||
width: 400px;
|
||||
display: flex;
|
||||
font-size: large;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.basket p,
|
||||
.favourites p {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 10px;
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*Кнопка опалты в корзине*/
|
||||
.buy {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background-color: greenyellow;
|
||||
text-align: center;
|
||||
border: 2px solid black;
|
||||
border-radius: 10px;
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.buy a {
|
||||
font-size: xx-large;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.buy:hover {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
/*Личный кабинет*/
|
||||
.account-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.account {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
/*margin: 50px auto;*/
|
||||
width: 300px;
|
||||
padding: 20px;
|
||||
border: 2px solid black;
|
||||
border-radius: 15px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.account img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
border-radius: 20px;
|
||||
border: 3px solid black;
|
||||
}
|
||||
|
||||
.account h3 {
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
/*Footer*/
|
||||
footer {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.contacts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-item p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.contact-item img {
|
||||
width: 24px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Адаптивная верстка */
|
||||
@media only screen and (min-width: 961px) {
|
||||
.goods {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.basket,
|
||||
.favourites {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 401px) and (max-width: 960px) {
|
||||
.goods {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.typeOfOrder{
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.typeOfOrder h2 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
width: 50%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.basket,
|
||||
.favourites {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.basket p,
|
||||
.favourites p {
|
||||
position: relative;
|
||||
display: block;
|
||||
bottom: -5px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.buy {
|
||||
width: 150px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 400px) {
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
navbar {
|
||||
position: static;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dropdown:hover .links {
|
||||
top: 180px;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.goods p {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.goods img {
|
||||
width: 97%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
li {
|
||||
width: auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.typeOfOrder{
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.typeOfOrder h2 {
|
||||
margin-left: 5;
|
||||
}
|
||||
|
||||
.basket,
|
||||
.favourites {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.basket p,
|
||||
.favourites p {
|
||||
position: relative;
|
||||
display: block;
|
||||
bottom: -5px;
|
||||
}
|
||||
|
||||
.buy {
|
||||
width: 110px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.buy a {
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
.account {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contacts {
|
||||
gap: 2px;
|
||||
}
|
||||
}
|
||||
74
db.json
74
db.json
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"products": [
|
||||
{
|
||||
"id": "1a54",
|
||||
"name": "новый товар",
|
||||
"price": 102,
|
||||
"image": "images/fork.jpg"
|
||||
},
|
||||
{
|
||||
"id": "f4f8",
|
||||
"name": "товарчик",
|
||||
"price": 111,
|
||||
"image": "images/chery.jpg"
|
||||
},
|
||||
{
|
||||
"id": "0fda",
|
||||
"name": "ложка",
|
||||
"price": 48,
|
||||
"image": "images/bananas.jpg"
|
||||
}
|
||||
],
|
||||
"basket": [
|
||||
{
|
||||
"id": "0fda",
|
||||
"name": "ложка",
|
||||
"price": 48,
|
||||
"image": "images/bananas.jpg"
|
||||
}
|
||||
],
|
||||
"favorites": [
|
||||
{
|
||||
"id": "1a54",
|
||||
"name": "новый товар",
|
||||
"price": 102,
|
||||
"image": "images/fork.jpg"
|
||||
}
|
||||
],
|
||||
"orders": [
|
||||
{
|
||||
"id": "1",
|
||||
"items": [
|
||||
{
|
||||
"name": "Ложка",
|
||||
"image": "images/spoon.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Вилка",
|
||||
"image": "images/fork.jpg"
|
||||
}
|
||||
],
|
||||
"status": "in-process"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"items": [
|
||||
{
|
||||
"name": "Утюг",
|
||||
"image": "images/iron.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Бананы",
|
||||
"image": "images/bananas.jpg"
|
||||
}
|
||||
],
|
||||
"status": "completed"
|
||||
}
|
||||
],
|
||||
"profile": {
|
||||
"id": 1,
|
||||
"firstName": "Иван",
|
||||
"lastName": "Иванов",
|
||||
"image": "images/бананы.jpg"
|
||||
}
|
||||
}
|
||||
15
index.html
15
index.html
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.jsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
56
newSite.html
Normal file
56
newSite.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Интернет-магазин</title>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<img src="images/logo.jpg" alt="'Название магазина'">
|
||||
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<span><b>Навигация ▾</b></span>
|
||||
<ul class="links">
|
||||
<li><a href="Basket.html">Корзина</a></li>
|
||||
<li><a href="Order.html">Заказы</a></li>
|
||||
<li><a href="Favorites.html">Избранное</a></li>
|
||||
<li><a href="Account.html">Личный кабинет</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<h2>Рекомендуемые товары:</h2>
|
||||
<div class="goods">
|
||||
<p><img src="images/iron.jpg">Утюг</p>
|
||||
<p><img src="images/child.jpg">Ребёнок</p>
|
||||
<p><img src="images/screwdriver.jpg">Отвертка</p>
|
||||
<p><img src="images/skateboard.jpg">Скейтборд</p>
|
||||
<p><img src="images/bananas.jpg">Бананы</p>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="contacts">
|
||||
<p>Помощь:</p>
|
||||
<div class="contact-item">
|
||||
<img src="images/phone.png" alt="Phone">
|
||||
<p>8 (800)-555-35-35</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/vk.png" alt="VK">
|
||||
<p>vk.com</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/telegram.png" alt="Telegram">
|
||||
<p>tg.me</p>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<img src="images/gmail.png" alt="Gmail">
|
||||
<p>ozon-zon-zon@mail.joke</p>
|
||||
</div>
|
||||
</div>
|
||||
<p><img src="images/бананы.jpg"></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
6335
package-lock.json
generated
6335
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "ip",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "npm-run-all --parallel start json-server",
|
||||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"server": "http-server -p 3000 ./dist/",
|
||||
"prod": "npm-run-all build server",
|
||||
"lint": "eslint …",
|
||||
"json-server": "json-server --watch db.json --port 5000"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap-icons": "1.11.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-router-dom": "^7.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "10.0.2",
|
||||
"eslint-plugin-html": "8.1.2",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.3",
|
||||
"http-server": "14.1.1",
|
||||
"json-server": "^1.0.0-beta.3",
|
||||
"npm-run-all": "4.1.5",
|
||||
"vite": "6.2.0"
|
||||
}
|
||||
}
|
||||
26
src/App.jsx
26
src/App.jsx
@@ -1,26 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import Header from "./components/Header";
|
||||
import Footer from "./components/Footer";
|
||||
import MainPage from "./pages/MainPage";
|
||||
import BasketPage from "./pages/BasketPage";
|
||||
import FavoritesPage from "./pages/FavoritesPage";
|
||||
import OrderPage from "./pages/OrderPage";
|
||||
import AccountPage from "./pages/AccountPage";
|
||||
|
||||
export default function App() {
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<MainPage />} />
|
||||
<Route path="/basket" element={<BasketPage />} />
|
||||
<Route path="/favorites" element={<FavoritesPage />} />
|
||||
<Route path="/orders" element={<OrderPage />} />
|
||||
<Route path="/account" element={<AccountPage />} />
|
||||
</Routes>
|
||||
<Footer />
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="container mt-5">
|
||||
<div className="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div className="d-flex flex-wrap">
|
||||
<div className="d-flex align-items-center me-4 mb-3">
|
||||
<i className="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" className="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div className="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" className="me-2" style={{ width: 24, height: 24 }} />
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank" className="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div className="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" className="me-2" style={{ width: 24, height: 24 }} />
|
||||
<a href="#" className="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div className="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" className="me-2" style={{ width: 24, height: 24 }} />
|
||||
<a href="mailto:ozon-zon-zon@mail.joke" className="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<header>
|
||||
<div className="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" className="me-3" style={{ width: 200, height: "auto" }} />
|
||||
<Link to="/" className="text-decoration-none text-dark m-1">
|
||||
<h1 className="display-4 h3 mt-3"><b>Название магазина</b></h1>
|
||||
</Link>
|
||||
</div>
|
||||
<nav>
|
||||
<div className="dropdown">
|
||||
<button className="btn btn-primary dropdown-toggle ms-3" type="button" id="navigationDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul className="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><Link className="dropdown-item d-flex justify-content-between align-items-center" to="/account">Личный кабинет<i className="bi bi-person-circle ms-2"></i></Link></li>
|
||||
<li><Link className="dropdown-item d-flex justify-content-between align-items-center" to="/basket">Корзина<i className="bi bi-cart4 ms-2"></i></Link></li>
|
||||
<li><Link className="dropdown-item d-flex justify-content-between align-items-center" to="/orders">Заказы<i className="bi bi-receipt ms-2"></i></Link></li>
|
||||
<li><Link className="dropdown-item d-flex justify-content-between align-items-center" to="/favorites">Избранное<i className="bi bi-heart-fill ms-2"></i></Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function ProductCard({ product, onEdit, onDelete, onAddToFavorites }) {
|
||||
return (
|
||||
<div className="col">
|
||||
<div className="card h-100">
|
||||
{product.image && (
|
||||
<img src={product.image} className="card-img-top" alt={product.name} style={{ height: 300, objectFit: 'cover' }} />
|
||||
)}
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">{product.name}</h5>
|
||||
<p className="card-text">Цена: {product.price} ₽</p>
|
||||
<button className="btn btn-sm btn-outline-primary me-2" onClick={() => onEdit(product)}>Изменить</button>
|
||||
<button className="btn btn-sm btn-outline-danger me-2" onClick={() => onDelete(product.id)}>Удалить</button>
|
||||
<button className="btn btn-sm btn-outline-success" onClick={() => onAddToFavorites(product)}>
|
||||
<i className="bi bi-heart"></i> В избранное
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export default function ProductForm({ initial, onSave, onCancel }) {
|
||||
const [form, setForm] = useState({ name: '', price: '', image: '' });
|
||||
|
||||
useEffect(() => {
|
||||
if (initial) {
|
||||
setForm({
|
||||
name: initial.name || '',
|
||||
price: initial.price || '',
|
||||
image: initial.image || ''
|
||||
});
|
||||
} else {
|
||||
setForm({ name: '', price: '', image: '' });
|
||||
}
|
||||
}, [initial]);
|
||||
|
||||
const handleChange = e => setForm({ ...form, [e.target.name]: e.target.value });
|
||||
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
onSave({ ...initial, name: form.name, price: Number(form.price), image: form.image });
|
||||
setForm({ name: '', price: '', image: '' });
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="card p-3 mb-4">
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Название</label>
|
||||
<input
|
||||
name="name"
|
||||
value={form.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Цена</label>
|
||||
<input
|
||||
name="price"
|
||||
value={form.price}
|
||||
onChange={handleChange}
|
||||
type="number"
|
||||
required
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Ссылка на картинку</label>
|
||||
<input
|
||||
name="image"
|
||||
value={form.image}
|
||||
onChange={handleChange}
|
||||
className="form-control"
|
||||
placeholder="Например: images/glasses.jpg или https://example.com/photo.jpg"
|
||||
/>
|
||||
</div>
|
||||
{form.image && (
|
||||
<div className="mb-2 text-center">
|
||||
<img
|
||||
src={form.image}
|
||||
alt="Превью"
|
||||
style={{ maxHeight: 120, objectFit: 'contain', maxWidth: "100%" }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<button type="submit" className="btn btn-primary mb-2" style={{ width: 'auto' }}>Сохранить</button>
|
||||
<button type="button" className="btn btn-secondary" style={{ width: 'auto' }} onClick={onCancel}>Отмена</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import ProductCard from './ProductCard';
|
||||
|
||||
export default function ProductList({ products, onEdit, onDelete, onAddToFavorites }) {
|
||||
return (
|
||||
<div className="row row-cols-1 row-cols-md-3 g-4">
|
||||
{products.map(prod => (
|
||||
<ProductCard
|
||||
key={prod.id}
|
||||
product={prod}
|
||||
onEdit={onEdit}
|
||||
onDelete={onDelete}
|
||||
onAddToFavorites={onAddToFavorites}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function useBasket() {
|
||||
const [basket, setBasket] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:5000/basket')
|
||||
.then(res => res.json())
|
||||
.then(setBasket);
|
||||
}, []);
|
||||
|
||||
const addToBasket = async (item) => {
|
||||
const res = await fetch('http://localhost:5000/basket', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(item)
|
||||
});
|
||||
const newItem = await res.json();
|
||||
setBasket([...basket, newItem]);
|
||||
};
|
||||
|
||||
const removeFromBasket = async (id) => {
|
||||
await fetch(`http://localhost:5000/basket/${id}`, { method: 'DELETE' });
|
||||
setBasket(basket.filter(item => item.id !== id));
|
||||
};
|
||||
|
||||
const clearBasket = async () => {
|
||||
for (let item of basket) {
|
||||
await fetch(`http://localhost:5000/basket/${item.id}`, { method: 'DELETE' });
|
||||
}
|
||||
setBasket([]);
|
||||
};
|
||||
|
||||
return { basket, addToBasket, removeFromBasket, clearBasket };
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function useFavorites() {
|
||||
const [favorites, setFavorites] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:5000/favorites')
|
||||
.then(res => res.json())
|
||||
.then(setFavorites);
|
||||
}, []);
|
||||
|
||||
const addToFavorites = async (item) => {
|
||||
const res = await fetch('http://localhost:5000/favorites', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(item)
|
||||
});
|
||||
const newItem = await res.json();
|
||||
setFavorites([...favorites, newItem]);
|
||||
};
|
||||
|
||||
const removeFromFavorites = async (id) => {
|
||||
await fetch(`http://localhost:5000/favorites/${id}`, { method: 'DELETE' });
|
||||
setFavorites(favorites.filter(item => item.id !== id));
|
||||
};
|
||||
|
||||
return { favorites, addToFavorites, removeFromFavorites };
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function useOrders() {
|
||||
const [orders, setOrders] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:5000/orders')
|
||||
.then(res => res.json())
|
||||
.then(setOrders);
|
||||
}, []);
|
||||
|
||||
const addOrder = async (order) => {
|
||||
const res = await fetch('http://localhost:5000/orders', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(order)
|
||||
});
|
||||
const newOrder = await res.json();
|
||||
setOrders([...orders, newOrder]);
|
||||
};
|
||||
|
||||
const inProcess = orders.filter(o => o.status === "in-process");
|
||||
const completed = orders.filter(o => o.status === "completed");
|
||||
|
||||
return { orders, addOrder, inProcess, completed };
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export default function useProducts() {
|
||||
const [products, setProducts] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:5000/products')
|
||||
.then(res => res.json())
|
||||
.then(setProducts);
|
||||
}, []);
|
||||
|
||||
const add = async prod => {
|
||||
const res = await fetch('http://localhost:5000/products', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(prod)
|
||||
});
|
||||
const newProd = await res.json();
|
||||
setProducts([...products, newProd]);
|
||||
};
|
||||
|
||||
const update = async prod => {
|
||||
await fetch(`http://localhost:5000/products/${prod.id}`, {
|
||||
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(prod)
|
||||
});
|
||||
setProducts(products.map(p => p.id === prod.id ? prod : p));
|
||||
};
|
||||
|
||||
const remove = async id => {
|
||||
await fetch(`http://localhost:5000/products/${id}`, { method: 'DELETE' });
|
||||
setProducts(products.filter(p => p.id !== id));
|
||||
};
|
||||
|
||||
return { products, add, update, remove };
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export default function useProfile() {
|
||||
const [profile, setProfile] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('http://localhost:5000/profile')
|
||||
.then(res => res.json())
|
||||
.then(setProfile);
|
||||
}, []);
|
||||
|
||||
const updateProfile = async (newProfile) => {
|
||||
// PATCH или PUT — по ситуации
|
||||
const res = await fetch('http://localhost:5000/profile', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(newProfile)
|
||||
});
|
||||
const updated = await res.json();
|
||||
setProfile(updated);
|
||||
};
|
||||
|
||||
return { profile, updateProfile };
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
|
||||
|
||||
const container = document.getElementById("root");
|
||||
createRoot(container).render(<App />);
|
||||
@@ -1,76 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import useProfile from "../hooks/useProfile";
|
||||
|
||||
export default function AccountPage() {
|
||||
const { profile, updateProfile } = useProfile();
|
||||
const [showEdit, setShowEdit] = useState(false);
|
||||
const [form, setForm] = useState(null);
|
||||
|
||||
if (!profile) {
|
||||
return <div className="text-center">Загрузка...</div>;
|
||||
}
|
||||
|
||||
const handleEdit = () => {
|
||||
setForm(profile);
|
||||
setShowEdit(true);
|
||||
};
|
||||
|
||||
const handleChange = e => {
|
||||
setForm({ ...form, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handleSave = async e => {
|
||||
e.preventDefault();
|
||||
await updateProfile(form);
|
||||
setShowEdit(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => setShowEdit(false);
|
||||
|
||||
return (
|
||||
<div className="container mt-5">
|
||||
<div className="card text-center mx-auto" style={{ maxWidth: 400, maxHeight: 400 }}>
|
||||
<img src={profile.image} className="card-img-top" alt="Профиль" style={{ width: "100%", height: 300, objectFit: "cover" }} />
|
||||
<div className="card-body">
|
||||
<h3 className="card-title">{profile.firstName} {profile.lastName}</h3>
|
||||
<button className="btn btn-primary" onClick={handleEdit}>
|
||||
<i className="bi bi-pencil"></i> Редактировать профиль
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showEdit && (
|
||||
<div className="modal d-block" tabIndex="-1" style={{ background: 'rgba(0,0,0,0.3)' }}>
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<form onSubmit={handleSave}>
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Редактировать профиль</h5>
|
||||
<button type="button" className="btn-close" onClick={handleCancel}></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Имя</label>
|
||||
<input className="form-control" name="firstName" value={form.firstName} onChange={handleChange} required />
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Фамилия</label>
|
||||
<input className="form-control" name="lastName" value={form.lastName} onChange={handleChange} required />
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">URL аватара</label>
|
||||
<input className="form-control" name="image" value={form.image} onChange={handleChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="submit" className="btn btn-success">Сохранить</button>
|
||||
<button type="button" className="btn btn-secondary" onClick={handleCancel}>Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import useBasket from "../hooks/useBasket";
|
||||
import useOrders from "../hooks/useOrders";
|
||||
|
||||
export default function BasketPage() {
|
||||
|
||||
const { basket, removeFromBasket, clearBasket } = useBasket();
|
||||
const { addOrder } = useOrders();
|
||||
|
||||
const handleCheckout = () => {
|
||||
if (basket.length === 0) {
|
||||
alert("Корзина пуста");
|
||||
return;
|
||||
}
|
||||
// Сформируем заказ
|
||||
addOrder({
|
||||
items: basket,
|
||||
status: "in-process"
|
||||
});
|
||||
clearBasket(); // Очищаем корзину
|
||||
alert("Заказ оформлен!");
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container d-flex justify-content-center align-items-center" style={{ minHeight: "60vh" }}>
|
||||
<div className="card p-4 shadow" style={{ minWidth: "60vw" }}>
|
||||
<h2 className="text-center">Корзина</h2>
|
||||
<ul className="list-group list-group-flush">
|
||||
{basket.map(item => (
|
||||
<li className="list-group-item d-flex align-items-center" key={item.id}>
|
||||
<img src={item.image} alt={item.name} style={{ width: 100, height: 100 }} className="me-3" />
|
||||
{item.name} <span className="ms-auto">{item.price} руб.</span>
|
||||
<button onClick={() => removeFromBasket(item.id)} className="btn btn-danger ms-2">Удалить</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="text-center mt-3">
|
||||
<button className={`btn w-100 ${basket.length === 0 ? "btn-secondary" : "btn-success"}`} onClick={handleCheckout}>
|
||||
Оплатить {}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from "react";
|
||||
import useBasket from "../hooks/useBasket";
|
||||
import useFavorites from "../hooks/useFavorites";
|
||||
|
||||
export default function FavoritesPage() {
|
||||
const { favorites, removeFromFavorites } = useFavorites();
|
||||
const { basket, addToBasket } = useBasket();
|
||||
|
||||
const handleAddToBasket = (item) => {
|
||||
addToBasket(item);
|
||||
removeFromFavorites(item.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mt-4">
|
||||
<h2 className="mb-4">Избранное</h2>
|
||||
<div className="row row-cols-1 row-cols-md-2 g-4">
|
||||
{favorites.map(item => (
|
||||
<div className="col" key={item.id}>
|
||||
<div className="card mx-auto" style={{ width: "70%" }}>
|
||||
<img src={item.image} className="card-img-top" alt={item.name} style={{ width: "100%", height: 300, objectFit: "cover" }} />
|
||||
<div className="card-body text-center">
|
||||
<h5 className="card-title">{item.name}</h5>
|
||||
<p className="card-text">{item.price}</p>
|
||||
<button className="btn btn-success me-2" onClick={() => handleAddToBasket(item)}>В корзину</button>
|
||||
<button onClick={() => removeFromFavorites(item.id)} className="btn btn-danger">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import useProducts from "../hooks/useProducts";
|
||||
import useFavorites from "../hooks/useFavorites";
|
||||
import ProductList from "../components/ProductList";
|
||||
import ProductForm from "../components/ProductForm";
|
||||
|
||||
export default function MainPage() {
|
||||
const { products, add, update, remove } = useProducts();
|
||||
const { favorites, addToFavorites } = useFavorites();
|
||||
const [editing, setEditing] = useState(null);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
const handleAdd = () => { setEditing(null); setShowForm(true); };
|
||||
const handleEdit = prod => { setEditing(prod); setShowForm(true); };
|
||||
const handleDelete = id => remove(id);
|
||||
const handleSave = prod => {
|
||||
editing ? update({ ...prod, id: editing.id }) : add(prod);
|
||||
setShowForm(false);
|
||||
};
|
||||
const handleCancel = () => setShowForm(false);
|
||||
|
||||
const handleAddToFavorites = product => {
|
||||
if (!favorites.some(fav => fav.id === product.id)) {
|
||||
addToFavorites(product);
|
||||
}
|
||||
else alert('Товар уже в избранном!');
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container my-4">
|
||||
<button className="btn btn-success mb-3" onClick={handleAdd}>Добавить товар</button>
|
||||
{showForm && <ProductForm initial={editing} onSave={handleSave} onCancel={handleCancel} />}
|
||||
<h2 className="text-center my-3">Рекомендуемые товары:</h2>
|
||||
<ProductList
|
||||
products={products}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
onAddToFavorites={handleAddToFavorites}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import React from "react";
|
||||
import useOrders from "../hooks/useOrders";
|
||||
|
||||
export default function OrderPage() {
|
||||
const { inProcess, completed } = useOrders();
|
||||
|
||||
return (
|
||||
<div className="container mt-4">
|
||||
<h1 className="text-center">Заказы</h1>
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-header bg-warning text-dark">
|
||||
<h2 className="h5 m-0">В процессе</h2>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
{inProcess.length === 0 ? (
|
||||
<div className="text-center text-muted">Нет заказов</div>
|
||||
) : (
|
||||
inProcess.map(order => (
|
||||
<ul className="list-group list-group-flush mb-3" key={order.id}>
|
||||
{order.items.map((item, idx) => (
|
||||
<li className="list-group-item d-flex align-items-center mb-2" key={idx}>
|
||||
<img src={item.image} className="me-2" style={{ width: 120, height: 120, objectFit: "cover" }} alt={item.name} /> {item.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-header bg-success text-white">
|
||||
<h2 className="h5 m-0">Завершённые</h2>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
{completed.length === 0 ? (
|
||||
<div className="text-center text-muted">Нет завершённых заказов</div>
|
||||
) : (
|
||||
completed.map(order => (
|
||||
<ul className="list-group list-group-flush mb-3" key={order.id}>
|
||||
{order.items.map((item, idx) => (
|
||||
<li className="list-group-item d-flex align-items-center mb-2" key={idx}>
|
||||
<img src={item.image} className="me-2" style={{ width: 120, height: 120, objectFit: "cover" }} alt={item.name} /> {item.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig } from "vite";
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
root: '.',
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, "newSite.html"),
|
||||
basketPage: resolve(__dirname, "Basket.html"),
|
||||
favouritesPage: resolve(__dirname, "Favorites.html"),
|
||||
orderPage: resolve(__dirname, "Order.html"),
|
||||
accountPage: resolve(__dirname, "Account.html"),
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
open: '/',
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
});
|
||||
BIN
Отчет2.docx
BIN
Отчет2.docx
Binary file not shown.
BIN
Отчет3.docx
BIN
Отчет3.docx
Binary file not shown.
BIN
Отчет4.docx
BIN
Отчет4.docx
Binary file not shown.
BIN
Отчет5.docx
BIN
Отчет5.docx
Binary file not shown.
BIN
Отчет6.docx
BIN
Отчет6.docx
Binary file not shown.
Reference in New Issue
Block a user