Files
syscom-master-admin-ui/src/app/features/authentication/pages/login/login.ts
T

123 lines
3.5 KiB
TypeScript

import { ChangeDetectorRef, Component, inject } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { AuthService } from '../../data-access/auth.service';
import { ReactiveFormsModule } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { catchError, finalize, of, switchMap, tap } from 'rxjs';
import { AppContextService } from '../../../../core/services/context/app-context.service';
@Component({
selector: 'app-login',
standalone: true,
imports: [RouterModule, ReactiveFormsModule],
templateUrl: './login.html',
styleUrl: './login.scss'
})
export class Login {
private readonly formBuilder = inject(FormBuilder);
private readonly cdr = inject(ChangeDetectorRef);
private readonly fallbackRoute = '/dashboards/crm';
public readonly adminLoginForm = this.formBuilder.nonNullable.group({
username: ['superadmin@syscom.dev', [Validators.required]],
password: ['', Validators.required],
rememberMe: [false]
});
public isSubmitting = false;
public loginError = '';
public visibilityMap: Record<string, boolean> = {
Angular: false
};
public iconMap: Record<string, string> = {
Angular: 'fe fe-eye-off'
};
constructor(
public authservice: AuthService,
private appContextService: AppContextService,
private route: ActivatedRoute,
private router: Router,
private toastr: ToastrService
) { }
login() {
this.loginError = '';
if (this.adminLoginForm.invalid) {
this.adminLoginForm.markAllAsTouched();
return;
}
if (this.isSubmitting) {
return;
}
this.isSubmitting = true;
const { username, password, rememberMe } = this.adminLoginForm.getRawValue();
this.authservice.login({ email: username, password }, !!rememberMe)
.pipe(
switchMap(() =>
this.appContextService.ensureMenuInitialized(true).pipe(
catchError(() => {
this.authservice.logout();
this.loginError = 'Unable to load application context.';
return of(null);
})
)
),
finalize(() => {
this.isSubmitting = false;
this.cdr.detectChanges();
})
)
.subscribe({
next: () => {
if (this.loginError) {
this.toastr.error(this.loginError);
return;
}
void this.router.navigateByUrl(this.getSafeReturnUrl());
this.toastr.success('Login successful', username);
},
error: (error) => {
this.loginError =
error?.error?.detail ||
error?.error?.message ||
'Invalid email or password';
this.toastr.error(this.loginError);
}
});
}
toggleVisibility(tab: string): void {
this.visibilityMap[tab] = !this.visibilityMap[tab];
this.iconMap[tab] = this.visibilityMap[tab] ? 'fe fe-eye' : 'fe fe-eye-off';
}
private getSafeReturnUrl(): string {
const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl')?.trim() ?? '';
if (!returnUrl) {
return this.fallbackRoute;
}
const lowerReturnUrl = returnUrl.toLowerCase();
const isSafeInternalUrl =
returnUrl.startsWith('/') &&
!returnUrl.startsWith('//') &&
!returnUrl.includes('\\') &&
!lowerReturnUrl.includes('http://') &&
!lowerReturnUrl.includes('https://');
return isSafeInternalUrl ? returnUrl : this.fallbackRoute;
}
}