diff --git a/smp-angular/src/app/login/login.component.html b/smp-angular/src/app/login/login.component.html index 6c01f9a9be280877a8ed690bf84030faa0ae8464..df99e9706fdce776d3c40e5a0d0759d1115fbdb9 100644 --- a/smp-angular/src/app/login/login.component.html +++ b/smp-angular/src/app/login/login.component.html @@ -29,7 +29,7 @@ <ng-template #loginFormContainer> - <div [formGroup]="loginForm" class="form-control"> + <div [formGroup]="loginForm" class="form-control" (ngSubmit)="login()" (keydown.enter)="onLoginFormEnterKeyDown($event)"> <mat-form-field style="width: 100%"> <mat-label>{{ "login.label.login.username" | translate }}</mat-label> <input matInput id="username_id" @@ -40,6 +40,7 @@ <mat-form-field style="width: 100%"> <mat-label>{{ "login.label.password" | translate }}</mat-label> <input matInput id="password_id" type="password" + formControlName="password" required> </mat-form-field> <button mat-raised-button color="primary" id="loginbutton_id" [disabled]="!loginForm.valid" diff --git a/smp-angular/src/app/login/login.component.ts b/smp-angular/src/app/login/login.component.ts index e0a10aac6a273d0817bb736ebf2a948f8089fc87..5a5ce8703232adc20767213aab17e2dedd3bb113 100644 --- a/smp-angular/src/app/login/login.component.ts +++ b/smp-angular/src/app/login/login.component.ts @@ -1,22 +1,33 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {SecurityService} from '../security/security.service'; -import {AlertMessageService} from '../common/alert-message/alert-message.service'; +import { + AlertMessageService +} from '../common/alert-message/alert-message.service'; import {SecurityEventService} from '../security/security-event.service'; import {User} from '../security/user.model'; import {MatDialog} from '@angular/material/dialog'; -import {DefaultPasswordDialogComponent} from 'app/security/default-password-dialog/default-password-dialog.component'; import {lastValueFrom, Subscription} from 'rxjs'; import { ExpiredPasswordDialogComponent } from '../common/dialogs/expired-password-dialog/expired-password-dialog.component'; import {GlobalLookups} from "../common/global-lookups"; -import {PasswordChangeDialogComponent} from "../common/dialogs/password-change-dialog/password-change-dialog.component"; -import {InformationDialogComponent} from "../common/dialogs/information-dialog/information-dialog.component"; +import { + PasswordChangeDialogComponent +} from "../common/dialogs/password-change-dialog/password-change-dialog.component"; +import { + InformationDialogComponent +} from "../common/dialogs/information-dialog/information-dialog.component"; import {formatDate} from "@angular/common"; -import {EntityStatus} from "../common/enums/entity-status.enum"; -import {FormControl, FormGroup, Validators} from "@angular/forms"; +import { + FormGroup, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from "@angular/forms"; import {TranslateService} from "@ngx-translate/core"; +import {WindowSpinnerService} from "../common/services/window-spinner.service"; @Component({ templateUrl: './login.component.html', @@ -24,8 +35,8 @@ import {TranslateService} from "@ngx-translate/core"; }) export class LoginComponent implements OnInit, OnDestroy { - loginForm: FormGroup; - resetForm: FormGroup; + loginForm: UntypedFormGroup; + resetForm: UntypedFormGroup; loading = false; returnUrl: string; sub: Subscription; @@ -39,17 +50,28 @@ export class LoginComponent implements OnInit, OnDestroy { private alertService: AlertMessageService, private securityEventService: SecurityEventService, private dialog: MatDialog, - private translateService: TranslateService) { + private translateService: TranslateService, + private fb: UntypedFormBuilder, + private windowSpinnerService: WindowSpinnerService) { + this.loginForm = fb.group({ + 'username': new UntypedFormControl({value: ''}, [Validators.required]), + 'password': new UntypedFormControl({value: ''}, [Validators.required]), + }); + this.resetForm = new FormGroup({ + 'resetUsername': new UntypedFormControl({value: ''}, [Validators.required]), + }); + this.loginForm.controls['username'].setValue(''); + this.loginForm.controls['password'].setValue(''); + this.resetForm.controls['resetUsername'].setValue(''); } ngOnInit() { - this.initForm(); this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; - + // make reference to this to be available in async functions where ''this'' is something else this.sub = this.securityEventService.onLoginSuccessEvent().subscribe( user => { - if (user && user.passwordExpired) { + if (user?.passwordExpired) { if (user.forceChangeExpiredPassword) { this.dialog.open(PasswordChangeDialogComponent, { data: { @@ -82,10 +104,10 @@ export class LoginComponent implements OnInit, OnDestroy { switch (error.status) { case HTTP_UNAUTHORIZED: message = error.error.errorDescription; - this.loginForm['password'].setValue(''); + this.loginForm.controls['password'].setValue(''); break; case HTTP_FORBIDDEN: - const forbiddenCode = error.message; + const forbiddenCode = error?.message; switch (forbiddenCode) { case USER_INACTIVE: message = await lastValueFrom(this.translateService.get("login.error.inactive.user")); @@ -93,7 +115,7 @@ export class LoginComponent implements OnInit, OnDestroy { default: message = await lastValueFrom(this.translateService.get("login.error.invalid.credentials", {errorStatus: error.status})); // clear the password - this.loginForm['password'].setValue(''); + this.loginForm.controls['password'].setValue(''); break; } break; @@ -109,24 +131,21 @@ export class LoginComponent implements OnInit, OnDestroy { }); } - private initForm() { - this.loginForm = new FormGroup({ - username: new FormControl('', Validators.required), - password: new FormControl('', Validators.required), - }); - - this.resetForm = new FormGroup({ - resetUsername: new FormControl('', Validators.required), - }); - + onLoginFormEnterKeyDown(event: KeyboardEvent) { + if (this.loginForm.valid + && !this.windowSpinnerService.showSpinner) { + event.preventDefault(); + this.login(); + } } async login() { + this.alertService.clearAlert(); if (this.loginForm.valid) { this.securityService.login( - this.loginForm.get('username').value, - this.loginForm.get('password').value + this.loginForm.controls['username'].value, + this.loginForm.controls['password'].value ); } else { this.alertService.error(await lastValueFrom(this.translateService.get("login.error.invalid.username.or.password"))); @@ -137,7 +156,7 @@ export class LoginComponent implements OnInit, OnDestroy { this.alertService.clearAlert(); if (this.resetForm.valid) { this.securityService.requestCredentialReset( - this.resetForm.get('resetUsername').value + this.resetForm.controls['resetUsername'].value ); } else { this.alertService.error(await lastValueFrom(this.translateService.get("login.error.invalid.username"))); diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts index edd2e2671383f1dbd21f40515e86fe0f5fe80984..b1e82dc40b2d2ce166d737040c49a1478adf3cd5 100644 --- a/smp-angular/src/app/security/security.service.ts +++ b/smp-angular/src/app/security/security.service.ts @@ -39,6 +39,7 @@ export class SecurityService { } login(username: string, password: string) { + this.windowSpinnerService.showSpinner = true; let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'}); return this.http.post<User>(SmpConstants.REST_PUBLIC_SECURITY_AUTHENTICATION, JSON.stringify({ @@ -52,9 +53,14 @@ export class SecurityService { this.securityEventService.notifyLoginSuccessEvent(response); }, error: (error: any) => { + this.windowSpinnerService.showSpinner = false this.alertService.error(error.error?.errorDescription) this.securityEventService.notifyLoginErrorEvent(error); + + }, complete: () => { + this.windowSpinnerService.showSpinner = false } + }); }