diff --git a/frontend/src/app/auth/login/login.html b/frontend/src/app/auth/login/login.html index 2d9a869..9a5814c 100644 --- a/frontend/src/app/auth/login/login.html +++ b/frontend/src/app/auth/login/login.html @@ -9,7 +9,7 @@

Sign in to continue to Post Assistant.

- +
@@ -40,7 +40,7 @@ diff --git a/frontend/src/app/auth/login/login.ts b/frontend/src/app/auth/login/login.ts index 19677a0..c1b7e9f 100644 --- a/frontend/src/app/auth/login/login.ts +++ b/frontend/src/app/auth/login/login.ts @@ -1,10 +1,11 @@ -import { Component, inject} from '@angular/core'; +import { Component, computed, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { RouterLink } from '@angular/router'; -import {AuthStore} from '../auth.store'; -import {environment} from '../../../environments/environment'; +import { AuthStore } from '../auth.store'; +import { environment } from '../../../environments/environment'; import { ValidationErrors } from '../../shared/validation-errors/validation-errors'; +import { getFormValidationErrors, getMergedValidationErrors } from '../../core/helpers/forms'; @Component({ selector: 'app-login', @@ -16,18 +17,29 @@ import { ValidationErrors } from '../../shared/validation-errors/validation-erro export class Login { loginForm: FormGroup; protected readonly authStore = inject(AuthStore); + private formErrors = signal(>{}); + + protected readonly allErrors = getMergedValidationErrors( + this.formErrors, + this.authStore.validationErrors, + ); constructor(private fb: FormBuilder) { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], - password: ['', Validators.required] + password: ['', Validators.required], }); } login() { - if (this.loginForm.valid) { - if(!environment.production) console.log('Login submitted:', this.loginForm.value); - this.authStore.login(this.loginForm.value); + if (this.loginForm.touched && !this.loginForm.valid) { + this.loginForm.markAllAsTouched(); + this.formErrors.set(getFormValidationErrors(this.loginForm)); + return; } + + if (!environment.production) console.log('Login submitted:', this.loginForm.value); + this.formErrors.set({}); + this.authStore.login(this.loginForm.value); } } diff --git a/frontend/src/app/auth/register/register.html b/frontend/src/app/auth/register/register.html index 47414ba..59d267b 100644 --- a/frontend/src/app/auth/register/register.html +++ b/frontend/src/app/auth/register/register.html @@ -1,14 +1,16 @@ -
+
-
+
-

Create Account

+

Create Account

Join us and start using Post Assistant.

+ +
@@ -61,8 +63,7 @@ diff --git a/frontend/src/app/auth/register/register.ts b/frontend/src/app/auth/register/register.ts index eb5431b..b03e5c3 100644 --- a/frontend/src/app/auth/register/register.ts +++ b/frontend/src/app/auth/register/register.ts @@ -1,20 +1,28 @@ -import { Component, inject} from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { RouterLink } from '@angular/router'; -import {environment} from '../../../environments/environment'; -import {AuthStore} from '../auth.store'; +import { environment } from '../../../environments/environment'; +import { AuthStore } from '../auth.store'; +import { getFormValidationErrors, getMergedValidationErrors } from '../../core/helpers/forms'; +import { ValidationErrors } from '../../shared/validation-errors/validation-errors'; @Component({ selector: 'app-register', standalone: true, - imports: [CommonModule, ReactiveFormsModule, RouterLink], + imports: [CommonModule, ReactiveFormsModule, RouterLink, ValidationErrors], templateUrl: './register.html', styleUrl: './register.css', }) export class Register { registerForm: FormGroup; - private readonly authStore = inject(AuthStore); + protected readonly authStore = inject(AuthStore); + private formErrors = signal(>{}); + + protected readonly allErrors = getMergedValidationErrors( + this.formErrors, + this.authStore.validationErrors, + ); constructor(private fb: FormBuilder) { this.registerForm = this.fb.group({ @@ -26,9 +34,14 @@ export class Register { } register() { - if (this.registerForm.valid) { - if(!environment.production) console.log('Register submitted:', this.registerForm.value); - this.authStore.register(this.registerForm.value); + if (this.registerForm.touched && !this.registerForm.valid) { + this.registerForm.markAllAsTouched(); + this.formErrors.set(getFormValidationErrors(this.registerForm)); + return; } + + if (!environment.production) console.log('Registration started:', this.registerForm.value); + this.formErrors.set({}); + this.authStore.register(this.registerForm.value); } } diff --git a/frontend/src/app/core/helpers/forms.ts b/frontend/src/app/core/helpers/forms.ts new file mode 100644 index 0000000..1541521 --- /dev/null +++ b/frontend/src/app/core/helpers/forms.ts @@ -0,0 +1,52 @@ +import { FormGroup, ValidationErrors } from '@angular/forms'; +import { computed, Signal } from '@angular/core'; + +export type FormErrors = Record | null | undefined; + +export function getFormValidationErrors(form: FormGroup): Record { + const result: Record = {}; + + Object.keys(form.controls).forEach((key) => { + const controlErrors: ValidationErrors | null | undefined = form.get(key)?.errors; + + if (controlErrors) { + result[key] = []; + + Object.keys(controlErrors).forEach((keyError) => { + let errorMessage = keyError; + + // Translate Angular's built-in errors into readable sentences. add other validation errors here + switch (keyError) { + case 'required': + errorMessage = `The ${key} field is required.`; + break; + case 'email': + errorMessage = `The ${key} must be a valid email address.`; + break; + case 'minlength': + errorMessage = `The ${key} must be at least ${controlErrors[keyError].requiredLength} characters.`; + break; + case 'maxlength': + errorMessage = `The ${key} must not be greater than ${controlErrors[keyError].requiredLength} characters.`; + break; + case 'pattern': + errorMessage = `The ${key} format is invalid.`; + break; + } + + result[key].push(errorMessage); + }); + } + }); + + return result; +} + +export function getMergedValidationErrors( + formErrors: Signal, + serverErrors: Signal, +): Signal { + return computed(() => { + return { ...formErrors(), ...serverErrors() }; + }); +}