Django add to cart not working with product variation

I am building an e-commerce website where both products with variations and products without variation can be sold. So I have a cart view which adds a product to a cart and creates a cart instance, but it is not working now that I have modified it for products with variation. So if a product has variations, eg colors and sizes, users can select the variations, eg red and XL, and the selected variations show up on this section of product_detail.html:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> <div id="selected-variations" class="mt-5">
<div class="text-title">Selected Variation:</div>
<div class="mt-3">
<span id="selected-color">Color: None</span> /
<span id="selected-size">Size: None</span>
</div>
</div>
</code>
<code> <div id="selected-variations" class="mt-5"> <div class="text-title">Selected Variation:</div> <div class="mt-3"> <span id="selected-color">Color: None</span> / <span id="selected-size">Size: None</span> </div> </div> </code>
                    <div id="selected-variations" class="mt-5">
                        <div class="text-title">Selected Variation:</div>
                        <div class="mt-3">
                            <span id="selected-color">Color: None</span> /
                            <span id="selected-size">Size: None</span>
                        </div>
                    </div>

Now what I want is to take those selected variation values from there and then add the product to the cart with the selected values. But in doing so, the js is not working, I think my cart view logic is fine. It all seemed very easy in the beginning, since I am literally printing the selected variation values from the user above, just was supposed to take those values and create a cart instance with them. But now for some reason my main.js code won’t accept it. Is there any way to fix this?

My models.py:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class Business(models.Model):
BUSINESS_TYPE_CHOICES = [
('product', 'Product Business'),
('service', 'Service Business'),
]
seller = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='business')
business_name = models.CharField(max_length=100)
description = models.TextField()
business_slug = models.SlugField(unique=True, blank=True)
business_type = models.CharField(max_length=20, choices=BUSINESS_TYPE_CHOICES)
countries = models.ManyToManyField(Country)
states = models.ManyToManyField(State) # Add this line
address = models.CharField(max_length=200)
phone = models.CharField(max_length=20)
website = models.URLField(blank=True, null=True)
email = models.EmailField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='business_profiles/', blank=True, null=True)
banner_image = models.ImageField(upload_to='business_banners/', blank=True, null=True)
is_featured = models.BooleanField(default=False)
def __str__(self):
return self.business_name
def save(self, *args, **kwargs):
if not self.business_slug:
self.business_slug = slugify(self.business_name)
super().save(*args, **kwargs)
class Product(models.Model):
name = models.CharField(max_length=100)
product_slug = models.SlugField(unique=True, blank=True)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
image = models.ImageField(upload_to='products/')
image2 = models.ImageField(upload_to='products/', null=True, blank=True)
business = models.ForeignKey(Business, on_delete=models.CASCADE, related_name='products')
in_stock = models.BooleanField(default=True)
is_popular = models.BooleanField(default=False)
is_best_seller = models.BooleanField(default=False)
min_delivery_time = models.PositiveIntegerField(null=True, help_text="Minimum estimated delivery time in business days")
max_delivery_time = models.PositiveIntegerField(null=True, help_text="Maximum estimated delivery time in business days")
has_variations = models.BooleanField(default=False)
def __str__(self):
return f'{self.business.business_name}, {self.name}'
def get_json_data(self):
data = {
'id': self.id,
'name': self.name,
'price': float(self.price),
'description': self.description,
'images': [self.image.url, self.image2.url] if self.image and self.image2 else [],
'min_delivery_time': self.min_delivery_time,
'max_delivery_time': self.max_delivery_time,
}
return json.dumps(data)
def save(self, *args, **kwargs):
if not self.product_slug:
self.product_slug = slugify(self.name)
super().save(*args, **kwargs)
VAR_CATEGORIES = (
('size', 'Size'),
('color', 'Color'),
)
class Variation(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variations')
name = models.CharField(max_length=50, choices=VAR_CATEGORIES, null=True, blank=True)
def __str__(self):
return f"{self.product.name} - {self.get_name_display()}"
class ProductVariation(models.Model):
variation = models.ForeignKey(Variation, on_delete=models.CASCADE, related_name='values', null=True)
value = models.CharField(max_length=50, null=True)
image = models.ImageField(upload_to='product_variations/', null=True, blank=True)
class Meta:
unique_together = (('variation', 'value'),)
def __str__(self):
return f"{self.variation.product.name} - {self.variation.get_name_display()} - {self.value}"
class Cart(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='cart')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
variation = models.ForeignKey(ProductVariation, on_delete=models.CASCADE, null=True, blank=True)
quantity = models.PositiveIntegerField(default=1)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username} - {self.product.name} - {self.variation}"
</code>
<code>class Business(models.Model): BUSINESS_TYPE_CHOICES = [ ('product', 'Product Business'), ('service', 'Service Business'), ] seller = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='business') business_name = models.CharField(max_length=100) description = models.TextField() business_slug = models.SlugField(unique=True, blank=True) business_type = models.CharField(max_length=20, choices=BUSINESS_TYPE_CHOICES) countries = models.ManyToManyField(Country) states = models.ManyToManyField(State) # Add this line address = models.CharField(max_length=200) phone = models.CharField(max_length=20) website = models.URLField(blank=True, null=True) email = models.EmailField(blank=True, null=True) profile_picture = models.ImageField(upload_to='business_profiles/', blank=True, null=True) banner_image = models.ImageField(upload_to='business_banners/', blank=True, null=True) is_featured = models.BooleanField(default=False) def __str__(self): return self.business_name def save(self, *args, **kwargs): if not self.business_slug: self.business_slug = slugify(self.business_name) super().save(*args, **kwargs) class Product(models.Model): name = models.CharField(max_length=100) product_slug = models.SlugField(unique=True, blank=True) description = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) image = models.ImageField(upload_to='products/') image2 = models.ImageField(upload_to='products/', null=True, blank=True) business = models.ForeignKey(Business, on_delete=models.CASCADE, related_name='products') in_stock = models.BooleanField(default=True) is_popular = models.BooleanField(default=False) is_best_seller = models.BooleanField(default=False) min_delivery_time = models.PositiveIntegerField(null=True, help_text="Minimum estimated delivery time in business days") max_delivery_time = models.PositiveIntegerField(null=True, help_text="Maximum estimated delivery time in business days") has_variations = models.BooleanField(default=False) def __str__(self): return f'{self.business.business_name}, {self.name}' def get_json_data(self): data = { 'id': self.id, 'name': self.name, 'price': float(self.price), 'description': self.description, 'images': [self.image.url, self.image2.url] if self.image and self.image2 else [], 'min_delivery_time': self.min_delivery_time, 'max_delivery_time': self.max_delivery_time, } return json.dumps(data) def save(self, *args, **kwargs): if not self.product_slug: self.product_slug = slugify(self.name) super().save(*args, **kwargs) VAR_CATEGORIES = ( ('size', 'Size'), ('color', 'Color'), ) class Variation(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variations') name = models.CharField(max_length=50, choices=VAR_CATEGORIES, null=True, blank=True) def __str__(self): return f"{self.product.name} - {self.get_name_display()}" class ProductVariation(models.Model): variation = models.ForeignKey(Variation, on_delete=models.CASCADE, related_name='values', null=True) value = models.CharField(max_length=50, null=True) image = models.ImageField(upload_to='product_variations/', null=True, blank=True) class Meta: unique_together = (('variation', 'value'),) def __str__(self): return f"{self.variation.product.name} - {self.variation.get_name_display()} - {self.value}" class Cart(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='cart') product = models.ForeignKey(Product, on_delete=models.CASCADE) variation = models.ForeignKey(ProductVariation, on_delete=models.CASCADE, null=True, blank=True) quantity = models.PositiveIntegerField(default=1) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return f"{self.user.username} - {self.product.name} - {self.variation}" </code>
class Business(models.Model):
    BUSINESS_TYPE_CHOICES = [
        ('product', 'Product Business'),
        ('service', 'Service Business'),
    ]
    seller = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='business')
    business_name = models.CharField(max_length=100)
    description = models.TextField()
    business_slug = models.SlugField(unique=True, blank=True)
    business_type = models.CharField(max_length=20, choices=BUSINESS_TYPE_CHOICES)
    countries = models.ManyToManyField(Country)
    states = models.ManyToManyField(State)  # Add this line
    address = models.CharField(max_length=200)
    phone = models.CharField(max_length=20)
    website = models.URLField(blank=True, null=True)
    email = models.EmailField(blank=True, null=True)
    profile_picture = models.ImageField(upload_to='business_profiles/', blank=True, null=True)
    banner_image = models.ImageField(upload_to='business_banners/', blank=True, null=True)
    is_featured = models.BooleanField(default=False)

    def __str__(self):
        return self.business_name

    def save(self, *args, **kwargs):
        if not self.business_slug:
            self.business_slug = slugify(self.business_name)
        super().save(*args, **kwargs)

