diff --git a/src/app/auth/components/register-form/register-form.component.html b/src/app/auth/components/register-form/register-form.component.html new file mode 100644 index 0000000..dd005e6 --- /dev/null +++ b/src/app/auth/components/register-form/register-form.component.html @@ -0,0 +1,89 @@ +
+

Sign Up

+
+
+ +
+ + @if (registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched) { + @if (registrationForm.get('email')?.errors?.['required']) { + Please input your email! + } + @if (registrationForm.get('email')?.errors?.['email']) { + Please input a valid email address! + } + } + @if (registrationForm.hasError('invalidUniqueKey')) { + Account with this email already exists! + } + @if (registrationForm.hasError('invalidEmail')) { + Email is wrong. Please input a valid email address! + } +
+
+
+ +
+ + @if (registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched) { + @if (registrationForm.get('password')?.errors?.['required']) { + Please input your password! + } + @if (registrationForm.get('password')?.errors?.['minlength']) { + Password must be at least 8 characters long! + } + } + @if (registrationForm.hasError('invalidPassword')) { + Password is wrong. Password must be at least 8 characters long + } +
+
+ +
+ +
+ + @if (registrationForm.get('confirm')?.invalid && registrationForm.get('confirm')?.touched) { + Please confirm your password! + } +
+
+ @if (registrationForm.hasError('passwordMismatch') && registrationForm.get('confirm')?.dirty) { + Passwords do not match! + } + @if (registrationForm.hasError('invalidFields')) { + Fields are empty. Please input email and password. + } +
+
+ + +
+
diff --git a/src/app/auth/components/register-form/register-form.component.scss b/src/app/auth/components/register-form/register-form.component.scss new file mode 100644 index 0000000..879cf15 --- /dev/null +++ b/src/app/auth/components/register-form/register-form.component.scss @@ -0,0 +1,56 @@ +@import 'variables'; + +:host { + display: contents; +} + +.register-form { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + width: $offset-xxxl * 5; + height: auto; + margin: auto; + padding: $offset-ms; + + background-color: $gray-100; + box-shadow: $shadow-100; + + .register-title { + margin-bottom: $offset-xs; + font-weight: 700; + } + + .register-form-item { + height: $offset-xl; + margin-bottom: $offset-xs; + + .register-field { + position: relative; + display: flex; + flex-direction: column; + } + } + + .register-button-container { + display: flex; + gap: $offset-xs; + margin-top: $offset-s; + } + + .p-error, + .register-input { + width: $offset-xl * 5; + } + + .p-error { + min-height: $offset-s; + margin-top: $four; + } + + .register-inputs-container { + height: $offset-xxxl * 3; + } +} diff --git a/src/app/auth/components/register-form/register-form.component.ts b/src/app/auth/components/register-form/register-form.component.ts new file mode 100644 index 0000000..55b8376 --- /dev/null +++ b/src/app/auth/components/register-form/register-form.component.ts @@ -0,0 +1,67 @@ +import { ChangeDetectionStrategy, Component, inject, OnDestroy } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { RouterLink } from '@angular/router'; + +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; +import { PasswordModule } from 'primeng/password'; +import { Subscription, take } from 'rxjs'; + +import { OverriddenHttpErrorResponse } from '@/app/api/models/errorResponse'; +import { APP_ROUTE } from '@/app/shared/constants/routes'; +import { PASSWORD_MIN_LENGTH } from '@/app/shared/validators/constants/constants'; +import { passwordMatchValidator } from '@/app/shared/validators/validators'; + +import { AuthService } from '../../services/auth-service/auth.service'; + +@Component({ + selector: 'app-register-form', + standalone: true, + imports: [ReactiveFormsModule, InputTextModule, ButtonModule, PasswordModule, RouterLink], + templateUrl: './register-form.component.html', + styleUrl: './register-form.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RegisterFormComponent implements OnDestroy { + public authService = inject(AuthService); + private fb = inject(FormBuilder); + private subscription = new Subscription(); + public registrationForm = this.fb.nonNullable.group( + { + email: ['', [Validators.required.bind(this), Validators.email.bind(this)]], + password: ['', [Validators.required.bind(this), Validators.minLength(PASSWORD_MIN_LENGTH).bind(this)]], + confirm: ['', [Validators.required.bind(this)]], + }, + { + validators: passwordMatchValidator, + }, + ); + public APP_ROUTE = APP_ROUTE; + + public submitForm(): void { + if (this.registrationForm.valid) { + this.authService.isRegistrationSuccess$$.set(null); + this.subscription.add( + this.authService + .registerUser(this.registrationForm.getRawValue()) + .pipe(take(1)) + .subscribe({ + next: () => { + this.authService.handleRegisterSuccess(); + }, + error: (err: OverriddenHttpErrorResponse) => { + this.authService.handleRegisterError(err); + this.registrationForm.setErrors({ [this.authService.errorMessage$$()]: true }); + }, + }), + ); + } else { + this.registrationForm.markAllAsTouched(); + this.registrationForm.updateValueAndValidity(); + } + } + + public ngOnDestroy(): void { + this.subscription.unsubscribe(); + } +} diff --git a/src/app/auth/pages/register/register.component.html b/src/app/auth/pages/register/register.component.html index c3c95ff..ecf18e3 100644 --- a/src/app/auth/pages/register/register.component.html +++ b/src/app/auth/pages/register/register.component.html @@ -1,91 +1,3 @@
-
-

Sign Up

-
-
- -
- - @if (registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched) { - @if (registrationForm.get('email')?.errors?.['required']) { - Please input your email! - } - @if (registrationForm.get('email')?.errors?.['email']) { - Please input a valid email address! - } - } - @if (registrationForm.hasError('invalidUniqueKey')) { - Account with this email already exists! - } - @if (registrationForm.hasError('invalidEmail')) { - Email is wrong. Please input a valid email address! - } -
-
-
- -
- - @if (registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched) { - @if (registrationForm.get('password')?.errors?.['required']) { - Please input your password! - } - @if (registrationForm.get('password')?.errors?.['minlength']) { - Password must be at least 8 characters long! - } - } - @if (registrationForm.hasError('invalidPassword')) { - Password is wrong. Password must be at least 8 characters long - } -
-
- -
- -
- - @if (registrationForm.get('confirm')?.invalid && registrationForm.get('confirm')?.touched) { - Please confirm your password! - } -
-
- @if (registrationForm.hasError('passwordMismatch') && registrationForm.get('confirm')?.dirty) { - Passwords do not match! - } - @if (registrationForm.hasError('invalidFields')) { - Fields are empty. Please input email and password. - } -
-
- - -
-
+
diff --git a/src/app/auth/pages/register/register.component.scss b/src/app/auth/pages/register/register.component.scss index 212f2ca..a450af9 100644 --- a/src/app/auth/pages/register/register.component.scss +++ b/src/app/auth/pages/register/register.component.scss @@ -5,54 +5,3 @@ justify-content: center; height: calc(100% - $offset-xl); } - -.register-form { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - - width: $offset-xxxl * 5; - height: auto; - margin: auto; - padding: $offset-ms; - - background-color: $gray-100; - box-shadow: $shadow-100; - - .register-title { - margin-bottom: $offset-xs; - font-weight: 700; - } - - .register-form-item { - height: $offset-xl; - margin-bottom: $offset-xs; - - .register-field { - position: relative; - display: flex; - flex-direction: column; - } - } - - .register-button-container { - display: flex; - gap: $offset-xs; - margin-top: $offset-s; - } - - .p-error, - .register-input { - width: $offset-xl * 5; - } - - .p-error { - min-height: $offset-s; - margin-top: $four; - } - - .register-inputs-container { - height: $offset-xxxl * 3; - } -} diff --git a/src/app/auth/pages/register/register.component.ts b/src/app/auth/pages/register/register.component.ts index e879cc4..e472f36 100644 --- a/src/app/auth/pages/register/register.component.ts +++ b/src/app/auth/pages/register/register.component.ts @@ -1,95 +1,13 @@ -import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component, effect, inject } from '@angular/core'; -import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; -import { RouterLink } from '@angular/router'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ButtonModule } from 'primeng/button'; -import { InputTextModule } from 'primeng/inputtext'; -import { MessageModule } from 'primeng/message'; -import { MessagesModule } from 'primeng/messages'; -import { PasswordModule } from 'primeng/password'; - -import { User } from '@/app/api/models/user'; -import { APP_PATH } from '@/app/shared/constants/routes'; -import { PASSWORD_MIN_LENGTH } from '@/app/shared/validators/constants/constants'; - -import { passwordMatchValidator } from '../../../shared/validators/validators'; -import { AuthService } from '../../services/auth-service/auth.service'; - -export interface RegisterForm { - email: FormControl; - password: FormControl; - confirm: FormControl; -} +import { RegisterFormComponent } from '../../components/register-form/register-form.component'; @Component({ selector: 'app-register', standalone: true, - imports: [ - CommonModule, - ReactiveFormsModule, - InputTextModule, - ButtonModule, - MessagesModule, - MessageModule, - PasswordModule, - RouterLink, - ], + imports: [RegisterFormComponent], templateUrl: './register.component.html', styleUrl: './register.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class RegisterComponent { - public authService = inject(AuthService); - private fb = inject(FormBuilder); - - public registrationForm = this.fb.group( - { - email: this.fb.control('', [Validators.required.bind(this), Validators.email.bind(this)]), - password: this.fb.control('', [ - Validators.required.bind(this), - Validators.minLength(PASSWORD_MIN_LENGTH).bind(this), - ]), - confirm: this.fb.control('', [Validators.required.bind(this)]), - }, - { - validators: passwordMatchValidator, - }, - ); - public signInPath = `/${APP_PATH.SIGN_IN.toLowerCase()}`; - - constructor() { - this.authService.isSignUpLoading$$.set(false); - effect(() => { - const isSuccess = this.authService.isRegistrationSuccess$$(); - if (isSuccess === false) { - this.registrationForm.setErrors({ [this.authService.errorMessage$$()]: true }); - } else if (isSuccess === true) { - this.registrationForm.reset(); - } - }); - } - - public submitForm(): void { - if (this.registrationForm.valid) { - this.authService.isRegistrationSuccess$$.set(null); - this.authService.registerUser(this.userData); - } else { - Object.values(this.registrationForm.controls).forEach((control) => { - if (control.invalid) { - control.markAsTouched(); - control.updateValueAndValidity({ - onlySelf: true, - }); - } - }); - } - } - - private get userData(): User { - return { - email: this.registrationForm.controls.email.value ?? '', - password: this.registrationForm.controls.password.value ?? '', - }; - } -} +export class RegisterComponent {} diff --git a/src/app/auth/services/auth-service/auth.service.ts b/src/app/auth/services/auth-service/auth.service.ts index 7f36b8b..1f12b85 100644 --- a/src/app/auth/services/auth-service/auth.service.ts +++ b/src/app/auth/services/auth-service/auth.service.ts @@ -1,8 +1,8 @@ -import { inject, Injectable, OnDestroy, signal } from '@angular/core'; +import { inject, Injectable, signal } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; -import { firstValueFrom, Subscription } from 'rxjs'; +import { firstValueFrom, Observable } from 'rxjs'; import { ADMIN_CREDENTIALS } from '@/app/admin/constants/adminCredentials'; import { OverriddenHttpErrorResponse } from '@/app/api/models/errorResponse'; @@ -24,7 +24,7 @@ import { isOverriddenHttpErrorResponse } from './helpers/helper'; @Injectable({ providedIn: 'root', }) -export class AuthService implements OnDestroy { +export class AuthService { private signUpService = inject(SignUpService); private signInService = inject(SignInService); private personalInfoService = inject(PersonalInfoService); @@ -42,25 +42,24 @@ export class AuthService implements OnDestroy { public isLoggedIn$$ = signal(this.localStorageService.getValueByKey(STORE_KEYS.TOKEN) !== null); public isAdmin$$ = signal(this.localStorageService.getValueByKey(STORE_KEYS.EMAIL) === ADMIN_CREDENTIALS.email); - private subscription: Subscription | null = null; - - public registerUser(user: User): void { + public registerUser(user: User): Observable { this.isSignUpLoading$$.set(true); - this.subscription = this.signUpService.signUp(user).subscribe({ - next: () => { - this.isRegistrationSuccess$$.set(true); - this.errorMessage$$.set(''); - this.router.navigate([APP_ROUTE.SIGN_IN]); - this.userMessageService.showSuccessMessage(USER_MESSAGE.REGISTRATION_SUCCESSFUL); - this.isSignUpLoading$$.set(false); - }, - error: (err: OverriddenHttpErrorResponse) => { - this.isRegistrationSuccess$$.set(false); - this.errorMessage$$.set(err.error.reason); - this.userMessageService.showErrorMessage(USER_MESSAGE.REGISTRATION_ERROR); - this.isSignUpLoading$$.set(false); - }, - }); + return this.signUpService.signUp(user); + } + + public handleRegisterSuccess(): void { + this.isRegistrationSuccess$$.set(true); + this.errorMessage$$.set(''); + this.router.navigate([APP_ROUTE.SIGN_IN]); + this.userMessageService.showSuccessMessage(USER_MESSAGE.REGISTRATION_SUCCESSFUL); + this.isSignUpLoading$$.set(false); + } + + public handleRegisterError(err: OverriddenHttpErrorResponse): void { + this.isRegistrationSuccess$$.set(false); + this.errorMessage$$.set(err.error.reason); + this.userMessageService.showErrorMessage(USER_MESSAGE.REGISTRATION_ERROR); + this.isSignUpLoading$$.set(false); } public async loginUser(userData: User, loginForm: FormGroup): Promise { @@ -99,10 +98,4 @@ export class AuthService implements OnDestroy { this.isLoggedIn$$.set(false); this.isAdmin$$.set(false); } - - public ngOnDestroy(): void { - if (this.subscription) { - this.subscription.unsubscribe(); - } - } }