Skip to content

Commit

Permalink
feat: add login form (#259)
Browse files Browse the repository at this point in the history
* 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
Mathh19 committed Jun 14, 2024
1 parent 56f635d commit 6e2835c
Show file tree
Hide file tree
Showing 7 changed files with 410 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/components/LoginForm/images/eye-slash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/LoginForm/images/facebook-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions src/components/LoginForm/images/google-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/components/LoginForm/images/paw-form-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
150 changes: 150 additions & 0 deletions src/components/LoginForm/index.js
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;
},
});
189 changes: 189 additions & 0 deletions src/components/LoginForm/index.scss
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;
}
}
Loading

0 comments on commit 6e2835c

Please sign in to comment.