class Product(models.Model):
    name = models.CharField(max_length=100)
    product_slug = models.SlugField(unique=True, blank=True)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    image = models.ImageField(upload_to='products/')
    image2 = models.ImageField(upload_to='products/', null=True, blank=True)
    business = models.ForeignKey(Business, on_delete=models.CASCADE, related_name='products')
    in_stock = models.BooleanField(default=True)
    is_popular = models.BooleanField(default=False)
    is_best_seller = models.BooleanField(default=False)
    min_delivery_time = models.PositiveIntegerField(null=True, help_text="Minimum estimated delivery time in business days")
    max_delivery_time = models.PositiveIntegerField(null=True, help_text="Maximum estimated delivery time in business days")
    has_variations = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.business.business_name}, {self.name}'

    def get_json_data(self):
        data = {
            'id': self.id,
            'name': self.name,
            'price': float(self.price),
            'description': self.description,
            'images': [self.image.url, self.image2.url] if self.image and self.image2 else [],
            'min_delivery_time': self.min_delivery_time,
            'max_delivery_time': self.max_delivery_time,
        }
        return json.dumps(data)

    def save(self, *args, **kwargs):
        if not self.product_slug:
            self.product_slug = slugify(self.name)
        super().save(*args, **kwargs)


VAR_CATEGORIES = (
    ('size', 'Size'),
    ('color', 'Color'),
)

class Variation(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variations')
    name = models.CharField(max_length=50, choices=VAR_CATEGORIES, null=True, blank=True)

    
    def __str__(self):
        return f"{self.product.name} - {self.get_name_display()}"

class ProductVariation(models.Model):
    variation = models.ForeignKey(Variation, on_delete=models.CASCADE, related_name='values', null=True)
    value = models.CharField(max_length=50, null=True)
    image = models.ImageField(upload_to='product_variations/', null=True, blank=True)

    class Meta:
        unique_together = (('variation', 'value'),)

    def __str__(self):
        return f"{self.variation.product.name} - {self.variation.get_name_display()} - {self.value}"

class Cart(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='cart')
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    variation = models.ForeignKey(ProductVariation, on_delete=models.CASCADE, null=True, blank=True)
    quantity = models.PositiveIntegerField(default=1)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.user.username} - {self.product.name} - {self.variation}"

