ЛАБ4 - ВСЁ????

This commit is contained in:
2025-09-26 21:15:37 +04:00
parent ae4a4e755f
commit 7fa9e60bab
10 changed files with 177 additions and 85 deletions

View File

@@ -78,12 +78,12 @@
</style>
<main class="container my-4">
<div class="accordion" id="accordionExample" >
<div class="accordion-item" >
<h2 class="accordion-header" id="headingOne" >
<button class="accordion-button" type="button" data-bs-toggle="collapse" style="background-color: #b0d8ff;" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Добавить товар
</button>
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" style="background-color: #b0d8ff;" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Добавить товар
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
@@ -98,6 +98,23 @@
<label for="productPrice" class="form-label">Цена</label>
<input type="text" class="form-control" id="productPrice" required>
</div>
<div class="col-md-6">
<label for="productCategory" class="form-label">Категория</label>
<select class="form-select" id="productCategory" required>
<option value="">Выберите категорию</option>
<option value="1">Для мужчин</option>
<option value="2">Для женщин</option>
<option value="3">Унисекс</option>
</select>
</div>
<div class="col-md-6">
<label for="productCondition" class="form-label">Состояние</label>
<select class="form-select" id="productCondition" required>
<option value="">Выберите состояние</option>
<option value="1">Новый</option>
<option value="2">Б/У</option>
</select>
</div>
<div class="col-12">
<label for="productDescription" class="form-label">Описание</label>
<textarea class="form-control" id="productDescription" rows="3" required></textarea>
@@ -130,7 +147,6 @@
<h4 class="card-title">Stone Island</h4>
<p class="card-text">super idol rovny pacan, groza rayona, mother's modnik, патч на месте</p>
<!-- Измененная секция Category и Condition -->
<div class="row">
<div class="col-6">
<h5 class="card-text mb-1">Category:</h5>
@@ -348,9 +364,15 @@
e.preventDefault();
const name = document.getElementById("productName").value;
const price = document.getElementById("productPrice").value;
const category = document.getElementById("productCategory").value;
const condition = document.getElementById("productCondition").value;
const description = document.getElementById("productDescription").value;
const image = document.getElementById("productImage").value;
// Получаем текстовые значения для отображения
const categoryText = document.getElementById("productCategory").options[document.getElementById("productCategory").selectedIndex].text;
const conditionText = document.getElementById("productCondition").options[document.getElementById("productCondition").selectedIndex].text;
const productCard = `
<div class="col">
<div class="card h-100 border-0 shadow">
@@ -358,6 +380,16 @@
<div class="card-body">
<h5 class="card-title">${name}</h5>
<p class="card-text">${description}</p>
<div class="row">
<div class="col-6">
<h5 class="card-text mb-1">Category:</h5>
<p class="card-text">${categoryText}</p>
</div>
<div class="col-6">
<h5 class="card-text mb-1">Condition:</h5>
<p class="card-text">${conditionText}</p>
</div>
</div>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-center">

View File

@@ -71,6 +71,10 @@ export class LikesController {
const description = card.querySelector('.card-text').textContent;
const image = card.querySelector('img').src;
// ✅ Вытаскиваем category и condition
const category = card.querySelector('.col-6:nth-child(1) p')?.textContent.trim() || '';
const condition = card.querySelector('.col-6:nth-child(2) p')?.textContent.trim() || '';
const id = btoa(`${name}-${priceText}`).substring(0, 8);
return {
@@ -78,7 +82,9 @@ export class LikesController {
name: name.trim(),
price: parseFloat(priceText),
description: description.trim(),
image: image
image: image,
category: category,
condition: condition
};
} catch (error) {
console.error('Ошибка при извлечении данных товара:', error);

View File

@@ -26,27 +26,47 @@ export class LikesView {
}
createLikesItemHTML(item) {
return `
<div class="col-md-4">
<div class="card h-100 border-0 shadow like-item" data-id="${item.id}">
<img src="${item.image}" class="card-img-top" alt="${item.name}">
<div class="card-body">
<h5 class="card-title">${item.name}</h5>
<p class="card-text">${item.description}</p>
<span class="fw-bold text-muted">$${item.price}</span>
return `
<div class="col-md-4">
<div class="card h-100 border-0 shadow like-item" data-id="${item.id}">
<img src="${item.image}" class="card-img-top" alt="${item.name}">
<div class="card-body">
<h5 class="card-title">${item.name}</h5>
<p class="card-text">${item.description}</p>
<!-- ✅ Добавляем Category и Condition -->
<div class="row">
<div class="col-6">
<h6 class="mb-1">Category:</h6>
<p class="card-text">${item.category || '-'}</p>
</div>
<div class="col-6">
<h6 class="mb-1">Condition:</h6>
<p class="card-text">${item.condition || '-'}</p>
</div>
</div>
<div class="card-footer bg-transparent d-flex justify-content-between">
<button class="btn btn-sm btn-outline-danger remove-like-btn">
<i class="bi bi-trash"></i> Удалить
</button>
<button class="btn btn-sm btn-outline-primary move-to-basket-btn">
<i class="bi bi-cart-plus"></i> В корзину
</button>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-end">
<!-- Цена слева -->
<span class="fw-bold text-muted fs-3 price-large">$${item.price}</span>
<!-- Кнопки справа -->
<div class="d-flex flex-column gap-1">
<button class="btn btn-sm btn-outline-primary move-to-basket-btn">
<i class="bi bi-cart-plus"></i> В корзину
</button>
<button class="btn btn-sm btn-outline-danger remove-like-btn">
<i class="bi bi-trash"></i> Удалить
</button>
</div>
</div>
</div>
</div>
`;
}
`;
}
showEmptyLikes() {
if (this.emptyLikesElement) {

22
db.json
View File

@@ -70,24 +70,6 @@
"name": "wu"
}
],
"likes": [
{
"id": "U3RvbmUgSXNs",
"name": "Stone Island",
"price": 1999.99,
"description": "super idol rovny pacan, groza rayona, mother's modnik, патч на месте",
"image": "http://192.168.31.212:4000/assets/stonik-D_cwcHTM.jpg"
}
],
"basket": [
{
"id": "U3RvbmUgSXNs",
"name": "Stone Island",
"price": 1999.99,
"description": "super idol rovny pacan, groza rayona, mother's modnik, патч на месте",
"image": "http://192.168.31.212:4000/assets/stonik-D_cwcHTM.jpg",
"quantity": 1,
"addedAt": "2025-09-26T10:54:28.952Z"
}
]
"likes": [],
"basket": []
}

View File

@@ -1,26 +0,0 @@
class r{constructor(){this.apiUrl="http://localhost:3000"}async request(t,e={}){const s=await fetch(`${this.apiUrl}${t}`,{...e,headers:{"Content-Type":"application/json",...e.headers}});if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);return await s.json()}async getLikesItems(){try{return await this.request("/likes")}catch(t){return console.error("Ошибка при получении избранного:",t),[]}}async addToLikes(t){try{if(!(await this.getLikesItems()).find(i=>i.id===t.id))return await(await fetch(`${this.apiUrl}/likes`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}catch(e){console.error("Ошибка при добавлении в избранное:",e)}}async removeFromLikes(t){try{await fetch(`${this.apiUrl}/likes/${t}`,{method:"DELETE"})}catch(e){console.error("Ошибка при удалении из избранного:",e)}}async moveToBasket(t){try{await(await fetch(`${this.apiUrl}/basket`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,quantity:1,addedAt:new Date().toISOString()})})).json(),await this.removeFromLikes(t.id)}catch(e){console.error("Ошибка при переносе товара в корзину:",e)}}}class c{constructor(){this.likesContainer=document.getElementById("likesContainer"),this.emptyLikesElement=document.querySelector(".empty-likes")}showLikes(t){if(!this.likesContainer)return;if(!t||t.length===0){this.showEmptyLikes();return}this.hideEmptyLikes();const e=t.map(s=>this.createLikesItemHTML(s)).join("");this.likesContainer.innerHTML=`
<h2 class="mb-4 text-center">Избранное</h2>
<div class="row g-3">${e}</div>
`}createLikesItemHTML(t){return`
<div class="col-md-4">
<div class="card h-100 border-0 shadow like-item" data-id="${t.id}">
<img src="${t.image}" class="card-img-top" alt="${t.name}">
<div class="card-body">
<h5 class="card-title">${t.name}</h5>
<p class="card-text">${t.description}</p>
<span class="fw-bold text-muted">$${t.price}</span>
</div>
<div class="card-footer bg-transparent d-flex justify-content-between">
<button class="btn btn-sm btn-outline-danger remove-like-btn">
<i class="bi bi-trash"></i> Удалить
</button>
<button class="btn btn-sm btn-outline-primary move-to-basket-btn">
<i class="bi bi-cart-plus"></i> В корзину
</button>
</div>
</div>
</div>
`}showEmptyLikes(){this.emptyLikesElement&&(this.emptyLikesElement.style.display="block"),this.likesContainer&&(this.likesContainer.innerHTML="")}hideEmptyLikes(){this.emptyLikesElement&&(this.emptyLikesElement.style.display="none")}showNotification(t,e="success"){const s=document.createElement("div");s.className=`alert alert-${e==="success"?"success":"danger"} alert-dismissible fade show`,s.style.cssText="position: fixed; top: 20px; right: 20px; z-index: 1050; min-width: 300px;",s.innerHTML=`
${t}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`,document.body.appendChild(s),setTimeout(()=>{s.parentNode&&s.remove()},3e3)}}class l{constructor(t,e){this.model=t,this.view=e,this.init()}async init(){window.location.pathname.includes("likes.html")&&(await this.loadLikes(),this.setupLikesEventListeners()),window.location.pathname.includes("catalog.html")&&this.setupCatalogEventListeners()}async loadLikes(){const t=await this.model.getLikesItems();this.view.showLikes(t)}setupLikesEventListeners(){document.addEventListener("click",async t=>{const e=t.target.closest(".like-item");if(!e)return;const s=e.dataset.id;if(t.target.closest(".remove-like-btn")&&(await this.model.removeFromLikes(s),this.view.showNotification("Товар удален из избранного"),await this.loadLikes()),t.target.closest(".move-to-basket-btn")){const o=(await this.model.getLikesItems()).find(n=>n.id===s);o&&(await this.model.moveToBasket(o),this.view.showNotification("Товар перенесен в корзину"),await this.loadLikes())}})}setupCatalogEventListeners(){document.addEventListener("click",async t=>{const e=t.target.closest(".card");if(e&&t.target.closest(".like-btn")){const s=this.extractProductData(e);s&&(await this.model.addToLikes(s),this.view.showNotification("Товар добавлен в избранное!"))}})}extractProductData(t){try{const e=t.querySelector(".card-title").textContent,s=t.querySelector(".text-muted").textContent.replace("$",""),i=t.querySelector(".card-text").textContent,o=t.querySelector("img").src;return{id:btoa(`${e}-${s}`).substring(0,8),name:e.trim(),price:parseFloat(s),description:i.trim(),image:o}}catch(e){return console.error("Ошибка при извлечении данных товара:",e),null}}}export{c as L,l as a,r as b};

46
dist/assets/controller-BsNF2rMP.js vendored Normal file
View File

@@ -0,0 +1,46 @@
class h{constructor(){this.apiUrl="http://localhost:3000"}async request(t,e={}){const s=await fetch(`${this.apiUrl}${t}`,{...e,headers:{"Content-Type":"application/json",...e.headers}});if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);return await s.json()}async getLikesItems(){try{return await this.request("/likes")}catch(t){return console.error("Ошибка при получении избранного:",t),[]}}async addToLikes(t){try{if(!(await this.getLikesItems()).find(i=>i.id===t.id))return await(await fetch(`${this.apiUrl}/likes`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).json()}catch(e){console.error("Ошибка при добавлении в избранное:",e)}}async removeFromLikes(t){try{await fetch(`${this.apiUrl}/likes/${t}`,{method:"DELETE"})}catch(e){console.error("Ошибка при удалении из избранного:",e)}}async moveToBasket(t){try{await(await fetch(`${this.apiUrl}/basket`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...t,quantity:1,addedAt:new Date().toISOString()})})).json(),await this.removeFromLikes(t.id)}catch(e){console.error("Ошибка при переносе товара в корзину:",e)}}}class m{constructor(){this.likesContainer=document.getElementById("likesContainer"),this.emptyLikesElement=document.querySelector(".empty-likes")}showLikes(t){if(!this.likesContainer)return;if(!t||t.length===0){this.showEmptyLikes();return}this.hideEmptyLikes();const e=t.map(s=>this.createLikesItemHTML(s)).join("");this.likesContainer.innerHTML=`
<h2 class="mb-4 text-center">Избранное</h2>
<div class="row g-3">${e}</div>
`}createLikesItemHTML(t){return`
<div class="col-md-4">
<div class="card h-100 border-0 shadow like-item" data-id="${t.id}">
<img src="${t.image}" class="card-img-top" alt="${t.name}">
<div class="card-body">
<h5 class="card-title">${t.name}</h5>
<p class="card-text">${t.description}</p>
<!-- ✅ Добавляем Category и Condition -->
<div class="row">
<div class="col-6">
<h6 class="mb-1">Category:</h6>
<p class="card-text">${t.category||"-"}</p>
</div>
<div class="col-6">
<h6 class="mb-1">Condition:</h6>
<p class="card-text">${t.condition||"-"}</p>
</div>
</div>
</div>
<div class="card-footer bg-transparent">
<div class="d-flex justify-content-between align-items-end">
<!-- Цена слева -->
<span class="fw-bold text-muted fs-3 price-large">$${t.price}</span>
<!-- Кнопки справа -->
<div class="d-flex flex-column gap-1">
<button class="btn btn-sm btn-outline-primary move-to-basket-btn">
<i class="bi bi-cart-plus"></i> В корзину
</button>
<button class="btn btn-sm btn-outline-danger remove-like-btn">
<i class="bi bi-trash"></i> Удалить
</button>
</div>
</div>
</div>
`}showEmptyLikes(){this.emptyLikesElement&&(this.emptyLikesElement.style.display="block"),this.likesContainer&&(this.likesContainer.innerHTML="")}hideEmptyLikes(){this.emptyLikesElement&&(this.emptyLikesElement.style.display="none")}showNotification(t,e="success"){const s=document.createElement("div");s.className=`alert alert-${e==="success"?"success":"danger"} alert-dismissible fade show`,s.style.cssText="position: fixed; top: 20px; right: 20px; z-index: 1050; min-width: 300px;",s.innerHTML=`
${t}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`,document.body.appendChild(s),setTimeout(()=>{s.parentNode&&s.remove()},3e3)}}class p{constructor(t,e){this.model=t,this.view=e,this.init()}async init(){window.location.pathname.includes("likes.html")&&(await this.loadLikes(),this.setupLikesEventListeners()),window.location.pathname.includes("catalog.html")&&this.setupCatalogEventListeners()}async loadLikes(){const t=await this.model.getLikesItems();this.view.showLikes(t)}setupLikesEventListeners(){document.addEventListener("click",async t=>{const e=t.target.closest(".like-item");if(!e)return;const s=e.dataset.id;if(t.target.closest(".remove-like-btn")&&(await this.model.removeFromLikes(s),this.view.showNotification("Товар удален из избранного"),await this.loadLikes()),t.target.closest(".move-to-basket-btn")){const o=(await this.model.getLikesItems()).find(a=>a.id===s);o&&(await this.model.moveToBasket(o),this.view.showNotification("Товар перенесен в корзину"),await this.loadLikes())}})}setupCatalogEventListeners(){document.addEventListener("click",async t=>{const e=t.target.closest(".card");if(e&&t.target.closest(".like-btn")){const s=this.extractProductData(e);s&&(await this.model.addToLikes(s),this.view.showNotification("Товар добавлен в избранное!"))}})}extractProductData(t){var e,s;try{const i=t.querySelector(".card-title").textContent,o=t.querySelector(".text-muted").textContent.replace("$",""),a=t.querySelector(".card-text").textContent,r=t.querySelector("img").src,c=((e=t.querySelector(".col-6:nth-child(1) p"))==null?void 0:e.textContent.trim())||"",l=((s=t.querySelector(".col-6:nth-child(2) p"))==null?void 0:s.textContent.trim())||"";return{id:btoa(`${i}-${o}`).substring(0,8),name:i.trim(),price:parseFloat(o),description:a.trim(),image:r,category:c,condition:l}}catch(i){return console.error("Ошибка при извлечении данных товара:",i),null}}}export{m as L,p as a,h as b};

View File

@@ -1 +1 @@
import"./modulepreload-polyfill-B5Qt9EMX.js";/* empty css */import{L as t,a as o,b as d}from"./controller-BaU4rR-2.js";import{V as i,C as s,M as r}from"./controller-BHzoImTo.js";document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("productsContainer")){const e=new d,n=new t;new o(e,n)}});document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("basketContainer")||document.getElementById("productsContainer")){const e=new r,n=new i;new s(e,n)}});
import"./modulepreload-polyfill-B5Qt9EMX.js";/* empty css */import{L as t,a as o,b as d}from"./controller-BsNF2rMP.js";import{V as i,C as s,M as r}from"./controller-BHzoImTo.js";document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("productsContainer")){const e=new d,n=new t;new o(e,n)}});document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("basketContainer")||document.getElementById("productsContainer")){const e=new r,n=new i;new s(e,n)}});

View File

@@ -1 +1 @@
import"./modulepreload-polyfill-B5Qt9EMX.js";/* empty css */import{L as o,a as t,b as i}from"./controller-BaU4rR-2.js";document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("likesContainer")){const e=new i,n=new o;new t(e,n)}});
import"./modulepreload-polyfill-B5Qt9EMX.js";/* empty css */import{L as o,a as t,b as i}from"./controller-BsNF2rMP.js";document.addEventListener("DOMContentLoaded",function(){if(document.getElementById("likesContainer")){const e=new i,n=new o;new t(e,n)}});

50
dist/catalog.html vendored
View File

@@ -26,9 +26,9 @@
.like-btn i {
transition: text-shadow 0.2s ease;
}
</style>
</style>
<script type="module" crossorigin src="/assets/page2-Crmhwurg.js"></script>
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
<link rel="modulepreload" crossorigin href="/assets/controller-BsNF2rMP.js">
<link rel="modulepreload" crossorigin href="/assets/controller-BHzoImTo.js">
<link rel="stylesheet" crossorigin href="/assets/styles-cpwRBDRQ.css">
@@ -82,12 +82,12 @@
}
</style>
<main class="container my-4">
<div class="accordion" id="accordionExample" >
<div class="accordion-item" >
<h2 class="accordion-header" id="headingOne" >
<button class="accordion-button" type="button" data-bs-toggle="collapse" style="background-color: #b0d8ff;" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Добавить товар
<main class="container my-4">
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" style="background-color: #b0d8ff;" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Добавить товар
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
@@ -102,6 +102,23 @@
<div class="col-md-6">
<label for="productPrice" class="form-label">Цена</label>
<input type="text" class="form-control" id="productPrice" required>
</div>
<div class="col-md-6">
<label for="productCategory" class="form-label">Категория</label>
<select class="form-select" id="productCategory" required>
<option value="">Выберите категорию</option>
<option value="1">Для мужчин</option>
<option value="2">Для женщин</option>
<option value="3">Унисекс</option>
</select>
</div>
<div class="col-md-6">
<label for="productCondition" class="form-label">Состояние</label>
<select class="form-select" id="productCondition" required>
<option value="">Выберите состояние</option>
<option value="1">Новый</option>
<option value="2">Б/У</option>
</select>
</div>
<div class="col-12">
<label for="productDescription" class="form-label">Описание</label>
@@ -134,7 +151,6 @@
<div class="card-body">
<h4 class="card-title">Stone Island</h4>
<p class="card-text">super idol rovny pacan, groza rayona, mother's modnik, патч на месте</p>
<div class="row">
<div class="col-6">
@@ -352,9 +368,15 @@
document.getElementById("addProductForm").addEventListener("submit", function (e) {
e.preventDefault();
const name = document.getElementById("productName").value;
const price = document.getElementById("productPrice").value;
const category = document.getElementById("productCategory").value;
const condition = document.getElementById("productCondition").value;
const description = document.getElementById("productDescription").value;
const image = document.getElementById("productImage").value;
// Получаем текстовые значения для отображения
const categoryText = document.getElementById("productCategory").options[document.getElementById("productCategory").selectedIndex].text;
const conditionText = document.getElementById("productCondition").options[document.getElementById("productCondition").selectedIndex].text;
const productCard = `
<div class="col">
@@ -362,6 +384,16 @@
<img src="${image}" class="card-img-top" alt="${name}">
<div class="card-body">
<h5 class="card-title">${name}</h5>
<p class="card-text">${description}</p>
<div class="row">
<div class="col-6">
<h5 class="card-text mb-1">Category:</h5>
<p class="card-text">${categoryText}</p>
</div>
<div class="col-6">
<h5 class="card-text mb-1">Condition:</h5>
<p class="card-text">${conditionText}</p>
</div>
</div>
</div>
<div class="card-footer bg-transparent">

4
dist/likes.html vendored
View File

@@ -7,9 +7,9 @@
<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">
<script type="module" crossorigin src="/assets/page4-DyyBeFxE.js"></script>
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
<link rel="modulepreload" crossorigin href="/assets/controller-BsNF2rMP.js">
<link rel="stylesheet" crossorigin href="/assets/styles-cpwRBDRQ.css">
</head>