Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858bd39322 | ||
|
|
4defafe3e7 | ||
|
|
da97a99436 | ||
|
|
d44d30cb05 | ||
|
|
ebbfcd0e93 | ||
|
|
e005c61030 | ||
| cbda4dee82 | |||
| 038f60d61e | |||
| d1b2cea66c | |||
| 311491d554 | |||
| 5fa65ad591 |
178
Account.html
178
Account.html
@@ -1,178 +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>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header">
|
||||
<div class="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" class="me-3" style="width: 200px; height: auto;">
|
||||
<a href="newSite.html" class="text-decoration-none text-dark m-1">
|
||||
<h1 class="display-4 h3 mt-3"><b>Название магазина</b></h1>
|
||||
</a>
|
||||
</div>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="navigationDropdown"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Account.html">Личный кабинет<i class="bi bi-person-circle ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Basket.html">Корзина<i class="bi bi-cart4 ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Order.html">Заказы<i class="bi bi-receipt ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Favorites.html">Избранное<i class="bi bi-heart-fill ms-2"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="container mt-5">
|
||||
<div class="card text-center mx-auto" style="max-width: 400px; max-height: 400px;">
|
||||
<img src="images/бананы.jpg" class="card-img-top" alt="Профиль"
|
||||
style="width: 100%; height: 300px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Имя Фамилия</h3>
|
||||
<p class="card-text">Описание</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="container mt-5">
|
||||
<div class="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<i class="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" class="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank"
|
||||
class="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="#" class="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="mailto:ozon-zon-zon@mail.joke"
|
||||
class="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
<script type="module">
|
||||
let profile = {
|
||||
firstName: "Иван",
|
||||
lastName: "Иванов",
|
||||
image: "images/бананы.jpg"
|
||||
};
|
||||
|
||||
function renderProfile() {
|
||||
const card = document.querySelector('.container .card');
|
||||
card.innerHTML = `
|
||||
<img src="${profile.image || 'images/бананы.jpg'}" class="card-img-top" alt="Профиль" style="width: 100%; height: 300px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">${profile.firstName} ${profile.lastName}</h3>
|
||||
<button class="btn btn-primary" id="editProfileBtn"><i class="bi bi-pencil"></i> Редактировать профиль</button>
|
||||
</div>
|
||||
`;
|
||||
card.querySelector('#editProfileBtn').onclick = showEditModal;
|
||||
}
|
||||
|
||||
function showEditModal() {
|
||||
let modalDiv = document.getElementById('profileModal');
|
||||
if (!modalDiv) {
|
||||
modalDiv = document.createElement('div');
|
||||
modalDiv.className = 'modal fade';
|
||||
modalDiv.id = 'profileModal';
|
||||
modalDiv.tabIndex = -1;
|
||||
modalDiv.innerHTML = `
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" id="profileModalContent"></div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modalDiv);
|
||||
}
|
||||
const modalContent = modalDiv.querySelector('#profileModalContent');
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Редактировать профиль</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form id="profileForm">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Имя</label>
|
||||
<input type="text" class="form-control" name="firstName" value="${profile.firstName}" required pattern="^[А-Яа-яЁё]+$" title="Только русские буквы!">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Фамилия</label>
|
||||
<input type="text" class="form-control" name="lastName" value="${profile.lastName}" required pattern="^[А-Яа-яЁё]+$" title="Только русские буквы!">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Фото профиля</label>
|
||||
<input type="file" class="form-control" name="image" accept="image/*">
|
||||
<img id="profilePreviewImage" src="${profile.image || ''}" style="max-width:100%;margin-top:10px;${profile.image ? '' : 'display:none;'}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-success">Сохранить</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
const modal = new bootstrap.Modal(modalDiv);
|
||||
modal.show();
|
||||
|
||||
// Предпросмотр картинки
|
||||
const fileInput = modalContent.querySelector('input[type="file"]');
|
||||
|
||||
document.getElementById('profileForm').onsubmit = function (e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const firstName = formData.get('firstName');
|
||||
const lastName = formData.get('lastName');
|
||||
// Валидация кириллицы
|
||||
if (!/^[А-Яа-яЁё]+$/.test(firstName) || !/^[А-Яа-яЁё]+$/.test(lastName)) {
|
||||
alert('Имя и фамилия должны быть только на русском!');
|
||||
return;
|
||||
}
|
||||
profile.firstName = firstName;
|
||||
profile.lastName = lastName;
|
||||
if (fileInput.files.length) {
|
||||
const file = fileInput.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
profile.image = event.target.result; // base64
|
||||
modal.hide();
|
||||
renderProfile();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
profile.image = fileInput.src || "images/бананы.jpg";
|
||||
modal.hide();
|
||||
renderProfile();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
renderProfile();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
80
Basket.html
80
Basket.html
@@ -1,80 +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>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<header">
|
||||
<div class="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" class="me-3" style="width: 200px; height: auto;">
|
||||
<a href="newSite.html" class="text-decoration-none text-dark m-1"><h1 class="display-4 h3 mt-3"><b>Название магазина</b></h1></a>
|
||||
</div>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="navigationDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Account.html">Личный кабинет<i class="bi bi-person-circle ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Basket.html">Корзина<i class="bi bi-cart4 ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Order.html">Заказы<i class="bi bi-receipt ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Favorites.html">Избранное<i class="bi bi-heart-fill ms-2"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<main class="container d-flex justify-content-center align-items-center" style="min-height: 60vh;">
|
||||
<div class="card p-4 shadow" style="min-width: 60vw;">
|
||||
<h2 class="text-center">Корзина</h2>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<img src="images/glasses.jpg" alt="Очки" style="width: 100px; height: 100px;" class="me-3">
|
||||
Очки <span class="ms-auto">349 руб.</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<img src="images/chery.jpg" alt="Chery Tiggo" style="width: 100px; height: 100px;" class="me-3">
|
||||
Chery Tiggo 7 Pro Max <span class="ms-auto">5 руб.</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<img src="images/vanadiy.jpg" alt="Ванадий" style="width: 100px; height: 100px;" class="me-3">
|
||||
Ванадий <span class="ms-auto">2099 руб.</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="text-center mt-3">
|
||||
<a href="https://xn----7sbon6aucai8a.xn--p1ai/wa-data/public/shop/products/98/27/2798/images/6060/6060.970.jpg" target="_blank" class="btn btn-success w-100">
|
||||
Оплатить
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="container mt-5">
|
||||
<div class="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<i class="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" class="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank" class="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="#" class="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="mailto:ozon-zon-zon@mail.joke" class="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,79 +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>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<header">
|
||||
<div class="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" class="me-3" style="width: 200px; height: auto;">
|
||||
<a href="newSite.html" class="text-decoration-none text-dark m-1"><h1 class="display-4 h3 mt-3"><b>Название магазина</b></h1></a>
|
||||
</div>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="navigationDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Account.html">Личный кабинет<i class="bi bi-person-circle ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Basket.html">Корзина<i class="bi bi-cart4 ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Order.html">Заказы<i class="bi bi-receipt ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Favorites.html">Избранное<i class="bi bi-heart-fill ms-2"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4">Избранное</h2>
|
||||
<div class="row row-cols-1 row-cols-md-2 g-4">
|
||||
<div class="col">
|
||||
<div class="card mx-auto" style="width: 70%;">
|
||||
<img src="images/masha.jpg" class="card-img-top" alt="Женщина" style="width: 100%; height: 300px; object-fit: cover;">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Женщина</h5>
|
||||
<p class="card-text">бесценна</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card mx-auto" style="width: 70%;">
|
||||
<img src="images/screwdriver.jpg" class="card-img-top" alt="Отвертка" style="width: 100%; height: 300px; object-fit: cover;">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Отвертка</h5>
|
||||
<p class="card-text">219 руб</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="container mt-5">
|
||||
<div class="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<i class="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" class="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank" class="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="#" class="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="mailto:ozon-zon-zon@mail.joke" class="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
101
Order.html
101
Order.html
@@ -1,101 +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>
|
||||
<link rel="stylesheet" href="css/style.css"/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<header">
|
||||
<div class="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" class="me-3" style="width: 200px; height: auto;">
|
||||
<a href="newSite.html" class="text-decoration-none text-dark m-1"><h1 class="display-4 h3 mt-3"><b>Название магазина</b></h1></a>
|
||||
</div>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="navigationDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Account.html">Личный кабинет<i class="bi bi-person-circle ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Basket.html">Корзина<i class="bi bi-cart4 ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Order.html">Заказы<i class="bi bi-receipt ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="Favorites.html">Избранное<i class="bi bi-heart-fill ms-2"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<div class="container mt-4">
|
||||
<h1 class="text-center">Заказы</h1>
|
||||
<div class="row">
|
||||
<!-- Заказы в процессе -->
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h2 class="h5 m-0">В процессе</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex align-items-center mb-2">
|
||||
<img src="images/spoon.jpg" class="me-2" style="min-width: 90px;"> Ложка
|
||||
</li>
|
||||
<li class="list-group-item d-flex align-items-center mb-2">
|
||||
<img src="images/fork.jpg" class="me-2" style="min-width: 90px;"> Вилка
|
||||
</li>
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<img src="images/knife.jpg" class="me-2" style="min-width: 90px;"> Нож
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Завершённые заказы -->
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2 class="h5 m-0">Завершённые</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex align-items-center mb-2">
|
||||
<img src="images/iron.jpg" class="me-2" style="min-width: 90px;"> Утюг
|
||||
</li>
|
||||
<li class="list-group-item d-flex align-items-center mb-2">
|
||||
<img src="images/bananas.jpg" class="me-2" style="min-width: 90px;"> Бананы
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="container mt-5">
|
||||
<div class="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<i class="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" class="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank" class="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="#" class="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="mailto:ozon-zon-zon@mail.joke" class="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,90 +0,0 @@
|
||||
/* Общие стили */
|
||||
body {
|
||||
background-color: #edf7f6;
|
||||
}
|
||||
|
||||
/* Заголовки */
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Изображения */
|
||||
img {
|
||||
width: 180px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Ссылки */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Навигация */
|
||||
navbar {
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
right: 20px;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
/* Контент */
|
||||
.goods img {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
/* Заказы */
|
||||
.inProcess h2 {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.done h2 {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
/* Кнопка оплаты */
|
||||
.buy {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background-color: greenyellow;
|
||||
text-align: center;
|
||||
border: 2px solid black;
|
||||
border-radius: 10px;
|
||||
width: 110px;
|
||||
height: 35px;
|
||||
transition: background-color 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.buy:hover {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
/* Личный кабинет */
|
||||
.account img {
|
||||
border: 3px solid black;
|
||||
}
|
||||
|
||||
.recomended .card {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.recomended .card:hover {
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media only screen and (min-width: 400px) {
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.buy {
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
15
index.html
Normal file
15
index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!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>
|
||||
@@ -1,52 +0,0 @@
|
||||
// js/controller.js
|
||||
|
||||
import Model from './model.js';
|
||||
import View from './view.js';
|
||||
|
||||
export default {
|
||||
async init() {
|
||||
this.products = await Model.getProducts();
|
||||
this.categories = await Model.getCategories();
|
||||
this.brands = await Model.getBrands();
|
||||
|
||||
View.renderProductList(this.products, this.handleEdit.bind(this), this.handleDelete.bind(this));
|
||||
|
||||
// Кнопка "Добавить товар"
|
||||
document.getElementById('addProductBtn').onclick = () => {
|
||||
View.showProductModal(
|
||||
{ categories: this.categories, brands: this.brands },
|
||||
this.handleAdd.bind(this)
|
||||
);
|
||||
};
|
||||
},
|
||||
|
||||
async handleAdd(productData, modal) {
|
||||
await Model.addProduct(productData);
|
||||
modal.hide();
|
||||
await this.refresh();
|
||||
},
|
||||
|
||||
async handleEdit(productId) {
|
||||
const product = await Model.getProductById(productId);
|
||||
View.showProductModal(
|
||||
{ product, categories: this.categories, brands: this.brands },
|
||||
async (formData, modal) => {
|
||||
await Model.updateProduct(productId, formData);
|
||||
modal.hide();
|
||||
await this.refresh();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
async handleDelete(productId) {
|
||||
if (confirm('Удалить товар?')) {
|
||||
await Model.deleteProduct(productId);
|
||||
await this.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
this.products = await Model.getProducts();
|
||||
View.renderProductList(this.products, this.handleEdit.bind(this), this.handleDelete.bind(this));
|
||||
}
|
||||
};
|
||||
46
js/model.js
46
js/model.js
@@ -1,46 +0,0 @@
|
||||
// js/model.js
|
||||
|
||||
const API_URL = 'http://localhost:5000';
|
||||
|
||||
export default {
|
||||
// --- PRODUCTS ---
|
||||
async getProducts() {
|
||||
const res = await fetch(`${API_URL}/products?_expand=category&_expand=brand`);
|
||||
return res.json();
|
||||
},
|
||||
async getProductById(id) {
|
||||
const res = await fetch(`${API_URL}/products/${id}`);
|
||||
return res.json();
|
||||
},
|
||||
async addProduct(product) {
|
||||
const res = await fetch(`${API_URL}/products`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(product)
|
||||
});
|
||||
return res.json();
|
||||
},
|
||||
async updateProduct(id, product) {
|
||||
const res = await fetch(`${API_URL}/products/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(product)
|
||||
});
|
||||
return res.json();
|
||||
},
|
||||
async deleteProduct(id) {
|
||||
await fetch(`${API_URL}/products/${id}`, { method: 'DELETE' });
|
||||
},
|
||||
|
||||
// --- CATEGORIES ---
|
||||
async getCategories() {
|
||||
const res = await fetch(`${API_URL}/categories`);
|
||||
return res.json();
|
||||
},
|
||||
|
||||
// --- BRANDS ---
|
||||
async getBrands() {
|
||||
const res = await fetch(`${API_URL}/brands`);
|
||||
return res.json();
|
||||
}
|
||||
};
|
||||
124
js/view.js
124
js/view.js
@@ -1,124 +0,0 @@
|
||||
// js/view.js
|
||||
|
||||
export default {
|
||||
renderProductList(products, onEdit, onDelete) {
|
||||
const list = document.getElementById('productsList');
|
||||
list.innerHTML = '';
|
||||
products.forEach(product => {
|
||||
const col = document.createElement('div');
|
||||
col.className = 'col';
|
||||
// Если image не начинается с "data:" — выводим как есть (старые товары), иначе как base64
|
||||
const imgSrc = product.image?.startsWith('data:') ? product.image : (product.image || 'images/no-image.png');
|
||||
col.innerHTML = `
|
||||
<div class="card h-100">
|
||||
<img src="${imgSrc}" class="card-img-top" alt="${product.name}" style="width: 100%; height: 300px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">${product.name}</h5>
|
||||
<p class="card-text">${product.price} руб</p>
|
||||
<p class="card-text">
|
||||
<small class="text-muted">Категория: ${product.category?.name || '-'}</small><br>
|
||||
<small class="text-muted">Бренд: ${product.brand?.name || '-'}</small>
|
||||
</p>
|
||||
<button class="btn btn-primary btn-sm me-2 edit-btn"><i class="bi bi-pencil"></i> Редактировать</button>
|
||||
<button class="btn btn-danger btn-sm delete-btn"><i class="bi bi-trash"></i> Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
// Навесить обработчики
|
||||
col.querySelector('.edit-btn').onclick = () => onEdit(product.id);
|
||||
col.querySelector('.delete-btn').onclick = () => onDelete(product.id);
|
||||
list.appendChild(col);
|
||||
});
|
||||
},
|
||||
|
||||
showProductModal({ product = {}, categories = [], brands = [] }, onSubmit) {
|
||||
const modalContent = document.getElementById('productModalContent');
|
||||
modalContent.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">${product.id ? 'Редактировать' : 'Добавить'} товар</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form id="productForm">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Название товара</label>
|
||||
<input type="text" class="form-control" name="name" value="${product.name || ''}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Цена (руб)</label>
|
||||
<input type="number" class="form-control" name="price" value="${product.price || ''}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Изображение</label>
|
||||
<input type="file" class="form-control" name="image" accept="image/*" ${product.id ? "" : "required"}>
|
||||
<img id="previewImage" src="${product.image || ''}" style="max-width:100%;margin-top:10px;${product.image ? '' : 'display:none;'}"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Категория</label>
|
||||
<select class="form-select" name="categoryId" required>
|
||||
<option value="">Выберите категорию</option>
|
||||
${categories.map(cat => `
|
||||
<option value="${cat.id}" ${product.categoryId == cat.id ? 'selected' : ''}>${cat.name}</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Бренд</label>
|
||||
<select class="form-select" name="brandId" required>
|
||||
<option value="">Выберите бренд</option>
|
||||
${brands.map(br => `
|
||||
<option value="${br.id}" ${product.brandId == br.id ? 'selected' : ''}>${br.name}</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-success">${product.id ? 'Сохранить' : 'Добавить'}</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
// Показываем модалку через Bootstrap JS
|
||||
const modal = new bootstrap.Modal(document.getElementById('productModal'));
|
||||
modal.show();
|
||||
|
||||
// Предпросмотр выбранной картинки
|
||||
const fileInput = modalContent.querySelector('input[type="file"]');
|
||||
const preview = modalContent.querySelector('#previewImage');
|
||||
fileInput.onchange = () => {
|
||||
const file = fileInput.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = '';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
// Обработка формы (с поддержкой base64-картинки)
|
||||
document.getElementById('productForm').onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const obj = Object.fromEntries(formData.entries());
|
||||
obj.price = +obj.price;
|
||||
obj.categoryId = +obj.categoryId;
|
||||
obj.brandId = +obj.brandId;
|
||||
|
||||
if (fileInput.files.length) {
|
||||
const file = fileInput.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
obj.image = event.target.result; // base64
|
||||
onSubmit(obj, modal);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
obj.image = product.image || '';
|
||||
onSubmit(obj, modal);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
99
newSite.html
99
newSite.html
@@ -1,99 +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>
|
||||
<link rel="stylesheet" href="css/style.css" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header">
|
||||
<div class="d-block mt-3 ms-3">
|
||||
<img src="images/logo.jpg" alt="Название магазина" class="me-3" style="width: 200px; height: auto;">
|
||||
<a href="newSite.html" class="text-decoration-none text-dark m-1">
|
||||
<h1 class="display-4 h3 mt-3"><b>Название магазина</b></h1>
|
||||
</a>
|
||||
</div>
|
||||
<navbar>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="navigationDropdown"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Навигация
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="navigationDropdown">
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Account.html">Личный кабинет<i class="bi bi-person-circle ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Basket.html">Корзина<i class="bi bi-cart4 ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Order.html">Заказы<i class="bi bi-receipt ms-2"></i></a></li>
|
||||
<li><a class="dropdown-item d-flex justify-content-between align-items-center"
|
||||
href="Favorites.html">Избранное<i class="bi bi-heart-fill ms-2"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</navbar>
|
||||
</header>
|
||||
<main class="container">
|
||||
<div class="d-flex justify-content-end my-4">
|
||||
<button class="btn btn-success" id="addProductBtn">
|
||||
<i class="bi bi-plus-circle"></i> Добавить товар
|
||||
</button>
|
||||
</div>
|
||||
<!-- Модальное окно для формы добавления/редактирования -->
|
||||
<div class="modal fade" id="productModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" id="productModalContent">
|
||||
<!-- JS сам отрисует тут форму через View.showProductForm() -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 class="text-center my-3">Рекомендуемые товары:</h2>
|
||||
<div class="row row-cols-1 row-cols-md-3 g-4" id="productsList">
|
||||
<!-- Здесь будут карточки товаров (рендерит JS) -->
|
||||
</div>
|
||||
</main>
|
||||
<footer class="container mt-5">
|
||||
<div class="bg-light p-4">
|
||||
<h5>Помощь:</h5>
|
||||
<div class="d-flex flex-wrap">
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<i class="bi bi-telephone-fill me-2"></i>
|
||||
<a href="#" class="text-decoration-none text-dark">8 (800)-555-35-35</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/vk.png" alt="VK" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="https://vk.com/howmakesite_nn?from=search" target="_blank"
|
||||
class="text-decoration-none text-dark">vk.com</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center me-4 mb-3">
|
||||
<img src="images/telegram.png" alt="Telegram" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="#" class="text-decoration-none text-dark">tg.me</a>
|
||||
</div>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<img src="images/gmail.png" alt="Gmail" class="me-2" style="width: 24px; height: 24px;">
|
||||
<a href="mailto:ozon-zon-zon@mail.joke"
|
||||
class="text-decoration-none text-dark">ozon-zon-zon@mail.joke</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4">
|
||||
<img src="images/бананы.jpg" alt="Бананы" class="img-fluid">
|
||||
</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
<script type="module">
|
||||
import Controller from './js/controller.js';
|
||||
Controller.init();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1312
package-lock.json
generated
1312
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -3,28 +3,35 @@
|
||||
"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 …"
|
||||
"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"
|
||||
"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": {
|
||||
"http-server": "14.1.1",
|
||||
"vite": "6.2.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"@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",
|
||||
"eslint-plugin-html": "8.1.2"
|
||||
"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
Normal file
26
src/App.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
29
src/components/Footer.jsx
Normal file
29
src/components/Footer.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
28
src/components/Header.jsx
Normal file
28
src/components/Header.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
22
src/components/ProductCard.jsx
Normal file
22
src/components/ProductCard.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
72
src/components/ProductForm.jsx
Normal file
72
src/components/ProductForm.jsx
Normal file
@@ -0,0 +1,72 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
18
src/components/ProductList.jsx
Normal file
18
src/components/ProductList.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
35
src/hooks/useBasket.jsx
Normal file
35
src/hooks/useBasket.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
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 };
|
||||
}
|
||||
28
src/hooks/useFavorites.jsx
Normal file
28
src/hooks/useFavorites.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
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 };
|
||||
}
|
||||
26
src/hooks/useOrders.jsx
Normal file
26
src/hooks/useOrders.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
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 };
|
||||
}
|
||||
33
src/hooks/useProducts.jsx
Normal file
33
src/hooks/useProducts.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
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 };
|
||||
}
|
||||
24
src/hooks/useProfile.jsx
Normal file
24
src/hooks/useProfile.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
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 };
|
||||
}
|
||||
8
src/index.jsx
Normal file
8
src/index.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
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 />);
|
||||
76
src/pages/AccountPage.jsx
Normal file
76
src/pages/AccountPage.jsx
Normal file
@@ -0,0 +1,76 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
45
src/pages/BasketPage.jsx
Normal file
45
src/pages/BasketPage.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
34
src/pages/FavoritesPage.jsx
Normal file
34
src/pages/FavoritesPage.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
42
src/pages/MainPage.jsx
Normal file
42
src/pages/MainPage.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
58
src/pages/OrderPage.jsx
Normal file
58
src/pages/OrderPage.jsx
Normal file
@@ -0,0 +1,58 @@
|
||||
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,8 +1,12 @@
|
||||
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"),
|
||||
@@ -13,4 +17,12 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
open: '/',
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
BIN
Отчет3.docx
BIN
Отчет3.docx
Binary file not shown.
BIN
Отчет4.docx
BIN
Отчет4.docx
Binary file not shown.
BIN
Отчет5.docx
Normal file
BIN
Отчет5.docx
Normal file
Binary file not shown.
BIN
Отчет6.docx
Normal file
BIN
Отчет6.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user