My product_detail view and cartview:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class ProductDetailView(View):
def get(self, request, business_slug, product_slug):
business = get_object_or_404(Business, business_slug=business_slug)
product = get_object_or_404(Product, product_slug=product_slug, business=business)
color_variations = Variation.objects.filter(product=product, name='color')
size_variations = Variation.objects.filter(product=product, name='size')
context = {
'product': product,
'business': business,
'color_variations': color_variations,
'size_variations': size_variations,
}
return render(request, 'business/product_detail.html', context)
"
class CartView(LoginRequiredMixin, View):
def get(self, request):
cart_items = Cart.objects.filter(user=request.user)
total_price = sum(item.product.price * item.quantity for item in cart_items)
return render(request, 'business/cart.html', {'cart_items': cart_items, 'total_price': total_price})
def post(self, request):
try:
data = json.loads(request.body)
product_id = data.get('product_id')
color_variation_id = data.get('color_variation')
size_variation_id = data.get('size_variation')
quantity = int(data.get('quantity', 1))
except json.JSONDecodeError:
logger.error("Invalid JSON data in the request body")
return JsonResponse({'error': 'Invalid JSON data'}, status=400)
product = get_object_or_404(Product, id=product_id)
variation = None
if product.has_variations:
if color_variation_id and size_variation_id:
color_variation = get_object_or_404(ProductVariation, id=color_variation_id)
size_variation = get_object_or_404(ProductVariation, id=size_variation_id)
if color_variation.variation.product != product or size_variation.variation.product != product:
return JsonResponse({'error': 'Invalid variations selected'}, status=400)
variation = color_variation if color_variation_id == size_variation_id else None
elif color_variation_id:
color_variation = get_object_or_404(ProductVariation, id=color_variation_id)
if color_variation.variation.product != product:
return JsonResponse({'error': 'Invalid color variation selected'}, status=400)
variation = color_variation
elif size_variation_id:
size_variation = get_object_or_404(ProductVariation, id=size_variation_id)
if size_variation.variation.product != product:
return JsonResponse({'error': 'Invalid size variation selected'}, status=400)
variation = size_variation
else:
return JsonResponse({'error': 'Variations required for this product'}, status=400)
cart_item, created = Cart.objects.get_or_create(user=request.user, product=product, variation=variation)
if not created:
cart_item.quantity += quantity
cart_item.save()
cart_items = Cart.objects.filter(user=request.user)
cart_data = []
for item in cart_items:
cart_data.append({
'id': item.id,
'product': {
'id': item.product.id,
'name': item.product.name,
'price': float(item.product.price),
'image': item.product.image.url if item.product.image else None,
},
'quantity': item.quantity,
})
total_price = sum(item.product.price * item.quantity for item in cart_items)
logger.debug("Returning updated cart data as JSON response")
return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})
def get_cart_data(self, request):
logger.debug("Received request to fetch cart data")
cart_items = Cart.objects.filter(user=request.user)
cart_data = []
for item in cart_items:
cart_data.append({
'id': item.id,
'product': {
'id': item.product.id,
'name': item.product.name,
'price': float(item.product.price),
'image': str(item.product.image.url) if item.product.image else None,
},
'quantity': item.quantity,
})
total_price = sum(item.product.price * item.quantity for item in cart_items)
logger.debug(f"Returning cart data: {cart_data}")
return JsonResponse({'items': cart_data, 'subtotal': total_price})
def update_quantity(self, request):
try:
data = json.loads(request.body)
item_id = data.get('item_id')
action = data.get('action')
except json.JSONDecodeError:
return JsonResponse({'error': 'Invalid JSON data'}, status=400)
# Retrieve the cart item
cart_item = get_object_or_404(Cart, id=item_id)
if action == 'increase':
cart_item.quantity += 1
elif action == 'decrease':
if cart_item.quantity > 1:
cart_item.quantity -= 1
cart_item.save()
# Calculate total price and prepare cart data
cart_items = Cart.objects.filter(user=request.user)
cart_data = [
{
'id': item.id,
'product': {
'id': item.product.id,
'name': item.product.name,
'price': float(item.product.price),
'image': item.product.image.url if item.product.image else None,
},
'quantity': item.quantity,
}
for item in cart_items
]
total_price = sum(item.product.price * item.quantity for item in cart_items)
return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})
def dispatch(self, request, *args, **kwargs):
if request.method == 'POST':
if request.path == '/cart/update_quantity/':
return self.update_quantity(request, *args, **kwargs)
else:
# Handle other POST requests here
pass
elif request.method == 'GET':
if request.path == '/cart/data/':
logger.debug(f"Request Path: {request.path}")
return self.get_cart_data(request)
else:
# Handle other GET requests here
pass
# Fall back to the default behavior if the request doesn't match any of the above conditions
return super().dispatch(request, *args, **kwargs)
</code>
<code>class ProductDetailView(View): def get(self, request, business_slug, product_slug): business = get_object_or_404(Business, business_slug=business_slug) product = get_object_or_404(Product, product_slug=product_slug, business=business) color_variations = Variation.objects.filter(product=product, name='color') size_variations = Variation.objects.filter(product=product, name='size') context = { 'product': product, 'business': business, 'color_variations': color_variations, 'size_variations': size_variations, } return render(request, 'business/product_detail.html', context) " class CartView(LoginRequiredMixin, View): def get(self, request): cart_items = Cart.objects.filter(user=request.user) total_price = sum(item.product.price * item.quantity for item in cart_items) return render(request, 'business/cart.html', {'cart_items': cart_items, 'total_price': total_price}) def post(self, request): try: data = json.loads(request.body) product_id = data.get('product_id') color_variation_id = data.get('color_variation') size_variation_id = data.get('size_variation') quantity = int(data.get('quantity', 1)) except json.JSONDecodeError: logger.error("Invalid JSON data in the request body") return JsonResponse({'error': 'Invalid JSON data'}, status=400) product = get_object_or_404(Product, id=product_id) variation = None if product.has_variations: if color_variation_id and size_variation_id: color_variation = get_object_or_404(ProductVariation, id=color_variation_id) size_variation = get_object_or_404(ProductVariation, id=size_variation_id) if color_variation.variation.product != product or size_variation.variation.product != product: return JsonResponse({'error': 'Invalid variations selected'}, status=400) variation = color_variation if color_variation_id == size_variation_id else None elif color_variation_id: color_variation = get_object_or_404(ProductVariation, id=color_variation_id) if color_variation.variation.product != product: return JsonResponse({'error': 'Invalid color variation selected'}, status=400) variation = color_variation elif size_variation_id: size_variation = get_object_or_404(ProductVariation, id=size_variation_id) if size_variation.variation.product != product: return JsonResponse({'error': 'Invalid size variation selected'}, status=400) variation = size_variation else: return JsonResponse({'error': 'Variations required for this product'}, status=400) cart_item, created = Cart.objects.get_or_create(user=request.user, product=product, variation=variation) if not created: cart_item.quantity += quantity cart_item.save() cart_items = Cart.objects.filter(user=request.user) cart_data = [] for item in cart_items: cart_data.append({ 'id': item.id, 'product': { 'id': item.product.id, 'name': item.product.name, 'price': float(item.product.price), 'image': item.product.image.url if item.product.image else None, }, 'quantity': item.quantity, }) total_price = sum(item.product.price * item.quantity for item in cart_items) logger.debug("Returning updated cart data as JSON response") return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price}) def get_cart_data(self, request): logger.debug("Received request to fetch cart data") cart_items = Cart.objects.filter(user=request.user) cart_data = [] for item in cart_items: cart_data.append({ 'id': item.id, 'product': { 'id': item.product.id, 'name': item.product.name, 'price': float(item.product.price), 'image': str(item.product.image.url) if item.product.image else None, }, 'quantity': item.quantity, }) total_price = sum(item.product.price * item.quantity for item in cart_items) logger.debug(f"Returning cart data: {cart_data}") return JsonResponse({'items': cart_data, 'subtotal': total_price}) def update_quantity(self, request): try: data = json.loads(request.body) item_id = data.get('item_id') action = data.get('action') except json.JSONDecodeError: return JsonResponse({'error': 'Invalid JSON data'}, status=400) # Retrieve the cart item cart_item = get_object_or_404(Cart, id=item_id) if action == 'increase': cart_item.quantity += 1 elif action == 'decrease': if cart_item.quantity > 1: cart_item.quantity -= 1 cart_item.save() # Calculate total price and prepare cart data cart_items = Cart.objects.filter(user=request.user) cart_data = [ { 'id': item.id, 'product': { 'id': item.product.id, 'name': item.product.name, 'price': float(item.product.price), 'image': item.product.image.url if item.product.image else None, }, 'quantity': item.quantity, } for item in cart_items ] total_price = sum(item.product.price * item.quantity for item in cart_items) return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price}) def dispatch(self, request, *args, **kwargs): if request.method == 'POST': if request.path == '/cart/update_quantity/': return self.update_quantity(request, *args, **kwargs) else: # Handle other POST requests here pass elif request.method == 'GET': if request.path == '/cart/data/': logger.debug(f"Request Path: {request.path}") return self.get_cart_data(request) else: # Handle other GET requests here pass # Fall back to the default behavior if the request doesn't match any of the above conditions return super().dispatch(request, *args, **kwargs) </code>
class ProductDetailView(View):
    def get(self, request, business_slug, product_slug):
        business = get_object_or_404(Business, business_slug=business_slug)
        product = get_object_or_404(Product, product_slug=product_slug, business=business)

        color_variations = Variation.objects.filter(product=product, name='color')
        size_variations = Variation.objects.filter(product=product, name='size')

        context = {
            'product': product,
            'business': business,
            'color_variations': color_variations,
            'size_variations': size_variations,
        }

        return render(request, 'business/product_detail.html', context)

