feat: improve customer address views

This commit is contained in:
2025-01-19 12:43:13 +01:00
parent b48f84c337
commit 301b8f9b34
12 changed files with 157 additions and 27 deletions
+2 -1
View File
@@ -1,8 +1,9 @@
import datetime import datetime
from config.settings.environ import * # noqa
from django.utils.text import gettext_lazy as _ from django.utils.text import gettext_lazy as _
from config.settings.environ import * # noqa
APP_NAME = "Shop" APP_NAME = "Shop"
DESCRIPTION = "" DESCRIPTION = ""
VERSION = "0.1.0" VERSION = "0.1.0"
+2
View File
@@ -6,6 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{{ title }}</title> <title>{{ title }}</title>
<link rel="manifest" href="{% url 'web:manifest_json' %}">
{% include "theme/seo.html" %} {% include "theme/seo.html" %}
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/> <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/>
<script src="https://unpkg.com/htmx.org@2.0.3"></script> <script src="https://unpkg.com/htmx.org@2.0.3"></script>
@@ -0,0 +1,24 @@
# Generated by Django 5.1.4 on 2025-01-19 10:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("web", "0001_initial"),
]
operations = [
migrations.RemoveField(
model_name="websettings",
name="logo_240",
),
migrations.AddField(
model_name="websettings",
name="logo_256",
field=models.ImageField(
blank=True, null=True, upload_to="assets", verbose_name="logo 256x256"
),
),
]
+45 -1
View File
@@ -1,6 +1,9 @@
from django.conf import settings from django.conf import settings
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils.text import gettext_lazy as _ from django import forms
from django.db import models
from django.views.generic import TemplateView
from django.shortcuts import get_object_or_404, render
class FilteredQuerysetMixin: class FilteredQuerysetMixin:
@@ -55,3 +58,44 @@ class StylingMixin:
"placeholder": self.placeholder_for_field.get(field_name), "placeholder": self.placeholder_for_field.get(field_name),
} }
) )
class ReadUpdateView(TemplateView):
model: models.Model = None
form_class: forms.ModelForm = None
template_name: str = ""
ok_template_name: str = ""
error_template_name: str = ""
def get_model(self):
return self.model
def get_object(self):
pk = self.kwargs.get('pk')
cls = self.get_model()
return get_object_or_404(cls, pk=pk)
def get_initial_values(self, instance):
return {}
def get_context_data(self, **kwargs):
instance = self.get_object()
form = self.form_class(initial=self.get_initial_values(instance))
return {
"form": form,
}
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
instance = form.save()
return render(request, self.ok_template_name, {
"form": form,
"instance": instance,
})
return render(request, self.error_template_name, {
"form": form,
})
+23 -2
View File
@@ -1,5 +1,9 @@
from io import BytesIO
from django.core.files.base import ContentFile
from django.db import models from django.db import models
from django.utils.text import gettext_lazy as _ from django.utils.text import gettext_lazy as _
from PIL import Image
from config.models import SingletonModel from config.models import SingletonModel
@@ -75,13 +79,30 @@ class WebSettings(SingletonModel):
theme_color = models.CharField( theme_color = models.CharField(
max_length=9, default="", blank=True, null=True, verbose_name=_("color de tema") max_length=9, default="", blank=True, null=True, verbose_name=_("color de tema")
) )
logo_240 = models.ImageField(
upload_to="assets", blank=True, null=True, verbose_name=_("logo 240x240") logo_256 = models.ImageField(
upload_to="assets", blank=True, null=True, verbose_name=_("logo 256x256")
) )
logo_128 = models.ImageField( logo_128 = models.ImageField(
upload_to="assets", blank=True, null=True, verbose_name=_("logo 128x128") upload_to="assets", blank=True, null=True, verbose_name=_("logo 128x128")
) )
def get_resized_logo(self, size):
width, height = size
image = Image.open(self.logo)
image = image.resize(size, Image.Resampling.LANCZOS)
buffer = BytesIO()
image.save(fp=buffer, format="PNG")
return ContentFile(buffer.getvalue(), name=f"logo_{width}.png")
def save(self, *args, **kwargs):
if self.logo:
self.logo_128 = self.get_resized_logo((128, 128))
self.logo_256 = self.get_resized_logo((256, 256))
super().save(*args, **kwargs)
class Meta: class Meta:
verbose_name = _("ajustes de la web") verbose_name = _("ajustes de la web")
verbose_name_plural = _("ajustes de la web") verbose_name_plural = _("ajustes de la web")
+3 -1
View File
@@ -31,7 +31,9 @@
<span class="text-gray-500 dark:text-gray-400">{% translate 'No hay ninguna dirección' %}</span> <span class="text-gray-500 dark:text-gray-400">{% translate 'No hay ninguna dirección' %}</span>
{% endif %} {% endif %}
{% include 'components/users/retrieve_update_address.html' %} <div hx-get="{% url 'web:list_customer_addresses_component' %}" hx-trigger="load, updated_addresses">
</div>
</div> </div>
</div> </div>
+3 -1
View File
@@ -122,7 +122,9 @@ class TestLogin(TestCase):
) )
assert response.status_code == 302 assert response.status_code == 302
assert response.url == f'{reverse("web:index")}?next={reverse("web:my_account")}' assert (
response.url == f'{reverse("web:index")}?next={reverse("web:my_account")}'
)
response = self.client.get(response.url) response = self.client.get(response.url)
assert response.status_code == 302 assert response.status_code == 302
+2 -5
View File
@@ -16,11 +16,8 @@ class TestManifest(TestCase):
os.path.join(settings.BASE_DIR, "web", "tests", "images", "trolley.png"), os.path.join(settings.BASE_DIR, "web", "tests", "images", "trolley.png"),
"rb", "rb",
) as f: ) as f:
file_128 = SimpleUploadedFile("logo_128.png", f.read()) file = SimpleUploadedFile("logo.png", f.read())
self.settings.logo_128 = file_128 self.settings.logo = file
file_240 = SimpleUploadedFile("logo_240.png", f.read())
self.settings.logo_240 = file_240
self.settings.logo = file_240
self.settings.save() self.settings.save()
def test_manifest_view(self): def test_manifest_view(self):
+16 -3
View File
@@ -6,10 +6,12 @@ from django.utils.text import gettext_lazy as _
from web.views.components import ( from web.views.components import (
cart, cart,
cart_dropdown, cart_dropdown,
list_customer_addresses_component,
list_products, list_products,
list_wishlisted_products, list_wishlisted_products,
update_customer_address,
user_info_component,
wishlist_button, wishlist_button,
user_info_component, update_customer_address,
) )
from web.views.users import ( from web.views.users import (
login, login,
@@ -39,7 +41,9 @@ app_name = "web"
i18n_resolvers = i18n_patterns( i18n_resolvers = i18n_patterns(
path("", index, name="index"), path("", index, name="index"),
path(_("wishlist"), login_required(wishlist, login_url=_("/login/")), name="wishlist"), path(
_("wishlist"), login_required(wishlist, login_url=_("/login/")), name="wishlist"
),
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"),
@@ -96,7 +100,16 @@ urlpatterns = [
delete_from_wishlist, delete_from_wishlist,
name="delete_from_wishlist", name="delete_from_wishlist",
), ),
path("web/customer-address/<int:pk>/", update_customer_address, name="update_customer_address"), path(
"web/customer-address/<int:pk>/",
update_customer_address,
name="update_customer_address",
),
path(
"web/customer-addresses/",
list_customer_addresses_component,
name="list_customer_addresses_component",
),
# manifest # manifest
path("manifest.json", manifest, name="manifest_json"), path("manifest.json", manifest, name="manifest_json"),
] ]
+13 -3
View File
@@ -77,7 +77,7 @@ def cart_dropdown(request, *args, **kwargs):
) )
if not request.user.is_authenticated: if not request.user.is_authenticated:
response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict")
return response return response
@@ -127,7 +127,7 @@ def cart(request, *args, **kwargs):
) )
if not request.user.is_authenticated: if not request.user.is_authenticated:
response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict")
return response return response
@@ -179,13 +179,22 @@ class UserInfoComponentView(TemplateView):
return render(request, self.template_name, {"form": form}) return render(request, self.template_name, {"form": form})
class ListCustomerAddressComponent(TemplateView):
template_name = "components/users/retrieve_update_address.html"
def get_context_data(self, **kwargs):
return {
"customer_addresses": CustomerAddress.objects.filter(user=self.request.user)
}
def update_customer_address(request, pk): def update_customer_address(request, pk):
customer_address = get_object_or_404(CustomerAddress, pk=pk, user=request.user) customer_address = get_object_or_404(CustomerAddress, pk=pk, user=request.user)
form = CustomerAddressForm(request.POST, instance=customer_address) form = CustomerAddressForm(request.POST, instance=customer_address)
if form.is_valid(): if form.is_valid():
form.save() form.save()
return HttpResponse(status=200) return HttpResponse(status=200, headers={"HX-Trigger": "updated_addresses"})
return HttpResponse(form.errors, status=400) return HttpResponse(form.errors, status=400)
@@ -195,3 +204,4 @@ list_wishlisted_products = ListWishlistedProducts.as_view()
wishlist_button = WishlistButton.as_view() wishlist_button = WishlistButton.as_view()
user_info_component = UserInfoComponentView.as_view() user_info_component = UserInfoComponentView.as_view()
list_customer_addresses_component = ListCustomerAddressComponent.as_view()
+7 -3
View File
@@ -6,7 +6,11 @@ from django.contrib.auth.tokens import default_token_generator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import IntegrityError from django.db import IntegrityError
from django.shortcuts import redirect, render, reverse from django.shortcuts import redirect, render, reverse
from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode, urlencode from django.utils.http import (
url_has_allowed_host_and_scheme,
urlencode,
urlsafe_base64_decode,
)
from django.utils.text import gettext_lazy as _ from django.utils.text import gettext_lazy as _
from django.views.generic import TemplateView, View from django.views.generic import TemplateView, View
@@ -30,10 +34,10 @@ class LoginView(View, RedirectionMixin):
login_form_class = LoginForm login_form_class = LoginForm
def get_redirection(self): def get_redirection(self):
next_page = self.request.GET.get('next', '') next_page = self.request.GET.get("next", "")
if next_page: if next_page:
return f'{reverse(self.redirect_to)}?next={next_page}' return f"{reverse(self.redirect_to)}?next={next_page}"
return reverse(self.redirect_to) return reverse(self.redirect_to)
+17 -7
View File
@@ -36,8 +36,8 @@ class IndexView(TemplateView):
} }
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if self.request.GET.get('next'): if self.request.GET.get("next"):
return redirect(self.request.GET.get('next')) return redirect(self.request.GET.get("next"))
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
@@ -230,7 +230,7 @@ def add_cart_item(request, *args, **kwargs):
response = HttpResponse(status=201, headers={"HX-Trigger": "updated-cart"}) response = HttpResponse(status=201, headers={"HX-Trigger": "updated-cart"})
if created and request.user.is_anonymous: if created and request.user.is_anonymous:
response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid) response.set_cookie(ANONYMOUS_CART_ID_COOKIE_NAME, cart.uuid, samesite="strict")
# Comprobamos si existe una línea de carrito para ese carrito de ese producto # Comprobamos si existe una línea de carrito para ese carrito de ese producto
existing_cart_item = CartItem.objects.filter(cart=cart, product=product).first() existing_cart_item = CartItem.objects.filter(cart=cart, product=product).first()
@@ -309,11 +309,21 @@ def manifest(request, *args, **kwargs):
"orientation": "portrait", "orientation": "portrait",
} }
if settings.logo_240: if settings.logo:
data["icons"].append( data["icons"].append(
{ {
"src": settings.logo_240.url, "src": settings.logo.url,
"sizes": "240x240", "sizes": "512x512",
"type": "image/png",
"purpose": "maskable any",
}
)
if settings.logo_256:
data["icons"].append(
{
"src": settings.logo_256.url,
"sizes": "256x256",
"type": "image/png", "type": "image/png",
"purpose": "maskable any", "purpose": "maskable any",
} }
@@ -323,7 +333,7 @@ def manifest(request, *args, **kwargs):
data["icons"].append( data["icons"].append(
{ {
"src": settings.logo_128.url, "src": settings.logo_128.url,
"sizes": "240x240", "sizes": "128x128",
"type": "image/png", "type": "image/png",
"purpose": "maskable any", "purpose": "maskable any",
} }