my-project/templates/cart_new.html
2026-03-23 11:35:48 +05:00

434 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}Моя корзина{% endblock %}
{% block content %}
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="page-title">Моя корзина</h2>
<a href="{{ url_for('catalog') }}" class="btn btn-primary">
<i class="fas fa-arrow-left"></i> Продолжить покупки
</a>
</div>
{% if cart_items %}
<!-- Таблица товаров в корзине -->
<div class="table-responsive">
<table class="table table-hover" id="cartTable">
<thead>
<tr>
<th>Товар</th>
<th>Артикул</th>
<th>Категория</th>
<th>Материал</th>
<th>Цена</th>
<th style="text-align: center;">Количество</th>
<th>Сумма</th>
<th>Действия</th>
</tr>
</thead>
<tbody id="cartBody">
{% for item in cart_items %}
{% set item_total = item.price * item.quantity %}
<tr id="item-{{ item.id }}" data-price="{{ item.price }}" data-max="{{ item.in_stock }}">
<td>{{ item.product_name }}</td>
<td>{{ item.article }}</td>
<td>{{ item.category_name }}</td>
<td>{{ item.material_name or '-' }}</td>
<td class="item-price">{{ item.price|int }} руб.</td>
<td style="text-align: center;">
<div class="quantity-control">
<button type="button" class="quantity-btn quantity-minus" data-item-id="{{ item.id }}">−</button>
<input type="number" class="quantity-input"
value="{{ item.quantity }}" min="1" max="{{ item.in_stock }}"
data-item-id="{{ item.id }}"
id="quantity-{{ item.id }}"
onkeypress="return isNumberKey(event)">
<button type="button" class="quantity-btn quantity-plus" data-item-id="{{ item.id }}">+</button>
<span class="stock-error" id="error-{{ item.id }}" style="display: none;">
Макс. {{ item.in_stock }}
</span>
</div>
</td>
<td class="item-total fw-bold">{{ item_total|int }} руб.</td>
<td>
<button class="btn btn-sm btn-outline-danger remove-item"
data-item-id="{{ item.id }}"
data-item-name="{{ item.product_name }}">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Расчет стоимости -->
<div class="row mt-4">
<div class="col-md-6 offset-md-6">
<div class="card">
<div class="card-header" style="background: linear-gradient(135deg, var(--steel-blue) 0%, var(--light-steel-blue) 100%); color: white;">
<h5 class="mb-0"><i class="fas fa-calculator"></i> Итого по заказу</h5>
</div>
<div class="card-body">
{% set ns = namespace(subtotal=0) %}
{% for item in cart_items %}
{% set ns.subtotal = ns.subtotal + (item.price * item.quantity) %}
{% endfor %}
{% set assembly_percent = 15 %}
{% set delivery_percent = 10 %}
{% set assembly_amount = ns.subtotal * assembly_percent // 100 %}
{% set delivery_amount = ns.subtotal * delivery_percent // 100 %}
{% set total = ns.subtotal + assembly_amount + delivery_amount %}
<div class="row mb-2">
<div class="col-6">Сумма без услуг:</div>
<div class="col-6 text-end fw-bold" id="cart-subtotal">{{ ns.subtotal|int }} руб.</div>
</div>
<div class="row mb-2">
<div class="col-6">Сборка ({{ assembly_percent }}%):</div>
<div class="col-6 text-end text-info" id="assembly-amount">+{{ assembly_amount|int }} руб.</div>
</div>
<div class="row mb-2">
<div class="col-6">Доставка ({{ delivery_percent }}%):</div>
<div class="col-6 text-end text-info" id="delivery-amount">+{{ delivery_amount|int }} руб.</div>
</div>
<hr>
<div class="row">
<div class="col-6 fs-5 fw-bold">ИТОГО:</div>
<div class="col-6 text-end fs-5 fw-bold" style="color: var(--steel-blue);" id="cart-total">{{ total|int }} руб.</div>
</div>
</div>
</div>
</div>
</div>
<!-- Кнопка оформления заказа -->
<div class="text-end mt-4">
<form method="POST" action="{{ url_for('checkout') }}" id="checkoutForm">
<button type="submit" class="btn btn-success btn-lg"
onclick="return confirm('Оформить заказ?')">
<i class="fas fa-check"></i> Оформить заказ ({{ total|int }} руб.)
</button>
</form>
</div>
{% else %}
<!-- Пустая корзина -->
<div class="text-center py-5">
<i class="fas fa-shopping-cart fa-4x mb-3" style="color: var(--light-steel-blue);"></i>
<h4>Корзина пуста</h4>
<p class="text-muted mb-4">Добавьте товары в корзину, чтобы оформить заказ</p>
<a href="{{ url_for('catalog') }}" class="btn btn-primary btn-lg">
<i class="fas fa-arrow-left"></i> Перейти в каталог
</a>
</div>
{% endif %}
</div>
<!-- JavaScript -->
<script>
function isNumberKey(evt) {
var charCode = (evt.which) ? evt.which : evt.keyCode;
if (charCode == 8 || charCode == 46 || charCode == 9 || charCode == 13 ||
charCode == 37 || charCode == 38 || charCode == 39 || charCode == 40) {
return true;
}
if (charCode >= 48 && charCode <= 57) {
return true;
}
return false;
}
document.addEventListener('DOMContentLoaded', function() {
const assemblyPercent = 15;
const deliveryPercent = 10;
function updateAllTotals() {
let subtotal = 0;
document.querySelectorAll('.item-total').forEach(cell => {
const value = parseFloat(cell.textContent) || 0;
subtotal += value;
});
const assemblyAmount = Math.round(subtotal * assemblyPercent / 100);
const deliveryAmount = Math.round(subtotal * deliveryPercent / 100);
const total = subtotal + assemblyAmount + deliveryAmount;
const subtotalEl = document.getElementById('cart-subtotal');
const assemblyEl = document.getElementById('assembly-amount');
const deliveryEl = document.getElementById('delivery-amount');
const totalEl = document.getElementById('cart-total');
const checkoutBtn = document.querySelector('#checkoutForm button');
if (subtotalEl) subtotalEl.textContent = subtotal.toFixed(0) + ' руб.';
if (assemblyEl) assemblyEl.textContent = '+' + assemblyAmount.toFixed(0) + ' руб.';
if (deliveryEl) deliveryEl.textContent = '+' + deliveryAmount.toFixed(0) + ' руб.';
if (totalEl) totalEl.textContent = total.toFixed(0) + ' руб.';
if (checkoutBtn) {
checkoutBtn.innerHTML = `<i class="fas fa-check"></i> Оформить заказ (${total.toFixed(0)} руб.)`;
}
}
function updateQuantity(itemId, newQuantity) {
const input = document.getElementById(`quantity-${itemId}`);
const row = document.getElementById(`item-${itemId}`);
const price = parseFloat(row.dataset.price);
const maxStock = parseInt(row.dataset.max);
if (newQuantity < 1) newQuantity = 1;
if (newQuantity > maxStock) {
newQuantity = maxStock;
const error = document.getElementById(`error-${itemId}`);
if (error) {
error.style.display = 'block';
setTimeout(() => {
error.style.display = 'none';
}, 2000);
}
}
input.value = newQuantity;
const total = price * newQuantity;
const totalCell = row.querySelector('.item-total');
if (totalCell) totalCell.textContent = total.toFixed(0) + ' руб.';
fetch(`/update_cart/${itemId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `quantity=${newQuantity}`
})
.then(response => {
if (response.ok) {
updateAllTotals();
}
})
.catch(error => console.error('Error:', error));
}
// Обработчики для кнопок
document.querySelectorAll('.quantity-minus').forEach(btn => {
btn.addEventListener('click', function() {
const itemId = this.dataset.itemId;
const input = document.getElementById(`quantity-${itemId}`);
if (input) {
let currentValue = parseInt(input.value) || 1;
updateQuantity(itemId, currentValue - 1);
}
});
});
document.querySelectorAll('.quantity-plus').forEach(btn => {
btn.addEventListener('click', function() {
const itemId = this.dataset.itemId;
const input = document.getElementById(`quantity-${itemId}`);
if (input) {
let currentValue = parseInt(input.value) || 1;
updateQuantity(itemId, currentValue + 1);
}
});
});
document.querySelectorAll('.quantity-input').forEach(input => {
input.addEventListener('paste', function(e) {
e.preventDefault();
return false;
});
let timeoutId;
input.addEventListener('input', function() {
this.value = this.value.replace(/[^0-9]/g, '');
const itemId = this.dataset.itemId;
const row = document.getElementById(`item-${itemId}`);
if (!row) return;
const price = parseFloat(row.dataset.price);
let quantity = parseInt(this.value);
if (isNaN(quantity) || quantity < 1) {
quantity = 1;
this.value = 1;
}
const total = price * quantity;
const totalCell = row.querySelector('.item-total');
if (totalCell) totalCell.textContent = total.toFixed(0) + ' руб.';
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
updateQuantity(itemId, quantity);
}, 500);
});
});
document.querySelectorAll('.remove-item').forEach(btn => {
btn.addEventListener('click', function() {
const itemId = this.dataset.itemId;
const itemName = this.dataset.itemName;
if (confirm(`Удалить "${itemName}" из корзины?`)) {
fetch(`/remove_from_cart/${itemId}`, {
method: 'POST'
})
.then(() => {
location.reload();
});
}
});
});
updateAllTotals();
});
</script>
<style>
.page-title {
color: var(--steel-blue);
font-size: 2rem;
font-weight: 600;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
}
.table thead th {
background-color: var(--steel-blue);
color: white;
font-weight: 600;
border: none;
padding: 12px;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
}
.table tbody tr:hover {
background-color: rgba(176, 196, 222, 0.2);
}
.table tbody td {
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
color: #2c3e50;
vertical-align: middle;
}
.quantity-control {
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
position: relative;
}
.quantity-btn {
width: 30px;
height: 30px;
border: 1px solid var(--light-steel-blue);
background: linear-gradient(135deg, var(--alice-blue) 0%, #E6F2FF 100%);
color: var(--steel-blue);
font-size: 1.2rem;
font-weight: bold;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.quantity-btn:hover {
background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);
border-color: var(--steel-blue);
}
.quantity-input {
width: 60px;
height: 30px;
border: 2px solid var(--light-steel-blue);
border-radius: 6px;
text-align: center;
font-size: 1rem;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
padding: 0;
margin: 0;
-moz-appearance: textfield;
appearance: textfield;
}
.quantity-input:focus {
border-color: var(--steel-blue);
box-shadow: 0 0 0 0.2rem rgba(70, 130, 180, 0.25);
outline: none;
}
.stock-error {
color: #dc3545;
font-size: 0.75rem;
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
background-color: white;
padding: 2px 8px;
border-radius: 12px;
border: 1px solid #dc3545;
z-index: 10;
}
.btn-success {
background: linear-gradient(135deg, #28a745 0%, #218838 100%);
border: none;
padding: 12px 30px;
font-size: 1.1rem;
}
.btn-success:hover {
background: linear-gradient(135deg, #218838 0%, #28a745 100%);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(40, 167, 69, 0.3);
}
.btn-primary {
background: linear-gradient(135deg, var(--steel-blue) 0%, #3a6d99 100%);
border: none;
padding: 12px 30px;
font-size: 1.1rem;
}
.btn-primary:hover {
background: linear-gradient(135deg, #3a6d99 0%, var(--steel-blue) 100%);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(70, 130, 180, 0.3);
}
.card {
border: 1px solid var(--light-steel-blue);
border-radius: 12px;
box-shadow: 0 4px 8px rgba(70, 130, 180, 0.1);
}
@media (max-width: 768px) {
.table {
font-size: 0.9rem;
}
.quantity-control {
gap: 3px;
}
.quantity-btn {
width: 25px;
height: 25px;
font-size: 1rem;
}
.quantity-input {
width: 45px;
height: 25px;
font-size: 0.9rem;
}
}
</style>
{% endblock %}