"
class CartView(LoginRequiredMixin, View):
    def get(self, request):
        cart_items = Cart.objects.filter(user=request.user)
        total_price = sum(item.product.price * item.quantity for item in cart_items)
        return render(request, 'business/cart.html', {'cart_items': cart_items, 'total_price': total_price})

    def post(self, request):
        try:
            data = json.loads(request.body)
            product_id = data.get('product_id')
            color_variation_id = data.get('color_variation')
            size_variation_id = data.get('size_variation')
            quantity = int(data.get('quantity', 1))
        except json.JSONDecodeError:
            logger.error("Invalid JSON data in the request body")
            return JsonResponse({'error': 'Invalid JSON data'}, status=400)

        product = get_object_or_404(Product, id=product_id)

        variation = None
        if product.has_variations:
            if color_variation_id and size_variation_id:
                color_variation = get_object_or_404(ProductVariation, id=color_variation_id)
                size_variation = get_object_or_404(ProductVariation, id=size_variation_id)
                if color_variation.variation.product != product or size_variation.variation.product != product:
                    return JsonResponse({'error': 'Invalid variations selected'}, status=400)
                variation = color_variation if color_variation_id == size_variation_id else None
            elif color_variation_id:
                color_variation = get_object_or_404(ProductVariation, id=color_variation_id)
                if color_variation.variation.product != product:
                    return JsonResponse({'error': 'Invalid color variation selected'}, status=400)
                variation = color_variation
            elif size_variation_id:
                size_variation = get_object_or_404(ProductVariation, id=size_variation_id)
                if size_variation.variation.product != product:
                    return JsonResponse({'error': 'Invalid size variation selected'}, status=400)
                variation = size_variation
            else:
                return JsonResponse({'error': 'Variations required for this product'}, status=400)

        cart_item, created = Cart.objects.get_or_create(user=request.user, product=product, variation=variation)
        if not created:
            cart_item.quantity += quantity
        cart_item.save()

        cart_items = Cart.objects.filter(user=request.user)
        cart_data = []
        for item in cart_items:
            cart_data.append({
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': item.product.image.url if item.product.image else None,
                },
                'quantity': item.quantity,
            })
        total_price = sum(item.product.price * item.quantity for item in cart_items)

        logger.debug("Returning updated cart data as JSON response")
        return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})

    def get_cart_data(self, request):
        logger.debug("Received request to fetch cart data")
        cart_items = Cart.objects.filter(user=request.user)
        cart_data = []
        for item in cart_items:
            cart_data.append({
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': str(item.product.image.url) if item.product.image else None,
                },
                'quantity': item.quantity,
            })
        total_price = sum(item.product.price * item.quantity for item in cart_items)
        logger.debug(f"Returning cart data: {cart_data}")
        return JsonResponse({'items': cart_data, 'subtotal': total_price})
    
    def update_quantity(self, request):
        try:
            data = json.loads(request.body)
            item_id = data.get('item_id')
            action = data.get('action')
        except json.JSONDecodeError:
            return JsonResponse({'error': 'Invalid JSON data'}, status=400)

        # Retrieve the cart item
        cart_item = get_object_or_404(Cart, id=item_id)

        if action == 'increase':
            cart_item.quantity += 1
        elif action == 'decrease':
            if cart_item.quantity > 1:
                cart_item.quantity -= 1

        cart_item.save()

        # Calculate total price and prepare cart data
        cart_items = Cart.objects.filter(user=request.user)
        cart_data = [
            {
                'id': item.id,
                'product': {
                    'id': item.product.id,
                    'name': item.product.name,
                    'price': float(item.product.price),
                    'image': item.product.image.url if item.product.image else None,
                },
                'quantity': item.quantity,
            }
            for item in cart_items
        ]
        total_price = sum(item.product.price * item.quantity for item in cart_items)

        return JsonResponse({'success': True, 'items': cart_data, 'subtotal': total_price})
    
    def dispatch(self, request, *args, **kwargs):
        if request.method == 'POST':
            if request.path == '/cart/update_quantity/':
                return self.update_quantity(request, *args, **kwargs)
            else:
                # Handle other POST requests here
                pass
        elif request.method == 'GET':
            if request.path == '/cart/data/':
                logger.debug(f"Request Path: {request.path}")
                return self.get_cart_data(request)
            else:
                # Handle other GET requests here
                pass

        # Fall back to the default behavior if the request doesn't match any of the above conditions
        return super().dispatch(request, *args, **kwargs)

My My urls.py:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> path('cart/', views.CartView.as_view(), name='cart'),
path('cart/update_quantity/', views.CartView.as_view(), name='update_quantity'),
path('cart/data/', views.CartView.as_view(), name='cart_data'),
path('cart/add/', views.CartView.as_view(), name='add'),
</code>
<code> path('cart/', views.CartView.as_view(), name='cart'), path('cart/update_quantity/', views.CartView.as_view(), name='update_quantity'), path('cart/data/', views.CartView.as_view(), name='cart_data'), path('cart/add/', views.CartView.as_view(), name='add'), </code>
    path('cart/', views.CartView.as_view(), name='cart'),
    path('cart/update_quantity/', views.CartView.as_view(), name='update_quantity'),
    path('cart/data/', views.CartView.as_view(), name='cart_data'),
    path('cart/add/', views.CartView.as_view(), name='add'),

