forked from devhatt/pet-dex-frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add login form * refactor: renamed variables and functions to enhance readability and maintainability * refactor: change p tag to span for less robust text * refactor: resolve issue with position:absolute and background color dependencies in login component * refactor: keep style import as last import * refactor: isolate breakpoint * refactor: fix the use of spacing and relative positions * refactor: improve visibility of the text in the input when there is an error * refactor: add class to control error display * refactor: remove line-height with unity * refactor: improve error message display for form inputs * refactor: remove early return * refactor: fix breakpoint * refactor: decrease button spacing * refactor: improve code reading * refactor: add emit login event
- Loading branch information
Showing
7 changed files
with
410 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import { Component } from 'pet-dex-utilities'; | ||
import TextInput from '../TextInput'; | ||
import Button from '../Button'; | ||
import Toggle from '../Toggle'; | ||
import './index.scss'; | ||
|
||
import pawIcon from './images/paw-form-icon.svg'; | ||
import eyeSlashIcon from './images/eye-slash.svg'; | ||
import googleIcon from './images/google-icon.svg'; | ||
import facebookIcon from './images/facebook-icon.svg'; | ||
|
||
const events = ['login']; | ||
|
||
const html = ` | ||
<div> | ||
<div class="login-form-container__container-paw-icon"> | ||
<img class="paw-icon" src="${pawIcon}" alt="Ícone de uma pata de animal" /> | ||
</div> | ||
<div class="login-form-container"> | ||
<h2 class="login-form-container__title">Sua petconta</h2> | ||
<form data-select="login-form" class="login-form-container__login-form" action="submit"> | ||
<div data-select="email-input-container"></div> | ||
<span data-select="email-error-message" class="error-message"></span> | ||
<div data-select="password-input-container"></div> | ||
<span data-select="password-error-message" class="error-message"></span> | ||
<div class="login-form-container__login-options"> | ||
<div data-select="remember-option" class="remember-option"> | ||
<p>Remember me</p> | ||
</div> | ||
<a href="#" class="forgot-password">Esqueceu a senha?</a> | ||
</div> | ||
</form> | ||
<span class="login-form-container__signup">Não tem uma conta? | ||
<a href="#" class="signup-link">Inscreva-se</a> | ||
</span> | ||
<div class="login-form-container__separator"> | ||
<hr class="divisor" /> | ||
<span class="separator-text">Ou</span> | ||
<hr class="divisor" /> | ||
</div> | ||
<div data-select="provider-container" class="login-form-container__provider-container"> | ||
<button class="provider-button"><img src="${googleIcon}" class="icon"/ >Google</button> | ||
<button class="provider-button"><img src="${facebookIcon}" class="icon"/ >Facebook</button> | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
|
||
export default function LoginForm() { | ||
Component.call(this, { html, events }); | ||
const $loginForm = this.selected.get('login-form'); | ||
const $emailInputContainer = this.selected.get('email-input-container'); | ||
const $passwordInputContainer = this.selected.get('password-input-container'); | ||
const $rememberOption = this.selected.get('remember-option'); | ||
const $emailErrorMessage = this.selected.get('email-error-message'); | ||
const $passwordErrorMessage = this.selected.get('password-error-message'); | ||
|
||
const emailInput = new TextInput({ placeholder: 'E-mail' }); | ||
const passwordInput = new TextInput({ | ||
placeholder: 'Senha', | ||
assetUrl: eyeSlashIcon, | ||
assetPosition: 'suffix', | ||
}); | ||
const toggle = new Toggle({ checked: false }); | ||
const submitButton = new Button({ | ||
text: 'Entrar', | ||
isFullWidth: true, | ||
isDisabled: true, | ||
}); | ||
|
||
emailInput.mount($emailInputContainer); | ||
passwordInput.mount($passwordInputContainer); | ||
submitButton.mount($loginForm); | ||
toggle.mount($rememberOption); | ||
|
||
emailInput.selected.get('input-text').type = 'email'; | ||
emailInput.selected.get('input-text').id = 'email'; | ||
passwordInput.selected.get('input-text').type = 'password'; | ||
|
||
const validateFields = () => { | ||
const email = emailInput.selected.get('input-text').value; | ||
const password = passwordInput.selected.get('input-text').value; | ||
|
||
if (email.trim() && password) { | ||
submitButton.enable(); | ||
} else { | ||
submitButton.disable(); | ||
} | ||
}; | ||
|
||
emailInput.selected | ||
.get('input-text') | ||
.addEventListener('input', validateFields); | ||
passwordInput.selected | ||
.get('input-text') | ||
.addEventListener('input', validateFields); | ||
|
||
submitButton.listen('click', () => { | ||
const email = emailInput.selected.get('input-text').value; | ||
const password = passwordInput.selected.get('input-text').value; | ||
let validEmail = true; | ||
let validPassword = true; | ||
|
||
if (!this.validateEmail(email)) { | ||
validEmail = false; | ||
$emailErrorMessage.classList.add('show-error'); | ||
$emailErrorMessage.innerText = 'E-mail inválido'; | ||
emailInput.inputError(); | ||
} | ||
|
||
if (!this.validatePassword(password)) { | ||
validPassword = false; | ||
$passwordErrorMessage.classList.add('show-error'); | ||
$passwordErrorMessage.innerText = | ||
'Senha inválida. Sua senha deve conter no mínimo 10 caracteres, incluindo pelo menos um caractere especial e uma letra maiúscula.'; | ||
passwordInput.inputError(); | ||
} | ||
|
||
if (validEmail) $emailErrorMessage.classList.remove('show-error'); | ||
if (validPassword) $passwordErrorMessage.classList.remove('show-error'); | ||
|
||
if (validEmail && validPassword) { | ||
this.login(); | ||
} | ||
}); | ||
} | ||
|
||
LoginForm.prototype = Object.assign(LoginForm.prototype, Component.prototype, { | ||
login() { | ||
this.emit('login'); | ||
}, | ||
validateEmail(email) { | ||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; | ||
|
||
return emailRegex.test(email); | ||
}, | ||
validatePassword(password) { | ||
const hasMinLength = password.length >= 10; | ||
const hasUppercase = /[A-Z]/g.test(password); | ||
const hasNumber = /[0-9]/g.test(password); | ||
const hasSpecialCharacter = /[!@#$%^&*(),.?":{}|<>]/g.test(password); | ||
|
||
return hasMinLength && hasUppercase && hasNumber && hasSpecialCharacter; | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
@use '~styles/colors.scss' as colors; | ||
@use '~styles/fonts.scss' as fonts; | ||
@use '~styles/breakpoints.scss' as breakpoints; | ||
|
||
$pawIconSize: 128px; | ||
|
||
.login-form-container { | ||
font-family: 'Poppins', sans-serif; | ||
|
||
padding: 6.2rem 4rem; | ||
|
||
background-color: rgb(255, 255, 255); | ||
border-radius: 1.5rem; | ||
|
||
&__container-paw-icon { | ||
width: 100%; | ||
max-width: $pawIconSize; | ||
height: $pawIconSize; | ||
|
||
display: flex; | ||
|
||
justify-content: center; | ||
|
||
margin: 0 auto; | ||
margin-bottom: calc($pawIconSize / -2); | ||
|
||
position: relative; | ||
z-index: 1; | ||
|
||
background-color: rgb(255, 255, 255); | ||
border-radius: 100%; | ||
|
||
.paw-icon { | ||
width: 100%; | ||
|
||
margin-bottom: 4.5rem; | ||
padding: 1.6rem; | ||
} | ||
} | ||
|
||
&__title { | ||
color: colors.$gray800; | ||
text-align: center; | ||
font-size: fonts.$xl2; | ||
font-weight: 700; | ||
line-height: 1.6; | ||
|
||
margin-bottom: 4rem; | ||
} | ||
|
||
&__login-form { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 2rem; | ||
|
||
.input-text-container { | ||
&__input { | ||
background-size: auto 40%; | ||
|
||
&.input-error { | ||
color: rgb(255, 255, 255); | ||
} | ||
} | ||
} | ||
|
||
.button { | ||
margin-top: 3rem; | ||
} | ||
|
||
.error-message { | ||
display: none; | ||
|
||
color: colors.$error100; | ||
|
||
&.show-error { | ||
display: block; | ||
} | ||
} | ||
} | ||
|
||
&__login-options { | ||
display: flex; | ||
gap: 0.6rem; | ||
|
||
align-items: center; | ||
justify-content: space-between; | ||
|
||
.remember-option { | ||
display: flex; | ||
flex-direction: row-reverse; | ||
gap: 1rem; | ||
|
||
align-items: center; | ||
} | ||
|
||
.forgot-password { | ||
color: colors.$blue600; | ||
text-align: right; | ||
text-decoration: none; | ||
} | ||
} | ||
|
||
&__signup { | ||
display: block; | ||
|
||
text-align: center; | ||
line-height: 2.4rem; | ||
|
||
margin: 3rem 0; | ||
|
||
.signup-link { | ||
color: colors.$blue600; | ||
font-weight: 600; | ||
text-decoration: none; | ||
} | ||
} | ||
|
||
&__error-message { | ||
display: block; | ||
|
||
color: colors.$error100; | ||
|
||
margin-top: 0.8rem; | ||
|
||
user-select: all; | ||
} | ||
|
||
&__separator { | ||
display: flex; | ||
|
||
.divisor { | ||
width: 100%; | ||
|
||
border: unset; | ||
border-top: 0.1rem solid colors.$gray200; | ||
} | ||
|
||
.separator-text { | ||
margin: 0 2.8rem; | ||
} | ||
} | ||
|
||
&__provider-container { | ||
display: flex; | ||
gap: 1.6rem; | ||
|
||
justify-content: space-between; | ||
|
||
margin-top: 3rem; | ||
|
||
.provider-button { | ||
width: 100%; | ||
|
||
display: flex; | ||
gap: 1.6rem; | ||
|
||
align-items: center; | ||
justify-content: center; | ||
|
||
font-size: 1.6rem; | ||
|
||
padding: 1.6rem; | ||
|
||
border: unset; | ||
|
||
background-color: colors.$secondary100; | ||
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.084); | ||
border-radius: 1.4rem; | ||
|
||
transition: 0.3s ease-in-out; | ||
|
||
cursor: pointer; | ||
|
||
&:hover:not(:disabled) { | ||
background-color: colors.$gray200; | ||
} | ||
|
||
.icon { | ||
max-width: 2.4rem; | ||
} | ||
} | ||
} | ||
} | ||
|
||
@include breakpoints.from667 { | ||
.login-form-container { | ||
padding: 6.2rem 8rem; | ||
} | ||
} |
Oops, something went wrong.