diff --git a/frontend/src/app/components/auth/forgot-password/email-request/email-request.component.ts b/frontend/src/app/components/auth/forgot-password/email-request/email-request.component.ts index a608396..aae3911 100644 --- a/frontend/src/app/components/auth/forgot-password/email-request/email-request.component.ts +++ b/frontend/src/app/components/auth/forgot-password/email-request/email-request.component.ts @@ -18,7 +18,7 @@ import { emailFormatValidator } from '../../../../validators/email-format.valida styleUrl: './email-request.component.scss', }) export class EmailRequestComponent implements OnInit { - @Output() submittedChange = new EventEmitter(); + @Output() submittedChangeEmail = new EventEmitter(); form: FormGroup = new FormGroup({}); @@ -86,6 +86,6 @@ export class EmailRequestComponent implements OnInit { * Emits an event indicating that the form was successfully submitted. */ private emitFormSubmitted(): void { - this.submittedChange.emit(true); + this.submittedChangeEmail.emit(true); } } diff --git a/frontend/src/app/components/auth/forgot-password/forgot-password.component.html b/frontend/src/app/components/auth/forgot-password/forgot-password.component.html index e20c37c..5c1dc4b 100644 --- a/frontend/src/app/components/auth/forgot-password/forgot-password.component.html +++ b/frontend/src/app/components/auth/forgot-password/forgot-password.component.html @@ -1,11 +1,11 @@ diff --git a/frontend/src/app/components/auth/forgot-password/forgot-password.component.ts b/frontend/src/app/components/auth/forgot-password/forgot-password.component.ts index d5745d0..f17c16a 100644 --- a/frontend/src/app/components/auth/forgot-password/forgot-password.component.ts +++ b/frontend/src/app/components/auth/forgot-password/forgot-password.component.ts @@ -6,22 +6,25 @@ import { ActivatedRoute, Params, RouterLink } from '@angular/router'; import { AuthService } from '../../../services/auth.service'; import { ErrorService } from '../../../services/error.service'; import { EmailRequestComponent } from './email-request/email-request.component'; +import { PasswordRequestComponent } from './password-request/password-request.component'; @Component({ selector: 'app-forgot-password', standalone: true, imports: [ CommonModule, - BtnLargeComponent, FormsModule, RouterLink, EmailRequestComponent, + PasswordRequestComponent, ], templateUrl: './forgot-password.component.html', styleUrl: './forgot-password.component.scss', }) export class ForgotPasswordComponent implements OnInit { - sendEmailSuccess: boolean = false; + emailSendSuccess: boolean = false; + passwordChangeSuccess: boolean = false; + queryEmail: boolean = false; queryEmailSuccess: boolean = false; @@ -29,10 +32,14 @@ export class ForgotPasswordComponent implements OnInit { email: '', token: '', password: '', - passwordConfirm: '', send: false, }; + queryData = { + email: '', + token: '', + }; + /** * Initializes the ForgotPasswordComponent with ActivatedRoute, AuthService, and ErrorService. */ @@ -50,8 +57,12 @@ export class ForgotPasswordComponent implements OnInit { this.handleQueryParams(); } - submittedChange(value: boolean): void { - this.sendEmailSuccess = value; + submittedChangeEmail(success: boolean): void { + this.emailSendSuccess = success; + } + + submittedChangePassword(success: boolean): void { + this.passwordChangeSuccess = success; } /** @@ -60,8 +71,7 @@ export class ForgotPasswordComponent implements OnInit { private handleQueryParams(): void { this.route.queryParams.subscribe((params) => { this.extractAuthParams(params); - this.queryEmailSuccess = params['pw-change'] || ''; - if (this.authData.email && this.authData.token) { + if (this.queryData.email && this.queryData.token) { this.queryEmail = true; } }); @@ -73,8 +83,8 @@ export class ForgotPasswordComponent implements OnInit { * @param params The query parameters from the URL. */ private extractAuthParams(params: Params): void { - this.authData.email = params['email'] || ''; - this.authData.token = params['token'] || ''; + this.queryData.email = params['email'] || ''; + this.queryData.token = params['token'] || ''; } /** @@ -120,11 +130,11 @@ export class ForgotPasswordComponent implements OnInit { this.authData.send = true; try { await this.authService.forgotPassword(body); - this.sendEmailSuccess = true; + this.emailSendSuccess = true; this.errorService.clearError(); } catch (error) { this.authData.send = false; - this.sendEmailSuccess = false; + this.emailSendSuccess = false; this.errorService.handleError(error); } } diff --git a/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.html b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.html new file mode 100644 index 0000000..4dc1c73 --- /dev/null +++ b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.html @@ -0,0 +1,79 @@ +
+

