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 %} +
+ + {% csrf_token %} + +
+{% else %} +
+ + {% csrf_token %} + +
+{% 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()