로그인 페이지 로그인 실패시 Alert 처리.
This commit is contained in:
parent
6550c419a1
commit
9ec163bac6
|
@ -2,6 +2,8 @@
|
|||
<div class="login-wrapper" fxLayout="column" fxLayoutAlign="center center">
|
||||
<ucap-account-login
|
||||
[companyList]="companyList$ | async"
|
||||
[loginBtnEnable]="loginBtnEnable"
|
||||
[loginBtnText]="loginBtnText"
|
||||
(login)="onLogin($event)"
|
||||
>
|
||||
</ucap-account-login>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
|
||||
import { Store, select } from '@ngrx/store';
|
||||
|
||||
|
@ -7,20 +7,45 @@ import { Company } from '@ucap-webmessenger/api-external';
|
|||
import * as AppStore from '@app/store';
|
||||
import * as AuthenticationStore from '@app/store/account/authentication';
|
||||
import * as CompanyStore from '@app/store/setting/company';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { tap, map } from 'rxjs/operators';
|
||||
import {
|
||||
DialogService,
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
} from '@ucap-webmessenger/ui';
|
||||
import { StringUtil } from '@ucap-webmessenger/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-account-login',
|
||||
templateUrl: './login.page.component.html',
|
||||
styleUrls: ['./login.page.component.scss']
|
||||
})
|
||||
export class LoginPageComponent implements OnInit {
|
||||
export class LoginPageComponent implements OnInit, OnDestroy {
|
||||
companyList$: Observable<Company[]>;
|
||||
|
||||
constructor(private store: Store<any>, private router: Router) {}
|
||||
loginFailureCount: Subscription;
|
||||
|
||||
defatulLoginBtnText: string;
|
||||
loginBtnText: string;
|
||||
loginBtnEnable: boolean;
|
||||
|
||||
timeChecker: any;
|
||||
defatulWaitingTime: number;
|
||||
waitingTime: number;
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private router: Router,
|
||||
private dialogService: DialogService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.defatulLoginBtnText = 'LOGIN';
|
||||
this.defatulWaitingTime = 5 * 60; // sec
|
||||
|
||||
this.store.dispatch(
|
||||
CompanyStore.companyList({
|
||||
companyGroupCode: 'LG'
|
||||
|
@ -30,6 +55,80 @@ export class LoginPageComponent implements OnInit {
|
|||
this.companyList$ = this.store.pipe(
|
||||
select(AppStore.SettingSelector.CompanySelector.companyList)
|
||||
);
|
||||
|
||||
this.loginFailureCount = this.store
|
||||
.pipe(
|
||||
select(AppStore.AccountSelector.AuthenticationSelector.loginFailCount),
|
||||
map(count => {
|
||||
if (count > 0) {
|
||||
if (count < 5) {
|
||||
this.dialogService.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
width: '360px',
|
||||
data: {
|
||||
title: 'Alert',
|
||||
html: `아이디 또는 패스워드가<br/>일치하지 않습니다.`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (count === 5) {
|
||||
this.dialogService.open<
|
||||
AlertDialogComponent,
|
||||
AlertDialogData,
|
||||
AlertDialogResult
|
||||
>(AlertDialogComponent, {
|
||||
width: '360px',
|
||||
data: {
|
||||
title: 'Alert',
|
||||
html: `비밀번호 오류 횟수 초과입니다.<br/>비밀번호를 확인하신 후<br/>잠시 후 다시 시작해 주세요.`
|
||||
}
|
||||
});
|
||||
|
||||
this.timeChecker = setInterval(() => this.getCheckTime(), 1000);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (!!this.timeChecker) {
|
||||
clearInterval(this.timeChecker);
|
||||
}
|
||||
this.waitingTime = this.defatulWaitingTime;
|
||||
this.loginBtnText = this.defatulLoginBtnText;
|
||||
this.loginBtnEnable = true;
|
||||
}
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.loginFailureCount) {
|
||||
this.loginFailureCount.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
getCheckTime() {
|
||||
if (this.waitingTime <= 0) {
|
||||
// reset.
|
||||
if (!!this.timeChecker) {
|
||||
clearInterval(this.timeChecker);
|
||||
}
|
||||
this.waitingTime = this.defatulWaitingTime;
|
||||
this.loginBtnText = this.defatulLoginBtnText;
|
||||
this.loginBtnEnable = true;
|
||||
} else {
|
||||
// wait.
|
||||
this.waitingTime = this.waitingTime - 1;
|
||||
|
||||
this.loginBtnText = `${StringUtil.zeroFill(
|
||||
Math.floor(this.waitingTime / 60),
|
||||
2
|
||||
)}:${StringUtil.zeroFill(this.waitingTime % 60, 2)}`;
|
||||
this.loginBtnEnable = false;
|
||||
}
|
||||
}
|
||||
|
||||
onLogin(value: {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
|||
import { LoginInfo } from '@app/types';
|
||||
import {
|
||||
UserPasswordSetRequest,
|
||||
UserPasswordSetResponse,
|
||||
UserPasswordSetResponse
|
||||
} from '@ucap-webmessenger/protocol-service';
|
||||
|
||||
export const webLogin = createAction(
|
||||
|
@ -43,6 +43,15 @@ export const loginFailure = createAction(
|
|||
props<{ error: any }>()
|
||||
);
|
||||
|
||||
export const increaseLoginFailCount = createAction(
|
||||
'[Account::Authentication] Increase Login Failure Count',
|
||||
props()
|
||||
);
|
||||
export const initialLoginFailCount = createAction(
|
||||
'[Account::Authentication] Initialize Login Failure Count',
|
||||
props()
|
||||
);
|
||||
|
||||
export const loginRedirect = createAction(
|
||||
'[Account::Authentication] Login Redirect'
|
||||
);
|
||||
|
|
|
@ -9,14 +9,14 @@ import { Actions, ofType, createEffect } from '@ngrx/effects';
|
|||
import {
|
||||
PiService,
|
||||
Login2Response,
|
||||
UserTermsActionResponse,
|
||||
UserTermsActionResponse
|
||||
} from '@ucap-webmessenger/pi';
|
||||
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
|
||||
import {
|
||||
DialogService,
|
||||
ConfirmDialogComponent,
|
||||
ConfirmDialogData,
|
||||
ConfirmDialogResult,
|
||||
ConfirmDialogResult
|
||||
} from '@ucap-webmessenger/ui';
|
||||
|
||||
import {
|
||||
|
@ -35,12 +35,14 @@ import {
|
|||
changePassword,
|
||||
changePasswordFailure,
|
||||
changePasswordSuccess,
|
||||
increaseLoginFailCount,
|
||||
initialLoginFailCount
|
||||
} from './actions';
|
||||
import {
|
||||
LoginInfo,
|
||||
KEY_LOGIN_INFO,
|
||||
EnvironmentsInfo,
|
||||
KEY_ENVIRONMENTS_INFO,
|
||||
KEY_ENVIRONMENTS_INFO
|
||||
} from '@app/types';
|
||||
import { AppAuthenticationService } from '@app/services/authentication.service';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
@ -48,7 +50,7 @@ import { Store } from '@ngrx/store';
|
|||
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
|
||||
import {
|
||||
ServiceProtocolService,
|
||||
UserPasswordSetResponse,
|
||||
UserPasswordSetResponse
|
||||
} from '@ucap-webmessenger/protocol-service';
|
||||
|
||||
@Injectable()
|
||||
|
@ -62,17 +64,19 @@ export class Effects {
|
|||
.login2({
|
||||
loginId: params.loginInfo.loginId,
|
||||
loginPw: params.loginInfo.loginPw,
|
||||
companyCode: params.loginInfo.companyCode,
|
||||
companyCode: params.loginInfo.companyCode
|
||||
})
|
||||
.pipe(
|
||||
map((res: Login2Response) => {
|
||||
if ('success' !== res.status.toLowerCase()) {
|
||||
this.store.dispatch(increaseLoginFailCount({}));
|
||||
return webLoginFailure({ error: 'Failed' });
|
||||
} else {
|
||||
this.store.dispatch(initialLoginFailCount({}));
|
||||
return webLoginSuccess({
|
||||
loginInfo: params.loginInfo,
|
||||
rememberMe: params.rememberMe,
|
||||
login2Response: res,
|
||||
login2Response: res
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
@ -147,8 +151,8 @@ export class Effects {
|
|||
width: '220px',
|
||||
data: {
|
||||
title: 'Logout',
|
||||
message: 'Logout ?',
|
||||
},
|
||||
message: 'Logout ?'
|
||||
}
|
||||
});
|
||||
|
||||
return result.choice;
|
||||
|
@ -177,7 +181,7 @@ export class Effects {
|
|||
token: loginRes.tokenString,
|
||||
deviceType: environmentsInfo.deviceType,
|
||||
localeCode: loginInfo.localeCode,
|
||||
textOnly: 'true',
|
||||
textOnly: 'true'
|
||||
});
|
||||
|
||||
const result = await this.dialogService.open<
|
||||
|
@ -189,9 +193,9 @@ export class Effects {
|
|||
height: '500px',
|
||||
disableClose: true,
|
||||
data: {
|
||||
title: '개인정보 동의',
|
||||
title: '개인정보 동의'
|
||||
// html: `<iframe id="ifm_privacy" src="${privacyTotalUrl}" style="width: 100%;height: 300px;" />`
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
if (result.choice) {
|
||||
|
@ -213,8 +217,8 @@ export class Effects {
|
|||
disableClose: true,
|
||||
data: {
|
||||
title: '패스워드 만료',
|
||||
message: '',
|
||||
},
|
||||
message: ''
|
||||
}
|
||||
});
|
||||
|
||||
if (result.choice) {
|
||||
|
@ -241,7 +245,7 @@ export class Effects {
|
|||
return {
|
||||
loginInfo,
|
||||
environmentsInfo,
|
||||
loginResponse: action.loginRes,
|
||||
loginResponse: action.loginRes
|
||||
};
|
||||
}),
|
||||
exhaustMap(params =>
|
||||
|
@ -249,7 +253,7 @@ export class Effects {
|
|||
.userTermsAction({
|
||||
userSeq: params.loginResponse.userSeq,
|
||||
token: params.loginResponse.tokenString,
|
||||
deviceType: params.environmentsInfo.deviceType,
|
||||
deviceType: params.environmentsInfo.deviceType
|
||||
})
|
||||
.pipe(
|
||||
map((res: UserTermsActionResponse) => {
|
||||
|
@ -273,7 +277,7 @@ export class Effects {
|
|||
this.serviceProtocolService.userPasswordSet(req).pipe(
|
||||
map((res: UserPasswordSetResponse) => {
|
||||
return changePasswordSuccess({
|
||||
res,
|
||||
res
|
||||
});
|
||||
}),
|
||||
catchError(error => of(changePasswordFailure({ error })))
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
import { Action, combineReducers, createReducer, on } from '@ngrx/store';
|
||||
import { State, initialState } from './state';
|
||||
import { loginSuccess } from './actions';
|
||||
import {
|
||||
loginSuccess,
|
||||
increaseLoginFailCount,
|
||||
initialLoginFailCount
|
||||
} from './actions';
|
||||
|
||||
export const reducer = createReducer(
|
||||
initialState,
|
||||
on(loginSuccess, (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
loginRes: action.loginRes,
|
||||
loginRes: action.loginRes
|
||||
};
|
||||
}),
|
||||
|
||||
on(increaseLoginFailCount, (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
loginFailCount: state.loginFailCount + 1
|
||||
};
|
||||
}),
|
||||
on(initialLoginFailCount, (state, action) => {
|
||||
return {
|
||||
...state,
|
||||
loginFailCount: 0
|
||||
};
|
||||
})
|
||||
);
|
||||
|
|
|
@ -4,14 +4,20 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
|||
// tslint:disable-next-line: no-empty-interface
|
||||
export interface State {
|
||||
loginRes: LoginResponse | null;
|
||||
loginFailCount: number;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
loginRes: null,
|
||||
loginFailCount: 0
|
||||
};
|
||||
|
||||
export function selectors<S>(selector: Selector<any, State>) {
|
||||
return {
|
||||
loginRes: createSelector(selector, (state: State) => state.loginRes),
|
||||
loginFailCount: createSelector(
|
||||
selector,
|
||||
(state: State) => state.loginFailCount
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +1,18 @@
|
|||
export class StringUtil {}
|
||||
export class StringUtil {
|
||||
/**
|
||||
* prefix zero fill
|
||||
* @param str target string
|
||||
* @param len fill in length
|
||||
*/
|
||||
public static zeroFill(str: any, len: number): string {
|
||||
if (typeof str === 'string') {
|
||||
let fillin = '';
|
||||
for (let i = 0; i < len - str.length; i++) {
|
||||
fillin += '0';
|
||||
}
|
||||
return fillin + str;
|
||||
} else if (typeof str === 'number') {
|
||||
return StringUtil.zeroFill(str.toString(), len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,17 @@
|
|||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="remember-forgot-password" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center">
|
||||
<mat-checkbox class="remember-me" formControlName="remember" aria-label="Remember Me">
|
||||
<div
|
||||
class="remember-forgot-password"
|
||||
fxLayout="row"
|
||||
fxLayout.xs="column"
|
||||
fxLayoutAlign="space-between center"
|
||||
>
|
||||
<mat-checkbox
|
||||
class="remember-me"
|
||||
formControlName="remember"
|
||||
aria-label="Remember Me"
|
||||
>
|
||||
Remember Me
|
||||
</mat-checkbox>
|
||||
|
||||
|
@ -49,10 +58,10 @@
|
|||
mat-raised-button
|
||||
class="submit-button bg-accent-dark"
|
||||
aria-label="LOG IN"
|
||||
[disabled]="loginForm.invalid"
|
||||
[disabled]="loginForm.invalid || !loginBtnEnable"
|
||||
(click)="onClickLogin()"
|
||||
>
|
||||
LOGIN
|
||||
{{ loginBtnText }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@ import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
|
|||
export class LoginComponent implements OnInit {
|
||||
@Input()
|
||||
companyList?: Company[];
|
||||
@Input()
|
||||
loginBtnText?: string;
|
||||
@Input()
|
||||
loginBtnEnable: boolean;
|
||||
|
||||
@Output()
|
||||
login = new EventEmitter<{
|
||||
|
@ -57,6 +61,10 @@ export class LoginComponent implements OnInit {
|
|||
loginPw: ['', Validators.required],
|
||||
remember: [remember]
|
||||
});
|
||||
|
||||
if (!this.loginBtnText || this.loginBtnText.trim().length === 0) {
|
||||
this.loginBtnText = 'LOGIN';
|
||||
}
|
||||
}
|
||||
|
||||
onClickLogin() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user