clean up shop

shop
Raz 8 months ago
parent 7f31708d86
commit ca9c37f2c8
  1. BIN
      media/products/WhatsApp_Image_2025-03-02_at_12.02.15_cfPyazE.jpeg
  2. 2
      shop/admin.py
  3. 15
      shop/cart.py
  4. 24
      shop/migrations/0004_cartitem_color_cartitem_size.py
  5. 23
      shop/migrations/0005_alter_color_name_alter_size_name.py
  6. 22
      shop/migrations/0006_alter_product_options_product_order.py
  7. 18
      shop/migrations/0007_product_cut.py
  8. 17
      shop/models.py
  9. 133
      shop/static/shop/css/shop.css
  10. 49
      shop/templates/shop/cart.html
  11. 51
      shop/templates/shop/product_item.html
  12. 6
      shop/templates/shop/product_list.html
  13. 1
      shop/urls.py
  14. 19
      shop/views.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

@ -3,7 +3,7 @@ from .models import Product, Color, Size
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ("title", "price")
list_display = ("title", "order", "price", "cut")
@admin.register(Color)
class ColorAdmin(admin.ModelAdmin):

@ -1,4 +1,4 @@
from .models import CartItem, Product
from .models import CartItem, Product, Color, Size
def get_or_create_cart_id(request):
"""Get the cart ID from the session or create a new one"""
@ -11,7 +11,7 @@ def get_cart_items(request):
cart_id = get_or_create_cart_id(request)
return CartItem.objects.filter(session_id=cart_id)
def add_to_cart(request, product_id, quantity=1):
def add_to_cart(request, product_id, quantity=1, color_id=None, size_id=None):
"""Add a product to the cart or update its quantity"""
product = Product.objects.get(id=product_id)
cart_id = get_or_create_cart_id(request)
@ -23,10 +23,14 @@ def add_to_cart(request, product_id, quantity=1):
cart_item.save()
except CartItem.DoesNotExist:
# Create new cart item
color = Color.objects.get(id=color_id) if color_id else None
size = Size.objects.get(id=size_id) if size_id else None
cart_item = CartItem.objects.create(
product=product,
quantity=quantity,
session_id=cart_id
session_id=cart_id,
color=color,
size=size
)
return cart_item
@ -50,3 +54,8 @@ def update_cart_item(request, product_id, quantity):
def get_cart_total(request):
"""Calculate the total price of all items in the cart"""
return sum(item.product.price * item.quantity for item in get_cart_items(request))
def clear_cart(request):
"""Clear the cart"""
cart_id = get_or_create_cart_id(request)
CartItem.objects.filter(session_id=cart_id).delete()

