ПРОГРЕСС УРА

This commit is contained in:
2025-09-25 16:29:37 +04:00
parent 632835f834
commit 229187dd4e
7 changed files with 129 additions and 62 deletions

View File

@@ -8,6 +8,33 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<link rel="stylesheet" href="./styles.css" />
<style>
@media (min-width: 992px) {
.navbar .dropdown:hover .dropdown-menu {
display: block;
}
.navbar .dropdown .dropdown-menu {
margin-top: 0;
}
}
/* прижимаем футер вниз */
html, body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1; /* растягивается, чтобы футер был внизу */
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #00264d;">
@@ -46,19 +73,7 @@
</div>
</div>
</nav>
<style>
@media (min-width: 992px) {
.navbar .dropdown:hover .dropdown-menu {
display: block;
}
.navbar .dropdown .dropdown-menu {
margin-top: 0;
}
}
</style>
<!-- В main контейнере basket.html замените содержимое на: -->
<main class="container my-4">
<div class="empty-basket text-center py-5">
<h1 class="mb-4">Здесь будут лежать твои товары</h1>
@@ -101,12 +116,13 @@
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="./components/basket/model.js"></script>
<script src="./components/basket/view.js"></script>
<script src="./components/basket/controller.js"></script>
<script>
<script type="module">
import { Model } from '/components/basket/model.js';
import { View } from '/components/basket/view.js';
import { Controller } from '/components/basket/controller.js';
document.addEventListener('DOMContentLoaded', function() {
// Проверяем, есть ли необходимые элементы на странице
if (document.getElementById('basketContainer') || document.getElementById('productsContainer')) {
const model = new Model();
const view = new View();
@@ -115,4 +131,4 @@
});
</script>
</body>
</html>
</html>

View File

@@ -354,12 +354,14 @@
});
</script>
<script src="./components/basket/model.js"></script>
<script src="./components/basket/view.js"></script>
<script src="./components/basket/controller.js"></script>
<script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script type="module">
import { Model } from '/components/basket/model.js';
import { View } from '/components/basket/view.js';
import { Controller } from '/components/basket/controller.js';
document.addEventListener('DOMContentLoaded', function() {
// Проверяем, есть ли необходимые элементы на странице
if (document.getElementById('basketContainer') || document.getElementById('productsContainer')) {
const model = new Model();
const view = new View();
@@ -367,5 +369,6 @@
}
});
</script>
</body>
</html>

49
dist/assets/controller-BWr64z2B.js vendored Normal file
View File

