Skip to content

Commit f53cf37

Browse files
committed
✨ add login feature
1 parent acbbe2d commit f53cf37

12 files changed

+271
-36
lines changed

src/app/auth/components/components.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@ import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
33
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
44

5+
import { LoginFormComponent } from './login-form/login-form.component';
56
import { RegisterFormComponent } from './register-form/register-form.component';
67

78
@NgModule({
89
declarations: [
9-
RegisterFormComponent
10+
RegisterFormComponent,
11+
LoginFormComponent
1012
],
1113
imports: [
1214
CommonModule,
1315
FormsModule,
1416
ReactiveFormsModule,
1517
],
1618
exports: [
17-
RegisterFormComponent
19+
RegisterFormComponent,
20+
LoginFormComponent
1821
]
1922
})
2023
export class ComponentsModule { }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<form [formGroup]="loginForm" (submit)="login()">
2+
<div class="input-group">
3+
<label for="email">Correo electrónico</label>
4+
<input formControlName="email" type="email" id="email" placeholder="Correo electrónico">
5+
</div>
6+
<div class="input-group">
7+
<label for="password">Contraseña</label>
8+
<div class="input-icon">
9+
<input formControlName="password" type="password" id="password" placeholder="Contraseña">
10+
<i class="bx bx-hide" id="password-icon" (click)="showPassword('password')"></i>
11+
</div>
12+
</div>
13+
<div class="options">
14+
<div class="input-group row">
15+
<input formControlName="remember" (change)="changeRemember($event)" type="checkbox" id="remember">
16+
<label for="remember">Recordar</label>
17+
</div>
18+
<a href="#">¿Olvidaste tu contraseña?</a>
19+
</div>
20+
<div class="input-container">
21+
<button class="btn btn-primary" [disabled]="loginForm.invalid" type="submit">Iniciar sesión</button>
22+
</div>
23+
<ng-container *ngIf="showErrors && errors.length > 0">
24+
<ul>
25+
<li *ngFor="let error of errors">{{ error }}</li>
26+
</ul>
27+
</ng-container>
28+
</form>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@import 'settings/_colors.scss';
2+
@import 'settings/_typography.scss';
3+
4+
.btn {
5+
width: 100%;
6+
border-radius: 0.5rem;
7+
}
8+
9+
.input-container {
10+
margin-top: 1rem;
11+
}
12+
13+
ul {
14+
list-style: none;
15+
padding: 0;
16+
margin: 0;
17+
18+
li {
19+
@include fs-6;
20+
@include fw-500;
21+
color: var(--fc-purple);
22+
margin-bottom: 0.5rem;
23+
}
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { LoginFormComponent } from './login-form.component';
4+
5+
describe('LoginFormComponent', () => {
6+
let component: LoginFormComponent;
7+
let fixture: ComponentFixture<LoginFormComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
declarations: [ LoginFormComponent ]
12+
})
13+
.compileComponents();
14+
});
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(LoginFormComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Component, EventEmitter, Input, Output } from '@angular/core';
2+
import { FormBuilder, Validators } from '@angular/forms';
3+
4+
import { ILogin } from '@interfaces/user.interface';
5+
6+
import { RegexClass } from '@utils/regex.util';
7+
import Storage from '@utils/storage.util';
8+
9+
@Component({
10+
selector: 'app-login-form',
11+
templateUrl: './login-form.component.html',
12+
styleUrls: ['./login-form.component.scss']
13+
})
14+
export class LoginFormComponent {
15+
private regexExpressions = RegexClass;
16+
@Input() showErrors = false;
17+
@Input() errors: string[] = [];
18+
@Output() loginData: EventEmitter<ILogin> = new EventEmitter<ILogin>();
19+
loginForm = this.fb.group({
20+
email: [Storage.getLocalStorage('email') || '', [
21+
Validators.required,
22+
Validators.pattern(this.regexExpressions.EMAIL),
23+
]],
24+
password: ['', [
25+
Validators.required,
26+
]],
27+
remember: [Storage.getLocalStorage('email') ? true : false],
28+
});
29+
30+
constructor(
31+
private fb: FormBuilder,
32+
) { }
33+
34+
validateForm(field: string): boolean | undefined | null {
35+
const myForm = this.loginForm.get(field);
36+
return myForm?.errors && (myForm?.dirty || myForm?.touched);
37+
}
38+
39+
validateField(field: string, error: string): boolean | undefined | null {
40+
return (this.loginForm.get(field)?.hasError(error));
41+
}
42+
43+
showPassword(id: string): void {
44+
const password = document.getElementById(id);
45+
const icon = document.getElementById(`${id}-icon`);
46+
if (password?.getAttribute('type') === 'password') {
47+
password.setAttribute('type', 'text');
48+
icon?.classList.remove('bx-hide');
49+
icon?.classList.add('bx-show');
50+
} else {
51+
password?.setAttribute('type', 'password');
52+
icon?.classList.remove('bx-show');
53+
icon?.classList.add('bx-hide');
54+
}
55+
}
56+
57+
login(): void {
58+
if (this.loginForm.valid) {
59+
this.loginData.emit(this.loginForm.value);
60+
}
61+
}
62+
63+
changeRemember(event: Event): void {
64+
const checked = (event.target as HTMLInputElement).checked;
65+
this.loginForm.patchValue({
66+
remember: checked
67+
});
68+
}
69+
}

