Skip to content

Commit

Permalink
Merge pull request #17 from ErhanCitil/improve/add-to-cart
Browse files Browse the repository at this point in the history
[#13] Improve add to cart functionality
  • Loading branch information
ErhanCitil committed Oct 9, 2023
2 parents 556edba + 657a867 commit ea640df
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 52 deletions.
4 changes: 3 additions & 1 deletion src/bobvance/base/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from django.urls import path, include
from bobvance.base.views import Home, ProductsView, ProductDetailView, CartView, AddToCartView
from bobvance.base.views import Home, ProductsView, ProductDetailView, CartView, AddToCartView, RemoveFromCartView, UpdateCartView

urlpatterns = [
path('', Home.as_view(), name='home'),
path('products/', ProductsView.as_view(), name='products'),
path('product/<int:pk>/', ProductDetailView.as_view(), name='product_detail'),
path('cart/', CartView.as_view(), name='cart'),
path('add_to_cart/', AddToCartView.as_view(), name='add_to_cart'),
path('remove-from-cart/', RemoveFromCartView.as_view(), name='remove-from-cart'),
path('update-cart/', UpdateCartView.as_view(), name='update_cart'),
]
41 changes: 40 additions & 1 deletion src/bobvance/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from django.shortcuts import get_object_or_404
from django.http import JsonResponse

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import json

class Home(TemplateView):
template_name = 'base/index.html'

Expand Down Expand Up @@ -46,11 +50,46 @@ def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
cart = self.request.session.get('cart', {})
products_in_cart = Product.objects.filter(id__in=cart.keys())
total_price = sum([product.price * cart[str(product.id)] for product in products_in_cart])
total_price = sum([product.price * int(cart[str(product.id)]) for product in products_in_cart])

context['cart_items'] = [
{'product': product, 'quantity': cart[str(product.id)]}
for product in products_in_cart
]
context['total_price'] = total_price
return context

@method_decorator(csrf_exempt, name='dispatch')
class RemoveFromCartView(View):
def post(self, request, *args, **kwargs):
data = json.loads(request.body)
product_id = str(data.get('product_id'))
cart = request.session.get('cart', {})

if product_id in cart:
del cart[product_id]
request.session['cart'] = cart
return JsonResponse({'status': 'success'})
else:
return JsonResponse({'status': 'error'}, status=400)

class UpdateCartView(View):
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)

def post(self, request, *args, **kwargs):
data = json.loads(request.body)
product_id = data.get('product_id')
quantity = data.get('quantity')
product = get_object_or_404(Product, id=product_id)

cart = request.session.get('cart', {})
cart[product_id] = quantity

request.session['cart'] = cart

products_in_cart = Product.objects.filter(id__in=cart.keys())
total_price = sum([product.price * int(cart[str(product.id)]) for product in products_in_cart])

return JsonResponse({'status': 'success', 'total_price': str(total_price)})
116 changes: 102 additions & 14 deletions src/bobvance/templates/base/cart.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
{% extends 'master.html' %}
{% block extra_css %}
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}

/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
</style>
{% endblock %}

{% block content%}
{% include 'components/header.html'%}

<body>
<div class="h-screen bg-gray-100 pt-20">
<h1 class="mb-10 text-center text-2xl font-bold">Winkelwagen </h1>
<div class="h-screen pt-20">
<h1 class="mb-10 text-center text-2xl font-bold">Winkelwagen</h1>
{% if cart_items %}
<div class="mx-auto max-w-5xl justify-center px-6 md:flex md:space-x-6 xl:px-0">
<div class="rounded-lg md:w-2/3">
Expand All @@ -16,28 +31,28 @@ <h1 class="mb-10 text-center text-2xl font-bold">Winkelwagen </h1>
<h2 class="text-lg font-bold text-gray-900">{{ item.product.name }}</h2>
<p class="mt-1 text-xs text-gray-700">{{ item.product.description|truncatewords:5 }}</p>
</div>
<div class="mt-4 flex justify-between sm:space-y-6 sm:mt-0 sm:block sm:space-x-6">
<div class="mt-4 flex justify-between sm:space-y-6 sm:mt-0 sm:block sm:space-x-6 ml-10">
<div class="flex items-center border-gray-100">
<span class="cursor-pointer rounded-l bg-gray-100 py-1 px-3.5 duration-100 hover:bg-blue-500 hover:text-blue-50"> - </span>
<input class="h-8 w-8 border bg-white text-center text-xs outline-none" type="number" value="{{ item.quantity }}" min="1" />
<span class="cursor-pointer rounded-r bg-gray-100 py-1 px-3 duration-100 hover:bg-blue-500 hover:text-blue-50"> + </span>
<span class="cursor-pointer rounded-l bg-gray-100 py-1 px-3.5 duration-100 hover:bg-blue-600 hover:text-blue-50 quantity-button" data-action="decrease"> - </span>
<input class="h-8 w-8 border bg-white text-center text-xs outline-none quantity-input" type="number" value="{{ item.quantity }}" min="1" data-product-id="{{ item.product.id }}" />
<span class="cursor-pointer rounded-r bg-gray-100 py-1 px-3 duration-100 hover:bg-blue-600 hover:text-blue-50 quantity-button" data-action="increase"> + </span>
</div>
<div class="flex items-center space-x-4">
<p class="text-sm">€{{ item.product.price }}</p>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5 cursor-pointer duration-150 hover:text-red-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
</div>
<div class="mt-52">
<button type="button" data-product-id="{{ item.product.id }}" class="remove-item focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Verwijderen</button>
</div>
</div>
</div>
{% endfor %}
</div>

<div class="mt-6 h-full rounded-lg border bg-white p-6 shadow-md md:mt-0 md:w-1/3">
<div class="mb-2 flex justify-between">
<p class="text-gray-700">Subtotal</p>
<p class="text-gray-700">€{{ total_price }}</p>
<p class="text-gray-700">Subtotaal</p>
<p class="text-gray-700 total-price">€{{ total_price }}</p>
</div>
<div class="flex justify-between">
<p class="text-gray-700">Verzenden</p>
Expand All @@ -47,16 +62,89 @@ <h2 class="text-lg font-bold text-gray-900">{{ item.product.name }}</h2>
<div class="flex justify-between">
<p class="text-lg font-bold">Totaal</p>
<div class="">
<p class="mb-1 text-lg font-bold">€{{ total_price }}</p>
<p class="mb-1 text-lg font-bold total-price">€{{ total_price }}</p>
<p class="text-sm text-gray-700">Inclusief BTW</p>
</div>
</div>
<button class="mt-6 w-full rounded-md bg-blue-500 py-1.5 font-medium text-blue-50 hover:bg-blue-600">Bestellen</button>
<button class="mt-6 w-full rounded-md bg-blue-600 py-1.5 font-medium text-blue-50 ">Bestellen</button>
</div>
</div>
{% endif %}
</div>
</body>

{% include 'components/footer.html' %}

<script>
document.querySelectorAll('.remove-item').forEach(button => {
button.addEventListener('click', function(e) {
const productId = this.dataset.productId;

fetch('/remove-from-cart/', {
method: 'POST',
body: JSON.stringify({
product_id: productId
}),
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
}
})
.then(response => response.json())
.then(data => {
if(data.status === 'success') {
location.reload();
} else {
alert('Er ging iets mis bij het verwijderen van het product.');
}
})
});
});
document.querySelectorAll('.quantity-button').forEach(button => {
button.addEventListener('click', function(e) {
const action = e.target.dataset.action;
const input = e.target.parentElement.querySelector('.quantity-input');
let quantity = parseInt(input.value);

if(action === 'increase') {
quantity = quantity + 1;
} else if(action === 'decrease' && quantity > 1) {
quantity = quantity - 1;
}

input.value = quantity;

input.dispatchEvent(new Event('change'));
});
});

document.querySelectorAll('.quantity-input').forEach(input => {
input.addEventListener('change', function(e) {
const productId = e.target.dataset.productId;
const quantity = e.target.value;

fetch('/update-cart/', {
method: 'POST',
body: JSON.stringify({
product_id: productId,
quantity: quantity,
}),
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
}
})
.then(response => response.json())
.then(data => {
if(data.status === 'success') {
document.querySelectorAll('.total-price').forEach(price => {
price.textContent = '€' + data.total_price;
});
} else {
alert('Er ging iets mis bij het updaten van de winkelwagen.');
}
})
});
});
</script>
{% endblock %}
9 changes: 4 additions & 5 deletions src/bobvance/templates/base/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ <h3 class="text-gray-600 text-2xl font-medium">Koelkasten</h3>
<div class="grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 mt-6">
{% for product in products %}
<a href="{% url 'product_detail' product.pk%}">
<div class="w-full max-w-sm mx-auto rounded-md shadow-md overflow-hidden">
<div class="flex items-end justify-end h-56 w-full relative">
<div style="background-image: url('{{ product.image.url }}'); background-size: contain; background-repeat: no-repeat; background-position: center; height: 100%; width: 100%;" class="absolute"></div>
<div class="w-full max-w-sm mx-auto rounded-md shadow-md overflow-hidden h-full flex flex-col">
<div class="flex-1 relative overflow-hidden" style="background-image: url('{{ product.image.url }}'); background-size: contain; background-repeat: no-repeat; background-position: center; min-height: 200px;">
</div>
<div class="px-5 py-3">
<h3 class="text-gray-700 uppercase">{{ product.name }}</h3>
<div class="px-5 py-3 flex flex-col">
<h3 class="text-gray-700 uppercase mb-auto">{{ product.name }}</h3>
<span class="text-gray-500 mt-2">€{{ product.price }}</span>
</div>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/bobvance/templates/base/product_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ <h3 class="text-gray-700 uppercase text-lg">{{ object.name }}</h3>
<p class="text-gray-700 text-sm overflow-y-auto h-20 mb-3">{{ object.description }}</p>
<hr class="my-3">
<div class="flex items-center mt-6">
<button class="px-8 py-2 bg-indigo-600 text-white text-sm font-medium rounded hover:bg-indigo-500 focus:outline-none focus:bg-indigo-500 add-to-cart">Voeg toe aan winkelwagen</button>
<button class="px-8 py-2 bg-blue-600 text-white text-sm font-medium rounded add-to-cart">Voeg toe aan winkelwagen</button>
</div>
</div>
</div>
Expand All @@ -49,12 +49,12 @@ <h3 class="text-gray-600 text-2xl font-medium">Meer Producten</h3>
<div class="grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 mt-6">
{% for product in more_products %}
<a href="{% url 'product_detail' product.pk %}">
<div class="w-full max-w-sm mx-auto rounded-md shadow-md overflow-hidden h-80">
<div class="flex items-end justify-end h-56 w-full relative">
<div class="w-full max-w-sm mx-auto rounded-md shadow-md overflow-hidden h-96">
<div class="flex items-end justify-end h-64 w-full relative">
<div style="background-image: url('{{ product.image.url }}'); background-size: contain; background-repeat: no-repeat; background-position: center; height: 100%; width: 100%;" class="absolute"></div>
</div>
<div class="px-5 py-3 h-24 pb-4">
<h3 class="text-gray-700 uppercase h-16 overflow-y-hidden">{{ product.name }}</h3>
<div class="px-5 py-3 h-32 pb-6">
<h3 class="text-gray-700 uppercase h-20 overflow-y-hidden">{{ product.name }}</h3>
<span class="text-gray-500 mt-2">€{{ product.price }}</span>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/bobvance/templates/base/products.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{% block content%}
{% include 'components/header.html'%}

<h1 class="text-3xl font-bold text-center m-8"> Ons Assortiment </h1>
<div class="flex flex-wrap">
{% for product in object_list %}
{% include 'components/product_card.html' %}
Expand Down
27 changes: 2 additions & 25 deletions src/bobvance/templates/components/footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,10 @@
</div>
<div class="flex flex-wrap flex-grow mt-10 -mb-10 text-center md:pl-20 md:mt-0 md:text-left">
<div class="w-full px-4 lg:w-1/4 md:w-1/2">
<h2 class="mb-3 text-sm font-medium tracking-widest text-gray-900 uppercase title-font">About</h2>
<h2 class="mb-3 text-sm font-medium tracking-widest text-gray-900 uppercase title-font">Over ons</h2>
<nav class="mb-10 list-none">
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Company</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Careers</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Blog</a>
</li>
</nav>
</div>
<div class="w-full px-4 lg:w-1/4 md:w-1/2">
<h2 class="mb-3 text-sm font-medium tracking-widest text-gray-900 uppercase title-font">Support</h2>
<nav class="mb-10 list-none">
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Contact Support</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Help Resources</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Release Updates</a>
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Wie zijn wij?</a>
</li>
</nav>
</div>
Expand All @@ -77,9 +57,6 @@ <h2 class="mb-3 text-sm font-medium tracking-widest text-gray-900 uppercase titl
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Terms &amp; Privacy</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">Pricing</a>
</li>
<li class="mt-3">
<a class="text-gray-500 cursor-pointer hover:text-gray-900">FAQ</a>
</li>
Expand Down
11 changes: 10 additions & 1 deletion src/bobvance/templates/components/header.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<header>
<div class="container mx-auto px-6 py-3">
<div class="flex items-center justify-end w-full">
<a href="{% url 'cart' %}">
<button class="text-gray-600 focus:outline-none mx-4 sm:mx-0">
<svg class="h-5 w-5" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor">
<path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</button>
</a>
</div>
<div class="flex items-center justify-between">
<div class="hidden w-full text-gray-600 md:flex md:items-center">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand All @@ -15,7 +24,7 @@
</div>
</div>
</div>
<nav :class="isOpen ? '' : 'hidden'" class="sm:flex sm:justify-center sm:items-center mt-4">
<nav class="sm:flex sm:justify-center sm:items-center mt-4">
<div class="flex flex-col sm:flex-row">
<a class="mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0" href="{% url 'home' %}">Home</a>
<a class="mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0" href="{% url 'products'%}">Shop</a>
Expand Down

0 comments on commit ea640df

Please sign in to comment.