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' %}