/
Cooolprogrammers
/
web-nodejs-labs
Обзор
Документация
Войти
/
Cooolprogrammers
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
public/lab9/task3/cart.html
517 строк
17 KB
darkvoid6@mail.ru
ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ
18 дек 2025, 07:59
18 дек 2025, 07:59
f2c4f06
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>ЛР9 - Моя корзина</title> <style> body { font-family: "Segoe UI", system-ui, sans-serif; max-width: 1100px; margin: 2em auto; padding: 0 20px; background: #0a0e14; color: #b0bec5; } .header { display: flex; justify-content: space-between; align-items: center; background: linear-gradient(135deg, #161b22 0%, #0d1117 100%); padding: 25px; border-radius: 12px; margin-bottom: 30px; border: 1px solid #30363d; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); } h1 { color: #7ee787; margin: 0 0 10px 0; } .back-link { color: #7ee787; text-decoration: none; font-weight: 500; border: 1px solid #238636; padding: 8px 16px; border-radius: 6px; transition: all 0.3s; } .back-link:hover { background: rgba(46, 160, 67, 0.1); border-color: #3fb950; } .cart-table { width: 100%; border-collapse: collapse; background: linear-gradient(145deg, #161b22 0%, #0d1117 100%); border-radius: 10px; overflow: hidden; border: 1px solid #30363d; margin-bottom: 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); } .cart-table th { background: linear-gradient(135deg, #238636 0%, #2ea043 100%); color: #f0f6fc; padding: 18px; text-align: left; font-weight: 600; font-size: 16px; } .cart-table td { padding: 18px; border-bottom: 1px solid #30363d; vertical-align: middle; } .cart-table tr:hover { background: rgba(46, 160, 67, 0.05); } .cart-table tr:last-child td { border-bottom: none; } .quantity-controls { display: flex; align-items: center; gap: 12px; } .quantity-btn { width: 36px; height: 36px; border: 1px solid #3fb950; background: rgba(46, 160, 67, 0.1); color: #7ee787; border-radius: 6px; cursor: pointer; font-weight: bold; font-size: 18px; transition: all 0.3s; } .quantity-btn:hover:not(:disabled) { background: rgba(46, 160, 67, 0.2); transform: translateY(-2px); } .quantity-btn:disabled { border-color: #30363d; color: #484f58; cursor: not-allowed; background: transparent; } .quantity-display { min-width: 36px; text-align: center; font-weight: 600; font-size: 18px; color: #7ee787; } .remove-btn { background: linear-gradient(135deg, #da3633 0%, #f85149 100%); color: #f0f6fc; border: none; padding: 10px 18px; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.3s; border: 1px solid #f85149; } .remove-btn:hover { background: linear-gradient(135deg, #f85149 0%, #ff6b6b 100%); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(248, 81, 73, 0.3); } .summary { background: linear-gradient(145deg, #161b22 0%, #0d1117 100%); padding: 25px; border-radius: 10px; border: 1px solid #30363d; margin-bottom: 30px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); } .summary-row { display: flex; justify-content: space-between; margin-bottom: 15px; font-size: 17px; } .summary-total { font-size: 24px; font-weight: bold; color: #3fb950; border-top: 2px solid #30363d; padding-top: 20px; margin-top: 20px; text-shadow: 0 2px 8px rgba(63, 185, 80, 0.3); } .actions { display: flex; gap: 20px; justify-content: flex-end; } .action-btn { padding: 16px 32px; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 600; transition: all 0.3s; border: 1px solid; } .checkout-btn { background: linear-gradient(135deg, #1f6feb 0%, #2ea043 100%); color: #f0f6fc; border-color: #3fb950; } .checkout-btn:hover:not(:disabled) { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(46, 160, 67, 0.4); } .purchase-btn { background: linear-gradient(135deg, #238636 0%, #3fb950 100%); color: #f0f6fc; border-color: #3fb950; } .purchase-btn:hover:not(:disabled) { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(46, 160, 67, 0.4); } .action-btn:disabled { background: #21262d; color: #8b949e; border-color: #30363d; cursor: not-allowed; transform: none !important; box-shadow: none !important; } .empty-cart { text-align: center; padding: 60px; color: #7ee787; font-size: 18px; } .empty-cart h3 { margin-bottom: 20px; font-size: 28px; color: #7ee787; } .error, .success, .warning { padding: 18px; border-radius: 8px; margin: 15px 0; font-weight: 500; border: 1px solid; background: transparent; } .error { color: #f85149; border-color: #f85149; background: rgba(248, 81, 73, 0.1); } .success { color: #3fb950; border-color: #3fb950; background: rgba(46, 160, 67, 0.1); } .warning { color: #e3b341; border-color: #e3b341; background: rgba(227, 179, 65, 0.1); } .loading { text-align: center; padding: 40px; color: #7ee787; font-style: italic; font-size: 18px; } strong { color: #7ee787; } </style> </head> <body> <div class="header"> <div> <h1 style="margin: 0;">🛒 Моя корзина</h1> <a href="/lab9/task3/" class="back-link">← Вернуться к товарам</a> </div> <div id="cartStats"></div> </div> <div id="cartContainer"> <div class="loading">Загрузка корзины...</div> </div> <script> // (скрипт остается без изменений) const API_BASE = '/api/lab9/task3'; let cart = getCartFromStorage(); function formatPrice(cents) { return (cents / 100).toLocaleString('ru-RU', { style: 'currency', currency: 'RUB' }); } function getCartFromStorage() { try { return JSON.parse(localStorage.getItem('lab9_cart') || '[]'); } catch { return []; } } function saveCartToStorage() { localStorage.setItem('lab9_cart', JSON.stringify(cart)); renderCart(); } function updateQuantity(productId, delta) { const item = cart.find(item => item.productId === productId); if (item) { const newQuantity = item.quantity + delta; if (newQuantity >= 1 && newQuantity <= 5 && newQuantity <= item.maxQuantity) { item.quantity = newQuantity; saveCartToStorage(); } } } function removeFromCart(productId) { if (confirm('Удалить товар из корзины?')) { cart = cart.filter(item => item.productId !== productId); saveCartToStorage(); showMessage('🗑️ Товар удален из корзины', 'success'); } } function calculateTotal() { return cart.reduce((total, item) => total + (item.price * item.quantity), 0); } function calculateTotalItems() { return cart.reduce((total, item) => total + item.quantity, 0); } function renderCart() { const container = document.getElementById('cartContainer'); const stats = document.getElementById('cartStats'); if (cart.length === 0) { container.innerHTML = ` <div class="empty-cart"> <h3>😔 Корзина пуста</h3> <p>Добавьте товары из каталога, чтобы сделать покупку</p> <a href="/lab9/task3/" style="color: #7ee787; text-decoration: none; font-weight: bold; border: 1px solid #238636; padding: 10px 20px; border-radius: 6px; display: inline-block; margin-top: 20px;"> → Перейти к товарам </a> </div> `; stats.innerHTML = ''; return; } const totalPrice = calculateTotal(); const totalItems = calculateTotalItems(); stats.innerHTML = ` <div style="text-align: right;"> <div style="font-size: 24px; font-weight: bold; color: #3fb950;">${formatPrice(totalPrice)}</div> <div style="color: #8b949e; font-size: 14px;">${totalItems} товар(ов)</div> </div> `; container.innerHTML = ` <table class="cart-table"> <thead> <tr> <th>Товар</th> <th>Цена</th> <th>Количество</th> <th>Стоимость</th> <th>Действия</th> </tr> </thead> <tbody> ${cart.map(item => ` <tr> <td> <strong>${item.productTitle}</strong> ${item.quantity > item.maxQuantity ? ` <div style="color: #f85149; font-size: 13px; margin-top: 8px;"> ⚠️ Максимум: ${item.maxQuantity} шт. </div> ` : ''} </td> <td>${formatPrice(item.price)}</td> <td> <div class="quantity-controls"> <button class="quantity-btn" onclick="updateQuantity(${item.productId}, -1)" ${item.quantity <= 1 ? 'disabled' : ''} >-</button> <span class="quantity-display">${item.quantity}</span> <button class="quantity-btn" onclick="updateQuantity(${item.productId}, 1)" ${item.quantity >= 5 || item.quantity >= item.maxQuantity ? 'disabled' : ''} >+</button> </div> </td> <td><strong>${formatPrice(item.price * item.quantity)}</strong></td> <td> <button class="remove-btn" onclick="removeFromCart(${item.productId})"> 🗑️ Удалить </button> </td> </tr> `).join('')} </tbody> </table> <div class="summary"> <div class="summary-row"> <span>Количество товаров:</span> <span>${totalItems} шт.</span> </div> <div class="summary-row summary-total"> <span>Общая стоимость:</span> <span>${formatPrice(totalPrice)}</span> </div> </div> <div class="actions"> <button class="action-btn checkout-btn" onclick="checkAvailability()"> ✅ Проверить наличие </button> <button class="action-btn purchase-btn" onclick="makePurchase()"> 🎉 Купить </button> </div> `; } async function checkAvailability() { const btn = document.querySelector('.checkout-btn'); btn.disabled = true; btn.textContent = '⏳ Проверка...'; try { const response = await fetch(`${API_BASE}/purchase`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ cart }) }); if (response.ok) { showMessage('✅ Все товары в наличии! Можно оформлять покупку.', 'success'); } else { const errorData = await response.json(); if (errorData.unavailableItems) { let message = '❌ Недостаточно товара на складе:\n'; errorData.unavailableItems.forEach(item => { message += `• ${item.productTitle}: нужно ${item.requested}, доступно ${item.available}\n`; }); showMessage(message, 'error'); } else { showMessage('❌ Ошибка при проверке наличия', 'error'); } } } catch (error) { showMessage(`❌ Ошибка сети: ${error.message}`, 'error'); } finally { btn.disabled = false; btn.textContent = '✅ Проверить наличие'; } } async function makePurchase() { const btn = document.querySelector('.purchase-btn'); btn.disabled = true; btn.textContent = '⏳ Оформление...'; try { const response = await fetch(`${API_BASE}/purchase`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ cart }) }); if (response.ok) { const result = await response.json(); showMessage('🎉 Покупка успешно оформлена! Товары списаны со склада.', 'success'); cart = []; saveCartToStorage(); setTimeout(() => { window.location.href = '/lab9/task3/'; }, 2000); } else { const errorData = await response.json(); showMessage(`❌ Ошибка: ${errorData.error}`, 'error'); } } catch (error) { showMessage(`❌ Ошибка сети: ${error.message}`, 'error'); } finally { btn.disabled = false; btn.textContent = '🎉 Купить'; } } function showMessage(message, type) { const messageDiv = document.createElement('div'); messageDiv.className = type === 'success' ? 'success' : 'error'; messageDiv.style.whiteSpace = 'pre-line'; messageDiv.textContent = message; document.body.insertBefore(messageDiv, document.getElementById('cartContainer')); setTimeout(() => { messageDiv.remove(); }, 5000); } window.addEventListener('load', () => { renderCart(); }); </script> </body> </html>