Please enter your new password and confirm it.

+
+ +
+
+ + + +
+ +
+ @if (form.controls['password'].touched && form.controls['password'].invalid) + { +
+ @if (form.controls['password'].errors?.['required']) { +

Please enter a password

+ } @if (form.controls['password'].errors?.['minlength']) { +

Password is too short, min 8 characters

+ } +
+ } +
+ +
+ + + +
+ +
+ @if ( form.controls['passwordConfirm'].touched && + form.controls['passwordConfirm'].invalid ) { +
+ @if (form.controls['passwordConfirm'].errors?.['required']) { +

Please confirm your password

+ } @if ( form.controls['password'].value !== + form.controls['passwordConfirm'].value && + form.controls['passwordConfirm'].touched ) { +

The passwords do not match

+ } +
+ } +
+ + +
diff --git a/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.scss b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.scss new file mode 100644 index 0000000..7ccd3a4 --- /dev/null +++ b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.scss @@ -0,0 +1,3 @@ +@use "./../../../../../assets/style/backgrounds.scss" as *; +@use "./../../../../../assets/style/form.scss" as *; +@use "./../../../../../assets/style/auth-layout.scss" as *; diff --git a/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.spec.ts b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.spec.ts new file mode 100644 index 0000000..7416c57 --- /dev/null +++ b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PasswordRequestComponent } from './password-request.component'; + +describe('PasswordRequestComponent', () => { + let component: PasswordRequestComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PasswordRequestComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PasswordRequestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.ts b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.ts new file mode 100644 index 0000000..1dc7ee6 --- /dev/null +++ b/frontend/src/app/components/auth/forgot-password/password-request/password-request.component.ts @@ -0,0 +1,99 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { BtnLargeComponent } from '../../../../shared/components/buttons/btn-large/btn-large.component'; +import { + FormBuilder, + FormGroup, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { AuthService } from '../../../../services/auth.service'; +import { ErrorService } from '../../../../services/error.service'; + +@Component({ + selector: 'app-password-request', + imports: [BtnLargeComponent, FormsModule, ReactiveFormsModule], + templateUrl: './password-request.component.html', + styleUrl: './password-request.component.scss', +}) +export class PasswordRequestComponent { + @Input() queryData!: { email: string; token: string }; + + @Output() submittedChangePassword = new EventEmitter(); + + form: FormGroup = new FormGroup({}); + + /** + * Initializes the PasswordRequestComponent with FormBuilder, AuthService, and ErrorService. + */ + constructor( + private fb: FormBuilder, + public authService: AuthService, + private errorService: ErrorService + ) {} + + /** + * Lifecycle hook that initializes the component. + * Initializes the form on component load. + */ + ngOnInit(): void { + this.createForm(); + } + + /** + * Handles the form submission for password change. + * Validates the form, sends a password change request, + * and emits a submission event on success. + * + * @returns A Promise that resolves when the process is complete. + */ + async onSubmit(): Promise { + if (this.form.valid) { + const body = this.createFormRequestBody(); + this.form.disable(); + + try { + await this.authService.changePassword(body); + this.emitFormSubmitted(); + } catch (error) { + this.errorService.handleError(error); + } finally { + this.form.enable(); + } + } + } + + /** + * Initializes the reactive form with validators. + */ + private createForm(): void { + this.form = this.fb.group({ + password: ['', Validators.required], + passwordConfirm: ['', Validators.required], + }); + } + + /** + * Constructs the request payload from the form values. + * + * @returns An object containing the email, token, and password. + */ + private createFormRequestBody(): { + email: string; + token: string; + password: string; + } { + return { + email: this.queryData.email, + token: this.queryData.token, + password: this.form.value.password, + }; + } + + /** + * Emits an event indicating that the form was successfully submitted. + */ + private emitFormSubmitted(): void { + this.submittedChangePassword.emit(true); + } +}