diff --git a/angular/RestClient/src/app/app.routes.ts b/angular/RestClient/src/app/app.routes.ts index 5bb87ab2d992781d5fc76a032136e593c152cbe3..13e66397bddf5dafcd427e12591f6b682ab1f4d2 100644 --- a/angular/RestClient/src/app/app.routes.ts +++ b/angular/RestClient/src/app/app.routes.ts @@ -7,6 +7,9 @@ import { BookingListComponent } from './core/features/bookings/booking-list/book import { UserBookingListComponent } from './core/features/user/user-booking-list/user-booking-list.component'; import { UserFormComponent } from './core/features/user/user-form/user-form.component'; +import { LoginComponent } from './core/features/auth/login/login.component'; +import { UserFormComponent } from './core/features/user/user-form/user-form.component'; + export const routes: Routes = [ { path: '', // Ruta principal @@ -15,6 +18,7 @@ export const routes: Routes = [ // auth { path: 'login', + component: LoginComponent, }, { path: 'register', diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.css b/angular/RestClient/src/app/core/features/auth/login/login.component.css new file mode 100644 index 0000000000000000000000000000000000000000..8fdfb8e0797b1434dd56017e25bd16af766914aa --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/login/login.component.css @@ -0,0 +1,131 @@ +/* General container styles */ +.container { + max-width: 400px; + margin: 50px auto; + padding: 30px; + background: linear-gradient(135deg, #1e1e2f, #2a2a45); + border-radius: 12px; + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3); + color: #fff; + font-family: 'Roboto', sans-serif; + } + + h2 { + text-align: center; + font-size: 1.8em; + font-weight: bold; + margin-bottom: 20px; + color: #fff; + letter-spacing: 1px; + text-transform: uppercase; + background: linear-gradient(90deg, #7f7fd5, #86a8e7, #91eae4); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + /* Form fields */ + form div { + margin-bottom: 20px; + } + + label { + font-size: 0.9em; + font-weight: bold; + margin-bottom: 8px; + display: block; + letter-spacing: 0.5px; + color: #b3b3d1; + } + + input { + width: 100%; + padding: 12px 15px; + font-size: 0.95em; + border: 1px solid #3e3e5e; + border-radius: 8px; + background: #252540; + color: #fff; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease-in-out; + } + + input:focus { + outline: none; + border-color: #7f7fd5; + background: #2e2e50; + box-shadow: 0 0 8px rgba(127, 127, 213, 0.8); + } + + /* Buttons */ + button { + width: 100%; + padding: 12px; + font-size: 1em; + font-weight: bold; + color: #fff; + background: linear-gradient(90deg, #7f7fd5, #86a8e7, #91eae4); + border: none; + border-radius: 8px; + cursor: pointer; + text-transform: uppercase; + transition: transform 0.2s, box-shadow 0.2s; + } + + button:hover { + transform: translateY(-3px); + box-shadow: 0 8px 15px rgba(127, 127, 213, 0.5); + } + + button:disabled { + background: #3e3e5e; + cursor: not-allowed; + opacity: 0.7; + box-shadow: none; + } + + /* Small error messages */ + small { + display: block; + margin-top: 5px; + font-size: 0.85em; + color: #ff6b6b; + } + + /* Extra animations and effects */ + .container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(120deg, rgba(127, 127, 213, 0.2), rgba(86, 168, 231, 0.1)); + z-index: -1; + filter: blur(20px); + } + + input::placeholder { + color: #b3b3d1; + } + + form div:last-child { + margin-top: 30px; + } + + /* Responsiveness */ + @media (max-width: 768px) { + .container { + padding: 20px; + margin: 20px; + } + + h2 { + font-size: 1.5em; + } + + input, + button { + font-size: 0.9em; + } + } + \ No newline at end of file diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.html b/angular/RestClient/src/app/core/features/auth/login/login.component.html new file mode 100644 index 0000000000000000000000000000000000000000..45790edab0d7039a0cef5f79d4b8d89450e28884 --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/login/login.component.html @@ -0,0 +1,23 @@ +<div class="login-container"> + <h2>Login</h2> + <form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> + <div> + <label for="email">Email:</label> + <input id="email" type="email" formControlName="email" /> + <div *ngIf="loginForm.get('email')?.invalid && loginForm.get('email')?.touched"> + <small>Email is required and must be valid.</small> + </div> + </div> + + <div> + <label for="password">Password:</label> + <input id="password" type="password" formControlName="password" /> + <div *ngIf="loginForm.get('password')?.invalid && loginForm.get('password')?.touched"> + <small>Password is required.</small> + </div> + </div> + + <button type="submit" [disabled]="loginForm.invalid">Login</button> + </form> + </div> + diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts b/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..18f3685d74d58daabcc34d3a66be081c6256a69d --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture<LoginComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.ts b/angular/RestClient/src/app/core/features/auth/login/login.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b58031ce3fef6f2d9c5dc886019d2faec2c5ead8 --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/login/login.component.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; + +@Component({ + standalone: true, + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'], + imports: [ReactiveFormsModule] +}) +export class LoginComponent { + loginForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', Validators.required] + }); + } + + onSubmit() { + if (this.loginForm.valid) { + const { email, password } = this.loginForm.value; + console.log('Login data:', { email, password }); + // Aquí iría el llamado al servicio de login con JWT + } + } +} diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.css b/angular/RestClient/src/app/core/features/auth/register/register.component.css new file mode 100644 index 0000000000000000000000000000000000000000..8fdfb8e0797b1434dd56017e25bd16af766914aa --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/register/register.component.css @@ -0,0 +1,131 @@ +/* General container styles */ +.container { + max-width: 400px; + margin: 50px auto; + padding: 30px; + background: linear-gradient(135deg, #1e1e2f, #2a2a45); + border-radius: 12px; + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3); + color: #fff; + font-family: 'Roboto', sans-serif; + } + + h2 { + text-align: center; + font-size: 1.8em; + font-weight: bold; + margin-bottom: 20px; + color: #fff; + letter-spacing: 1px; + text-transform: uppercase; + background: linear-gradient(90deg, #7f7fd5, #86a8e7, #91eae4); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + /* Form fields */ + form div { + margin-bottom: 20px; + } + + label { + font-size: 0.9em; + font-weight: bold; + margin-bottom: 8px; + display: block; + letter-spacing: 0.5px; + color: #b3b3d1; + } + + input { + width: 100%; + padding: 12px 15px; + font-size: 0.95em; + border: 1px solid #3e3e5e; + border-radius: 8px; + background: #252540; + color: #fff; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); + transition: all 0.3s ease-in-out; + } + + input:focus { + outline: none; + border-color: #7f7fd5; + background: #2e2e50; + box-shadow: 0 0 8px rgba(127, 127, 213, 0.8); + } + + /* Buttons */ + button { + width: 100%; + padding: 12px; + font-size: 1em; + font-weight: bold; + color: #fff; + background: linear-gradient(90deg, #7f7fd5, #86a8e7, #91eae4); + border: none; + border-radius: 8px; + cursor: pointer; + text-transform: uppercase; + transition: transform 0.2s, box-shadow 0.2s; + } + + button:hover { + transform: translateY(-3px); + box-shadow: 0 8px 15px rgba(127, 127, 213, 0.5); + } + + button:disabled { + background: #3e3e5e; + cursor: not-allowed; + opacity: 0.7; + box-shadow: none; + } + + /* Small error messages */ + small { + display: block; + margin-top: 5px; + font-size: 0.85em; + color: #ff6b6b; + } + + /* Extra animations and effects */ + .container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(120deg, rgba(127, 127, 213, 0.2), rgba(86, 168, 231, 0.1)); + z-index: -1; + filter: blur(20px); + } + + input::placeholder { + color: #b3b3d1; + } + + form div:last-child { + margin-top: 30px; + } + + /* Responsiveness */ + @media (max-width: 768px) { + .container { + padding: 20px; + margin: 20px; + } + + h2 { + font-size: 1.5em; + } + + input, + button { + font-size: 0.9em; + } + } + \ No newline at end of file diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.html b/angular/RestClient/src/app/core/features/auth/register/register.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8f54cfe88e9dc8c5d3babb878e5ddb0a4165fb4e --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/register/register.component.html @@ -0,0 +1,31 @@ +<div class="register-container"> + <h2>Register</h2> + <form [formGroup]="registerForm" (ngSubmit)="onSubmit()"> + <div> + <label for="email">Email:</label> + <input id="email" type="email" formControlName="email" /> + <div *ngIf="registerForm.get('email')?.invalid && registerForm.get('email')?.touched"> + <small>Email is required and must be valid.</small> + </div> + </div> + + <div> + <label for="password">Password:</label> + <input id="password" type="password" formControlName="password" /> + <div *ngIf="registerForm.get('password')?.invalid && registerForm.get('password')?.touched"> + <small>Password is required.</small> + </div> + </div> + + <div> + <label for="confirmPassword">Confirm Password:</label> + <input id="confirmPassword" type="password" formControlName="confirmPassword" /> + <div *ngIf="registerForm.get('confirmPassword')?.invalid && registerForm.get('confirmPassword')?.touched"> + <small>Passwords must match.</small> + </div> + </div> + + <button type="submit" [disabled]="registerForm.invalid">Register</button> + </form> + </div> + \ No newline at end of file diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts b/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..757b8952cf4bbb851f3b079286c5c711dad73119 --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture<RegisterComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegisterComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.ts b/angular/RestClient/src/app/core/features/auth/register/register.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..586790f70598414db832d76e7c05d0851a38076c --- /dev/null +++ b/angular/RestClient/src/app/core/features/auth/register/register.component.ts @@ -0,0 +1,31 @@ +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +@Component({ + selector: 'app-register', + templateUrl: './register.component.html', + styleUrls: ['./register.component.css'] +}) +export class RegisterComponent { + registerForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.registerForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', Validators.required], + confirmPassword: ['', Validators.required] + }); + } + + onSubmit() { + if (this.registerForm.valid) { + const { email, password, confirmPassword } = this.registerForm.value; + if (password === confirmPassword) { + console.log('Register data:', { email, password }); + // Aquí iría el llamado al servicio de register con JWT + } else { + console.error('Passwords do not match.'); + } + } + } +}