feat(profile): enhance profile form with validation messages and submission logic
This commit is contained in:
parent
7507580c2c
commit
036b2a950f
|
|
@ -1,56 +1,71 @@
|
||||||
<form [formGroup]="profileForm" class="profile-form">
|
<form [formGroup]="profileForm" class="profile-form" (ngSubmit)="onSubmit()">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Nombre y Apellido:</label>
|
<label>Nombre y Apellido:</label>
|
||||||
<app-input
|
<app-input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Descripción"
|
placeholder="Nombre y Apellido"
|
||||||
[value]="profileForm.controls['fullName'].value"
|
[value]="profileForm.controls['fullName'].value"
|
||||||
(valueChange)="profileForm.controls['fullName'].setValue($event)"
|
(valueChange)="profileForm.controls['fullName'].setValue($event)"
|
||||||
></app-input>
|
></app-input>
|
||||||
</div>
|
@if (getControlError('fullName'); as errorMessage) {
|
||||||
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Email:</label>
|
<label>Email:</label>
|
||||||
<app-input
|
<app-input
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Descripción"
|
placeholder="Email"
|
||||||
[value]="profileForm.controls['email'].value"
|
[value]="profileForm.controls['email'].value"
|
||||||
(valueChange)="profileForm.controls['email'].setValue($event)"
|
(valueChange)="profileForm.controls['email'].setValue($event)"
|
||||||
></app-input>
|
></app-input>
|
||||||
</div>
|
@if (getControlError('email'); as errorMessage) {
|
||||||
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>DNI:</label>
|
<label>DNI:</label>
|
||||||
<app-input
|
<app-input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Descripción"
|
placeholder="DNI"
|
||||||
[value]="profileForm.controls['dni'].value"
|
[value]="profileForm.controls['dni'].value"
|
||||||
(valueChange)="profileForm.controls['dni'].setValue($event)"
|
(valueChange)="profileForm.controls['dni'].setValue($event)"
|
||||||
></app-input>
|
></app-input>
|
||||||
</div>
|
@if (getControlError('dni'); as errorMessage) {
|
||||||
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Teléfono:</label>
|
<label>Teléfono:</label>
|
||||||
<app-input
|
<app-input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Descripción"
|
placeholder="Teléfono"
|
||||||
[value]="profileForm.controls['phone'].value"
|
[value]="profileForm.controls['phone'].value"
|
||||||
(valueChange)="profileForm.controls['phone'].setValue($event)"
|
(valueChange)="profileForm.controls['phone'].setValue($event)"
|
||||||
></app-input>
|
></app-input>
|
||||||
</div>
|
@if (getControlError('phone'); as errorMessage) {
|
||||||
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Contraseña:</label>
|
<label>Contraseña (dejar en blanco para no modificar):</label>
|
||||||
<app-input
|
<app-input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Descripción"
|
placeholder="Contraseña"
|
||||||
[value]="profileForm.controls['password'].value"
|
[value]="profileForm.controls['password'].value"
|
||||||
(valueChange)="profileForm.controls['password'].setValue($event)"
|
(valueChange)="profileForm.controls['password'].setValue($event)"
|
||||||
></app-input>
|
></app-input>
|
||||||
</div>
|
@if (getControlError('password'); as errorMessage) {
|
||||||
|
<small class="text-danger">{{ errorMessage }}</small>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions mt-3">
|
||||||
<app-button type="button" variant="cancel" hostClass="action-btn" buttonClass="w-100">Cancelar</app-button>
|
<app-button type="button" variant="cancel" hostClass="action-btn" buttonClass="w-100" (click)="onCancel()">Cancelar</app-button>
|
||||||
<app-button type="submit" variant="primary" hostClass="action-btn" buttonClass="w-100">Guardar</app-button>
|
<app-button type="submit" variant="primary" hostClass="action-btn" buttonClass="w-100" [disabled]="isSubmitting()">Guardar</app-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { Component, inject, effect } from '@angular/core';
|
import { Component, inject, effect, signal } from '@angular/core';
|
||||||
import { ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';
|
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { InputComponent } from '../../../../../../shared/components/input/input.component';
|
import { InputComponent } from '../../../../../../shared/components/input/input.component';
|
||||||
import { ButtonComponent } from '../../../../../../shared/components/button/button.component';
|
import { ButtonComponent } from '../../../../../../shared/components/button/button.component';
|
||||||
import { AuthService } from '../../../../../../core/services/auth/auth.service';
|
import { AuthService } from '../../../../../../core/services/auth/auth.service';
|
||||||
|
import { ToastService } from '../../../../../../core/services/toast.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profile-form',
|
selector: 'app-profile-form',
|
||||||
|
|
@ -14,14 +15,16 @@ import { AuthService } from '../../../../../../core/services/auth/auth.service';
|
||||||
export class ProfileForm {
|
export class ProfileForm {
|
||||||
profileForm: FormGroup;
|
profileForm: FormGroup;
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
|
private toastService = inject(ToastService);
|
||||||
|
isSubmitting = signal(false);
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {
|
constructor(private fb: FormBuilder) {
|
||||||
this.profileForm = this.fb.group({
|
this.profileForm = this.fb.group({
|
||||||
fullName: [''],
|
fullName: ['', [Validators.required, Validators.maxLength(255)]],
|
||||||
email: [''],
|
email: ['', [Validators.required, Validators.email, Validators.maxLength(255)]],
|
||||||
dni: [''],
|
dni: ['', [Validators.pattern(/^[0-9]{7,8}$/)]],
|
||||||
phone: [''],
|
phone: ['', [Validators.pattern(/^\+?[0-9\s\-]{10,20}$/)]],
|
||||||
password: ['']
|
password: ['', [Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}$/)]]
|
||||||
});
|
});
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
|
|
@ -37,4 +40,61 @@ export class ProfileForm {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getControlError(controlName: string): string | null {
|
||||||
|
const control = this.profileForm.get(controlName);
|
||||||
|
if (!control || !control.errors || (!control.touched && !this.isSubmitting())) return null;
|
||||||
|
|
||||||
|
if (control.errors['required']) return 'Este campo es obligatorio.';
|
||||||
|
if (control.errors['email']) return 'Ingresa un email válido.';
|
||||||
|
if (control.errors['maxlength']) return 'Excede el largo máximo.';
|
||||||
|
if (controlName === 'dni' && control.errors['pattern']) return 'DNI inválido (7-8 números).';
|
||||||
|
if (controlName === 'phone' && control.errors['pattern']) return 'Teléfono inválido.';
|
||||||
|
if (controlName === 'password' && control.errors['pattern']) return 'Debe contener mayúscula, minúscula, carácter especial y mínimo 8 caracteres.';
|
||||||
|
|
||||||
|
return 'Valor inválido.';
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.profileForm.invalid) {
|
||||||
|
this.profileForm.markAllAsTouched();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSubmitting.set(true);
|
||||||
|
const formValue = this.profileForm.value;
|
||||||
|
const payload: any = {
|
||||||
|
nombre_apellido: formValue.fullName,
|
||||||
|
email: formValue.email,
|
||||||
|
};
|
||||||
|
if (formValue.dni) payload.dni = formValue.dni;
|
||||||
|
if (formValue.phone) payload.telefono = formValue.phone;
|
||||||
|
if (formValue.password) payload.password = formValue.password;
|
||||||
|
|
||||||
|
this.authService.updateProfile(payload).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.isSubmitting.set(false);
|
||||||
|
this.toastService.success('Perfil actualizado correctamente.');
|
||||||
|
},
|
||||||
|
error: (error: any) => {
|
||||||
|
this.isSubmitting.set(false);
|
||||||
|
const errorMessage = error.error?.message || 'Error al actualizar el perfil.';
|
||||||
|
this.toastService.danger(errorMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel() {
|
||||||
|
const user = this.authService.user();
|
||||||
|
if (user) {
|
||||||
|
this.profileForm.reset({
|
||||||
|
fullName: user.nombre_apellido,
|
||||||
|
email: user.email,
|
||||||
|
dni: user.dni || '',
|
||||||
|
phone: user.telefono || '',
|
||||||
|
password: ''
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.profileForm.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue