feat: added user info form

This commit is contained in:
Pablo Moreno
2025-01-18 13:55:33 +01:00
parent 3995fa9c5a
commit 249521aa4a
10 changed files with 186 additions and 53 deletions
+68 -41
View File
@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-17 21:17+0100\n" "POT-Creation-Date: 2025-01-18 13:27+0100\n"
"PO-Revision-Date: 2025-01-17 21:23+0100\n" "PO-Revision-Date: 2025-01-18 13:28+0100\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: es\n" "Language: es\n"
@@ -688,11 +688,19 @@ msgstr "No se ha recibido ningún valor para Ds_MerchantParameters"
msgid "No se ha realizado el pago. Motivo: {reason}" msgid "No se ha realizado el pago. Motivo: {reason}"
msgstr "No se ha realizado el pago. Motivo: {reason}" msgstr "No se ha realizado el pago. Motivo: {reason}"
#: .\users\forms\login.py:13 .\users\forms\register.py:14 #: .\users\forms\info.py:13 .\users\forms\login.py:13
#: .\users\forms\reset_password.py:16 #: .\users\forms\register.py:14 .\users\forms\reset_password.py:16
msgid "pablo@shoppy.com" msgid "pablo@shoppy.com"
msgstr "pablo@shoppy.com" msgstr "pablo@shoppy.com"
#: .\users\forms\info.py:14 .\users\forms\register.py:15
msgid "Pablo"
msgstr "Pablo"
#: .\users\forms\info.py:15 .\users\forms\register.py:16
msgid "Moreno"
msgstr "Moreno"
#: .\users\forms\login.py:26 #: .\users\forms\login.py:26
msgid "Contraseña" msgid "Contraseña"
msgstr "Contraseña" msgstr "Contraseña"
@@ -701,14 +709,6 @@ msgstr "Contraseña"
msgid "Mantener sesión iniciada" msgid "Mantener sesión iniciada"
msgstr "Mantener sesión iniciada" msgstr "Mantener sesión iniciada"
#: .\users\forms\register.py:15
msgid "Pablo"
msgstr "Pablo"
#: .\users\forms\register.py:16
msgid "Moreno"
msgstr "Moreno"
#: .\users\forms\reset_password.py:42 #: .\users\forms\reset_password.py:42
msgid "El token no es válido o ha expirado" msgid "El token no es válido o ha expirado"
msgstr "El token no es válido o ha expirado" msgstr "El token no es válido o ha expirado"
@@ -810,7 +810,6 @@ msgid "Volver a inicio"
msgstr "Volver a inicio" msgstr "Volver a inicio"
#: .\web\templates\components\cart\cart.html:80 #: .\web\templates\components\cart\cart.html:80
#: .\web\templates\users\user_details.html:67
#: .\web\templates\web\order.html:74 #: .\web\templates\web\order.html:74
msgid "Dirección de envío" msgid "Dirección de envío"
msgstr "Dirección de envío" msgstr "Dirección de envío"
@@ -824,7 +823,7 @@ msgstr "E-Mail"
#: .\web\templates\components\cart\cart.html:111 #: .\web\templates\components\cart\cart.html:111
#: .\web\templates\components\cart\cart.html:199 #: .\web\templates\components\cart\cart.html:199
#: .\web\templates\components\cart\cart.html:204 #: .\web\templates\components\cart\cart.html:204
#: .\web\templates\users\user_details.html:40 #: .\web\templates\users\user_details.html:39
msgid "Nombre completo" msgid "Nombre completo"
msgstr "Nombre completo" msgstr "Nombre completo"
@@ -832,7 +831,7 @@ msgstr "Nombre completo"
#: .\web\templates\components\cart\cart.html:122 #: .\web\templates\components\cart\cart.html:122
#: .\web\templates\components\cart\cart.html:210 #: .\web\templates\components\cart\cart.html:210
#: .\web\templates\components\cart\cart.html:216 #: .\web\templates\components\cart\cart.html:216
#: .\web\templates\users\user_details.html:46 #: .\web\templates\users\user_details.html:45
msgid "Dirección" msgid "Dirección"
msgstr "Dirección" msgstr "Dirección"
@@ -840,7 +839,7 @@ msgstr "Dirección"
#: .\web\templates\components\cart\cart.html:136 #: .\web\templates\components\cart\cart.html:136
#: .\web\templates\components\cart\cart.html:224 #: .\web\templates\components\cart\cart.html:224
#: .\web\templates\components\cart\cart.html:230 #: .\web\templates\components\cart\cart.html:230
#: .\web\templates\users\user_details.html:72 #: .\web\templates\users\user_details.html:61
msgid "Localidad" msgid "Localidad"
msgstr "Localidad" msgstr "Localidad"
@@ -848,7 +847,7 @@ msgstr "Localidad"
#: .\web\templates\components\cart\cart.html:150 #: .\web\templates\components\cart\cart.html:150
#: .\web\templates\components\cart\cart.html:238 #: .\web\templates\components\cart\cart.html:238
#: .\web\templates\components\cart\cart.html:244 #: .\web\templates\components\cart\cart.html:244
#: .\web\templates\users\user_details.html:52 #: .\web\templates\users\user_details.html:51
msgid "Código postal" msgid "Código postal"
msgstr "Código postal" msgstr "Código postal"
@@ -856,7 +855,7 @@ msgstr "Código postal"
#: .\web\templates\components\cart\cart.html:165 #: .\web\templates\components\cart\cart.html:165
#: .\web\templates\components\cart\cart.html:253 #: .\web\templates\components\cart\cart.html:253
#: .\web\templates\components\cart\cart.html:259 #: .\web\templates\components\cart\cart.html:259
#: .\web\templates\users\user_details.html:76 #: .\web\templates\users\user_details.html:65
msgid "Provincia" msgid "Provincia"
msgstr "Provincia" msgstr "Provincia"
@@ -868,7 +867,6 @@ msgid "País"
msgstr "País" msgstr "País"
#: .\web\templates\components\cart\cart.html:190 #: .\web\templates\components\cart\cart.html:190
#: .\web\templates\users\user_details.html:65
#: .\web\templates\web\order.html:89 #: .\web\templates\web\order.html:89
msgid "Dirección de facturación" msgid "Dirección de facturación"
msgstr "Dirección de facturación" msgstr "Dirección de facturación"
@@ -930,17 +928,17 @@ msgid "Ver carrito"
msgstr "Ver carrito" msgstr "Ver carrito"
#: .\web\templates\components\generic\navbar.html:22 #: .\web\templates\components\generic\navbar.html:22
#: .\web\templates\components\generic\navbar.html:88 #: .\web\templates\components\generic\navbar.html:90
msgid "Inicio" msgid "Inicio"
msgstr "Inicio" msgstr "Inicio"
#: .\web\templates\components\generic\navbar.html:53 #: .\web\templates\components\generic\navbar.html:54
#: .\web\templates\components\users\user_dropdown.html:6 #: .\web\templates\components\users\user_dropdown.html:6
#: .\web\templates\users\user_details.html:10 #: .\web\templates\users\user_details.html:10
msgid "Mi cuenta" msgid "Mi cuenta"
msgstr "Mi cuenta" msgstr "Mi cuenta"
#: .\web\templates\components\generic\navbar.html:76 #: .\web\templates\components\generic\navbar.html:78
msgid "Abrir menú" msgid "Abrir menú"
msgstr "Abrir menú" msgstr "Abrir menú"
@@ -953,10 +951,15 @@ msgid "Cerrar"
msgstr "Cerrar" msgstr "Cerrar"
#: .\web\templates\components\products\list_filters.html:14 #: .\web\templates\components\products\list_filters.html:14
#: .\web\templates\components\products\list_filters.html:23 #: .\web\templates\components\products\list_filters.html:24
msgid "Buscar" msgid "Buscar"
msgstr "Buscar" msgstr "Buscar"
#: .\web\templates\components\users\info.html:24
#| msgid "Editar datos"
msgid "Actualizar datos"
msgstr "Actualizar datos"
#: .\web\templates\components\users\user_dropdown.html:12 #: .\web\templates\components\users\user_dropdown.html:12
#: .\web\templates\web\orders.html:10 #: .\web\templates\web\orders.html:10
msgid "Mis pedidos" msgid "Mis pedidos"
@@ -1056,19 +1059,19 @@ msgstr "Restablecer contraseña"
msgid "Se te ha enviado un correo electrónico para restablecer tu contraseña" msgid "Se te ha enviado un correo electrónico para restablecer tu contraseña"
msgstr "Se te ha enviado un correo electrónico para restablecer tu contraseña" msgstr "Se te ha enviado un correo electrónico para restablecer tu contraseña"
#: .\web\templates\users\user_details.html:24 #: .\web\templates\users\user_details.html:23
msgid "Mis direcciones" msgid "Mis direcciones"
msgstr "Mis direcciones" msgstr "Mis direcciones"
#: .\web\templates\users\user_details.html:32 #: .\web\templates\users\user_details.html:31
msgid "No hay ninguna dirección" msgid "No hay ninguna dirección"
msgstr "No hay ninguna dirección" msgstr "No hay ninguna dirección"
#: .\web\templates\users\user_details.html:56 #: .\web\templates\users\user_details.html:55
msgid "Teléfono" msgid "Teléfono"
msgstr "Teléfono" msgstr "Teléfono"
#: .\web\templates\users\user_details.html:90 #: .\web\templates\users\user_details.html:79
msgid "Editar datos" msgid "Editar datos"
msgstr "Editar datos" msgstr "Editar datos"
@@ -1128,55 +1131,59 @@ msgstr "Devuelto"
msgid "Ver pedido" msgid "Ver pedido"
msgstr "Ver pedido" msgstr "Ver pedido"
#: .\web\urls.py:41 #: .\web\urls.py:42
msgid "wishlist" msgid "wishlist"
msgstr "lista-deseados" msgstr "lista-deseados"
#: .\web\urls.py:42 #: .\web\urls.py:42 .\web\urls.py:46 .\web\urls.py:65 .\web\urls.py:87
msgid "/login/"
msgstr "/iniciar-sesion/"
#: .\web\urls.py:43
msgid "categories/<str:slug>/" msgid "categories/<str:slug>/"
msgstr "categorias/<str:slug>/" msgstr "categorias/<str:slug>/"
#: .\web\urls.py:43 #: .\web\urls.py:44
msgid "products/<int:pk>/<str:slug>/" msgid "products/<int:pk>/<str:slug>/"
msgstr "productos/<int:pk>/<str:slug>/" msgstr "productos/<int:pk>/<str:slug>/"
#: .\web\urls.py:44 #: .\web\urls.py:45
msgid "cart/" msgid "cart/"
msgstr "carrito/" msgstr "carrito/"
#: .\web\urls.py:45 #: .\web\urls.py:46
msgid "orders/" msgid "orders/"
msgstr "pedidos/" msgstr "pedidos/"
#: .\web\urls.py:46 #: .\web\urls.py:47
msgid "order/<str:uuid>/" msgid "order/<str:uuid>/"
msgstr "pedidos/<str:uuid>/" msgstr "pedidos/<str:uuid>/"
#: .\web\urls.py:48 #: .\web\urls.py:49
msgid "login/" msgid "login/"
msgstr "iniciar-sesion/" msgstr "iniciar-sesion/"
#: .\web\urls.py:49 #: .\web\urls.py:50
msgid "logout/" msgid "logout/"
msgstr "cerrar-sesion/" msgstr "cerrar-sesion/"
#: .\web\urls.py:50 #: .\web\urls.py:51
msgid "register/" msgid "register/"
msgstr "crear-cuenta/" msgstr "crear-cuenta/"
#: .\web\urls.py:51 #: .\web\urls.py:52
msgid "reset-password/" msgid "reset-password/"
msgstr "restablecer-contraseña/" msgstr "restablecer-contraseña/"
#: .\web\urls.py:53 #: .\web\urls.py:54
msgid "reset-password/<uidb64>/<token>/" msgid "reset-password/<uidb64>/<token>/"
msgstr "restablecer-contrasena/<uidb64>/<token>/" msgstr "restablecer-contrasena/<uidb64>/<token>/"
#: .\web\urls.py:58 #: .\web\urls.py:59
msgid "reset-password/email-sent/" msgid "reset-password/email-sent/"
msgstr "restablecer-contrasena/email-enviado/" msgstr "restablecer-contrasena/email-enviado/"
#: .\web\urls.py:62 #: .\web\urls.py:64
msgid "my-account/" msgid "my-account/"
msgstr "mi-cuenta/" msgstr "mi-cuenta/"
@@ -1201,6 +1208,26 @@ msgstr "Establecer nueva contraseña"
msgid "{settings.web_title} - {category.name}" msgid "{settings.web_title} - {category.name}"
msgstr "{settings.web_title} - {category.name}" msgstr "{settings.web_title} - {category.name}"
#: .\web\views\web.py:82 #: .\web\views\web.py:80
#, python-brace-format
msgid "{web_settings.web_title} - Carrito"
msgstr "{web_settings.web_title} - Carrito"
#: .\web\views\web.py:83
msgid "resumen del carrito" msgid "resumen del carrito"
msgstr "resumen del carrito" msgstr "resumen del carrito"
#: .\web\views\web.py:169 .\web\views\web.py:170
#, python-brace-format
msgid "{settings.web_title} - Comprar"
msgstr "{settings.web_title} - Comprar"
#: .\web\views\web.py:190
#, python-brace-format
msgid "{settings.web_title} - Lista de deseados"
msgstr "{settings.web_title} - Lista de deseados"
#: .\web\views\web.py:209
#, python-brace-format
msgid "{settings.web_title} - Mis pedidos"
msgstr "{settings.web_title} - Mis pedidos"
+1
View File
@@ -34,6 +34,7 @@ server {
add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Accept-Encoding "gzip, compress, br"
# gzip # gzip
gzip on; gzip on;
+24
View File
@@ -0,0 +1,24 @@
from django import forms
from django.contrib.auth import get_user_model
from django.utils.text import gettext_lazy as _
from web.mixins import StylingMixin
User = get_user_model()
class UserInfoForm(StylingMixin, forms.ModelForm):
styled_fields = ("email", "first_name", "last_name")
placeholder_for_field = {
"email": _("pablo@shoppy.com"),
"first_name": _("Pablo"),
"last_name": _("Moreno"),
}
class Meta:
model = User
fields = (
"email",
"first_name",
"last_name",
)
+28
View File
@@ -0,0 +1,28 @@
{% load i18n %}
{% load static %}
<form hx-post="{% url 'web:user_info_component' %}" hx-swap="none">
{% csrf_token %}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}"
class="inline-block text-sm font-medium text-gray-900 dark:text-white ml-2">
{{ field.label }}
</label>
{{ field }}
</div>
{% endfor %}
{% for k, error in form.errors.items %}
<span class="flex justify-center mb-2 text-sm font-medium text-red-900 dark:text-red-400">{{ error }}</span>
{% endfor %}
<button class="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none
focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600
dark:hover:bg-primary-700 dark:focus:ring-primary-800"
>
{% translate 'Actualizar datos' %}
</button>
</form>
+1 -1
View File
@@ -17,7 +17,7 @@
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white"> <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
{% translate 'Inicia sesión en tu cuenta' %} {% translate 'Inicia sesión en tu cuenta' %}
</h1> </h1>
<form action="{% url 'web:login' %}" method="post"> <form action="{% url 'web:login' %}{% querystring %}" method="post">
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
{% if field.id_for_label != 'id_remember_me' %} {% if field.id_for_label != 'id_remember_me' %}
+4 -5
View File
@@ -9,12 +9,11 @@
> >
{% translate 'Mi cuenta' %} {% translate 'Mi cuenta' %}
</h2> </h2>
<div
class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600"
hx-get="{% url 'web:user_info_component' %}" hx-trigger="load"
>
<div class="flex space-x-4">
<div>
<h2 class="flex items-center text-lg font-bold leading-none text-gray-900 dark:text-white sm:text-xl">
{{ user.first_name }} {{ user.last_name }}</h2>
</div>
</div> </div>
<div class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600"> <div class="mt-4 p-6 border border-gray-100 rounded-md dark:border-gray-600">
+13 -3
View File
@@ -9,6 +9,7 @@ from web.views.components import (
list_products, list_products,
list_wishlisted_products, list_wishlisted_products,
wishlist_button, wishlist_button,
user_info_component,
) )
from web.views.users import ( from web.views.users import (
login, login,
@@ -38,11 +39,11 @@ 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"),
path(_("orders/"), login_required(orders, login_url="/login/"), name="orders"), path(_("orders/"), login_required(orders, login_url=_("/login/")), name="orders"),
path(_("order/<str:uuid>/"), order_detail, name="order"), path(_("order/<str:uuid>/"), order_detail, name="order"),
# users # users
path(_("login/"), login, name="login"), path(_("login/"), login, name="login"),
@@ -59,7 +60,11 @@ i18n_resolvers = i18n_patterns(
reset_password_email_sent, reset_password_email_sent,
name="reset_password_email_sent", name="reset_password_email_sent",
), ),
path(_("my-account/"), my_account, name="my_account"), path(
_("my-account/"),
login_required(my_account, login_url=_("/login/")),
name="my_account",
),
) )
urlpatterns = [ urlpatterns = [
@@ -77,6 +82,11 @@ urlpatterns = [
wishlist_button, wishlist_button,
name="wishlist_button", name="wishlist_button",
), ),
path(
"web/components/user-info/",
login_required(user_info_component, login_url=_("/login/")),
name="user_info_component",
),
# 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"),
+38
View File
@@ -1,5 +1,6 @@
from decimal import Decimal from decimal import Decimal
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.views.generic import TemplateView from django.views.generic import TemplateView
@@ -12,7 +13,9 @@ from shop.models import (
ShippingMethod, ShippingMethod,
WishlistedProduct, WishlistedProduct,
) )
from users.forms.info import UserInfoForm
from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin from web.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
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
@@ -142,6 +145,41 @@ class WishlistButton(TemplateView):
} }
class UserInfoComponentView(TemplateView):
template_name = "components/users/info.html"
form_class = UserInfoForm
def get_context_data(self, **kwargs):
web_settings = WebSettings.load()
user = self.request.user
form = self.form_class(
initial={
"email": user.email,
"first_name": user.first_name,
"last_name": user.last_name,
}
)
return {
"form": form,
}
def post(self, request, *args, **kwargs):
user = self.request.user
form = self.form_class(request.POST)
if form.is_valid():
user.email = form.cleaned_data.get("email")
user.first_name = form.cleaned_data.get("first_name")
user.last_name = form.cleaned_data.get("last_name")
user.save()
return HttpResponse(status=200)
return render(request, self.template_name, {"form": form})
list_products = ListProducts.as_view() list_products = ListProducts.as_view()
list_wishlisted_products = ListWishlistedProducts.as_view() list_wishlisted_products = ListWishlistedProducts.as_view()
wishlist_button = WishlistButton.as_view() wishlist_button = WishlistButton.as_view()
user_info_component = UserInfoComponentView.as_view()
+7 -1
View File
@@ -1,3 +1,5 @@
import urllib
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth import login as login_user from django.contrib.auth import login as login_user
@@ -6,7 +8,7 @@ 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 from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode, urlencode
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
@@ -29,6 +31,10 @@ class LoginView(View, RedirectionMixin):
redirect_to = "web:index" redirect_to = "web:index"
login_form_class = LoginForm login_form_class = LoginForm
def get_redirection(self):
query_param_string = urlencode(self.request.GET)
return f'{reverse(self.redirect_to)}?{query_param_string.join("")}'
def get_login_form(self, request): def get_login_form(self, request):
return self.login_form_class(data=request.POST) return self.login_form_class(data=request.POST)
+2 -2
View File
@@ -187,7 +187,7 @@ class WishlistView(TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
settings = WebSettings.load() settings = WebSettings.load()
return { return {
"title": settings.web_title, "title": _(f"{settings.web_title} - Lista de deseados"),
"description": settings.web_description, "description": settings.web_description,
} }
@@ -206,7 +206,7 @@ class OrdersView(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin):
page = self.get_paginated_queryset(qs) page = self.get_paginated_queryset(qs)
return { return {
"title": settings.web_title, "title": _(f"{settings.web_title} - Mis pedidos"),
"page": page, "page": page,
"has_next_page": page.has_next(), "has_next_page": page.has_next(),
"has_previous_page": page.has_previous(), "has_previous_page": page.has_previous(),