shop
Raz 8 months ago
parent 8365e548be
commit cc0f9d64e0
  1. 9
      shop/cart.py
  2. 2
      shop/forms.py
  3. 230
      shop/static/shop/css/shop.css
  4. 37
      shop/templates/shop/cart.html
  5. 46
      shop/templates/shop/checkout.html
  6. 8
      shop/templates/shop/payment.html
  7. 21
      shop/templates/shop/payment_cancel.html
  8. 8
      shop/templates/shop/payment_success.html
  9. 2
      shop/urls.py
  10. 40
      shop/views.py
  11. 3
      tournaments/templates/registration/login.html

@ -59,3 +59,12 @@ def clear_cart(request):
"""Clear the cart""" """Clear the cart"""
cart_id = get_or_create_cart_id(request) cart_id = get_or_create_cart_id(request)
CartItem.objects.filter(session_id=cart_id).delete() CartItem.objects.filter(session_id=cart_id).delete()
# Add this function to your cart.py file
def get_cart_item(request, item_id):
"""Get a specific cart item by its ID"""
cart_id = get_or_create_cart_id(request)
try:
return CartItem.objects.get(id=item_id, session_id=cart_id)
except CartItem.DoesNotExist:
raise Exception("Cart item not found")

@ -2,4 +2,4 @@ from django import forms
class GuestCheckoutForm(forms.Form): class GuestCheckoutForm(forms.Form):
email = forms.EmailField(required=True) email = forms.EmailField(required=True)
phone = forms.CharField(max_length=20, required=True) phone = forms.CharField(max_length=20, required=True, label="Téléphone portable")

