feat: wishlist
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{% load i18n %}
|
||||
{% if is_wishlisted %}
|
||||
<form action="" hx-post="{% url 'web:delete_from_wishlist' pk=product.pk %}" hx-swap="none">
|
||||
<input type="hidden" name="product" value="{{ product.pk }}">
|
||||
{% csrf_token %}
|
||||
<button class="flex items-center justify-center py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
||||
{% include 'components/icons/like.svg' %}
|
||||
{% translate 'Quitar de la lista de deseados' %}
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="" hx-post="{% url 'web:add_to_wishlist' %}" hx-swap="none">
|
||||
<input type="hidden" name="product" value="{{ product.pk }}">
|
||||
{% csrf_token %}
|
||||
<button class="flex items-center justify-center py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
||||
{% include 'components/icons/like.svg' %}
|
||||
{% translate 'Añadir a lista de deseados' %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
@@ -51,15 +51,11 @@
|
||||
</div>
|
||||
|
||||
<div class="my-4 sm:gap-4 sm:items-center sm:flex sm:m-8">
|
||||
<a
|
||||
href="#"
|
||||
title=""
|
||||
class="flex items-center justify-center py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
||||
role="button"
|
||||
>
|
||||
{% include 'components/icons/like.svg' %}
|
||||
{% translate 'Añadir a lista de deseados' %}
|
||||
</a>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<div hx-get="{% url 'web:wishlist_button' pk=product.pk %}" hx-trigger="load, updated-wishlist">
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'components/cart/add_to_cart.html' %}
|
||||
|
||||
|
||||
+10
-1
@@ -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/<str:slug>/", category_view, name="category_view"),
|
||||
path("products/<int:pk>/<str:slug>/", 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/<int:pk>/", wishlist_button, name="wishlist_button"),
|
||||
|
||||
# api
|
||||
path("web/add-cart-item/", add_cart_item, name="add_cart_item"),
|
||||
path("web/delete-cart-item/<int:pk>/", delete_cart_item, name="delete_cart_item"),
|
||||
path("web/add-to-wishlist/", add_to_wishlist, name="add_to_wishlist"),
|
||||
path("web/delete-from-wishlist/<int:pk>/", delete_from_wishlist, name="delete_from_wishlist"),
|
||||
|
||||
# manifest
|
||||
path("manifest.json", manifest, name="manifest_json"),
|
||||
]
|
||||
|
||||
+16
-2
@@ -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()
|
||||
|
||||
+32
-1
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user