feat: wishlist
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@ WORKDIR /code
|
|||||||
COPY requirements.txt /code
|
COPY requirements.txt /code
|
||||||
|
|
||||||
RUN apt-get update && \
|
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 && \
|
pip install -r requirements.txt && \
|
||||||
apt-get autoremove -y
|
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:
|
class Meta:
|
||||||
verbose_name = _("método de envío")
|
verbose_name = _("método de envío")
|
||||||
verbose_name_plural = _("métodos 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>
|
||||||
|
|
||||||
<div class="my-4 sm:gap-4 sm:items-center sm:flex sm:m-8">
|
<div class="my-4 sm:gap-4 sm:items-center sm:flex sm:m-8">
|
||||||
<a
|
|
||||||
href="#"
|
{% if user.is_authenticated %}
|
||||||
title=""
|
<div hx-get="{% url 'web:wishlist_button' pk=product.pk %}" hx-trigger="load, updated-wishlist">
|
||||||
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"
|
</div>
|
||||||
role="button"
|
{% endif %}
|
||||||
>
|
|
||||||
{% include 'components/icons/like.svg' %}
|
|
||||||
{% translate 'Añadir a lista de deseados' %}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{% include 'components/cart/add_to_cart.html' %}
|
{% include 'components/cart/add_to_cart.html' %}
|
||||||
|
|
||||||
|
|||||||
+10
-1
@@ -1,11 +1,13 @@
|
|||||||
from django.urls import path
|
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.users import login, logout, my_account, register, reset_password
|
||||||
from web.views.web import (
|
from web.views.web import (
|
||||||
add_cart_item,
|
add_cart_item,
|
||||||
|
add_to_wishlist,
|
||||||
cart_detail,
|
cart_detail,
|
||||||
delete_cart_item,
|
delete_cart_item,
|
||||||
|
delete_from_wishlist,
|
||||||
index,
|
index,
|
||||||
manifest,
|
manifest,
|
||||||
product_detail,
|
product_detail,
|
||||||
@@ -19,19 +21,26 @@ urlpatterns = [
|
|||||||
path("categories/<str:slug>/", category_view, name="category_view"),
|
path("categories/<str:slug>/", category_view, name="category_view"),
|
||||||
path("products/<int:pk>/<str:slug>/", product_detail, name="product_detail"),
|
path("products/<int:pk>/<str:slug>/", product_detail, name="product_detail"),
|
||||||
path("cart/", cart_detail, name="cart_detail"),
|
path("cart/", cart_detail, name="cart_detail"),
|
||||||
|
|
||||||
# users
|
# users
|
||||||
path("login/", login, name="login"),
|
path("login/", login, name="login"),
|
||||||
path("logout/", logout, name="logout"),
|
path("logout/", logout, name="logout"),
|
||||||
path("register/", register, name="register"),
|
path("register/", register, name="register"),
|
||||||
path("reset-password/", reset_password, name="reset-password"),
|
path("reset-password/", reset_password, name="reset-password"),
|
||||||
path("my-account/", my_account, name="my_account"),
|
path("my-account/", my_account, name="my_account"),
|
||||||
|
|
||||||
# components
|
# components
|
||||||
path("web/components/products/", list_products, name="list_products"),
|
path("web/components/products/", list_products, name="list_products"),
|
||||||
path("web/components/cart-dropdown/", cart_dropdown, name="cart_dropdown"),
|
path("web/components/cart-dropdown/", cart_dropdown, name="cart_dropdown"),
|
||||||
path("web/components/cart/", cart, name="cart"),
|
path("web/components/cart/", cart, name="cart"),
|
||||||
|
path("web/components/wishlist-button/<int:pk>/", wishlist_button, name="wishlist_button"),
|
||||||
|
|
||||||
# api
|
# api
|
||||||
path("web/add-cart-item/", add_cart_item, name="add_cart_item"),
|
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/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
|
# manifest
|
||||||
path("manifest.json", manifest, name="manifest_json"),
|
path("manifest.json", manifest, name="manifest_json"),
|
||||||
]
|
]
|
||||||
|
|||||||
+16
-2
@@ -1,10 +1,10 @@
|
|||||||
from decimal import Decimal
|
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 django.views.generic import TemplateView
|
||||||
|
|
||||||
from shop.filters import ProductFilter
|
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.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
|
||||||
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
|
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
|
||||||
from web.utils import get_or_create_cart
|
from web.utils import get_or_create_cart
|
||||||
@@ -75,4 +75,18 @@ def cart(request, *args, **kwargs):
|
|||||||
return response
|
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()
|
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.decorators.http import require_http_methods
|
||||||
from django.views.generic import TemplateView
|
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.models import WebSettings
|
||||||
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
|
from web.settings import ANONYMOUS_CART_ID_COOKIE_NAME
|
||||||
from web.utils import get_or_create_cart
|
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"})
|
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):
|
def manifest(request, *args, **kwargs):
|
||||||
settings = WebSettings.load()
|
settings = WebSettings.load()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user