My product_detail.html:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> <div id="selected-variations" class="mt-5">
<div class="text-title">Selected Variation:</div>
<div class="mt-3">
<span id="selected-color">Color: None</span> /
<span id="selected-size">Size: None</span>
</div>
</div>
{% if color_variations %}
<div class="choose-color mt-5">
<div class="text-title">Colors:</div>
<div class="list-color flex items-center gap-2 flex-wrap mt-3">
{% for color_variation in color_variations %}
{% for product_variation in color_variation.values.all %}
<div class="color-item w-12 h-12 rounded-xl duration-300 relative"
data-color="{{ product_variation.value }}"
data-variation-id="{{ product_variation.id }}">
{% if product_variation.image %}
<img style="width: 100%; height: 100%; object-fit: cover"
src="{{ product_variation.image.url }}"
alt="color" class="rounded-xl">
{% endif %}
<div class="tag-action bg-black text-white caption2 capitalize px-1.5 py-0.5 rounded-sm">
{{ product_variation.value }}
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
{% if size_variations %}
<div class="choose-size mt-5">
<div class="heading flex items-center justify-between">
<div class="text-title">Sizes:</div>
<div class="caption1 size-guide text-red underline">Size Guide</div>
</div>
<div class="list-size flex items-center gap-2 flex-wrap mt-3">
{% for size_variation in size_variations %}
{% for product_variation in size_variation.values.all %}
<div class="size-item w-12 h-12 flex items-center justify-center text-button rounded-full bg-white border border-line"
data-size="{{ product_variation.value }}"
data-variation-id="{{ product_variation.id }}">
{{ product_variation.value }}
</div>
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
<div class="text-title mt-5">Quantity:</div>
<div class="choose-quantity flex items-center max-xl:flex-wrap lg:justify-between gap-5 mt-3">
<div
class="quantity-block md:p-3 max-md:py-1.5 max-md:px-3 flex items-center justify-between rounded-lg border border-line sm:w-[140px] w-[120px] flex-shrink-0">
<i class="ph-bold ph-minus cursor-pointer body1"></i>
<div class="quantity body1 font-semibold">1</div>
<i class="ph-bold ph-plus cursor-pointer body1"></i>
</div>
<div
class="add-cart-btn button-main whitespace-nowrap w-full text-center bg-white text-black border border-black" data-product-id="{{ product.id }}">
Add To Cart
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let selectedColor = null;
let selectedSize = null;
document.querySelectorAll('.color-item').forEach(function (item) {
item.addEventListener('click', function () {
selectedColor = this.dataset.variationId;
document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color;
});
});
document.querySelectorAll('.size-item').forEach(function (item) {
item.addEventListener('click', function () {
selectedSize = this.dataset.variationId;
document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size;
});
});
document.querySelector('.add-cart-btn').addEventListener('click', function () {
const productId = this.dataset.productId;
const quantity = document.querySelector('.quantity').textContent;
fetch('/cart/add/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({
product_id: productId,
color_variation: selectedColor,
size_variation: selectedSize,
quantity: quantity
})
}).then(response => response.json())
.then(data => {
if (data.success) {
alert('Product added to cart successfully!');
} else {
alert('Error adding product to cart.');
}
});
});
});
</script>
</code>
<code> <div id="selected-variations" class="mt-5"> <div class="text-title">Selected Variation:</div> <div class="mt-3"> <span id="selected-color">Color: None</span> / <span id="selected-size">Size: None</span> </div> </div> {% if color_variations %} <div class="choose-color mt-5"> <div class="text-title">Colors:</div> <div class="list-color flex items-center gap-2 flex-wrap mt-3"> {% for color_variation in color_variations %} {% for product_variation in color_variation.values.all %} <div class="color-item w-12 h-12 rounded-xl duration-300 relative" data-color="{{ product_variation.value }}" data-variation-id="{{ product_variation.id }}"> {% if product_variation.image %} <img style="width: 100%; height: 100%; object-fit: cover" src="{{ product_variation.image.url }}" alt="color" class="rounded-xl"> {% endif %} <div class="tag-action bg-black text-white caption2 capitalize px-1.5 py-0.5 rounded-sm"> {{ product_variation.value }} </div> </div> {% endfor %} {% endfor %} </div> </div> {% endif %} {% if size_variations %} <div class="choose-size mt-5"> <div class="heading flex items-center justify-between"> <div class="text-title">Sizes:</div> <div class="caption1 size-guide text-red underline">Size Guide</div> </div> <div class="list-size flex items-center gap-2 flex-wrap mt-3"> {% for size_variation in size_variations %} {% for product_variation in size_variation.values.all %} <div class="size-item w-12 h-12 flex items-center justify-center text-button rounded-full bg-white border border-line" data-size="{{ product_variation.value }}" data-variation-id="{{ product_variation.id }}"> {{ product_variation.value }} </div> {% endfor %} {% endfor %} </div> </div> {% endif %} <div class="text-title mt-5">Quantity:</div> <div class="choose-quantity flex items-center max-xl:flex-wrap lg:justify-between gap-5 mt-3"> <div class="quantity-block md:p-3 max-md:py-1.5 max-md:px-3 flex items-center justify-between rounded-lg border border-line sm:w-[140px] w-[120px] flex-shrink-0"> <i class="ph-bold ph-minus cursor-pointer body1"></i> <div class="quantity body1 font-semibold">1</div> <i class="ph-bold ph-plus cursor-pointer body1"></i> </div> <div class="add-cart-btn button-main whitespace-nowrap w-full text-center bg-white text-black border border-black" data-product-id="{{ product.id }}"> Add To Cart </div> </div> <script> document.addEventListener('DOMContentLoaded', function () { let selectedColor = null; let selectedSize = null; document.querySelectorAll('.color-item').forEach(function (item) { item.addEventListener('click', function () { selectedColor = this.dataset.variationId; document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color; }); }); document.querySelectorAll('.size-item').forEach(function (item) { item.addEventListener('click', function () { selectedSize = this.dataset.variationId; document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size; }); }); document.querySelector('.add-cart-btn').addEventListener('click', function () { const productId = this.dataset.productId; const quantity = document.querySelector('.quantity').textContent; fetch('/cart/add/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' }, body: JSON.stringify({ product_id: productId, color_variation: selectedColor, size_variation: selectedSize, quantity: quantity }) }).then(response => response.json()) .then(data => { if (data.success) { alert('Product added to cart successfully!'); } else { alert('Error adding product to cart.'); } }); }); }); </script> </code>
                    <div id="selected-variations" class="mt-5">
                        <div class="text-title">Selected Variation:</div>
                        <div class="mt-3">
                            <span id="selected-color">Color: None</span> /
                            <span id="selected-size">Size: None</span>
                        </div>
                    </div>
                    
                    {% if color_variations %}
                        <div class="choose-color mt-5">
                            <div class="text-title">Colors:</div>
                            <div class="list-color flex items-center gap-2 flex-wrap mt-3">
                                {% for color_variation in color_variations %}
                                    {% for product_variation in color_variation.values.all %}
                                        <div class="color-item w-12 h-12 rounded-xl duration-300 relative"
                                             data-color="{{ product_variation.value }}" 
                                             data-variation-id="{{ product_variation.id }}">
                                            {% if product_variation.image %}
                                                <img style="width: 100%; height: 100%; object-fit: cover" 
                                                     src="{{ product_variation.image.url }}" 
                                                     alt="color" class="rounded-xl">
                                            {% endif %}
                                            <div class="tag-action bg-black text-white caption2 capitalize px-1.5 py-0.5 rounded-sm">
                                                {{ product_variation.value }}
                                            </div>
                                        </div>
                                    {% endfor %}
                                {% endfor %}
                            </div>
                        </div>
                    {% endif %}
                    
                    {% if size_variations %}
                        <div class="choose-size mt-5">
                            <div class="heading flex items-center justify-between">
                                <div class="text-title">Sizes:</div>
                                <div class="caption1 size-guide text-red underline">Size Guide</div>
                            </div>
                            <div class="list-size flex items-center gap-2 flex-wrap mt-3">
                                {% for size_variation in size_variations %}
                                    {% for product_variation in size_variation.values.all %}
                                        <div class="size-item w-12 h-12 flex items-center justify-center text-button rounded-full bg-white border border-line"
                                             data-size="{{ product_variation.value }}" 
                                             data-variation-id="{{ product_variation.id }}">
                                            {{ product_variation.value }}
                                        </div>
                                    {% endfor %}
                                {% endfor %}
                            </div>
                        </div>
                    {% endif %}
                    
                    <div class="text-title mt-5">Quantity:</div>
                    <div class="choose-quantity flex items-center max-xl:flex-wrap lg:justify-between gap-5 mt-3">
                        <div
                            class="quantity-block md:p-3 max-md:py-1.5 max-md:px-3 flex items-center justify-between rounded-lg border border-line sm:w-[140px] w-[120px] flex-shrink-0">
                            <i class="ph-bold ph-minus cursor-pointer body1"></i>
                            <div class="quantity body1 font-semibold">1</div>
                            <i class="ph-bold ph-plus cursor-pointer body1"></i>
                        </div>
                        <div
                            class="add-cart-btn button-main whitespace-nowrap w-full text-center bg-white text-black border border-black" data-product-id="{{ product.id }}">
                            Add To Cart
                        </div>
                    </div>

                    <script>
                        document.addEventListener('DOMContentLoaded', function () {
                            let selectedColor = null;
                            let selectedSize = null;
                        
                            document.querySelectorAll('.color-item').forEach(function (item) {
                                item.addEventListener('click', function () {
                                    selectedColor = this.dataset.variationId;
                                    document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color;
                                });
                            });
                        
                            document.querySelectorAll('.size-item').forEach(function (item) {
                                item.addEventListener('click', function () {
                                    selectedSize = this.dataset.variationId;
                                    document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size;
                                });
                            });
                        
                            document.querySelector('.add-cart-btn').addEventListener('click', function () {
                                const productId = this.dataset.productId;
                                const quantity = document.querySelector('.quantity').textContent;
                        
                                fetch('/cart/add/', {
                                    method: 'POST',
                                    headers: {
                                        'Content-Type': 'application/json',
                                        'X-CSRFToken': '{{ csrf_token }}'
                                    },
                                    body: JSON.stringify({
                                        product_id: productId,
                                        color_variation: selectedColor,
                                        size_variation: selectedSize,
                                        quantity: quantity
                                    })
                                }).then(response => response.json())
                                  .then(data => {
                                      if (data.success) {
                                          alert('Product added to cart successfully!');
                                      } else {
                                          alert('Error adding product to cart.');
                                      }
                                  });
                            });
                        });
                    </script>