@ -1,3 +1,4 @@
/* Product Display */
.options-container { .options-container {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
@ -18,15 +19,16 @@
} }
.product-title { .product-title {
font-size: 16px; /* Increase the font size for a bigger title */ font-size: 16px;
font-weight: bold; /* Make the title bold */ font-weight: bold;
} }
.product-price { .product-price {
font-size: 16px; /* Increase the font size for a bigger title */ font-size: 16px;
font-weight: bold; /* Make the title bold */ font-weight: bold;
} }
/* Grid Layout */
.option-element { .option-element {
align-content: center; align-content: center;
} }
@ -47,10 +49,12 @@
grid-column: 1; grid-column: 1;
grid-row: 2; grid-row: 2;
} }
.option-element:nth-child(4) { .option-element:nth-child(4) {
grid-column: 2; grid-column: 2;
grid-row: 2; grid-row: 2;
} }
.option-element:nth-child(5) { .option-element:nth-child(5) {
grid-column: 3; grid-column: 3;
grid-row: 2; grid-row: 2;
@ -60,30 +64,38 @@
grid-column: 1 / span 2; grid-column: 1 / span 2;
grid-row: 3; grid-row: 3;
} }
.option-element:nth-child(7) { .option-element:nth-child(7) {
grid-column: 3; grid-column: 3;
grid-row: 3; grid-row: 3;
text-align: right; text-align: right;
} }
/* Buttons */
.add-to-cart-button,
.checkout-button {
background-color: #90ee90;
color: #707070;
border: none;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-decoration: none;
transition: background-color 0.3s ease;
cursor: pointer;
}
.add-to-cart-button { .add-to-cart-button {
margin-top: 10px; margin-top: 10px;
width: 100%; width: 100%;
background-color: #90ee90; /* Green background */
text-align: center; text-align: center;
display: inline-block; display: inline-block;
height: 40px; /* Set the height to 40 pixels */ height: 40px;
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 { .checkout-button {
background-color: #f39200; padding: 10px 20px;
color: white; display: inline-block;
} }
.confirm-nav-button { .confirm-nav-button {
@ -95,12 +107,51 @@
color: white; color: white;
} }
.remove-btn {
background-color: #e84039;
color: white;
border: none;
padding: 5px 10px;
border-radius: 12px;
cursor: pointer;
font-size: 12px;
font-weight: 600;
transition: background-color 0.3s ease;
}
.add-to-cart-button:hover,
.confirm-nav-button:hover, .confirm-nav-button:hover,
.cancel-nav-button:hover { .cancel-nav-button:hover,
background-color: orange; .remove-btn:hover {
background-color: #f39200;
color: white; color: white;
} }
/* Cart Table */
.cart-table {
width: 100%;
border-collapse: collapse;
}
.cart-table th,
.cart-table td {
padding: 10px;
text-align: center;
}
.text-left {
text-align: left !important;
}
.price-column {
text-align: right;
}
.cart-table thead,
.cart-table tfoot {
background-color: #f5f5f5;
}
.cart-table tbody tr.odd { .cart-table tbody tr.odd {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
@ -109,11 +160,6 @@
background-color: #e8e8e8; background-color: #e8e8e8;
} }
.cart-table th.price-column,
.cart-table td.price-column {
text-align: right;
}
.cart-table tfoot .total-label { .cart-table tfoot .total-label {
font-weight: bold; font-weight: bold;
} }
@ -122,3 +168,143 @@
font-weight: bold; font-weight: bold;
font-size: 1.2em; font-size: 1.2em;
} }
/* Quantity Controls */
.quantity-controls {
display: flex;
align-items: center;
justify-content: center;
}
.quantity-form {
display: flex;
align-items: center;
}
.quantity-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid #ddd;
background-color: #f8f8f8;
cursor: pointer;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
margin: 0 3px;
font-size: 16px;
transition: background-color 0.3s ease;
}
.quantity-btn:hover:not([disabled]) {
background-color: #f39200;
color: white;
border-color: #f39200;
}
.quantity-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.quantity-value {
margin: 0 5px;
min-width: 20px;
text-align: center;
font-weight: bold;
font-size: 14px;
}
/* Cart Summary & Checkout */
.cart-summary {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.guest-checkout-notice {
margin-top: 10px;
font-size: 0.9em;
background-color: #f8f9fa;
padding: 10px;
border-radius: 10px;
width: 100%;
}
/* Checkout Page */
.checkout-container {
padding: 25px;
}
.checkout-section {
margin-bottom: 25px;
}
.checkout-title {
color: #333;
font-size: 1.4em;
margin-bottom: 15px;
text-align: center;
}
.checkout-description {
text-align: center;
margin-bottom: 20px;
color: #666;
}
.guest-checkout-form {
background-color: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.form-fields p {
margin-bottom: 15px;
}
.form-fields label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-fields input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 1em;
}
.button-container {
display: flex;
justify-content: center;
margin-top: 20px;
}
.checkout-options {
text-align: center;
}
.checkout-options p {
margin: 15px 0;
}
/* Links */
.styled-link {
color: #f39200;
text-decoration: none;
font-weight: bold;
transition: color 0.3s;
}
.styled-link:hover {
color: #e84039;
text-decoration: underline;
}

@ -25,47 +25,66 @@
<table class="cart-table"> <table class="cart-table">
<thead> <thead>
<tr> <tr>
<th>Produit</th> <th class="text-left">Produit</th>
<th>Couleur</th> <th>Couleur</th>
<th>Taille</th> <th>Taille</th>
<th>Quantité</th> <th>Quantité</th>
<th class="price-column">Prix</th> <th class="price-column">Prix</th>
<th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in cart_items %} {% for item in cart_items %}
<tr class="{% cycle 'odd' 'even' %}"> <tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td> <td class="text-left">{{ item.product.title }}</td>
<td>{{ item.color.name }}</td> <td>{{ item.color.name }}</td>
<td>{{ item.size.name }}</td> <td>{{ item.size.name }}</td>
<td>{{ item.quantity }}</td> <td>
<div class="quantity-controls">
<form method="post" action="{% url 'shop:update_cart_item' %}" class="quantity-form">
{% csrf_token %}
<input type="hidden" name="item_id" value="{{ item.id }}">
<button type="submit" name="action" value="decrease" class="quantity-btn" {% if item.quantity <= 1 %}disabled{% endif %}>-</button>
<span class="quantity-value">{{ item.quantity }}</span>
<button type="submit" name="action" value="increase" class="quantity-btn">+</button>
</form>
</div>
</td>
<td class="price-column">{{ item.get_total_price }} €</td> <td class="price-column">{{ item.get_total_price }} €</td>
<td>
<form method="post" action="{% url 'shop:remove_from_cart' %}" class="remove-form">
{% csrf_token %}
<input type="hidden" name="item_id" value="{{ item.id }}">
<button type="submit" class="remove-btn">Supprimer</button>
</form>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="total-label">Total:</td> <td colspan="3" class="total-label text-left">Total:</td>
<td class="total-quantity">{{ cart_items.total_quantity }}</td> <td class="total-quantity">{{ total_quantity }}</td>
<td class="price-column total-price">{{ total }} €</td> <td class="price-column total-price">{{ total }} €</td>
<td></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div class="cart-summary"> <div class="cart-summary">
{% if cart_items %} {% if cart_items %}
<a class="cancel-nav-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a> <a class="cancel-nav-button checkout-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a>
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
<!-- For authenticated users: Direct Stripe payment button --> <!-- For authenticated users: Direct Stripe payment button -->
<button id="checkout-button" class="confirm-nav-button">Procéder au paiement</button> <button id="checkout-button" class="confirm-nav-button checkout-button">Procéder au paiement</button>
{% else %} {% else %}
<!-- For guest users: Regular checkout path --> <!-- For guest users: Regular checkout path -->
<a class="confirm-nav-button" href="{% url 'shop:checkout' %}">Passer la commande</a> <a class="confirm-nav-button checkout-button" href="{% url 'shop:checkout' %}">Passer la commande</a>
<div class="guest-checkout-notice"> <div class="guest-checkout-notice">
<p>Connectez-vous pour un paiement plus rapide.</p> <p>Connectez-vous pour un paiement plus rapide.</p>
<a href="{% url 'login' %}?next={% url 'shop:view_cart' %}">Se connecter</a> <a class="styled-link" href="{% url 'login' %}?next={% url 'shop:view_cart' %}">Se connecter</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

@ -17,29 +17,37 @@
<a href="{% url 'login' %}">Se connecter</a> <a href="{% url 'login' %}">Se connecter</a>
{% endif %} {% endif %}
</nav> </nav>
<h1 class="club my-block topmargin20">Votre panier</h1> <h1 class="club my-block topmargin20">Validation de la commande</h1>
<div class="grid-x"> <div class="grid-x">
<div class="small-12 medium-6 large-6 my-block"> <div class="small-12 medium-6 large-6 my-block">
<div class="bubble"> <div class="bubble checkout-container">
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<p>Vous êtes déjà connecté en tant que {{ request.user.email }}.</p> <div class="checkout-section">
<a href="{% url 'shop:checkout' %}">Passer à la commande</a> <p>Vous êtes déjà connecté en tant que <strong>{{ request.user.email }}</strong>.</p>
<a href="{% url 'shop:checkout' %}" class="checkout-button confirm-nav-button">Passer à la commande</a>
</div>
{% else %} {% else %}
<p>Vous n'êtes pas connecté. Veuillez choisir une option :</p> <div class="checkout-section">
<h3 class="checkout-title">Finaliser votre commande</h3>
<form method="post"> <p class="checkout-description">Vous n'êtes pas connecté. Veuillez choisir une option :</p>
{% csrf_token %} </div>
{{ form.as_p }}
<button type="submit" name="guest_checkout">Continuer sans créer de compte</button> <div class="checkout-section">
</form> <form method="post" class="guest-checkout-form">
{% csrf_token %}
<hr> <div class="form-fields">
{{ form.as_p }}
<p>Ou <a href="{% url 'login' %}?next={% url 'shop:checkout' %}">connectez-vous</a> si vous avez déjà un compte.</p> </div>
<div class="button-container">
<hr> <button class="checkout-button confirm-nav-button" type="submit" name="guest_checkout">Continuer sans créer de compte</button>
</div>
<p>Pas encore de compte ? <a href="{% url 'signup' %}?next={% url 'shop:checkout' %}">Créez-en un maintenant</a></p> </form>
</div>
<div class="checkout-section checkout-options">
<p>Ou <a class="styled-link" href="{% url 'login' %}?next={% url 'shop:checkout' %}">connectez-vous</a> si vous avez déjà un compte.</p>
<p>Pas encore de compte ? <a class="styled-link" href="{% url 'signup' %}?next={% url 'shop:checkout' %}">Créez-en un maintenant</a></p>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>

@ -27,7 +27,7 @@
<table class="cart-table"> <table class="cart-table">
<thead> <thead>
<tr> <tr>
<th>Produit</th> <th class="text-left">Produit</th>
<th>Couleur</th> <th>Couleur</th>
<th>Taille</th> <th>Taille</th>
<th>Quantité</th> <th>Quantité</th>
@ -37,7 +37,7 @@
<tbody> <tbody>
{% for item in order_items %} {% for item in order_items %}
<tr class="{% cycle 'odd' 'even' %}"> <tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td> <td class="text-left">{{ item.product.title }}</td>
<td>{{ item.color.name|default:"N/A" }}</td> <td>{{ item.color.name|default:"N/A" }}</td>
<td>{{ item.size.name|default:"N/A" }}</td> <td>{{ item.size.name|default:"N/A" }}</td>
<td>{{ item.quantity }}</td> <td>{{ item.quantity }}</td>
@ -47,7 +47,7 @@
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="total-label">Total:</td> <td colspan="3" class="total-label text-left">Total:</td>
<td class="total-quantity">{{ order_items.count }}</td> <td class="total-quantity">{{ order_items.count }}</td>
<td class="price-column total-price">{{ order.total_price }} €</td> <td class="price-column total-price">{{ order.total_price }} €</td>
</tr> </tr>
@ -56,7 +56,7 @@
<!-- Stripe checkout button --> <!-- Stripe checkout button -->
<div id="payment-container"> <div id="payment-container">
<button id="checkout-button" class="confirm-nav-button">Procéder au paiement</button> <button id="checkout-button" class="confirm-nav-button checkout-button">Procéder au paiement</button>
</div> </div>
</div> </div>
</div> </div>

@ -1,18 +1,33 @@
<!-- padelclub_backend/shop/templates/shop/payment_cancel.html --> <!-- padelclub_backend/shop/templates/shop/payment_cancel.html -->
{% extends 'tournaments/base.html' %} {% extends 'tournaments/base.html' %}
{% block head_title %}Paiement annulé{% endblock %} {% block head_title %}La boutique{% endblock %}
{% block first_title %}La boutique Padel Club{% endblock %}
{% block second_title %}Paiement annulé{% endblock %}
{% block content %} {% block content %}
<nav class="margin10">
<a href="{% url 'shop:product_list' %}">La Boutique</a>
<a href="{% url 'index' %}" class="orange">Accueil</a>
<a href="{% url 'clubs' %}" class="orange">Clubs</a>
{% if user.is_authenticated %}
<a href="{% url 'my-tournaments' %}" class="orange">Mes tournois</a>
<a href="{% url 'profile' %}">Mon compte</a>
{% else %}
<a href="{% url 'login' %}">Se connecter</a>
{% endif %}
</nav>
<div class="grid-x"> <div class="grid-x">
<div class="cell medium-6 large-6 my-block"> <div class="cell medium-6 large-6 my-block">
<h1 class="club my-block topmargin20">Paiement annulé</h1> <h1 class="club my-block topmargin20">Paiement</h1>
<div class="bubble"> <div class="bubble">
<h2>Le paiement a été annulé</h2> <h2>Le paiement a été annulé</h2>
<p>Votre commande n'a pas été finalisée car le paiement a été annulé.</p> <p>Votre commande n'a pas été finalisée car le paiement a été annulé.</p>
<div class="cart-summary"> <div class="cart-summary">
<a class="confirm-nav-button" href="{% url 'shop:view_cart' %}">Retour au panier</a> <a class="confirm-nav-button checkout-button" href="{% url 'shop:view_cart' %}">Retour au panier</a>
</div> </div>
</div> </div>
</div> </div>

