feat: register page

This commit is contained in:
Pablo Moreno
2025-01-16 00:31:27 +01:00
parent 1a518a36d5
commit 66303392dd
6 changed files with 245 additions and 54 deletions
+5 -4
View File
@@ -5,7 +5,7 @@ from django.utils.text import gettext_lazy as _
class LoginForm(AuthenticationForm): class LoginForm(AuthenticationForm):
username = UsernameField( username = UsernameField(
widget=forms.TextInput( widget=forms.EmailInput(
attrs={ attrs={
"autofocus": True, "autofocus": True,
"class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 " "class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 "
@@ -20,9 +20,10 @@ class LoginForm(AuthenticationForm):
widget=forms.PasswordInput( widget=forms.PasswordInput(
attrs={ attrs={
"autocomplete": "current-password", "autocomplete": "current-password",
"class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 " "class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg "
"focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 " "focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 "
"dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500", "dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 "
"dark:focus:border-primary-500",
} }
), ),
) )
+33
View File
@@ -0,0 +1,33 @@
from django import forms
from django.contrib.auth.forms import SetPasswordMixin, BaseUserCreationForm
from django.contrib.auth.models import User
from django.forms import EmailField
class RegisterForm(BaseUserCreationForm, SetPasswordMixin):
password1, password2 = SetPasswordMixin.create_password_fields()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name in self.fields.keys():
self.fields[field_name].widget.attrs.update(
{
"class": "my-2 bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg "
"focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 "
"dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 "
"dark:focus:border-primary-500"
}
)
class Meta:
model = User
fields = (
"email",
"first_name",
"last_name",
)
field_classes = {
"email": EmailField,
"first_name": forms.CharField,
"last_name": forms.CharField,
}
+1 -2
View File
@@ -34,7 +34,6 @@
</label> </label>
{{ field }} {{ field }}
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@@ -44,7 +43,7 @@
</button> </button>
<p class="text-sm font-light text-gray-500 mt-4 dark:text-gray-400"> <p class="text-sm font-light text-gray-500 mt-4 dark:text-gray-400">
{% translate '¿Aún no tienes cuenta?' %} {% translate '¿Aún no tienes cuenta?' %}
<a href="#" class="font-medium text-primary-600 hover:underline dark:text-primary-500">{% translate 'Regístrate aquí' %}</a> <a href="{% url 'web:register' %}" class="font-medium text-primary-600 hover:underline dark:text-primary-500">{% translate 'Regístrate aquí' %}</a>
</p> </p>
</form> </form>
</div> </div>
+50 -41
View File
@@ -1,45 +1,54 @@
{% extends 'base/base.html' %} {% extends 'theme/base.html' %}
{% load i18n %}
{% block content %} {% block main %}
<section class="bg-gray-50 dark:bg-gray-900"> <section class="bg-gray-50 dark:bg-gray-900">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0"> <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<a href="#" class="flex items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
<img class="w-8 h-8 mr-2" src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/logo.svg" alt="logo"> <a href="{% url 'web:index' %}" class="flex items-center mb-6 text-2xl font-semibold text-gray-900 dark:text-white">
Flowbite <img class="w-8 h-8 mr-2" src="{{ logo }}" alt="logo">
{{ title }}
</a> </a>
<div class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8"> <div
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white"> class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
Create and account <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
</h1> <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
<form class="space-y-4 md:space-y-6" action="#"> {% translate 'Crea tu cuenta' %}
<div> </h1>
<label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label> <form action="{% url 'web:register' %}" method="post">
<input type="email" name="email" id="email" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-blue-500" placeholder="name@company.com" required=""> {% csrf_token %}
</div> {% for field in form %}
<div> <div class="my-2">
<label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password</label> <label for="{{ field.id_for_label }}"
<input type="password" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-blue-500" required=""> class="inline-block text-sm font-medium text-gray-900 dark:text-white mr-2">
</div> {{ field.label }}
<div> </label>
<label for="confirm-password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Confirm password</label>
<input type="confirm-password" name="confirm-password" id="confirm-password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-blue-500" required=""> {{ field }}
</div> </div>
<div class="flex items-start">
<div class="flex items-center h-5"> {% endfor %}
<input id="terms" aria-describedby="terms" type="checkbox" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800" required="">
</div> {% if errors %}
<div class="ml-3 text-sm"> {% for error in errors %}
<label for="terms" class="font-light text-gray-500 dark:text-gray-300">I accept the <a class="font-medium text-primary-600 hover:underline dark:text-primary-500" href="#">Terms and Conditions</a></label> <span class="inline-block text-sm font-medium text-red-900 dark:text-red-400 mr-2">{{ error }}</span>
</div> {% endfor %}
</div> {% endif %}
<button type="submit" 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">Create an account</button>
<p class="text-sm font-light text-gray-500 dark:text-gray-400"> <button class="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none
Already have an account? <a href="#" class="font-medium text-primary-600 hover:underline dark:text-primary-500">Login here</a> focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center
</p> dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800 mt-4">
</form> {% translate 'Crear cuenta' %}
</div> </button>
<p class="text-sm font-light text-gray-500 mt-4 dark:text-gray-400">
{% translate '¿Ya tienes cuenta?' %}
<a href="{% url 'web:login' %}" class="font-medium text-primary-600 hover:underline dark:text-primary-500">{% translate 'Inicia sesión aquí' %}</a>
</p>
</form>
</div>
</div> </div>
</div> </div>
</section> </section>
{% endblock %} {% endblock %}
+100
View File
@@ -0,0 +1,100 @@
from django.contrib.auth import get_user_model
from django.shortcuts import reverse
from django.test import TestCase
User = get_user_model()
class TestRegister(TestCase):
def setUp(self) -> None:
self.password = "theonering"
self.email = "sauron@mordor.middleearth"
self.first_name = "Sauron"
self.last_name = "The Lord of the Rings"
def test_create_user_account(self):
response = self.client.post(
reverse("web:register"),
{
"email": self.email,
"password1": self.password,
"password2": self.password,
"first_name": self.first_name,
"last_name": self.last_name,
},
)
user = User.objects.filter(
username=self.email,
email=self.email,
first_name=self.first_name,
last_name=self.last_name,
).first()
assert user is not None
assert response.status_code == 302
assert response.url == reverse("web:index")
def test_create_user_account_distinct_password(self):
response = self.client.post(
reverse("web:register"),
{
"email": self.email,
"password1": self.password,
"password2": "otherpassword",
"first_name": self.first_name,
"last_name": self.last_name,
},
)
assert not User.objects.filter(
username=self.email,
email=self.email,
first_name=self.first_name,
last_name=self.last_name,
).exists()
assert response.status_code == 400
def test_create_user_account_no_first_name(self):
response = self.client.post(
reverse("web:register"),
{
"email": self.email,
"password1": self.password,
"password2": "otherpassword",
"first_name": "",
"last_name": self.last_name,
},
)
assert not User.objects.filter(
username=self.email,
email=self.email,
first_name="",
last_name=self.last_name,
).exists()
assert response.status_code == 400
def test_create_user_account_no_last_name(self):
response = self.client.post(
reverse("web:register"),
{
"email": self.email,
"password1": self.password,
"password2": "otherpassword",
"first_name": self.first_name,
"last_name": "",
},
)
assert not User.objects.filter(
username=self.email,
email=self.email,
first_name=self.first_name,
last_name="",
).exists()
assert response.status_code == 400
+55 -6
View File
@@ -1,14 +1,19 @@
from django.contrib.auth import login as login_user from django.contrib.auth import login as login_user, authenticate
from django.contrib.auth import logout as logout_user from django.contrib.auth import logout as logout_user
from django.contrib.auth import get_user_model
from django.db import IntegrityError
from django.shortcuts import redirect, render, reverse from django.shortcuts import redirect, render, reverse
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
from shop.models import CustomerAddress from shop.models import CustomerAddress
from users.forms import LoginForm from users.forms import LoginForm, RegisterForm
from web.models import WebSettings from web.models import WebSettings
User = get_user_model()
class RedirectionMixin: class RedirectionMixin:
redirect_to = "" redirect_to = ""
@@ -55,6 +60,54 @@ class LoginView(View, RedirectionMixin):
return render(request, self.template_name, {"form": login_form}, status=400) return render(request, self.template_name, {"form": login_form}, status=400)
class RegisterView(TemplateView, RedirectionMixin):
template_name = "users/register.html"
redirect_to = "web:index"
form_class = RegisterForm
def get_context_data(self, **kwargs):
settings = WebSettings.load()
return {
"form": self.form_class(),
"title": settings.web_title,
"description": _("crear cuenta"),
}
def post(self, request, *args, **kwargs):
settings = WebSettings.load()
form = self.form_class(request.POST)
errors = []
try:
if form.is_valid():
user = form.save()
user.username = user.email
user.first_name = form.cleaned_data.get("first_name")
user.last_name = form.cleaned_data.get("last_name")
user.save()
login(request, user)
return redirect(self.get_redirection())
else:
errors = form.errors
except IntegrityError as e:
errors = [_("Ya existe un usuario con ese correo electrónico"), ]
return render(
request,
self.template_name,
{
"form": form,
"title": settings.web_title,
"description": _("Crear cuenta"),
"errors": errors,
},
status=400,
)
class LogoutView(View, RedirectionMixin): class LogoutView(View, RedirectionMixin):
redirect_to = "web:index" redirect_to = "web:index"
@@ -67,10 +120,6 @@ class LogoutView(View, RedirectionMixin):
return redirect(self.get_redirection()) return redirect(self.get_redirection())
class RegisterView(TemplateView):
template_name = "web/register.html"
class ResetPasswordView(TemplateView): class ResetPasswordView(TemplateView):
template_name = "web/reset-password.html" template_name = "web/reset-password.html"