From dbcceed912d11fc6b4ebe9cb532fb23e26bff01c Mon Sep 17 00:00:00 2001 From: Hugo <hugo.cubino@estudiantes.uva.es> Date: Wed, 27 Nov 2024 09:39:33 +0100 Subject: [PATCH] Implementacion de formulario con datos de usuario --- angular/RestClient/src/app/app.routes.ts | 5 + .../user/user-form/user-form.component.css | 68 ++++++++++++++ .../user/user-form/user-form.component.html | 79 ++++++++++++++++ .../user-form/user-form.component.spec.ts | 91 +++++++++++++++++++ .../user/user-form/user-form.component.ts | 61 +++++++++++++ .../src/app/shared/user-client.service.ts | 29 ++++++ 6 files changed, 333 insertions(+) create mode 100644 angular/RestClient/src/app/core/features/user/user-form/user-form.component.css create mode 100644 angular/RestClient/src/app/core/features/user/user-form/user-form.component.html create mode 100644 angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts create mode 100644 angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts diff --git a/angular/RestClient/src/app/app.routes.ts b/angular/RestClient/src/app/app.routes.ts index b8ee77b..1ce26ae 100644 --- a/angular/RestClient/src/app/app.routes.ts +++ b/angular/RestClient/src/app/app.routes.ts @@ -5,6 +5,7 @@ import { HotelRegisterComponent } from './core/features/hotel/hotel-register/hot import { MainPageComponent } from './core/features/user/main-page/main-page.component'; import { BookingListComponent } from './core/features/bookings/booking-list/booking-list.component'; import { UserBookingListComponent } from './core/features/user/user-booking-list/user-booking-list.component'; +import { UserFormComponent} from './core/features/user/user-form/user-form.component'; export const routes: Routes = [ { @@ -35,6 +36,10 @@ export const routes: Routes = [ path: 'hotels/:id', component: HotelRegisterComponent, }, + { + path: 'users/:id', + component: UserFormComponent, + }, { path: '**', redirectTo: '', diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css new file mode 100644 index 0000000..66cd833 --- /dev/null +++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css @@ -0,0 +1,68 @@ +.form-container { + width: 100%; + max-width: 500px; + margin: 0 auto; + padding: 20px; + background: #f9f9f9; + border: 1px solid #ddd; + border-radius: 8px; + } + + h2 { + text-align: center; + margin-bottom: 20px; + } + + .form-group { + margin-bottom: 15px; + } + + label { + display: block; + font-weight: bold; + margin-bottom: 5px; + } + + input { + width: 100%; + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + } + + input[readonly] { + background-color: #f5f5f5; + cursor: not-allowed; + } + + .button-group { + display: flex; + gap: 10px; + justify-content: center; + } + + button { + padding: 10px 20px; + font-size: 16px; + border-radius: 4px; + border: none; + cursor: pointer; + } + + .btn-primary { + background-color: #007bff; + color: #fff; + } + + .btn-secondary { + background-color: #6c757d; + color: #fff; + } + + .btn-success { + background-color: #28a745; + color: #fff; + } + \ No newline at end of file diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html new file mode 100644 index 0000000..0a6e33f --- /dev/null +++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html @@ -0,0 +1,79 @@ +<div class="form-container"> + <h2>Perfil de Usuario</h2> + <form [formGroup]="userForm"> + <div class="form-group"> + <label for="name">Nombre:</label> + <input + id="name" + type="text" + class="form-control" + formControlName="name" + [readonly]="!isEditing" + /> + </div> + + <div class="form-group"> + <label for="email">Email:</label> + <input + id="email" + type="email" + class="form-control" + formControlName="email" + [readonly]="!isEditing" + /> + </div> + + <div class="form-group" *ngIf="isEditing"> + <label for="currentPassword">Contraseña actual:</label> + <input + id="currentPassword" + type="password" + class="form-control" + formControlName="currentPassword" + placeholder="Introduce tu contraseña actual" + /> + </div> + + <div class="form-group" *ngIf="isEditing"> + <label for="newPassword">Nueva contraseña:</label> + <input + id="newPassword" + type="password" + class="form-control" + formControlName="newPassword" + placeholder="Introduce tu nueva contraseña" + /> + </div> + + <div class="button-group"> + <button + *ngIf="!isEditing" + type="button" + class="btn btn-primary" + (click)="toggleEdit()" + > + Editar + </button> + + <button + *ngIf="isEditing" + type="button" + class="btn btn-secondary" + (click)="cancelEdit()" + > + Cancelar + </button> + + <button + *ngIf="isEditing" + type="submit" + class="btn btn-success" + (click)="saveChanges()" + [disabled]="!userForm.valid" + > + Guardar + </button> + </div> + </form> + </div> + \ No newline at end of file diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts new file mode 100644 index 0000000..292f234 --- /dev/null +++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts @@ -0,0 +1,91 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HttpResponse } from '@angular/common/http'; +import { UserFormComponent } from './user-form.component'; +import { UserClientService } from '../../../../shared/user-client.service'; +import { of } from 'rxjs'; + +describe('UserFormComponent', () => { + let component: UserFormComponent; + let fixture: ComponentFixture<UserFormComponent>; + let userService: UserClientService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ReactiveFormsModule, FormsModule], + declarations: [UserFormComponent], + providers: [UserClientService], + }).compileComponents(); + + fixture = TestBed.createComponent(UserFormComponent); + component = fixture.componentInstance; + userService = TestBed.inject(UserClientService); + + spyOn(userService, 'getCurrentUser').and.returnValue( + of({ + id: 1, + name: 'John Doe', + email: 'johndoe@example.com', + rol: 'CONSUMER', + status: 'WITH_ACTIVE_BOOKINGS', + }) + ); + + spyOn(userService, 'updateUser').and.returnValue(of(new HttpResponse({ body: 'User updated successfully' }))); + spyOn(userService, 'updatePassword').and.returnValue(of(new HttpResponse({ body: 'Password updated successfully' }))); + + fixture.detectChanges(); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should load user data on initialization', () => { + expect(component.userForm.value).toEqual({ + name: 'John Doe', + email: 'johndoe@example.com', + password: '', + confirmPassword: '', + }); + }); + + it('should call updateUser when saving valid user data', () => { + component.userForm.patchValue({ + name: 'Jane Doe', + email: 'janedoe@example.com', + }); + + component.saveChanges(); + + expect(userService.updateUser).toHaveBeenCalledWith({ + name: 'Jane Doe', + email: 'janedoe@example.com', + }); + }); + + it('should call updatePassword when password is updated and matches confirmPassword', () => { + component.userForm.patchValue({ + password: 'newpassword123', + confirmPassword: 'newpassword123', + }); + + component.saveChanges(); + + expect(userService.updatePassword).toHaveBeenCalledWith( + '', + 'newpassword123' + ); + }); + + it('should not call updatePassword if password and confirmPassword do not match', () => { + component.userForm.patchValue({ + password: 'newpassword123', + confirmPassword: 'differentpassword', + }); + + component.saveChanges(); + + expect(userService.updatePassword).not.toHaveBeenCalled(); + }); +}); diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts new file mode 100644 index 0000000..238d811 --- /dev/null +++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts @@ -0,0 +1,61 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { UserClientService } from '../../../../shared/user-client.service'; + +@Component({ + selector: 'app-user-form', + templateUrl: './user-form.component.html', + styleUrls: ['./user-form.component.css'], +}) +export class UserFormComponent implements OnInit { + userForm!: FormGroup; + isEditing = false; + + constructor(private fb: FormBuilder, private userService: UserClientService) {} + + ngOnInit(): void { + this.initializeForm(); + this.loadUserData(); + } + + private initializeForm(): void { + this.userForm = this.fb.group({ + name: [{ value: '', disabled: true }, Validators.required], + email: [{ value: '', disabled: true }, [Validators.required, Validators.email]], + currentPassword: [''], // Solo habilitado en modo edición + newPassword: [''], // Solo habilitado en modo edición + }); + } + + private loadUserData(): void { + this.userService.getCurrentUser().subscribe((user) => { + this.userForm.patchValue({ + name: user.name, + email: user.email, + }); + }); + } + + toggleEdit(): void { + this.isEditing = true; + this.userForm.get('name')?.enable(); + this.userForm.get('email')?.enable(); + } + + cancelEdit(): void { + this.isEditing = false; + this.loadUserData(); // Volver a cargar los datos originales + this.userForm.get('name')?.disable(); + this.userForm.get('email')?.disable(); + } + + saveChanges(): void { + if (this.userForm.valid) { + const updatedData = this.userForm.value; + this.userService.updateUser(updatedData).subscribe(() => { + this.isEditing = false; + this.loadUserData(); + }); + } + } +} diff --git a/angular/RestClient/src/app/shared/user-client.service.ts b/angular/RestClient/src/app/shared/user-client.service.ts index 2a09c6d..3e42700 100644 --- a/angular/RestClient/src/app/shared/user-client.service.ts +++ b/angular/RestClient/src/app/shared/user-client.service.ts @@ -8,18 +8,22 @@ import { User, UserState } from '../../types'; }) export class UserClientService { private readonly URI = environment.userAPI; + constructor(private http: HttpClient) {} + // Obtener un usuario por ID getUser(userId: number) { return this.http.get<User>(`${this.URI}/${userId}`); } + // Obtener todos los usuarios getAllUsers() { return this.http.get<User[]>(this.URI, { observe: 'body', }); } + // Cambiar estado de un usuario alterUserStatus(userId: number, status: UserState) { return this.http.patch( `${this.URI}/${userId}`, @@ -32,4 +36,29 @@ export class UserClientService { } ); } + + // Obtener el usuario actual (autenticado) + getCurrentUser() { + return this.http.get<User>(`${this.URI}/me`); + } + + // Actualizar los datos del usuario + updateUser(user: Partial<User>) { + return this.http.patch(`${this.URI}/me`, user, { + observe: 'response', + responseType: 'text', + }); + } + + // Cambiar la contraseña del usuario + updatePassword(currentPassword: string, newPassword: string) { + return this.http.patch( + `${this.URI}/me/password`, + { currentPassword, newPassword }, + { + observe: 'response', + responseType: 'text', + } + ); + } } -- GitLab