795 lines
30 KiB
JavaScript
795 lines
30 KiB
JavaScript
import React from "react";
|
||
|
||
// Модель
|
||
class BookModel {
|
||
constructor() {
|
||
this.API_URL = "http://localhost:3001";
|
||
}
|
||
|
||
async fetchGenres() {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/genres`);
|
||
if (!response.ok) throw new Error("Ошибка загрузки жанров");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("fetchGenres error:", error);
|
||
return [];
|
||
}
|
||
}
|
||
async createGenre(genreData) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/genres`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify(genreData),
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка создания жанра");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("createGenre error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
async fetchBooksByGenre(genreId) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/books?genreId=${genreId}`);
|
||
if (!response.ok) throw new Error("Ошибка загрузки книг");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("fetchBooksByGenre error:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
async fetchBook(id) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/books/${id}`);
|
||
if (!response.ok) throw new Error("Ошибка загрузки книги");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("fetchBook error:", error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async createBook(bookData) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/books`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify(bookData),
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка создания книги");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("createBook error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async updateBook(id, bookData) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/books/${id}`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify(bookData),
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка обновления книги");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("updateBook error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async deleteBook(id) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/books/${id}`, {
|
||
method: "DELETE",
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка удаления книги");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("deleteBook error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async fetchCartItems() {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/cart?_expand=book`);
|
||
if (!response.ok) throw new Error("Ошибка загрузки корзины");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("fetchCartItems error:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
async addToCart(bookId) {
|
||
try {
|
||
const existingItem = await this.getCartItemByBookId(bookId);
|
||
|
||
if (existingItem) {
|
||
return await this.updateCartItem(existingItem.id, { quantity: existingItem.quantity + 1 });
|
||
} else {
|
||
const response = await fetch(`${this.API_URL}/cart`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ bookId, quantity: 1 }),
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка добавления в корзину");
|
||
return await response.json();
|
||
}
|
||
} catch (error) {
|
||
console.error("addToCart error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async getCartItemByBookId(bookId) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/cart?bookId=${bookId}`);
|
||
if (!response.ok) throw new Error("Ошибка проверки корзины");
|
||
const items = await response.json();
|
||
return items[0] || null;
|
||
} catch (error) {
|
||
console.error("getCartItemByBookId error:", error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
async updateCartItem(id, data) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/cart/${id}`, {
|
||
method: "PATCH",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify(data),
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка обновления корзины");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("updateCartItem error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async removeFromCart(id) {
|
||
try {
|
||
const response = await fetch(`${this.API_URL}/cart/${id}`, {
|
||
method: "DELETE",
|
||
});
|
||
if (!response.ok) throw new Error("Ошибка удаления из корзины");
|
||
return await response.json();
|
||
} catch (error) {
|
||
console.error("removeFromCart error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async clearCart() {
|
||
try {
|
||
const items = await this.fetchCartItems();
|
||
await Promise.all(items.map((item) => this.removeFromCart(item.id)));
|
||
return true;
|
||
} catch (error) {
|
||
console.error("clearCart error:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Представление
|
||
class BookView {
|
||
constructor() {
|
||
this.bookModal = document.getElementById("bookModal")
|
||
? new bootstrap.Modal(document.getElementById("bookModal"))
|
||
: null;
|
||
this.bookModalTitle = document.getElementById("bookModalLabel");
|
||
this.bookForm = document.getElementById("bookForm");
|
||
this.genreSelect = document.getElementById("bookGenre");
|
||
this.genreModal = document.getElementById("genreModal")
|
||
? new bootstrap.Modal(document.getElementById("genreModal"))
|
||
: null;
|
||
this.genreModalTitle = document.getElementById("genreModalLabel");
|
||
this.genreForm = document.getElementById("genreForm");
|
||
this.cartModal = document.getElementById("cartModal")
|
||
? new bootstrap.Modal(document.getElementById("cartModal"))
|
||
: null;
|
||
|
||
// Ищем контейнеры корзины на разных страницах
|
||
this.cartItemsContainer = document.getElementById("cartItemsContainer") || document.getElementById("cartItems");
|
||
this.cartTotal = document.getElementById("cartTotal") || document.querySelector("#cartModal #cartTotal");
|
||
// Инициализация refs для модальных окон
|
||
this.modalRefs = {
|
||
bookModal: React.createRef(),
|
||
genreModal: React.createRef(),
|
||
cartModal: React.createRef(),
|
||
};
|
||
}
|
||
|
||
renderBooks(books, genreName) {
|
||
const container = document.getElementById("books-container");
|
||
if (!container) return;
|
||
container = document.getElementById(`${genreName.toLowerCase()}-books`);
|
||
if (!container) {
|
||
console.warn(`Контейнер для жанра ${genreName} не найден`);
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = "";
|
||
|
||
if (!books || books.length === 0) {
|
||
container.innerHTML = '<div class="col-12 text-muted">Книги не найдены</div>';
|
||
return;
|
||
}
|
||
|
||
books.forEach((book) => {
|
||
const bookCard = this.createBookCard(book);
|
||
container.appendChild(bookCard);
|
||
});
|
||
}
|
||
|
||
renderGenresSections(genres) {
|
||
const main = document.querySelector("main");
|
||
if (!main) return;
|
||
|
||
// Удаляем старые секции жанров
|
||
document.querySelectorAll(".genre-section").forEach((section) => section.remove());
|
||
|
||
// Создаём новые секции для каждого жанра
|
||
genres.forEach((genre) => {
|
||
const section = document.createElement("section");
|
||
section.className = "mb-5 genre-section";
|
||
section.innerHTML = `
|
||
<div class="genre-title bg-light p-3 rounded text-center mb-4">
|
||
<h3>${genre.name}</h3>
|
||
</div>
|
||
<div class="row g-4" id="${genre.name.toLowerCase()}-books"></div>
|
||
`;
|
||
main.insertBefore(section, main.querySelector("footer"));
|
||
});
|
||
}
|
||
|
||
createBookCard(book) {
|
||
const col = document.createElement("div");
|
||
col.className = "col-md-6 mb-4";
|
||
|
||
col.innerHTML = `
|
||
<div class="card h-100 border-0 shadow-sm" data-id="${book.id}">
|
||
<div class="row g-0">
|
||
<div class="col-md-4">
|
||
<img src="${book.image || "images/default-book.jpg"}"
|
||
class="img-fluid rounded-start h-100"
|
||
alt="${book.title || "Без названия"}"
|
||
style="object-fit: cover"
|
||
onerror="this.src='images/default-book.jpg'">
|
||
</div>
|
||
<div class="col-md-8">
|
||
<div class="card-body">
|
||
<h5 class="card-title">${book.title || "Без названия"}</h5>
|
||
<p class="card-text text-muted">${book.author || "Автор не указан"}</p>
|
||
<p class="card-text">${book.description || "Описание отсутствует"}</p>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<p class="h5 mb-0">${book.price || 0} руб.</p>
|
||
<div>
|
||
<button class="btn btn-primary me-2 mb-2 add-to-cart">В корзину</button>
|
||
<button class="btn btn-outline-secondary me-2 mb-2 edit-book">Редактировать</button>
|
||
<button class="btn btn-outline-danger delete-book">Удалить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
return col;
|
||
}
|
||
|
||
renderGenreSelect(genres) {
|
||
if (!this.genreSelect) {
|
||
console.error("Элемент выбора жанра не найден");
|
||
return;
|
||
}
|
||
|
||
this.genreSelect.innerHTML = (genres || [])
|
||
.map((genre) => `<option value="${genre.id}">${genre.name}</option>`)
|
||
.join("");
|
||
}
|
||
|
||
renderCart(cartItems) {
|
||
if (!this.cartItemsContainer || !this.cartTotal) {
|
||
console.error("Элементы корзины не найдены");
|
||
return;
|
||
}
|
||
|
||
this.cartItemsContainer.innerHTML = "";
|
||
|
||
let total = 0;
|
||
|
||
if (!cartItems || cartItems.length === 0) {
|
||
this.cartItemsContainer.innerHTML = '<p class="text-muted">Корзина пуста</p>';
|
||
this.cartTotal.textContent = "0 руб.";
|
||
return;
|
||
}
|
||
|
||
cartItems.forEach((item) => {
|
||
if (!item.book) return;
|
||
|
||
const book = item.book;
|
||
const itemTotal = (book.price || 0) * (item.quantity || 1);
|
||
total += itemTotal;
|
||
|
||
const cartItem = document.createElement("div");
|
||
cartItem.className = "card mb-3";
|
||
cartItem.innerHTML = `
|
||
<div class="card-body">
|
||
<div class="row align-items-center">
|
||
<div class="col-md-2">
|
||
<img src="${book.image || "images/default-book.jpg"}"
|
||
alt="${book.title || "Без названия"}"
|
||
class="img-fluid rounded"
|
||
onerror="this.src='images/default-book.jpg'">
|
||
</div>
|
||
<div class="col-md-6">
|
||
<h5>${book.title || "Без названия"}</h5>
|
||
<p class="text-muted">${book.author || "Автор не указан"}</p>
|
||
<p>Цена: ${book.price || 0} руб. × ${item.quantity || 1} = ${itemTotal} руб.</p>
|
||
</div>
|
||
<div class="col-md-2">
|
||
<input type="number" min="1" value="${item.quantity || 1}"
|
||
class="form-control cart-item-quantity"
|
||
data-id="${item.id}">
|
||
</div>
|
||
<div class="col-md-2 text-center">
|
||
<button class="btn btn-outline-danger remove-from-cart" data-id="${item.id}">
|
||
<i class="bi bi-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
this.cartItemsContainer.appendChild(cartItem);
|
||
});
|
||
|
||
this.cartTotal.textContent = `${total} руб.`;
|
||
}
|
||
|
||
showBookModal(title, bookData = null) {
|
||
if (!this.bookModal || !this.bookModalTitle || !this.bookForm) {
|
||
console.error("Элементы модального окна книги не найдены");
|
||
return;
|
||
}
|
||
|
||
this.bookModalTitle.textContent = title || "Книга";
|
||
|
||
if (bookData) {
|
||
document.getElementById("bookId").value = bookData.id || "";
|
||
document.getElementById("bookTitle").value = bookData.title || "";
|
||
document.getElementById("bookAuthor").value = bookData.author || "";
|
||
document.getElementById("bookPrice").value = bookData.price || "";
|
||
document.getElementById("bookDescription").value = bookData.description || "";
|
||
document.getElementById("bookImage").value = (bookData.image || "").replace("images/", "");
|
||
document.getElementById("bookGenre").value = bookData.genreId || "";
|
||
} else {
|
||
this.bookForm.reset();
|
||
document.getElementById("bookId").value = "";
|
||
}
|
||
|
||
this.bookModal.show();
|
||
}
|
||
|
||
showCartModal() {
|
||
if (this.cartModal) {
|
||
this.cartModal.show();
|
||
} else {
|
||
console.error("Модальное окно корзины не найдено");
|
||
}
|
||
}
|
||
|
||
bindAddBook(handler) {
|
||
document.querySelectorAll(".add-book-btn").forEach((btn) => {
|
||
btn.addEventListener("click", handler);
|
||
});
|
||
}
|
||
|
||
bindEditBook(handler) {
|
||
document.addEventListener("click", (event) => {
|
||
if (event.target.classList.contains("edit-book")) {
|
||
const card = event.target.closest(".card");
|
||
if (card) {
|
||
const id = parseInt(card.dataset.id);
|
||
if (!isNaN(id)) handler(id);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
bindDeleteBook(handler) {
|
||
document.addEventListener("click", (event) => {
|
||
if (event.target.classList.contains("delete-book")) {
|
||
const card = event.target.closest(".card");
|
||
if (card) {
|
||
const id = parseInt(card.dataset.id);
|
||
if (!isNaN(id) && confirm("Вы уверены, что хотите удалить эту книгу?")) {
|
||
handler(id);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
bindAddToCart(handler) {
|
||
document.addEventListener("click", (event) => {
|
||
if (event.target.classList.contains("add-to-cart")) {
|
||
const card = event.target.closest(".card");
|
||
if (card) {
|
||
const id = parseInt(card.dataset.id);
|
||
if (!isNaN(id)) handler(id);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
bindShowCart(handler) {
|
||
document.querySelectorAll(".show-cart-btn").forEach((btn) => {
|
||
btn.addEventListener("click", (e) => {
|
||
e.preventDefault();
|
||
handler();
|
||
});
|
||
});
|
||
}
|
||
|
||
bindRemoveFromCart(handler) {
|
||
document.addEventListener("click", (event) => {
|
||
const removeBtn = event.target.closest(".remove-from-cart");
|
||
if (removeBtn) {
|
||
const id = parseInt(removeBtn.dataset.id);
|
||
if (!isNaN(id)) handler(id);
|
||
}
|
||
});
|
||
}
|
||
|
||
bindUpdateCartItem(handler) {
|
||
document.addEventListener("change", (event) => {
|
||
if (event.target.classList.contains("cart-item-quantity")) {
|
||
const id = parseInt(event.target.dataset.id);
|
||
const quantity = parseInt(event.target.value);
|
||
if (!isNaN(id) && !isNaN(quantity) && quantity > 0) {
|
||
handler(id, quantity);
|
||
} else {
|
||
event.target.value = 1;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
bindClearCart(handler) {
|
||
const clearBtn = document.getElementById("clearCartBtn");
|
||
if (clearBtn) {
|
||
clearBtn.addEventListener("click", async (e) => {
|
||
e.preventDefault();
|
||
if (confirm("Вы уверены, что хотите очистить корзину?")) {
|
||
await handler();
|
||
// После очистки обновляем отображение
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.renderCart(cartItems);
|
||
await this.controller.updateCartCount();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
bindCheckout(handler) {
|
||
const checkoutBtn = document.getElementById("checkoutBtn");
|
||
if (checkoutBtn) {
|
||
checkoutBtn.addEventListener("click", handler);
|
||
}
|
||
}
|
||
|
||
bindSubmitBookForm(handler) {
|
||
if (this.bookForm) {
|
||
this.bookForm.addEventListener("submit", (event) => {
|
||
event.preventDefault();
|
||
|
||
const id = document.getElementById("bookId").value;
|
||
const bookData = {
|
||
title: document.getElementById("bookTitle").value,
|
||
author: document.getElementById("bookAuthor").value,
|
||
price: parseInt(document.getElementById("bookPrice").value) || 0,
|
||
description: document.getElementById("bookDescription").value,
|
||
image: document.getElementById("bookImage").value.startsWith("http")
|
||
? document.getElementById("bookImage").value
|
||
: `images/${document.getElementById("bookImage").value}`,
|
||
genreId: parseInt(document.getElementById("bookGenre").value),
|
||
};
|
||
|
||
if (!bookData.title || !bookData.author) {
|
||
alert("Пожалуйста, заполните обязательные поля");
|
||
return;
|
||
}
|
||
|
||
handler(id, bookData);
|
||
});
|
||
}
|
||
}
|
||
// Метод для показа модального окна жанра
|
||
showGenreModal(title) {
|
||
if (!this.genreModal || !this.genreModalTitle || !this.genreForm) {
|
||
console.error("Элементы модального окна жанра не найдены");
|
||
return;
|
||
}
|
||
|
||
this.genreModalTitle.textContent = title || "Жанр";
|
||
this.genreForm.reset();
|
||
this.genreModal.show();
|
||
}
|
||
|
||
// Привязка обработчика добавления жанра
|
||
bindAddGenre(handler) {
|
||
document.querySelectorAll(".add-genre-btn").forEach((btn) => {
|
||
btn.addEventListener("click", handler);
|
||
});
|
||
}
|
||
|
||
// Привязка обработчика отправки формы жанра
|
||
bindSubmitGenreForm(handler) {
|
||
if (this.genreForm) {
|
||
this.genreForm.addEventListener("submit", (event) => {
|
||
event.preventDefault();
|
||
|
||
const genreData = {
|
||
name: document.getElementById("genreName").value,
|
||
};
|
||
|
||
if (!genreData.name) {
|
||
alert("Пожалуйста, укажите название жанра");
|
||
return;
|
||
}
|
||
|
||
handler(genreData);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Контроллер
|
||
class BookController {
|
||
constructor(model, view) {
|
||
this.model = model;
|
||
this.view = view;
|
||
this.view.controller = this;
|
||
this.init();
|
||
}
|
||
|
||
async init() {
|
||
try {
|
||
// Инициализация обработчиков событий
|
||
this.view.bindAddGenre(() => this.handleAddGenre());
|
||
this.view.bindSubmitGenreForm((genreData) => this.handleSubmitGenreForm(genreData));
|
||
this.view.bindAddBook(() => this.handleAddBook());
|
||
this.view.bindEditBook((id) => this.handleEditBook(id));
|
||
this.view.bindDeleteBook((id) => this.handleDeleteBook(id));
|
||
this.view.bindAddToCart((id) => this.handleAddToCart(id));
|
||
this.view.bindShowCart(() => this.handleShowCart());
|
||
this.view.bindRemoveFromCart((id) => this.handleRemoveFromCart(id));
|
||
this.view.bindUpdateCartItem((id, quantity) => this.handleUpdateCartItem(id, quantity));
|
||
this.view.bindClearCart(() => this.handleClearCart());
|
||
this.view.bindCheckout(() => this.handleCheckout());
|
||
this.view.bindSubmitBookForm((id, bookData) => this.handleSubmitBookForm(id, bookData));
|
||
|
||
// Загрузка данных только если мы на странице каталога
|
||
if (document.getElementById("fantasy-books")) {
|
||
await this.loadData();
|
||
}
|
||
|
||
// Обновляем счетчик корзины всегда
|
||
await this.updateCartCount();
|
||
|
||
// Если мы на странице корзины, загружаем ее содержимое
|
||
if (document.getElementById("cartItemsContainer")) {
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.view.renderCart(cartItems);
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка инициализации:", error);
|
||
}
|
||
}
|
||
// Новые методы обработчиков:
|
||
async handleAddGenre() {
|
||
this.view.showGenreModal("Добавить новый жанр");
|
||
}
|
||
|
||
async handleSubmitGenreForm(genreData) {
|
||
try {
|
||
const newGenre = await this.model.createGenre(genreData);
|
||
const genres = await this.model.fetchGenres();
|
||
|
||
// Обновляем интерфейс
|
||
this.view.renderGenreSelect(genres);
|
||
this.view.renderGenresSections(genres);
|
||
|
||
// Загружаем книги для ВСЕХ жанров после обновления секций
|
||
for (const genre of genres) {
|
||
const books = await this.model.fetchBooksByGenre(genre.id);
|
||
this.view.renderBooks(books, genre.name);
|
||
}
|
||
|
||
if (this.view.genreModal) {
|
||
this.view.genreModal.hide();
|
||
}
|
||
alert("Жанр успешно добавлен!");
|
||
} catch (error) {
|
||
console.error("Ошибка сохранения жанра:", error);
|
||
alert("Не удалось сохранить жанр");
|
||
}
|
||
}
|
||
async loadData() {
|
||
try {
|
||
const genres = await this.model.fetchGenres();
|
||
this.view.renderGenreSelect(genres);
|
||
this.view.renderGenresSections(genres);
|
||
|
||
for (const genre of genres) {
|
||
const books = await this.model.fetchBooksByGenre(genre.id);
|
||
this.view.renderBooks(books, genre.name);
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка загрузки данных:", error);
|
||
}
|
||
}
|
||
|
||
async handleAddBook() {
|
||
this.view.showBookModal("Добавить новую книгу");
|
||
}
|
||
|
||
async handleEditBook(id) {
|
||
try {
|
||
const book = await this.model.fetchBook(id);
|
||
if (book) {
|
||
this.view.showBookModal("Редактировать книгу", book);
|
||
} else {
|
||
alert("Книга не найдена");
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка редактирования книги:", error);
|
||
alert("Не удалось загрузить данные книги");
|
||
}
|
||
}
|
||
|
||
async handleDeleteBook(id) {
|
||
try {
|
||
await this.model.deleteBook(id);
|
||
location.reload();
|
||
} catch (error) {
|
||
console.error("Ошибка удаления книги:", error);
|
||
alert("Не удалось удалить книгу");
|
||
}
|
||
}
|
||
|
||
async handleAddToCart(bookId) {
|
||
try {
|
||
await this.model.addToCart(bookId);
|
||
await this.updateCartCount();
|
||
alert("Книга добавлена в корзину");
|
||
} catch (error) {
|
||
console.error("Ошибка добавления в корзину:", error);
|
||
alert("Не удалось добавить книгу в корзину");
|
||
}
|
||
}
|
||
|
||
async handleShowCart() {
|
||
try {
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.view.renderCart(cartItems);
|
||
this.view.showCartModal();
|
||
} catch (error) {
|
||
console.error("Ошибка загрузки корзины:", error);
|
||
alert("Не удалось загрузить корзину");
|
||
}
|
||
}
|
||
|
||
async handleRemoveFromCart(id) {
|
||
try {
|
||
await this.model.removeFromCart(id);
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.view.renderCart(cartItems);
|
||
await this.updateCartCount();
|
||
} catch (error) {
|
||
console.error("Ошибка удаления из корзины:", error);
|
||
alert("Не удалось удалить товар из корзины");
|
||
}
|
||
}
|
||
|
||
async handleUpdateCartItem(id, quantity) {
|
||
try {
|
||
await this.model.updateCartItem(id, { quantity });
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.view.renderCart(cartItems);
|
||
await this.updateCartCount();
|
||
} catch (error) {
|
||
console.error("Ошибка обновления корзины:", error);
|
||
alert("Не удалось обновить количество товара");
|
||
}
|
||
}
|
||
|
||
async handleClearCart() {
|
||
try {
|
||
await this.model.clearCart();
|
||
// Обновляем отображение корзины
|
||
const cartItems = await this.model.fetchCartItems();
|
||
this.view.renderCart(cartItems);
|
||
await this.updateCartCount();
|
||
|
||
// Показываем сообщение об успехе
|
||
alert("Корзина успешно очищена");
|
||
} catch (error) {
|
||
console.error("Ошибка очистки корзины:", error);
|
||
alert("Не удалось очистить корзину");
|
||
}
|
||
}
|
||
|
||
async handleCheckout() {
|
||
try {
|
||
alert("Заказ оформлен! Спасибо за покупку!");
|
||
await this.model.clearCart();
|
||
this.view.renderCart([]);
|
||
await this.updateCartCount();
|
||
} catch (error) {
|
||
console.error("Ошибка оформления заказа:", error);
|
||
alert("Не удалось оформить заказ");
|
||
}
|
||
}
|
||
|
||
async handleSubmitBookForm(id, bookData) {
|
||
try {
|
||
if (id) {
|
||
await this.model.updateBook(id, bookData);
|
||
} else {
|
||
await this.model.createBook(bookData);
|
||
}
|
||
|
||
location.reload();
|
||
} catch (error) {
|
||
console.error("Ошибка сохранения книги:", error);
|
||
alert("Не удалось сохранить книгу");
|
||
}
|
||
}
|
||
|
||
async updateCartCount() {
|
||
try {
|
||
const cartItems = await this.model.fetchCartItems();
|
||
const totalItems = cartItems.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
||
|
||
document.querySelectorAll(".cart-count").forEach((el) => {
|
||
el.textContent = totalItems;
|
||
el.style.display = totalItems > 0 ? "inline-block" : "none";
|
||
});
|
||
} catch (error) {
|
||
console.error("Ошибка обновления счетчика корзины:", error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Инициализация приложения
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
try {
|
||
const model = new BookModel();
|
||
const view = new BookView();
|
||
new BookController(model, view);
|
||
} catch (error) {
|
||
console.error("Ошибка инициализации приложения:", error);
|
||
}
|
||
});
|