@ -30,7 +30,7 @@
<table class="cart-table"> <table class="cart-table">
<thead> <thead>
<tr> <tr>
<th>Produit</th> <th class="text-left">Produit</th>
<th>Couleur</th> <th>Couleur</th>
<th>Taille</th> <th>Taille</th>
<th>Quantité</th> <th>Quantité</th>
@ -40,7 +40,7 @@
<tbody> <tbody>
{% for item in order_items %} {% for item in order_items %}
<tr class="{% cycle 'odd' 'even' %}"> <tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td> <td class="text-left">{{ item.product.title }}</td>
<td>{{ item.color.name|default:"N/A" }}</td> <td>{{ item.color.name|default:"N/A" }}</td>
<td>{{ item.size.name|default:"N/A" }}</td> <td>{{ item.size.name|default:"N/A" }}</td>
<td>{{ item.quantity }}</td> <td>{{ item.quantity }}</td>
@ -50,7 +50,7 @@
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="total-label">Total:</td> <td colspan="3" class="total-label text-left">Total:</td>
<td class="total-quantity">{{ order_items.count }}</td> <td class="total-quantity">{{ order_items.count }}</td>
<td class="price-column total-price">{{ total }} €</td> <td class="price-column total-price">{{ total }} €</td>
</tr> </tr>
@ -58,7 +58,7 @@
</table> </table>
<div class="cart-summary"> <div class="cart-summary">
<a class="confirm-nav-button" href="{% url 'shop:product_list' %}">Retour à la boutique</a> <a class="confirm-nav-button checkout-button" href="{% url 'shop:product_list' %}">Retour à la boutique</a>
</div> </div>
</div> </div>
</div> </div>

@ -18,5 +18,7 @@ urlpatterns = [
path('payment/cancel/<int:order_id>/', views.payment_cancel, name='payment_cancel'), path('payment/cancel/<int:order_id>/', views.payment_cancel, name='payment_cancel'),
# path('webhook/stripe/', views.stripe_webhook, name='stripe_webhook'), # path('webhook/stripe/', views.stripe_webhook, name='stripe_webhook'),
path('create-checkout-session/', views.create_checkout_session, name='create_checkout_session'), path('create-checkout-session/', views.create_checkout_session, name='create_checkout_session'),
path('cart/update-item/', views.update_cart_item, name='update_cart_item'),
path('cart/remove-item/', views.remove_from_cart, name='remove_from_cart'),
] ]

@ -344,3 +344,43 @@ def create_checkout_session(request):
return JsonResponse({'id': checkout_session.id}) return JsonResponse({'id': checkout_session.id})
except Exception as e: except Exception as e:
return JsonResponse({'error': str(e)}, status=400) return JsonResponse({'error': str(e)}, status=400)
@require_POST
def update_cart_item(request):
"""Update a cart item quantity (increase/decrease)"""
item_id = request.POST.get('item_id')
action = request.POST.get('action')
try:
cart_item = cart.get_cart_item(request, item_id)
if action == 'increase':
# Increase quantity by 1
cart_item.quantity += 1
cart_item.save()
messages.success(request, "Quantity increased.")
elif action == 'decrease' and cart_item.quantity > 1:
# Decrease quantity by 1
cart_item.quantity -= 1
cart_item.save()
messages.success(request, "Quantity decreased.")
except Exception as e:
messages.error(request, f"Error updating cart: {str(e)}")
return redirect('shop:view_cart')
@require_POST
def remove_from_cart(request):
"""Remove an item from cart by item_id"""
item_id = request.POST.get('item_id')
try:
cart_item = cart.get_cart_item(request, item_id)
cart_item.delete()
messages.success(request, "Item removed from cart.")
except Exception as e:
messages.error(request, f"Error removing item: {str(e)}")
return redirect('shop:view_cart')

@ -43,9 +43,6 @@
</form> </form>
<p>Pas encore de compte ? <a href="{% url 'signup' %}" class="styled-link">Créer le tout de suite !</a></p> <p>Pas encore de compte ? <a href="{% url 'signup' %}" class="styled-link">Créer le tout de suite !</a></p>
</div> </div>
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
</div> </div>
</div> </div>

Loading…
Cancel
Save