src/app/auth/login/login.component.html

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,11 @@
1313
<a routerLink="/register">Regístrate</a>
1414
</span>
1515
</div>
16-
<form>
17-
<div class="input-group">
18-
<label for="email">Correo electrónico</label>
19-
<input type="email" id="email" placeholder="Correo electrónico">
20-
</div>
21-
<div class="input-group">
22-
<label for="password">Contraseña</label>
23-
<input type="password" id="password" placeholder="Contraseña">
24-
</div>
25-
<div class="options">
26-
<div class="input-group row">
27-
<input type="checkbox" id="remember">
28-
<label for="remember">Recordar</label>
29-
</div>
30-
<a href="#">¿Olvidaste tu contraseña?</a>
31-
</div>
32-
<div class="input-container">
33-
<button class="btn btn-primary">Iniciar sesión</button>
34-
</div>
35-
</form>
16+
<app-login-form
17+
(loginData)="login($event)"
18+
[showErrors]="showErrors"
19+
[errors]="errors"
20+
></app-login-form>
3621
</section>
3722
</div>
3823
</div>

src/app/auth/login/login.component.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,41 @@
11
import { Component } from '@angular/core';
2+
import { Router } from '@angular/router';
3+
4+
import { AuthService } from '@services/auth.service';
5+
6+
import { ILogin } from '@interfaces/user.interface';
7+
8+
import Storage from '@utils/storage.util';
29

310
@Component({
411
selector: 'app-login',
512
templateUrl: './login.component.html',
613
styleUrls: ['./login.component.scss']
714
})
8-
export class LoginComponent {}
15+
export class LoginComponent {
16+
showErrors = false;
17+
errors: string[] = [];
18+
19+
constructor(
20+
private authService: AuthService,
21+
private router: Router
22+
) { }
23+
24+
login(data: ILogin): void {
25+
this.authService.login(data).subscribe({
26+
next: () => {
27+
if (data.remember) Storage.savelocalStorage('email', data.email);
28+
else Storage.removeLocalStorage('email');
29+
30+
this.router.navigateByUrl('/');
31+
},
32+
error: (err) => {
33+
this.errors = [];
34+
if (err.status === 400 && err.error.msg === 'email or password are incorrect') {
35+
this.showErrors = true;
36+
this.errors.push('correo o contraseña incorrectos');
37+
}
38+
}
39+
});
40+
}
41+
}

src/app/core/interfaces/response.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export interface IUserCreated {
55
ok: boolean;
66
user: User;
77
token: string;
8-
root: Folder;
8+
root?: Folder;
99
status?: number;
1010
msg?: string;
1111
}

src/app/core/interfaces/user.interface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ export interface ICreateAccount {
44
email: string;
55
password: string;
66
}
7+
8+
export interface ILogin {
9+
email: string;
10+
password: string;
11+
remember: boolean;
12+
}

src/app/core/services/auth.service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { environment } from 'environments/environment';
66
import { Observable, map } from 'rxjs';
77

88
import { IUserCreated } from '@interfaces/response.interface';
9-
import { ICreateAccount } from '@interfaces/user.interface';
9+
import { ICreateAccount, ILogin } from '@interfaces/user.interface';
1010

1111
const base_url = environment.base_url;
1212

@@ -24,4 +24,9 @@ export class AuthService {
2424
const url = `${base_url}/user`;
2525
return this.http.post<IUserCreated>(url, data);
2626
}
27+
28+
login(data: ILogin): Observable<IUserCreated> {
29+
const url = `${base_url}/auth/login`;
30+
return this.http.post<IUserCreated>(url, data);
31+
}
2732
}

0 commit comments

Comments
 (0)