@ -0,0 +1,24 @@
# Generated by Django 4.2.11 on 2025-03-18 08:00
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('shop', '0003_rename_productcolor_color_rename_productsize_size_and_more'),
]
operations = [
migrations.AddField(
model_name='cartitem',
name='color',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.color'),
),
migrations.AddField(
model_name='cartitem',
name='size',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='shop.size'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 4.2.11 on 2025-03-18 08:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0004_cartitem_color_cartitem_size'),
]
operations = [
migrations.AlterField(
model_name='color',
name='name',
field=models.CharField(choices=[('Red', 'Red'), ('Blue', 'Blue'), ('Green', 'Green'), ('Black', 'Black'), ('White', 'White')], max_length=20, unique=True),
),
migrations.AlterField(
model_name='size',
name='name',
field=models.CharField(choices=[('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ('XL', 'X-Large'), ('SINGLE', 'Unique')], max_length=20, unique=True),
),
]

@ -0,0 +1,22 @@
# Generated by Django 4.2.11 on 2025-03-18 13:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0005_alter_color_name_alter_size_name'),
]
operations = [
migrations.AlterModelOptions(
name='product',
options={'ordering': ['order']},
),
migrations.AddField(
model_name='product',
name='order',
field=models.IntegerField(default=0),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2025-03-18 13:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0006_alter_product_options_product_order'),
]
operations = [
migrations.AddField(
model_name='product',
name='cut',
field=models.IntegerField(choices=[(1, 'Women'), (2, 'Men'), (3, 'Kids')], default=2),
),
]

@ -14,15 +14,21 @@ class SizeChoices(models.TextChoices):
MEDIUM = "M", "Medium"
LARGE = "L", "Large"
XLARGE = "XL", "X-Large"
SINGLE = "SINGLE", "Unique"
class CutChoices(models.IntegerChoices):
WOMEN = 1, 'Women'
MEN = 2, 'Men'
KIDS = 3, 'Kids'
class Color(models.Model):
name = models.CharField(max_length=10, choices=ColorChoices.choices, unique=True)
name = models.CharField(max_length=20, choices=ColorChoices.choices, unique=True)
def __str__(self):
return self.name
class Size(models.Model):
name = models.CharField(max_length=5, choices=SizeChoices.choices, unique=True)
name = models.CharField(max_length=20, choices=SizeChoices.choices, unique=True)
def __str__(self):
return self.name
@ -35,6 +41,11 @@ class Product(models.Model):
# Use string references to prevent circular imports
colors = models.ManyToManyField("shop.Color", blank=True, related_name="products")
sizes = models.ManyToManyField("shop.Size", blank=True, related_name="products")
order = models.IntegerField(default=0, blank=False)
cut = models.IntegerField(choices=CutChoices.choices, default=CutChoices.MEN)
class Meta:
ordering = ['order', 'cut'] # Add this line to sort by title
def __str__(self):
return self.title
@ -43,6 +54,8 @@ class CartItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
color = models.ForeignKey(Color, on_delete=models.SET_NULL, null=True, blank=True)
size = models.ForeignKey(Size, on_delete=models.SET_NULL, null=True, blank=True)
session_id = models.CharField(max_length=255, null=True, blank=True)
date_added = models.DateTimeField(auto_now_add=True)

@ -1,37 +1,124 @@
.options-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
column-gap: 8px;
align-content: center;
}
.no-image,
.product-image {
height: 240px;
width: 100%;
height: 180px;
object-fit: contain;
display: block;
margin: 0 auto;
object-fit: contain; /* This will maintain the aspect ratio of the image */
background-color: black;
}
.bubble {
width: 100%;
height: 365px;
display: flex;
flex-direction: column;
.no-image {
background-color: white;
}
.bubble h3 {
margin-top: 10px;
font-size: 1.2em;
flex-grow: 1;
.product-title {
font-size: 16px; /* Increase the font size for a bigger title */
font-weight: bold; /* Make the title bold */
}
.product-price {
font-weight: bold;
color: #f39200;
margin: 10px 0;
font-size: 16px; /* Increase the font size for a bigger title */
font-weight: bold; /* Make the title bold */
}
.option-element {
align-content: center;
}
.option-element:nth-child(1) {
grid-column: 1 / span 2;
grid-row: 1;
height: 64px;
}
.option-element:nth-child(2) {
grid-column: 3;
grid-row: 1;
text-align: right;
}
.option-element:nth-child(3) {
grid-column: 1;
grid-row: 2;
}
.option-element:nth-child(4) {
grid-column: 2;
grid-row: 2;
}
.option-element:nth-child(5) {
grid-column: 3;
grid-row: 2;
}
.option-element:nth-child(6) {
grid-column: 1 / span 2;
grid-row: 3;
}
.option-element:nth-child(7) {
grid-column: 3;
grid-row: 3;
text-align: right;
}
.add-to-cart-button {
margin-top: 10px;
width: 100%;
background-color: #90ee90; /* Green background */
text-align: center;
display: inline-block;
height: 40px; /* Set the height to 40 pixels */
transition: background-color 0.3s ease; /* Smooth transition on hover */
color: #707070;
border-radius: 12px;
text-decoration: none;
font-size: 12px;
font-weight: 600;
}
.add-to-cart-button:hover {
background-color: #f39200;
color: white;
}
.confirm-nav-button {
background-color: #90ee90;
}
.cancel-nav-button {
background-color: #e84039;
color: white;
}
.confirm-nav-button:hover,
.cancel-nav-button:hover {
background-color: orange;
color: white;
}
.cart-table tbody tr.odd {
background-color: #f0f0f0;
}
.cart-table tbody tr.even {
background-color: #e8e8e8;
}
.add-to-cart-form {
display: flex;
gap: 10px;
margin-top: auto;
.cart-table th.price-column,
.cart-table td.price-column {
text-align: right;
}
.quantity-input {
width: 50px;
padding: 5px;
.cart-table tfoot .total-label {
font-weight: bold;
}
.cart-table tfoot .total-price {
font-weight: bold;
font-size: 1.2em;
}

@ -19,26 +19,43 @@
</nav>
<div class="grid-x">
<div class="cell medium-6 large-6 my-block">
<h1 class="club my-block topmargin20">Votre panier</h1 >
<h1 class="club my-block topmargin20">Votre panier</h1>
<div class="bubble">
{% if cart_items %}
<ul class="cart-items-list">
{% for item in cart_items %}
<li class="cart-item">
<div class="cart-item-details">
<span class="cart-item-title">{{ item.product.title }}</span>
<span class="cart-item-quantity">x{{ item.quantity }}</span>
</div>
<span class="cart-item-price">{{ item.get_total_price }} €</span>
</li>
{% endfor %}
</ul>
<table class="cart-table">
<thead>
<tr>
<th>Produit</th>
<th>Couleur</th>
<th>Taille</th>
<th>Quantité</th>
<th class="price-column">Prix</th>
</tr>
</thead>
<tbody>
{% for item in cart_items %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td>
<td>{{ item.color.name }}</td>
<td>{{ item.size.name }}</td>
<td>{{ item.quantity }}</td>
<td class="price-column">{{ item.get_total_price }} €</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="total-label">Total:</td>
<td class="total-quantity">{{ cart_items.total_quantity }}</td>
<td class="price-column total-price">{{ total }} €</td>
</tr>
</tfoot>
</table>
<div class="cart-summary">
<div class="cart-total">
<strong>Total:</strong> {{ total }} €
</div>
<a href="{% url 'shop:view_cart' %}" class="button">Voir le panier</a>
{% if cart_items %}
<a class="cancel-nav-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a>
{% endif %}
</div>
{% else %}
<p>Votre panier est vide.</p>

@ -1,17 +1,56 @@
<div class="cell small-12 medium-4 large-3 my-block">
<div class="small-12 medium-4 large-3 my-block">
<div class="bubble">
{% if product.image %}
<img src="{{ product.image.url }}" alt="{{ product.title }}" class="product-image">
{% else %}
<div class="no-image">No Image Available</div>
{% endif %}
<h3>{{ product.title }}</h3>
<div class="product-price">{{ product.price }} €</div>
<form method="post" action="{% url 'shop:add_to_cart' product.id %}" class="add-to-cart-form">
{% csrf_token %}
<input type="number" name="quantity" value="1" min="1" max="10" class="quantity-input">
<button type="submit" class="btn styled-link">Ajouter au panier</button>
<div class="options-container">
<div class="option-element product-title">{{ product.title }}</div>
<div class="option-element product-price">{{ product.price }} €</div>
<div class="option-element form-group">
{% if product.colors.exists %}
<select name="color" id="color-{{ product.id }}" class="form-control" required>
<option value="" disabled selected>Couleur</option>
{% for color in product.colors.all %}
<option value="{{ color.id }}" {% if forloop.first %}selected{% endif %}>{{ color.name }}</option>
{% endfor %}
</select>
{% endif %}
</div>
<div class="option-element form-group">
{% if product.sizes.exists %}
<select name="size" id="size-{{ product.id }}" class="form-control" required>
<option value="" disabled selected>Taille</option>
{% for size in product.sizes.all %}
<option value="{{ size.id }}" {% if forloop.first %}selected{% endif %}>{{ size.name }}</option>
{% endfor %}
</select>
{% endif %}
</div>
<div class="option-element form-group quantity-group">
<input type="number" id="quantity-{{ product.id }}" name="quantity" value="1" min="1" max="10" class="quantity-input" oninput="updateTotal({{ product.id }}, {{ product.price }})">
</div>
<div class="option-element form-group">Total</div>
<div class="option-element form-group"><span id="total-price-{{ product.id }}">{{ product.price }}</span></div>
</div>
<button type="submit" class="add-to-cart-button">Ajouter au panier</button>
</form>
</div>
</div>
<script>
function updateTotal(productId, price) {
let quantityInput = document.getElementById(`quantity-${productId}`);
let totalPriceElement = document.getElementById(`total-price-${productId}`);
let quantity = parseInt(quantityInput.value) || 1; // Default to 1 if invalid
let totalPrice = (quantity * price).toFixed(2); // Ensure two decimal places
totalPriceElement.textContent = totalPrice;
}
</script>

@ -19,7 +19,11 @@
</nav>
<nav class="margin10">
<a style="background-color: #90ee90" href="{% url 'shop:view_cart' %}">Voir mon panier ({{ total }} €)</a>
<a class="confirm-nav-button" href="{% url 'shop:view_cart' %}">Voir mon panier ({{ total }} €)</a>
{% if cart_items %}
<a class="cancel-nav-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a>
{% endif %}
</nav>
{% if products %}

@ -11,5 +11,6 @@ urlpatterns = [
path('cart/add/<int:product_id>/', views.add_to_cart_view, name='add_to_cart'),
path('cart/update/<int:product_id>/', views.update_cart_view, name='update_cart'),
path('cart/remove/<int:product_id>/', views.remove_from_cart_view, name='remove_from_cart'),
path('clear-cart/', views.clear_cart, name='clear_cart'),
]

@ -1,6 +1,9 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .models import Product, CartItem
from django.db.models import Sum
from .cart import add_to_cart
from . import cart
# Create your views here.
@ -18,18 +21,22 @@ def view_cart(request):
"""Display the shopping cart"""
cart_items = cart.get_cart_items(request)
total = cart.get_cart_total(request)
total_quantity = cart_items.aggregate(total_quantity=Sum('quantity'))['total_quantity']
return render(request, 'shop/cart.html', {
'cart_items': cart_items,
'total': total
'total': total,
'total_quantity': total_quantity
})
def add_to_cart_view(request, product_id):
"""Add a product to the cart"""
product = get_object_or_404(Product, id=product_id)
quantity = int(request.POST.get('quantity', 1))
color_id = request.POST.get('color')
size_id = request.POST.get('size')
cart.add_to_cart(request, product_id, quantity)
messages.success(request, f'{product.title} added to your cart')
cart_item = add_to_cart(request, product_id, quantity, color_id, size_id)
messages.success(request, f'{cart_item.quantity} x {product.title} added to your cart')
return redirect('shop:product_list')
@ -44,3 +51,9 @@ def remove_from_cart_view(request, product_id):
"""Remove item from cart"""
cart.remove_from_cart(request, product_id)
return redirect('shop:view_cart')
def clear_cart(request):
"""Clear the cart"""
cart.clear_cart(request)
messages.success(request, "Your cart has been cleared.")
return redirect('shop:product_list')

Loading…
Cancel
Save