@@ -0,0 +1,49 @@
(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))e(a);new MutationObserver(a=>{for(const i of a)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&e(o)}).observe(document,{childList:!0,subtree:!0});function s(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function e(a){if(a.ep)return;a.ep=!0;const i=s(a);fetch(a.href,i)}})();class r{constructor(){this.apiUrl="http://localhost:3000"}async request(t,s={}){try{const e=await fetch(`${this.apiUrl}${t}`,{...s,headers:{"Content-Type":"application/json",...s.headers}});if(!e.ok)throw new Error(`HTTP error! status: ${e.status}`);return await e.json()}catch(e){throw console.error("Request failed:",e),e}}async getBasketItems(){try{return await this.request("/basket")}catch(t){return console.error("Ошибка при получении корзины:",t),[]}}async addToBasket(t){try{const e=(await this.getBasketItems()).find(a=>a.id===t.id);if(e)await this.updateBasketItem(t.id,e.quantity+1);else{const a={...t,quantity:1,addedAt:new Date().toISOString()};return await(await fetch(`${this.apiUrl}/basket`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)})).json()}}catch(s){console.error("Ошибка при добавлении в корзину:",s)}}async updateBasketItem(t,s){try{return await(await fetch(`${this.apiUrl}/basket/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({quantity:s})})).json()}catch(e){console.error("Ошибка при обновлении корзины:",e)}}async removeFromBasket(t){try{await fetch(`${this.apiUrl}/basket/${t}`,{method:"DELETE"})}catch(s){console.error("Ошибка при удалении из корзины:",s)}}async clearBasket(){try{const t=await this.getBasketItems();for(const s of t)await this.removeFromBasket(s.id)}catch(t){console.error("Ошибка при очистке корзины:",t)}}async getProducts(){try{return await(await fetch(`${this.apiUrl}/shmots`)).json()}catch(t){return console.error("Ошибка при получении товаров:",t),[]}}}class c{constructor(){this.basketContainer=document.getElementById("basketContainer"),this.emptyBasketElement=document.querySelector(".empty-basket")}showBasket(t){if(t.length===0){this.showEmptyBasket();return}this.hideEmptyBasket();const s=t.map(e=>this.createBasketItemHTML(e)).join("");this.basketContainer.innerHTML=`
<div class="row">
<div class="col-12">
<div class="card border-0 shadow">
<div class="card-header" style="background-color: #00264d; color: white;">
<h5 class="mb-0"><i class="bi bi-cart me-2"></i>Корзина</h5>
</div>
<div class="card-body">
${s}
<div class="d-flex justify-content-between align-items-center mt-4 pt-3 border-top">
<h5>Итого: $${this.calculateTotal(t).toFixed(2)}</h5>
<button class="btn btn-lg" style="background-color: #00264d; color: white;" id="checkoutBtn">
<i class="bi bi-credit-card me-2"></i>Оформить заказ
</button>
</div>
</div>
</div>
</div>
</div>
`}createBasketItemHTML(t){return`
<div class="row align-items-center mb-3 basket-item" data-id="${t.id}">
<div class="col-md-2">
<img src="${t.image}" alt="${t.name}" class="img-fluid rounded" style="max-height: 80px;">
</div>
<div class="col-md-4">
<h6 class="mb-1">${t.name}</h6>
<p class="text-muted small mb-0">${t.description}</p>
</div>
<div class="col-md-2">
<span class="fw-bold">$${t.price}</span>
</div>
<div class="col-md-2">
<div class="input-group input-group-sm">
<button class="btn btn-outline-secondary decrease-btn" type="button">-</button>
<input type="number" class="form-control text-center quantity-input" value="${t.quantity}" min="1">
<button class="btn btn-outline-secondary increase-btn" type="button">+</button>
</div>
</div>
<div class="col-md-2">
<span class="fw-bold me-3">$${(t.price*t.quantity).toFixed(2)}</span>
<button class="btn btn-sm btn-outline-danger remove-btn">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
`}showEmptyBasket(){this.emptyBasketElement&&(this.emptyBasketElement.style.display="block"),this.basketContainer&&(this.basketContainer.innerHTML="")}hideEmptyBasket(){this.emptyBasketElement&&(this.emptyBasketElement.style.display="none")}calculateTotal(t){return t.reduce((s,e)=>s+e.price*e.quantity,0)}showNotification(t,s="success"){const e=document.createElement("div");e.className=`alert alert-${s==="success"?"success":"danger"} alert-dismissible fade show`,e.style.cssText="position: fixed; top: 20px; right: 20px; z-index: 1050; min-width: 300px;",e.innerHTML=`
${t}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`,document.body.appendChild(e),setTimeout(()=>{e.parentNode&&e.remove()},3e3)}}class l{constructor(t,s){this.model=t,this.view=s,this.init()}async init(){window.location.pathname.includes("basket.html")&&(await this.loadBasket(),this.setupBasketEventListeners()),window.location.pathname.includes("catalog.html")&&this.setupCatalogEventListeners()}async loadBasket(){const t=await this.model.getBasketItems();this.view.showBasket(t)}setupBasketEventListeners(){document.addEventListener("click",async t=>{const s=t.target.closest(".basket-item");if(!s)return;const e=s.dataset.id;if(t.target.closest(".remove-btn")&&(await this.model.removeFromBasket(e),this.view.showNotification("Товар удален из корзины"),await this.loadBasket()),t.target.closest(".increase-btn")){const a=s.querySelector(".quantity-input"),i=parseInt(a.value)+1;a.value=i,await this.model.updateBasketItem(e,i),await this.loadBasket()}if(t.target.closest(".decrease-btn")){const a=s.querySelector(".quantity-input");let i=parseInt(a.value)-1;i<1&&(i=1),a.value=i,await this.model.updateBasketItem(e,i),await this.loadBasket()}}),document.addEventListener("change",async t=>{if(t.target.classList.contains("quantity-input")){const e=t.target.closest(".basket-item").dataset.id,a=parseInt(t.target.value)||1;if(a<1){t.target.value=1;return}await this.model.updateBasketItem(e,a),await this.loadBasket()}}),document.addEventListener("click",async t=>{if(t.target.id==="checkoutBtn"){if((await this.model.getBasketItems()).length===0){this.view.showNotification("Корзина пуста","error");return}this.view.showNotification("Заказ оформлен! Спасибо за покупку!"),await this.model.clearBasket(),await this.loadBasket()}})}setupCatalogEventListeners(){document.addEventListener("click",async t=>{if(t.target.closest(".btn")&&t.target.closest(".btn").textContent.includes("В корзину")){const s=t.target.closest(".card"),e=this.extractProductData(s);e&&(await this.model.addToBasket(e),this.view.showNotification("Товар добавлен в корзину!"))}})}extractProductData(t){try{const s=t.querySelector(".card-title").textContent,e=t.querySelector(".text-muted").textContent.replace("$",""),a=t.querySelector(".card-text").textContent,i=t.querySelector("img").src;return{id:btoa(`${s}-${e}`).substring(0,8),name:s.trim(),price:parseFloat(e),description:a.trim(),image:i}}catch(s){return console.error("Ошибка при извлечении данных товара:",s),null}}}export{l as C,r as M,c as V};

1
dist/assets/page2-Cyv2CTPS.js vendored Normal file
View File

@@ -0,0 +1 @@
import{V as t,C as o,M as d}from"./controller-BWr64z2B.js";/* empty css */document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("basketContainer")||document.getElementById("productsContainer")){const e=new d,n=new t;new o(e,n)}});

1
dist/assets/page5-Cyv2CTPS.js vendored Normal file
View File

@@ -0,0 +1 @@
import{V as t,C as o,M as d}from"./controller-BWr64z2B.js";/* empty css */document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("basketContainer")||document.getElementById("productsContainer")){const e=new d,n=new t;new o(e,n)}});

57
dist/basket.html vendored
View File

@@ -7,6 +7,35 @@
<link rel="shortcut icon" href="/assets/favicon-cXvr3Sfo.ico" type="image/x-icon">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<style>
@media (min-width: 992px) {
.navbar .dropdown:hover .dropdown-menu {
display: block;
}
.navbar .dropdown .dropdown-menu {
margin-top: 0;
}
}
/* прижимаем футер вниз */
html, body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1; /* растягивается, чтобы футер был внизу */
}
</style>
<script type="module" crossorigin src="/assets/page5-Cyv2CTPS.js"></script>
<link rel="modulepreload" crossorigin href="/assets/controller-BWr64z2B.js">
<link rel="stylesheet" crossorigin href="/assets/styles-cpwRBDRQ.css">
</head>
@@ -46,19 +75,7 @@
</ul>
</div>
</div>
</nav>
<style>
@media (min-width: 992px) {
.navbar .dropdown:hover .dropdown-menu {
display: block;
}
.navbar .dropdown .dropdown-menu {
margin-top: 0;
}
}
</nav>
<main class="container my-4">
<div class="empty-basket text-center py-5">
@@ -101,18 +118,6 @@
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="./components/basket/model.js"></script>
<script src="./components/basket/view.js"></script>
<script src="./components/basket/controller.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Проверяем, есть ли необходимые элементы на странице
if (document.getElementById('basketContainer') || document.getElementById('productsContainer')) {
const model = new Model();
const view = new View();
new Controller(model, view);
}
});
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>

18
dist/catalog.html vendored
View File

@@ -26,6 +26,8 @@
.like-btn i {
transition: text-shadow 0.2s ease;
}
</style>
<script type="module" crossorigin src="/assets/page2-Cyv2CTPS.js"></script>
<link rel="modulepreload" crossorigin href="/assets/controller-BWr64z2B.js">
<link rel="stylesheet" crossorigin href="/assets/styles-cpwRBDRQ.css">
</head>
@@ -354,18 +356,8 @@
this.reset();
});
</script>
<script src="./components/basket/model.js"></script>
<script src="./components/basket/view.js"></script>
<script src="./components/basket/controller.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Проверяем, есть ли необходимые элементы на странице
if (document.getElementById('basketContainer') || document.getElementById('productsContainer')) {
const model = new Model();
const view = new View();
new Controller(model, view);
}
});
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>