diff --git a/Dockerfile b/Dockerfile
index c304f92..98ab9f3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,7 +9,7 @@ WORKDIR /code
COPY requirements.txt /code
RUN apt-get update && \
- apt-get install -y git nginx gettext vim && \
+ apt-get install -y gcc git nginx gettext vim libpq-dev && \
pip install -r requirements.txt && \
apt-get autoremove -y
diff --git a/shop/migrations/0002_wishlistedproduct.py b/shop/migrations/0002_wishlistedproduct.py
new file mode 100644
index 0000000..3134301
--- /dev/null
+++ b/shop/migrations/0002_wishlistedproduct.py
@@ -0,0 +1,62 @@
+# Generated by Django 5.1.3 on 2025-01-02 09:35
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("shop", "0001_initial"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="WishlistedProduct",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "creation_date",
+ models.DateTimeField(
+ auto_now_add=True, verbose_name="fecha de creación"
+ ),
+ ),
+ (
+ "last_modification_date",
+ models.DateTimeField(
+ auto_now=True, verbose_name="fecha de última modificación"
+ ),
+ ),
+ (
+ "product",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="shop.product",
+ verbose_name="producto",
+ ),
+ ),
+ (
+ "user",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="usuario",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "productos deseados",
+ "verbose_name_plural": "productos deseados",
+ },
+ ),
+ ]
diff --git a/shop/models.py b/shop/models.py
index ddfe4ab..857012e 100644
--- a/shop/models.py
+++ b/shop/models.py
@@ -501,3 +501,12 @@ class ShippingMethod(models.Model):
class Meta:
verbose_name = _("método de envío")
verbose_name_plural = _("métodos de envío")
+
+
+class WishlistedProduct(TimestampedModel):
+ user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('usuario'))
+ product = models.ForeignKey('shop.Product', on_delete=models.CASCADE, verbose_name=_('producto'))
+
+ class Meta:
+ verbose_name = _('productos deseados')
+ verbose_name_plural = _('productos deseados')
diff --git a/web/templates/components/products/wishlist_button.html b/web/templates/components/products/wishlist_button.html
new file mode 100644
index 0000000..3e24ef3
--- /dev/null
+++ b/web/templates/components/products/wishlist_button.html
@@ -0,0 +1,20 @@
+{% load i18n %}
+{% if is_wishlisted %}
+
+{% else %}
+
+{% endif %}
\ No newline at end of file
diff --git a/web/templates/web/product_detail.html b/web/templates/web/product_detail.html
index baeb010..c8737dc 100644
--- a/web/templates/web/product_detail.html
+++ b/web/templates/web/product_detail.html
@@ -51,15 +51,11 @@
-
- {% include 'components/icons/like.svg' %}
- {% translate 'Añadir a lista de deseados' %}
-
+
+ {% if user.is_authenticated %}
+
+
+ {% endif %}
{% include 'components/cart/add_to_cart.html' %}
diff --git a/web/urls.py b/web/urls.py
index 3eb3d71..71ba9a5 100644
--- a/web/urls.py
+++ b/web/urls.py
@@ -1,11 +1,13 @@
from django.urls import path
-from web.views.components import cart, cart_dropdown, list_products
+from web.views.components import cart, cart_dropdown, list_products, wishlist_button
from web.views.users import login, logout, my_account, register, reset_password
from web.views.web import (
add_cart_item,
+ add_to_wishlist,
cart_detail,
delete_cart_item,
+ delete_from_wishlist,
index,
manifest,
product_detail,
@@ -19,19 +21,26 @@ urlpatterns = [
path("categories/
/", category_view, name="category_view"),
path("products///", product_detail, name="product_detail"),
path("cart/", cart_detail, name="cart_detail"),
+
# users
path("login/", login, name="login"),
path("logout/", logout, name="logout"),
path("register/", register, name="register"),
path("reset-password/", reset_password, name="reset-password"),
path("my-account/", my_account, name="my_account"),
+
# components
path("web/components/products/", list_products, name="list_products"),
path("web/components/cart-dropdown/", cart_dropdown, name="cart_dropdown"),
path("web/components/cart/", cart, name="cart"),
+ path("web/components/wishlist-button//", wishlist_button, name="wishlist_button"),
+
# api
path("web/add-cart-item/", add_cart_item, name="add_cart_item"),
path("web/delete-cart-item//", delete_cart_item, name="delete_cart_item"),
+ path("web/add-to-wishlist/", add_to_wishlist, name="add_to_wishlist"),
+ path("web/delete-from-wishlist//", delete_from_wishlist, name="delete_from_wishlist"),
+
# manifest
path("manifest.json", manifest, name="manifest_json"),
]
diff --git a/web/views/components.py b/web/views/components.py
index a3fc175..271d779 100644
--- a/web/views/components.py
+++ b/web/views/components.py
@@ -1,10 +1,10 @@
from decimal import Decimal
-from django.shortcuts import render
+from django.shortcuts import render, get_object_or_404
from django.views.generic import TemplateView
from shop.filters import ProductFilter
-from shop.models import CartItem, Product, ProductPrice
+from shop.models import CartItem, Product, ProductPrice, WishlistedProduct
from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
from web.utils import get_or_create_cart
@@ -75,4 +75,18 @@ def cart(request, *args, **kwargs):
return response
+class WishlistButton(TemplateView):
+ template_name = 'components/products/wishlist_button.html'
+
+ def get_context_data(self, **kwargs):
+ product = get_object_or_404(Product, pk=self.kwargs.get('pk'))
+ is_wishlisted = WishlistedProduct.objects.filter(user=self.request.user, product=product).exists()
+
+ return {
+ "is_wishlisted": is_wishlisted,
+ "product": product,
+ }
+
+
list_products = ListProducts.as_view()
+wishlist_button = WishlistButton.as_view()
diff --git a/web/views/web.py b/web/views/web.py
index fe18543..c9e20e4 100644
--- a/web/views/web.py
+++ b/web/views/web.py
@@ -4,7 +4,7 @@ from django.utils.text import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView
-from shop.models import CartItem, Product, ProductCategory
+from shop.models import CartItem, Product, ProductCategory, WishlistedProduct
from web.models import WebSettings
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
from web.utils import get_or_create_cart
@@ -114,6 +114,37 @@ def delete_cart_item(request, pk, *args, **kwargs):
return HttpResponse(status=204, headers={"HX-Trigger": "updated-cart"})
+@require_http_methods(
+ [
+ "POST",
+ ]
+)
+def add_to_wishlist(request, *args, **kwargs):
+ product = get_object_or_404(Product, pk=request.POST.get("product"))
+ response = HttpResponse(status=201, headers={"HX-Trigger": "updated-wishlist"})
+
+ if request.user.is_anonymous:
+ return HttpResponse(status=400)
+
+ WishlistedProduct.objects.get_or_create(
+ user=request.user,
+ product=product,
+ )
+
+ return response
+
+
+@require_http_methods(
+ [
+ "POST",
+ ]
+)
+def delete_from_wishlist(request, pk, *args, **kwargs):
+ wishlisted_item = get_object_or_404(WishlistedProduct, product_id=pk, user=request.user)
+ wishlisted_item.delete()
+ return HttpResponse(status=204, headers={"HX-Trigger": "updated-wishlist"})
+
+
def manifest(request, *args, **kwargs):
settings = WebSettings.load()