191 lines
5.2 KiB
TypeScript
191 lines
5.2 KiB
TypeScript
import {Component, OnInit, OnDestroy} from '@angular/core';
|
|
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
|
import {Router, ActivatedRoute} from '@angular/router';
|
|
import {Subject} from 'rxjs';
|
|
import {takeUntil} from 'rxjs/operators';
|
|
import {ApiService, LoginCredentials} from '../../services/api';
|
|
import {NgClass} from '@angular/common';
|
|
|
|
@Component({
|
|
selector: 'app-login',
|
|
templateUrl: './login.component.html',
|
|
imports: [
|
|
ReactiveFormsModule,
|
|
NgClass
|
|
],
|
|
styleUrls: ['./login.component.css']
|
|
})
|
|
export class LoginComponent implements OnInit, OnDestroy {
|
|
loginForm: FormGroup;
|
|
loading = false;
|
|
error = '';
|
|
success = '';
|
|
showPassword = false;
|
|
returnUrl = '';
|
|
|
|
private destroy$ = new Subject<void>();
|
|
|
|
constructor(
|
|
private formBuilder: FormBuilder,
|
|
private apiService: ApiService,
|
|
private router: Router,
|
|
private route: ActivatedRoute
|
|
) {
|
|
// Redirect to dashboard if already logged in
|
|
if (this.apiService.isAuthenticated) {
|
|
this.router.navigate(['/dashboard']);
|
|
}
|
|
|
|
this.loginForm = this.formBuilder.group({
|
|
email: ['', [Validators.required, Validators.email]],
|
|
password: ['', [Validators.required, Validators.minLength(6)]],
|
|
rememberMe: [false]
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
// Get return url from route parameters or default to dashboard
|
|
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/dashboard';
|
|
|
|
// Clear any existing error messages
|
|
this.clearMessages();
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.destroy$.next();
|
|
this.destroy$.complete();
|
|
}
|
|
|
|
// Getter for easy access to form fields
|
|
get f() {
|
|
return this.loginForm.controls;
|
|
}
|
|
|
|
// Get specific field error message
|
|
getFieldError(fieldName: string): string {
|
|
const field = this.loginForm.get(fieldName);
|
|
if (field?.errors && field.touched) {
|
|
if (field.errors['required']) {
|
|
return `${this.capitalizeFirst(fieldName)} is required`;
|
|
}
|
|
if (field.errors['email']) {
|
|
return 'Please enter a valid email address';
|
|
}
|
|
if (field.errors['minlength']) {
|
|
return `${this.capitalizeFirst(fieldName)} must be at least ${field.errors['minlength'].requiredLength} characters`;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// Check if field has error
|
|
hasFieldError(fieldName: string): boolean {
|
|
const field = this.loginForm.get(fieldName);
|
|
return !!(field?.errors && field.touched);
|
|
}
|
|
|
|
// Capitalize first letter
|
|
private capitalizeFirst(str: string): string {
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
}
|
|
|
|
// Toggle password visibility
|
|
togglePasswordVisibility(): void {
|
|
this.showPassword = !this.showPassword;
|
|
}
|
|
|
|
// Clear error and success messages
|
|
clearMessages(): void {
|
|
this.error = '';
|
|
this.success = '';
|
|
}
|
|
|
|
// Handle form submission
|
|
onSubmit(): void {
|
|
this.clearMessages();
|
|
|
|
// Mark all fields as touched to show validation errors
|
|
this.loginForm.markAllAsTouched();
|
|
|
|
if (this.loginForm.invalid) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
|
|
const credentials: LoginCredentials = {
|
|
email: this.f['email'].value,
|
|
password: this.f['password'].value
|
|
};
|
|
|
|
this.apiService.login(credentials)
|
|
.pipe(takeUntil(this.destroy$))
|
|
.subscribe({
|
|
next: (response) => {
|
|
this.loading = false;
|
|
|
|
if (response.success) {
|
|
this.success = 'Login successful! Redirecting...';
|
|
|
|
// Store remember me preference if needed
|
|
if (this.f['rememberMe'].value) {
|
|
localStorage.setItem('rememberMe', 'true');
|
|
}
|
|
|
|
// Redirect after a short delay to show success message
|
|
setTimeout(() => {
|
|
this.router.navigate([this.returnUrl]);
|
|
}, 1000);
|
|
|
|
} else {
|
|
this.error = response.message || 'Login failed. Please try again.';
|
|
}
|
|
},
|
|
error: (error) => {
|
|
this.loading = false;
|
|
|
|
// Handle specific error cases
|
|
if (error.status === 422 && error.error?.errors) {
|
|
// Validation errors from server
|
|
const serverErrors = error.error.errors;
|
|
let errorMessages: string[] = [];
|
|
|
|
Object.keys(serverErrors).forEach(key => {
|
|
if (Array.isArray(serverErrors[key])) {
|
|
errorMessages = errorMessages.concat(serverErrors[key]);
|
|
}
|
|
});
|
|
|
|
this.error = errorMessages.join(', ');
|
|
} else if (error.status === 401) {
|
|
this.error = 'Invalid email or password. Please try again.';
|
|
} else {
|
|
this.error = error.userMessage || 'An error occurred. Please try again.';
|
|
}
|
|
|
|
console.error('Login error:', error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Navigate to register page
|
|
goToRegister(): void {
|
|
this.router.navigate(['/register'], {
|
|
queryParams: {returnUrl: this.returnUrl}
|
|
});
|
|
}
|
|
|
|
// Navigate to forgot password page
|
|
goToForgotPassword(): void {
|
|
this.router.navigate(['/forgot-password']);
|
|
}
|
|
|
|
// Demo login (optional - for testing)
|
|
demoLogin(): void {
|
|
this.loginForm.patchValue({
|
|
email: 'demo@example.com',
|
|
password: 'password123'
|
|
});
|
|
}
|
|
}
|