My main.js:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>const cartIcon = document.querySelector(".cart-icon");
const modalCart = document.querySelector(".modal-cart-block");
const modalCartMain = document.querySelector(".modal-cart-block .modal-cart-main");
const closeCartIcon = document.querySelector(".modal-cart-main .close-btn");
const continueCartIcon = document.querySelector(".modal-cart-main .continue");
const addCartBtns = document.querySelectorAll(".add-cart-btn");
const openModalCart = () => {
modalCartMain.classList.add("open");
};
const closeModalCart = () => {
modalCartMain.classList.remove("open");
};
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
const addToCart = (productId, colorVariation, sizeVariation, quantity) => {
console.log('Product ID:', productId);
fetch('/cart/add/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({
product_id: productId,
color_variation: colorVariation,
size_variation: sizeVariation,
quantity: quantity
}),
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to add product to cart');
}
return response.json();
})
.then(data => {
if (data.success) {
console.log('Product added successfully:', data);
updateCartModalContent(); // Ensure this function is called immediately after adding the product
openModalCart();
}
})
.catch(error => console.error('Error:', error));
};
document.addEventListener("DOMContentLoaded", function() {
let selectedColor = null;
let selectedSize = null;
document.querySelectorAll('.color-item').forEach(function (item) {
item.addEventListener('click', function () {
selectedColor = this.dataset.variationId;
document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color;
});
});
document.querySelectorAll('.size-item').forEach(function (item) {
item.addEventListener('click', function () {
selectedSize = this.dataset.variationId;
document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size;
});
});
document.querySelector('.add-cart-btn').addEventListener('click', function () {
const productId = this.dataset.productId;
const quantity = document.querySelector('.quantity').textContent;
addToCart(productId, selectedColor, selectedSize, quantity);
});
const plusIcons = document.querySelectorAll(".ph-plus");
const minusIcons = document.querySelectorAll(".ph-minus");
plusIcons.forEach(icon => {
icon.addEventListener("click", function () {
const quantityElement = this.closest('.quantity-block').querySelector('.quantity');
let quantity = parseInt(quantityElement.textContent);
quantityElement.textContent = ++quantity;
});
});
minusIcons.forEach(icon => {
icon.addEventListener("click", function () {
const quantityElement = this.closest('.quantity-block').querySelector('.quantity');
let quantity = parseInt(quantityElement.textContent);
if (quantity > 1) {
quantityElement.textContent = --quantity;
}
});
});
});
function updateQuantity(itemId, action) {
fetch('/cart/update_quantity/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ item_id: itemId, action: action }),
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to update quantity');
}
return response.json();
})
.then(data => {
if (data.success) {
updateCartModalContent();
}
})
.catch(error => console.error('Error:', error));
}
function updateCartModalContent() {
console.log('Updating cart modal content...');
fetchCartData()
.then((cartData) => {
console.log('Cart data fetched:', cartData);
const cartItemsContainer = document.querySelector('.modal-cart-main .list-product');
cartItemsContainer.innerHTML = '';
if (cartData.items.length === 0) {
cartItemsContainer.innerHTML = '<p class="mt-1">No product in cart</p>';
} else {
cartData.items.forEach((item) => {
const cartItem = createCartItemElement(item);
cartItemsContainer.appendChild(cartItem);
});
}
const subtotalElement = document.querySelector('.modal-cart-main .total-price');
const subtotal = parseFloat(cartData.subtotal) || 0;
subtotalElement.textContent = `$${subtotal.toFixed(2)}`;
})
}
function fetchCartData() {
return fetch('/cart/data/')
.then((response) => response.json())
.then((data) => data);
}
function createCartItemElement(item) {
console.log('Creating cart item element for:', item);
const cartItemElement = document.createElement('div');
cartItemElement.classList.add('item', 'py-5', 'flex', 'items-center', 'justify-between', 'gap-3', 'border-b', 'border-line');
cartItemElement.dataset.item = item.id;
const imageUrl = item.product.image || '/static/path/to/default-image.png';
cartItemElement.innerHTML = `
<div class="infor flex items-center gap-3 w-full">
<div class="bg-img w-[100px] aspect-square flex-shrink-0 rounded-lg overflow-hidden">
<img src="${imageUrl}" alt="product" class="w-full h-full">
</div>
<div class="w-full">
<div class="flex items-center justify-between w-full">
<div class="name text-button">${item.product.name}</div>
<div class="remove-cart-btn remove-btn caption1 font-semibold text-red underline cursor-pointer">
Remove
</div>
</div>
<div class="flex items-center justify-between gap-2 mt-3 w-full">
<div class="flex items-center text-secondary2 capitalize">
${item.product.color || 'N/A'}/${item.product.size || 'N/A'}
</div>
<div class="product-price text-title">$${item.product.price}</div>
</div>
</div>
</div>
`;
return cartItemElement;
}
cartIcon.addEventListener("click", openModalCart);
modalCart.addEventListener("click", closeModalCart);
closeCartIcon.addEventListener("click", closeModalCart);
continueCartIcon.addEventListener("click", closeModalCart);
modalCartMain.addEventListener("click", (e) => {
e.stopPropagation();
});
</code>
<code>const cartIcon = document.querySelector(".cart-icon"); const modalCart = document.querySelector(".modal-cart-block"); const modalCartMain = document.querySelector(".modal-cart-block .modal-cart-main"); const closeCartIcon = document.querySelector(".modal-cart-main .close-btn"); const continueCartIcon = document.querySelector(".modal-cart-main .continue"); const addCartBtns = document.querySelectorAll(".add-cart-btn"); const openModalCart = () => { modalCartMain.classList.add("open"); }; const closeModalCart = () => { modalCartMain.classList.remove("open"); }; function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const csrftoken = getCookie('csrftoken'); const addToCart = (productId, colorVariation, sizeVariation, quantity) => { console.log('Product ID:', productId); fetch('/cart/add/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken, }, body: JSON.stringify({ product_id: productId, color_variation: colorVariation, size_variation: sizeVariation, quantity: quantity }), }) .then(response => { if (!response.ok) { throw new Error('Failed to add product to cart'); } return response.json(); }) .then(data => { if (data.success) { console.log('Product added successfully:', data); updateCartModalContent(); // Ensure this function is called immediately after adding the product openModalCart(); } }) .catch(error => console.error('Error:', error)); }; document.addEventListener("DOMContentLoaded", function() { let selectedColor = null; let selectedSize = null; document.querySelectorAll('.color-item').forEach(function (item) { item.addEventListener('click', function () { selectedColor = this.dataset.variationId; document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color; }); }); document.querySelectorAll('.size-item').forEach(function (item) { item.addEventListener('click', function () { selectedSize = this.dataset.variationId; document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size; }); }); document.querySelector('.add-cart-btn').addEventListener('click', function () { const productId = this.dataset.productId; const quantity = document.querySelector('.quantity').textContent; addToCart(productId, selectedColor, selectedSize, quantity); }); const plusIcons = document.querySelectorAll(".ph-plus"); const minusIcons = document.querySelectorAll(".ph-minus"); plusIcons.forEach(icon => { icon.addEventListener("click", function () { const quantityElement = this.closest('.quantity-block').querySelector('.quantity'); let quantity = parseInt(quantityElement.textContent); quantityElement.textContent = ++quantity; }); }); minusIcons.forEach(icon => { icon.addEventListener("click", function () { const quantityElement = this.closest('.quantity-block').querySelector('.quantity'); let quantity = parseInt(quantityElement.textContent); if (quantity > 1) { quantityElement.textContent = --quantity; } }); }); }); function updateQuantity(itemId, action) { fetch('/cart/update_quantity/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken, }, body: JSON.stringify({ item_id: itemId, action: action }), }) .then(response => { if (!response.ok) { throw new Error('Failed to update quantity'); } return response.json(); }) .then(data => { if (data.success) { updateCartModalContent(); } }) .catch(error => console.error('Error:', error)); } function updateCartModalContent() { console.log('Updating cart modal content...'); fetchCartData() .then((cartData) => { console.log('Cart data fetched:', cartData); const cartItemsContainer = document.querySelector('.modal-cart-main .list-product'); cartItemsContainer.innerHTML = ''; if (cartData.items.length === 0) { cartItemsContainer.innerHTML = '<p class="mt-1">No product in cart</p>'; } else { cartData.items.forEach((item) => { const cartItem = createCartItemElement(item); cartItemsContainer.appendChild(cartItem); }); } const subtotalElement = document.querySelector('.modal-cart-main .total-price'); const subtotal = parseFloat(cartData.subtotal) || 0; subtotalElement.textContent = `$${subtotal.toFixed(2)}`; }) } function fetchCartData() { return fetch('/cart/data/') .then((response) => response.json()) .then((data) => data); } function createCartItemElement(item) { console.log('Creating cart item element for:', item); const cartItemElement = document.createElement('div'); cartItemElement.classList.add('item', 'py-5', 'flex', 'items-center', 'justify-between', 'gap-3', 'border-b', 'border-line'); cartItemElement.dataset.item = item.id; const imageUrl = item.product.image || '/static/path/to/default-image.png'; cartItemElement.innerHTML = ` <div class="infor flex items-center gap-3 w-full"> <div class="bg-img w-[100px] aspect-square flex-shrink-0 rounded-lg overflow-hidden"> <img src="${imageUrl}" alt="product" class="w-full h-full"> </div> <div class="w-full"> <div class="flex items-center justify-between w-full"> <div class="name text-button">${item.product.name}</div> <div class="remove-cart-btn remove-btn caption1 font-semibold text-red underline cursor-pointer"> Remove </div> </div> <div class="flex items-center justify-between gap-2 mt-3 w-full"> <div class="flex items-center text-secondary2 capitalize"> ${item.product.color || 'N/A'}/${item.product.size || 'N/A'} </div> <div class="product-price text-title">$${item.product.price}</div> </div> </div> </div> `; return cartItemElement; } cartIcon.addEventListener("click", openModalCart); modalCart.addEventListener("click", closeModalCart); closeCartIcon.addEventListener("click", closeModalCart); continueCartIcon.addEventListener("click", closeModalCart); modalCartMain.addEventListener("click", (e) => { e.stopPropagation(); }); </code>
const cartIcon = document.querySelector(".cart-icon");
const modalCart = document.querySelector(".modal-cart-block");
const modalCartMain = document.querySelector(".modal-cart-block .modal-cart-main");
const closeCartIcon = document.querySelector(".modal-cart-main .close-btn");
const continueCartIcon = document.querySelector(".modal-cart-main .continue");
const addCartBtns = document.querySelectorAll(".add-cart-btn");

