feat: implement shopping cart feature with user authentication support
All checks were successful
Build and Push Docker Image / build (push) Successful in 42s

- Added `Cart` and `CartItem` models with migrations for managing user-specific carts and items
- Developed services for cart-related operations: adding, removing, and updating cart items
- Built specialized templates (`cart.html`) and styled them with new CSS assets (`cart.css`)
- Enabled real-time UI updates via AJAX in shopping cart interactions (`cart.js`)
- Modified header to dynamically display cart link with user authentication awareness
- Added Leaflet-based interactive map to the contacts page and integrated CSS/JS dependencies
- Expanded tests for cart functionality, item detail page cart integration, and contacts page maps
This commit is contained in:
2026-05-15 23:18:16 +02:00
parent d2126e0fba
commit 52cbc1c2b3
22 changed files with 1317 additions and 18 deletions

113
templates/orders/cart.html Normal file
View File

@@ -0,0 +1,113 @@
{% extends "_layouts/base.html" %}
{% load static %}
{% block title %}Корзина{% endblock %}
{% block add_css %}
<link rel="stylesheet" href="{% static "css/cart.css" %}">
{% endblock %}
{% block content %}
{% include "_fragments/header.html" %}
{% include '_fragments/breadcrumbs.html' with url_1='/' text_1='Главная' url_2='/orders/cart/' text_2='Корзина' %}
<main class="container cart-page">
<section class="cart-hero">
<p class="cart-kicker">Покупки</p>
<div class="cart-hero__row">
<div>
<h1 class="cart-title">Корзина</h1>
<p class="cart-description">
Проверьте выбранные позиции, измените количество или удалите лишнее перед оформлением.
</p>
</div>
<div class="cart-summary-card">
<span class="cart-summary-card__value" data-cart-total-quantity>{{ total_quantity }}</span>
<span class="cart-summary-card__label">товаров в корзине</span>
</div>
</div>
</section>
<section class="cart-content" aria-label="Содержимое корзины">
{% if cart_items %}
<div class="cart-items">
{% for cart_item in cart_items %}
{% with item=cart_item.item %}
<article class="cart-item" data-auth-cart-item data-current-quantity="{{ cart_item.quantity }}">
<a class="cart-item__media" href="{% url 'detail' item.category.slug item.slug %}">
{% with primary_image=item.primary_image %}
{% if primary_image %}
<img src="{{ primary_image.image.url }}" alt="{{ primary_image.alt|default:item.name }}">
{% else %}
<div class="cart-item__placeholder">Нет фото</div>
{% endif %}
{% endwith %}
</a>
<div class="cart-item__body">
<div class="cart-item__main">
<p class="cart-item__category">
{{ item.category.name }}{% if item.subcategory %} / {{ item.subcategory.name }}{% endif %}
</p>
<a class="cart-item__title" href="{% url 'detail' item.category.slug item.slug %}">{{ item.name }}</a>
<p class="cart-item__price">{{ item.price }} руб.</p>
</div>
<div class="cart-item__controls">
<div class="cart-quantity">
<button
class="cart-quantity__button"
type="button"
data-cart-decrement
data-remove-form-id="cart-remove-{{ cart_item.id }}"
data-update-form-id="cart-decrement-{{ cart_item.id }}"
data-current-quantity="{{ cart_item.quantity }}"
>
-
</button>
<span class="cart-quantity__value">{{ cart_item.quantity }}</span>
<form id="cart-increment-{{ cart_item.id }}" method="post" action="{% url 'orders:update_quantity' %}">
{% csrf_token %}
<input type="hidden" name="item_id" value="{{ item.id }}">
<input type="hidden" name="quantity" value="{{ cart_item.quantity|add:1 }}">
<input type="hidden" name="next" value="{% url 'orders:cart' %}">
<button class="cart-quantity__button" type="submit">+</button>
</form>
<form id="cart-decrement-{{ cart_item.id }}" method="post" action="{% url 'orders:update_quantity' %}">
{% csrf_token %}
<input type="hidden" name="item_id" value="{{ item.id }}">
<input type="hidden" name="quantity" value="{{ cart_item.quantity|add:-1 }}">
<input type="hidden" name="next" value="{% url 'orders:cart' %}">
</form>
</div>
<div class="cart-item__aside">
<span class="cart-item__subtotal">{{ cart_item.subtotal }} руб.</span>
<form id="cart-remove-{{ cart_item.id }}" method="post" action="{% url 'orders:remove' %}">
{% csrf_token %}
<input type="hidden" name="item_id" value="{{ item.id }}">
<input type="hidden" name="next" value="{% url 'orders:cart' %}">
<button class="cart-item__remove" type="submit">Удалить</button>
</form>
</div>
</div>
</div>
</article>
{% endwith %}
{% endfor %}
</div>
<aside class="cart-totals">
<p class="cart-totals__label">Итого</p>
<p class="cart-totals__value" data-cart-total-amount>{{ total_amount }}</p>
<p class="cart-totals__meta">Позиции можно изменить прямо на этой странице.</p>
</aside>
{% else %}
<p class="cart-empty">Корзина пока пуста.</p>
{% endif %}
</section>
</main>
{% include "_fragments/footer.html" %}
{% endblock %}