From 2392de93a7b0d0841d0d55890cce2252a38ec4fa Mon Sep 17 00:00:00 2001 From: Michele de Barros Santos <michele.fenix.17@gmail.com> Date: Sun, 1 Oct 2023 21:10:31 -0300 Subject: [PATCH] feat: form login criar conta validacoes --- db.sqlite3 | Bin 159744 -> 159744 bytes filme/forms.py | 38 +++++++++++ filme/templates/criarconta.html | 55 +++++++++++++++- filme/templates/homepage.html | 19 ++++-- filme/templates/login.html | 41 +++++++++++- filme/urls.py | 4 +- filme/views.py | 38 +++++++++-- hashflix/settings.py | 6 ++ static/css/style_criarconta.css | 104 +++++++++++++++++++++++++++++- static/css/style_homepage.css | 13 ++++ static/css/style_login.css | 108 ++++++++++++++++++++++++++++++++ templates/base.html | 7 +-- 12 files changed, 412 insertions(+), 21 deletions(-) create mode 100644 filme/forms.py diff --git a/db.sqlite3 b/db.sqlite3 index 6606a28ea583329f2ab82fa769b7c8671f2ce9fb..be10f5a722d9a8e09d46d22bc79d7c1fe695ce63 100644 GIT binary patch delta 801 zcmaixO-vI(6vuaFx3#OlYgLS&p(Lfon6|UK-Cf$GkqRj(P)h*$@&M(d?Y6W9b{CY3 zARatwYHYl6m4jvzJb3nIOo(#O7()<ZLNFRJny3f2jTjORPA2coOn(3Wyu3HR&d#s1 zch8{0y|c}z@ML(Y?&KQ9g4+Vm;3M3EWw-_pVG3HoZPb%0D7t-4WGS}#x{f)l^m&w2 zCUWCLx~eCpl>#m<`z|mPMIANX_#}cthJE;@)qFrh#zxYE5L>jTJx6Y58J3EM?r^7& zVNb{Lam*85@Q{Fq_d9tZ=x+>iobf2bn*!r~)_vcGYZfVxYZi@q=WNZ*1rAqJ3!$*B zp}M-k#)c<HvSUepNKGerQF2M7yj;=Clf6BC9r4ta{?6`6jhFQviD)iAuWD_5?YcOc z*JsB_mo(L_sk6~Y?5Z@_(wvWUM~O_7@OUg43stJ(h=*`aP7eBoAZhdxE+G1em{Xck zxr8#|9WCVcpASknugJ>*St!YXa<%{5L?I|jUb9{z38fbu_;l$NAkKzim4a2+FdXyG zq1H|}p#ARn935sW^aCeisu<X$;3w?D4s4o&*8o<Ap%B81g=Q!g;itPk<mfcHEed|Y zC-??iCbtF`4>72WF;<2(GhIm*^UkvDVRL*8>`?F<e!y4QF$M3yWuYrP%_5pH1E%Rc za=T1^fo=G1l5gQenPkiY%D&}jsjy3riMdzo_=DFuo(kL$8}oB|LL%8D&ucR(8Odsi z5hWAIaQ=LWzJ!goJQ+&``A9UTc1*Vn_Fq@pgmgUIp4Df%@^flxG_DV5emOG`;XC_! i2NLRD=Q*)FtiSD*h$seDn(%tve}7#W#-G7)K>P<VWzxU^ delta 535 zcmZp8z}fJCbAmLZ&qNt#MxTudi{vF{GH~*%vGVWcf6m{;zlJ}W|0;hWzdOI$W<>)Y z{>f_k3XDvX-SvZn85kIZIny$8a#Q0|3o?uIQ!?`>uhG|J5ny9h-+WWw%z%+~vp~W* zHWscTM)l2)(sUUaIVKBa=x#2}P-2oZGB7gMHL%n*GEp!xwlcA>GBgAlXvv`K-=ZwZ zIr&nO-saU=MnEl_?`5k^VPoVsXW%ynIzf}4<pM*a?R0Z~##~X5ehUQy11ke_D^nvq zLvuqT3-jq`e3+!BU*=~t1nPRk!2f8oV8ac57FlLTPO#K81x6MQCjQS1{J;1=1LdCc zZ~v^%C>Q_~`@+EgoBs<?>;=CB8w)2RBP$aN3nwQh7tm=;{NEY)|L}hY%D(0oWnt!I zWMXE9NqqxJeFI9p;^$&z2I&LIE!toJMB4<I9_uL>=cSfqS|(>^BpR6~8x`c3W)>$^ zRFs!y<(QOaWMx-PXSQeB?3I%m<!cq{7gCm7mgH`d<y%<f9qQy&5tM9J5p1FBXr5Q% wUzipcU!0s_S{zlRTMiCILjzqyBjivpH@7g}=D_qoo}jJU5}4le3u0&n09@pjrvLx| diff --git a/filme/forms.py b/filme/forms.py new file mode 100644 index 00000000..36d24dd8 --- /dev/null +++ b/filme/forms.py @@ -0,0 +1,38 @@ +from django import forms +from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.forms import UserCreationForm +from .models import Usuario + + +class FormHomepage(forms.Form): + email = forms.EmailField(label=False) + + +class CustomLoginForm(AuthenticationForm): + username = forms.EmailField(required=True, widget=forms.TextInput( + attrs={'placeholder': 'Email'})) + + def clean_username(self): + username = self.cleaned_data.get('username') + # Exemplo de validação simples de formato de email: + if not username.endswith('@'): + raise forms.ValidationError( + 'Este campo deve ser um email válido.') + return username + + +class CriarContaForm(UserCreationForm): + # se o campo não for obrigatório dentro dos parenteses incluir (required=False) + email = forms.EmailField() + + class Meta: # Quando se cria um UserCreationForm ele espera uma class Meta indicando qual o modelo ele ira se basear + model = Usuario + fields = ('username', 'email', 'password1', 'password2') + + def clean_email(self): + email = self.cleaned_data.get('email') + if Usuario.objects.filter(email=email).exists(): + raise forms.ValidationError( + 'Já existe uma conta com este e-mail.' + ) + return email diff --git a/filme/templates/criarconta.html b/filme/templates/criarconta.html index 02f49a28..173c2f83 100644 --- a/filme/templates/criarconta.html +++ b/filme/templates/criarconta.html @@ -11,7 +11,60 @@ {% block content %} <div class="content"> - <h2>Criar Conta</h2> + <header class="header"> + <div class="content-form"> + <div class="title-form"> + Criação de Conta + </div> + + <form method="post" class="form-criarconta"> + {% csrf_token %} + <div class="form-group"> + <input type="text" name="{{ form.username.name }}" placeholder="Username (Email)" required> + <small>{{form.username.help_text }}</small> + </div> + <div class="form-group"> + {% if form.email.errors %} + <input type="text" class="is-invalid" name="{{ form.email.name }}" placeholder="Email" required> + <div class="invalid-feedback"> + {% for erro in form.email.errors %} + {{ erro }} + {% endfor %} + </div> + {% else %} + <input type="text" name="{{ form.email.name }}" placeholder="Email" required> + {% endif %} + <small>{{form.email.help_text }}</small> + </div> + <div class="form-group"> + <input type="password" name="{{ form.password1.name }}" placeholder="Senha" required> + <small>{{form.password1.help_text }}</small> + </div> + <div class="form-group"> + {% if form.password2.errors %} + <input type="password" class="is-invalid" name="{{ form.password2.name }}" placeholder="Confirmação de Senha" required> + <div class="invalid-feedback"> + {% for erro in form.password2.errors %} + {{ erro }} + {% endfor %} + </div> + {% else %} + <input type="password" name="{{ form.password2.name }}" placeholder="Confirmação de Senha" required> + <small>{{form.password2.help_text }}</small> + {% endif %} + </div> + + <div class="form-group"> + <button type="submit" name="button"> + <a href="{% url 'filme:login' %}"> + Finalizar Cadastro + </a> + </button> + </div> + </form> + + </div> + </header> </div> {% endblock %} diff --git a/filme/templates/homepage.html b/filme/templates/homepage.html index 527761d3..707a536a 100644 --- a/filme/templates/homepage.html +++ b/filme/templates/homepage.html @@ -19,13 +19,22 @@ Quer começar? Coloque seu e-mail abaixo para acessar. </div> <form method="POST" action="" class="form-access"> - <input type="text" name="email" id="email" class="input-email"/> - <div class="btn-access"> - <a href="#"> + {% csrf_token %} + {% if form.email.errors %} + <input type="text" class="input-email is-invalid" name="{{ form.email.name }}" placeholder="E-mail"/> + <div class="invalid-feedback"> + {% for erro in form.email.errors %} + {{ erro }} + {% endfor %} + </div> + + {% else %} + <input type="text" class="input-email" name="{{ form.email.name }}" placeholder="E-mail"/> + {% endif %} + <button type="submit" class="btn-access"> <span>Acesse</span> <ion-icon name="chevron-forward-outline"></ion-icon> - </a> - </div> + </button> </form> </header> diff --git a/filme/templates/login.html b/filme/templates/login.html index 3920204e..eddac825 100644 --- a/filme/templates/login.html +++ b/filme/templates/login.html @@ -11,6 +11,45 @@ {% block content %} <div class="content"> - <h2>Login</h2> + <header class="header"> + <div class="content-form"> + <div class="title-form"> + Entrar + </div> + + <form method="post" class="form-login"> + {% csrf_token %} + <div class="form-group"> + {% if form.username.errors %} + <input type="text" class="is-invalid" name="{{ form.username.name }}" placeholder="Email" required> + <div class="invalid-feedback"> + {% for erro in form.username.errors %} + {{ erro }} + {% endfor %} + </div> + {% else %} + <input type="text" name="{{ form.username.name }}" placeholder="Email" required> + {% endif %} + </div> + <div class="form-group"> + <input type="password" name="{{ form.password.name }}" placeholder="Senha" required> + </div> + + <div class="form-group"> + <button type="submit" name="button"> + <a href="{% url 'filme:login' %}"> + Entrar + </a> + </button> + </div> + </form> + + <div class="inscrever"> + Não tem uma conta ainda? + <p><a href="{% url 'filme:criarconta' %}">Clique aqui para criar uma conta</a></p> + </div> + + </div> + </header> </div> {% endblock %} diff --git a/filme/urls.py b/filme/urls.py index 24284855..7ca25783 100644 --- a/filme/urls.py +++ b/filme/urls.py @@ -1,5 +1,5 @@ from django.urls import path, include -from .views import Homepage, HomeFilmes, DetalhesFilme, PesquisaFilme, EditarPerfil, CriarConta +from .views import Homepage, HomeFilmes, DetalhesFilme, PesquisaFilme, EditarPerfil, CriarConta, CustomLoginView from django.contrib.auth import views as auth_view @@ -11,7 +11,7 @@ path('filmes/', HomeFilmes.as_view(), name='homefilmes'), path('filmes/<int:pk>', DetalhesFilme.as_view(), name='detalhesfilme'), path('pesquisa/', PesquisaFilme.as_view(), name='pesquisafilme'), - path('login/', auth_view.LoginView.as_view( + path('login/', CustomLoginView.as_view( template_name='login.html'), name='login'), path('logout/', auth_view.LogoutView.as_view( template_name='logout.html'), name='logout'), diff --git a/filme/views.py b/filme/views.py index d9302db2..100ab1c4 100644 --- a/filme/views.py +++ b/filme/views.py @@ -1,13 +1,16 @@ -from django.shortcuts import render, redirect -from .models import Filme -from django.views.generic import TemplateView, ListView, DetailView +from django.shortcuts import render, redirect, reverse +from .models import Filme, Usuario +from django.views.generic import TemplateView, ListView, DetailView, FormView from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.views import LoginView +from .forms import CustomLoginForm, CriarContaForm, FormHomepage # Create your views here. -class Homepage(TemplateView): +class Homepage(FormView): template_name = 'homepage.html' + form_class = FormHomepage def get(self, request, *args, **kwargs): if request.user.is_authenticated: @@ -17,6 +20,15 @@ def get(self, request, *args, **kwargs): # Redireciona para a url final da view neste caso a homepage return super().get(request, *args, **kwargs) + def get_success_url(self): + email = self.request.POST.get("email") + usuarios = Usuario.objects.filter(email=email) + if usuarios: + return reverse('filme:login') + else: + return reverse('filme:criarconta') + # print(self.request.POST) + class HomeFilmes(LoginRequiredMixin, ListView): template_name = "homefilmes.html" @@ -68,5 +80,21 @@ class EditarPerfil(LoginRequiredMixin, TemplateView): template_name = 'editarperfil.html' -class CriarConta(TemplateView): +class CriarConta(FormView): template_name = 'criarconta.html' + form_class = CriarContaForm + + # Verifica se todos os campos do formulário foram preenchido corretamente e salva no banco de dados + def form_valid(self, form): + form.save() + return super().form_valid(form) + + # Se a validação for bem sucedida tem que redirecionar para um link + + def get_success_url(self): + return reverse('filme:login') # Retorna o texto do link + + +class CustomLoginView(LoginView): + template_name = 'login.html' + form_class = CustomLoginForm diff --git a/hashflix/settings.py b/hashflix/settings.py index 7cafbfdd..58e4f585 100644 --- a/hashflix/settings.py +++ b/hashflix/settings.py @@ -39,6 +39,8 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'filme', + # 'crispy_forms', + # 'crispy_bootstrap5' ] MIDDLEWARE = [ @@ -144,3 +146,7 @@ LOGIN_REDIRECT_URL = 'filme:homefilmes' LOGIN_URL = 'filme:login' + +# CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' + +# CRISPY_TEMPLATE_PACK = 'bootstrap5' diff --git a/static/css/style_criarconta.css b/static/css/style_criarconta.css index fc54b212..84d18ebb 100644 --- a/static/css/style_criarconta.css +++ b/static/css/style_criarconta.css @@ -1,3 +1,103 @@ -.content { - padding-top: 4.68rem; +@import url("https://fonts.googleapis.com/css2?family=Montserrat&family=Roboto:wght@500&display=swap"); + +.header { + min-height: 100vh; + position: relative; /* Adicione uma posição relativa para que possamos posicionar o pseudo-elemento */ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.header::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url("../images/background_netflix.jpg"); + background-repeat: no-repeat; + background-blend-mode: overlay; + background-size: cover; /* Ajuste para cobrir todo o cabeçalho */ + background-color: rgb(0, 0, 0, 0.7); + opacity: 0.7; /* Opacidade para a imagem de fundo */ + z-index: -1; /* Coloque o pseudo-elemento atrás do conteúdo */ +} + +.content-form { + width: 30rem; + height: 40rem; + background-color: rgb(0, 0, 0, 0.5); + display: flex; + flex-direction: column; +} + +.form-criarconta { + display: flex; + flex-direction: column; + justify-content: center; + padding: 2rem 4rem; + gap: 1rem; +} + +.title-form { + font-family: "Montserrat", sans-serif; + color: rgb(255, 255, 255); + font: 2rem bold; + padding-left: 4.9rem; + padding-top: 2rem; +} + +.form-criarconta input { + height: 3rem; + padding-left: 0.8rem; + border-radius: 0.3rem; + outline: none; + border: 1px rgb(204, 204, 204); + background-color: #313235; + color: hsl(0, 0%, 100%); +} + +.form-criarconta button { + height: 3rem; + border-radius: 0.3rem; + outline: none; + width: 90%; + background-color: rgb(255, 0, 0); + border: transparent; + color: hsl(0, 0%, 100%); + text-align: center; + font-weight: bold; + transition: background-color -3s ease; +} + +.form-criarconta button:hover { + background-color: rgb(189, 8, 8); + cursor: pointer; +} + +.form-criarconta small { +} + +.form-criarconta ul { + list-style-type: none; /* Retira o simbolo da lista */ +} + +.form-group { + display: flex; + flex-direction: column; + justify-content: center; +} + + + +.is-invalid { + border: 1px solid rgb(255, 0, 0) !important; +} + +.invalid-feedback { + outline: none; + font-size: 0.8rem; + color: rgb(255, 0, 0); } diff --git a/static/css/style_homepage.css b/static/css/style_homepage.css index 2a0e14fa..b29e29d2 100644 --- a/static/css/style_homepage.css +++ b/static/css/style_homepage.css @@ -40,6 +40,7 @@ .form-access { display: flex; + flex-direction: column; gap: 0.3rem; justify-content: center; align-items: center; @@ -47,6 +48,8 @@ .btn-access { background-color: rgb(255, 0, 0); padding: 0.55rem; + color: #fff; + border-color: rgb(255, 0, 0); } .input-email { @@ -69,3 +72,13 @@ font: 3rem bold; padding: 0.8rem; } + +.is-invalid { + border: 1px solid rgb(255, 0, 0) !important; +} + +.invalid-feedback { + outline: none; + font-size: 0.8rem; + color: rgb(255, 0, 0); +} diff --git a/static/css/style_login.css b/static/css/style_login.css index e69de29b..191e1bf4 100644 --- a/static/css/style_login.css +++ b/static/css/style_login.css @@ -0,0 +1,108 @@ +@import url("https://fonts.googleapis.com/css2?family=Montserrat&family=Roboto:wght@500&display=swap"); + +.header { + min-height: 100vh; + position: relative; /* Adicione uma posição relativa para que possamos posicionar o pseudo-elemento */ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.header::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url("../images/background_netflix.jpg"); + background-repeat: no-repeat; + background-blend-mode: overlay; + background-size: cover; /* Ajuste para cobrir todo o cabeçalho */ + background-color: rgb(0, 0, 0, 0.7); + opacity: 0.7; /* Opacidade para a imagem de fundo */ + z-index: -1; /* Coloque o pseudo-elemento atrás do conteúdo */ +} + +.content-form { + width: 30rem; + height: 35rem; + background-color: rgb(0, 0, 0, 0.5); + display: flex; + flex-direction: column; +} + +.form-login { + display: flex; + flex-direction: column; + justify-content: center; + padding: 4rem; + gap: 1rem; +} + +.title-form { + font-family: "Montserrat", sans-serif; + color: rgb(255, 255, 255); + font: 2rem bold; + padding-left: 4.9rem; + padding-top: 4.1rem; +} + +.form-login input { + height: 3rem; + padding-left: 0.8rem; + border-radius: 0.3rem; + outline: none; + width: 90%; + border: 1px rgb(204, 204, 204); + background-color: #313235; + color: hsl(0, 0%, 100%); +} + +.form-login button { + height: 3rem; + border-radius: 0.3rem; + outline: none; + width: 90%; + background-color: rgb(255, 0, 0); + border: transparent; + color: hsl(0, 0%, 100%); + text-align: center; + font-weight: bold; + transition: background-color -3s ease; +} + +.form-login button:hover { + background-color: rgb(189, 8, 8); + cursor: pointer; +} + +.form-group { + display: flex; + flex-direction: column; + justify-content: center; +} + +.inscrever { + color: rgb(255, 255, 255); + font: 1rem bold; + display: flex; + flex-direction: column; + text-align: center; +} + +.inscrever p { + text-decoration: underline; + color: rgb(197, 82, 66); +} + +.is-invalid { + border: 1px solid rgb(255, 0, 0) !important; +} + +.invalid-feedback { + outline: none; + font-size: 0.8rem; + color: rgb(255, 0, 0); +} diff --git a/templates/base.html b/templates/base.html index 02918067..a95f4dd1 100644 --- a/templates/base.html +++ b/templates/base.html @@ -12,13 +12,10 @@ <link rel="stylesheet" type="text/css" href="{% static 'css/style_base.css' %}" /> <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script> <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet"> {% block head %} {% endblock %} - <!-- <script src="https://cdn.tailwindcss.com"></script>--> - <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">--> + <!-- <script src="https://cdn.tailwindcss.com"></script> --> + <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous"> --> </head> <body class="body"> {% include 'navbar.html' %}