/
Cooolprogrammers
/
web-nodejs-labs
Обзор
Документация
Войти
/
Cooolprogrammers
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
public/lab10/task1/script.js
272 строки
9 KB
darkvoid6@mail.ru
ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ
18 дек 2025, 07:59
18 дек 2025, 07:59
f2c4f06
Код
Авторство
О чём код?
const API_BASE = '/api/lab10'; // --- CATALOG FUNCTIONS --- async function initCatalog() { await loadCategories(); loadProducts(1); } async function loadCategories() { try { const res = await fetch(`${API_BASE}/categories`); const categories = await res.json(); const select = document.getElementById('categorySelect'); categories.forEach(c => { const opt = document.createElement('option'); opt.value = c.id; opt.textContent = c.name; select.appendChild(opt); }); } catch (e) { console.error(e); } } async function loadProducts(page = 1) { const container = document.getElementById('products-container'); const search = document.getElementById('searchInput').value; const category = document.getElementById('categorySelect').value; container.innerHTML = '<div class="loader">⏳ Загрузка каталога...</div>'; const params = new URLSearchParams({ page, limit: 9 }); if (search) params.append('q', search); if (category) params.append('category', category); try { const res = await fetch(`${API_BASE}/products?${params}`); const { data, total, limit } = await res.json(); renderProducts(data); renderPagination(total, page, limit); } catch (err) { container.innerHTML = '<div class="loader" style="color:var(--danger)">❌ Ошибка загрузки товаров</div>'; } } function renderProducts(products) { const container = document.getElementById('products-container'); container.innerHTML = ''; if (products.length === 0) { container.innerHTML = '<div class="loader">🔍 Товары не найдены</div>'; return; } products.forEach(p => { const priceRub = (p.price / 100).toLocaleString('ru-RU'); // Stock Logic for Badge let stockClass = 'stock-high'; let stockText = 'В наличии'; if (p.amount === 0) { stockClass = 'stock-out'; stockText = 'Раскуплено'; } else if (p.amount < 5) { stockClass = 'stock-low'; stockText = `Мало (${p.amount})`; } const card = document.createElement('div'); card.className = 'product-card'; card.innerHTML = ` <div class="product-image-placeholder"> 📦 </div> <div class="product-body"> <div class="product-category">${p.category_name || 'Без категории'}</div> <h3 class="product-title">${p.title}</h3> <div class="product-meta"> <span class="stock-badge ${stockClass}">${stockText}</span> <span class="price-tag">${priceRub} ₽</span> </div> </div> <div class="product-footer"> ${p.amount > 0 ? `<button onclick="addToCart(${p.id}, '${p.title}', ${p.price}, ${p.amount})" class="btn btn-primary">В корзину</button>` : `<button disabled class="btn">Нет в наличии</button>` } </div> `; container.appendChild(card); }); } function renderPagination(total, currentPage, limit) { const pagination = document.getElementById('pagination'); const totalPages = Math.ceil(total / limit); pagination.innerHTML = ''; if (totalPages <= 1) return; for (let i = 1; i <= totalPages; i++) { const btn = document.createElement('div'); btn.textContent = i; btn.className = `page-link ${i === currentPage ? 'active' : ''}`; btn.onclick = () => loadProducts(i); pagination.appendChild(btn); } } function applyFilters() { loadProducts(1); } // --- CART FUNCTIONS (LocalStorage) --- function getCart() { return JSON.parse(localStorage.getItem('lab10_cart') || '{}'); } function saveCart(cart) { localStorage.setItem('lab10_cart', JSON.stringify(cart)); updateCartBadge(); } function updateCartBadge() { const badge = document.getElementById('cart-count'); if (!badge) return; const cart = getCart(); const count = Object.values(cart).reduce((sum, item) => sum + item.quantity, 0); badge.textContent = count; } function addToCart(id, title, price, maxAmount) { const cart = getCart(); if (!cart[id]) { cart[id] = { id, title, price, quantity: 0, max: maxAmount }; } if (cart[id].quantity < maxAmount) { cart[id].quantity++; saveCart(cart); showToast(`"${title}" добавлен в корзину`, 'success'); } else { showToast('Больше нет в наличии на складе!', 'error'); } } function renderCart() { const container = document.getElementById('cart-items-container'); const summary = document.getElementById('cart-summary'); if (!container) return; // We are not on cart page const cart = getCart(); const items = Object.values(cart); container.innerHTML = ''; let total = 0; if (items.length === 0) { container.innerHTML = '<div class="loader">🛒 Ваша корзина пуста</div>'; if (summary) summary.classList.add('hidden'); return; } if (summary) summary.classList.remove('hidden'); items.forEach(item => { const itemTotal = item.price * item.quantity; total += itemTotal; const div = document.createElement('div'); div.className = 'cart-item'; div.innerHTML = ` <div class="cart-item-info"> <h3>${item.title}</h3> <div class="cart-item-price">${(item.price / 100).toLocaleString()} ₽ × ${item.quantity} шт.</div> </div> <div class="qty-controls"> <button onclick="changeQty(${item.id}, -1)" class="qty-btn">−</button> <span>${item.quantity}</span> <button onclick="changeQty(${item.id}, 1)" class="qty-btn">+</button> </div> <button onclick="removeFromCart(${item.id})" class="btn btn-danger"> Удалить </button> `; container.appendChild(div); }); const totalEl = document.getElementById('total-price'); if (totalEl) totalEl.textContent = (total / 100).toLocaleString() + ' ₽'; } function changeQty(id, delta) { const cart = getCart(); if (cart[id]) { const newItemQty = cart[id].quantity + delta; if (newItemQty > 0 && newItemQty <= cart[id].max) { cart[id].quantity = newItemQty; saveCart(cart); renderCart(); } else if (newItemQty > cart[id].max) { showToast('Достигнут лимит товара на складе', 'error'); } } } function removeFromCart(id) { const cart = getCart(); delete cart[id]; saveCart(cart); renderCart(); showToast('Товар удален', 'success'); } // --- CHECKOUT (TRANSACTION) --- async function processCheckout() { const cart = getCart(); const items = Object.values(cart).map(i => ({ id: i.id, quantity: i.quantity })); if (items.length === 0) { showToast('Корзина пуста', 'error'); return; } if (!confirm(`Оформить заказ на сумму ${document.getElementById('total-price').textContent}?`)) return; try { const res = await fetch(`${API_BASE}/checkout`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items }) }); const result = await res.json(); if (res.ok) { localStorage.removeItem('lab10_cart'); showToast('Заказ успешно создан!', 'success'); setTimeout(() => { window.location.href = 'index.html'; }, 1500); } else { showToast(`Ошибка: ${result.message}`, 'error'); } } catch (err) { console.error(err); showToast('Ошибка соединения с сервером', 'error'); } } // --- UTILS (TOAST NOTIFICATIONS) --- function showToast(message, type = 'success') { const container = document.getElementById('toast-container'); if (!container) return; // Prevent errors if container is missing const toast = document.createElement('div'); toast.className = `toast ${type}`; const icon = type === 'success' ? '✅' : '⚠️'; toast.innerHTML = `<span>${icon}</span><span>${message}</span>`; container.appendChild(toast); // Remove after 3 seconds setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateY(20px)'; setTimeout(() => toast.remove(), 300); }, 3000); }