const openModalCart = () => {
  modalCartMain.classList.add("open");
};

const closeModalCart = () => {
  modalCartMain.classList.remove("open");
};

function getCookie(name) {
  let cookieValue = null;
  if (document.cookie && document.cookie !== '') {
    const cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === (name + '=')) {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

const csrftoken = getCookie('csrftoken');

const addToCart = (productId, colorVariation, sizeVariation, quantity) => {
  console.log('Product ID:', productId);

  fetch('/cart/add/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken,
    },
    body: JSON.stringify({
      product_id: productId,
      color_variation: colorVariation,
      size_variation: sizeVariation,
      quantity: quantity
    }),
  })
    .then(response => {
      if (!response.ok) {
        throw new Error('Failed to add product to cart');
      }
      return response.json();
    })
    .then(data => {
      if (data.success) {
        console.log('Product added successfully:', data);
        updateCartModalContent(); // Ensure this function is called immediately after adding the product
        openModalCart();
      }
    })
    .catch(error => console.error('Error:', error));
};

document.addEventListener("DOMContentLoaded", function() {
  let selectedColor = null;
  let selectedSize = null;

  document.querySelectorAll('.color-item').forEach(function (item) {
    item.addEventListener('click', function () {
      selectedColor = this.dataset.variationId;
      document.getElementById('selected-color').textContent = 'Color: ' + this.dataset.color;
    });
  });

  document.querySelectorAll('.size-item').forEach(function (item) {
    item.addEventListener('click', function () {
      selectedSize = this.dataset.variationId;
      document.getElementById('selected-size').textContent = 'Size: ' + this.dataset.size;
    });
  });

  document.querySelector('.add-cart-btn').addEventListener('click', function () {
    const productId = this.dataset.productId;
    const quantity = document.querySelector('.quantity').textContent;

    addToCart(productId, selectedColor, selectedSize, quantity);
  });

  const plusIcons = document.querySelectorAll(".ph-plus");
  const minusIcons = document.querySelectorAll(".ph-minus");

  plusIcons.forEach(icon => {
    icon.addEventListener("click", function () {
      const quantityElement = this.closest('.quantity-block').querySelector('.quantity');
      let quantity = parseInt(quantityElement.textContent);
      quantityElement.textContent = ++quantity;
    });
  });

  minusIcons.forEach(icon => {
    icon.addEventListener("click", function () {
      const quantityElement = this.closest('.quantity-block').querySelector('.quantity');
      let quantity = parseInt(quantityElement.textContent);
      if (quantity > 1) {
        quantityElement.textContent = --quantity;
      }
    });
  });
});

function updateQuantity(itemId, action) {
  fetch('/cart/update_quantity/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken,
    },
    body: JSON.stringify({ item_id: itemId, action: action }),
  })
    .then(response => {
      if (!response.ok) {
        throw new Error('Failed to update quantity');
      }
      return response.json();
    })
    .then(data => {
      if (data.success) {
        updateCartModalContent();
      }
    })
    .catch(error => console.error('Error:', error));
}

function updateCartModalContent() {
  console.log('Updating cart modal content...');
  fetchCartData()
    .then((cartData) => {
      console.log('Cart data fetched:', cartData);
      const cartItemsContainer = document.querySelector('.modal-cart-main .list-product');
      cartItemsContainer.innerHTML = '';

      if (cartData.items.length === 0) {
        cartItemsContainer.innerHTML = '<p class="mt-1">No product in cart</p>';
      } else {
        cartData.items.forEach((item) => {
          const cartItem = createCartItemElement(item);
          cartItemsContainer.appendChild(cartItem);
        });
      }

      const subtotalElement = document.querySelector('.modal-cart-main .total-price');
      const subtotal = parseFloat(cartData.subtotal) || 0;
      subtotalElement.textContent = `$${subtotal.toFixed(2)}`;
    })
}

function fetchCartData() {
  return fetch('/cart/data/')
    .then((response) => response.json())
    .then((data) => data);
}

function createCartItemElement(item) {
  console.log('Creating cart item element for:', item);
  const cartItemElement = document.createElement('div');
  cartItemElement.classList.add('item', 'py-5', 'flex', 'items-center', 'justify-between', 'gap-3', 'border-b', 'border-line');
  cartItemElement.dataset.item = item.id;

  const imageUrl = item.product.image || '/static/path/to/default-image.png';

  cartItemElement.innerHTML = `
    <div class="infor flex items-center gap-3 w-full">
      <div class="bg-img w-[100px] aspect-square flex-shrink-0 rounded-lg overflow-hidden">
        <img src="${imageUrl}" alt="product" class="w-full h-full">
      </div>
      <div class="w-full">
        <div class="flex items-center justify-between w-full">
          <div class="name text-button">${item.product.name}</div>
          <div class="remove-cart-btn remove-btn caption1 font-semibold text-red underline cursor-pointer">
            Remove
          </div>
        </div>
        <div class="flex items-center justify-between gap-2 mt-3 w-full">
          <div class="flex items-center text-secondary2 capitalize">
            ${item.product.color || 'N/A'}/${item.product.size || 'N/A'}
          </div>
          <div class="product-price text-title">$${item.product.price}</div>
        </div>
      </div>
    </div>
  `;

  return cartItemElement;
}

cartIcon.addEventListener("click", openModalCart);
modalCart.addEventListener("click", closeModalCart);
closeCartIcon.addEventListener("click", closeModalCart);
continueCartIcon.addEventListener("click", closeModalCart);

modalCartMain.addEventListener("click", (e) => {
  e.stopPropagation();
});

Now when I click add to cart button, my console says:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Product ID from button: 9
main.js:500
POST http://127.0.0.1:8000/cart/ 400 (Bad Request)
addToCart @ main.js:500
(anonymous) @ main.js:596
main.js:520 Error: Error: Failed to add product to cart
at main.js:510:15
(anonymous) @ main.js:520
Promise.catch (async)
addToCart @ main.js:520
(anonymous) @ main.js:596
main.js:500
POST http://127.0.0.1:8000/cart/ 400 (Bad Request)
addToCart @ main.js:500
(anonymous) @ main.js:681
main.js:520 Error: Error: Failed to add product to cart
at main.js:510:15
</code>
<code>Product ID from button: 9 main.js:500 POST http://127.0.0.1:8000/cart/ 400 (Bad Request) addToCart @ main.js:500 (anonymous) @ main.js:596 main.js:520 Error: Error: Failed to add product to cart at main.js:510:15 (anonymous) @ main.js:520 Promise.catch (async) addToCart @ main.js:520 (anonymous) @ main.js:596 main.js:500 POST http://127.0.0.1:8000/cart/ 400 (Bad Request) addToCart @ main.js:500 (anonymous) @ main.js:681 main.js:520 Error: Error: Failed to add product to cart at main.js:510:15 </code>
Product ID from button: 9
main.js:500 
        
        
       POST http://127.0.0.1:8000/cart/ 400 (Bad Request)
addToCart @ main.js:500
(anonymous) @ main.js:596
main.js:520 Error: Error: Failed to add product to cart
    at main.js:510:15
(anonymous) @ main.js:520
Promise.catch (async)
addToCart @ main.js:520
(anonymous) @ main.js:596
main.js:500 
        
        
       POST http://127.0.0.1:8000/cart/ 400 (Bad Request)
addToCart @ main.js:500
(anonymous) @ main.js:681
main.js:520 Error: Error: Failed to add product to cart
    at main.js:510:15

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật