src/app/modules/public/module/sign-in/sso/components/update-contact/update-contact.component.ts
OnInit
AfterViewInit
styleUrls | ./update-contact.component.scss |
templateUrl | ./update-contact.component.html |
constructor(activatedRoute: ActivatedRoute, tenantService: TenantService, resourceService: ResourceService, userService: UserService, otpService: OtpService, toasterService: ToasterService, navigationHelperService: NavigationHelperService, orgDetailsService: OrgDetailsService, utilService: UtilService, signupService: SignupService, telemetryService: TelemetryService, tncService: TncService)
|
|||||||||||||||||||||||||||||||||||||||
Parameters :
|
Private checkUserExist | ||||
checkUserExist(captchaResponse?)
|
||||
Parameters :
Returns :
void
|
fetchTncConfiguration |
fetchTncConfiguration()
|
Fetches tnc related configuration
Returns :
void
|
Private generateInteractEvent | ||||||
generateInteractEvent(cData, eData)
|
||||||
Used to generate interact telemetry
Parameters :
Returns :
void
|
Private generateOtp |
generateOtp()
|
Returns :
void
|
getQueryParams | ||||
getQueryParams(queryObj)
|
||||
Parameters :
Returns :
string
|
handleFormChangeEvent |
handleFormChangeEvent()
|
Returns :
void
|
Public handleOtpValidationFailed |
handleOtpValidationFailed()
|
Returns :
void
|
Public handleOtpValidationSuccess |
handleOtpValidationSuccess()
|
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
Public onFormUpdate | ||||
onFormUpdate(captchaResponse?)
|
||||
Parameters :
Returns :
void
|
Private prepareOtpData |
prepareOtpData()
|
Returns :
void
|
resetForm | ||||||||
resetForm(type: string)
|
||||||||
Parameters :
Returns :
void
|
resetGoogleCaptcha |
resetGoogleCaptcha()
|
Returns :
void
|
resolved | ||||||||
resolved(captchaResponse: string)
|
||||||||
Parameters :
Returns :
void
|
Private setTenantInfo |
setTenantInfo()
|
Returns :
void
|
showAndHidePopup | ||||||
showAndHidePopup(mode: boolean)
|
||||||
Parameters :
Returns :
void
|
submitForm |
submitForm()
|
Returns :
void
|
telemetryLogEvents |
telemetryLogEvents(api: any, status: boolean)
|
Returns :
void
|
toggleTncCheckBox | ||||||
toggleTncCheckBox(e)
|
||||||
Toogles contact form value based on event data
Parameters :
Returns :
void
|
Public activatedRoute |
Type : ActivatedRoute
|
captchaRef |
Type : RecaptchaComponent
|
Decorators :
@ViewChild('captchaRef')
|
captchaValidationFailed |
Default value : false
|
Private contactDetailsForm |
Decorators :
@ViewChild('contactDetailsForm')
|
Public contactForm |
Type : object
|
Default value : {
value: '',
type: 'phone',
tncAccepted: false
}
|
Public custodianOrgDetails |
Public disableSubmitBtn |
Default value : true
|
googleCaptchaSiteKey |
Type : string
|
instance |
Type : string
|
isP1CaptchaEnabled |
Type : any
|
Public isValidIdentifier |
Public navigationHelperService |
Type : NavigationHelperService
|
Public otpData |
Type : object
|
Default value : {}
|
Public otpService |
Type : OtpService
|
Public resourceService |
Type : ResourceService
|
Public showError |
Default value : false
|
Public showMergeConfirmation |
Default value : false
|
Public showOtpComp |
Default value : false
|
showTncPopup |
Default value : false
|
Public signupService |
Type : SignupService
|
Public telemetryImpression |
Public telemetryService |
Type : TelemetryService
|
Public tenantInfo |
Type : any
|
Default value : {}
|
termsAndConditionLink |
Type : string
|
tncLatestVersion |
Type : string
|
Public tncService |
Type : TncService
|
Public toasterService |
Type : ToasterService
|
Public userBlocked |
Default value : false
|
Public userDetails |
Type : any
|
Default value : {}
|
Public userExist |
Default value : false
|
Public userService |
Type : UserService
|
Public utilService |
Type : UtilService
|
Public validationPattern |
Type : object
|
Default value : {
phone: /^[6-9]\d{9}$/,
email: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[a-z]{2,4}$/
}
|
import { ActivatedRoute } from '@angular/router';
import { Component, OnInit, AfterViewInit, ViewChild } from '@angular/core';
import { TenantService, UserService, OtpService, OrgDetailsService, TncService } from '@sunbird/core';
import { first, delay } from 'rxjs/operators';
import {
ResourceService, ToasterService, NavigationHelperService,
ServerResponse, UtilService
} from '@sunbird/shared';
import * as _ from 'lodash-es';
import {SignupService} from '../../../../signup/services';
import { map } from 'rxjs/operators';
import { combineLatest } from 'rxjs';
import { TelemetryService } from '@sunbird/telemetry';
import { RecaptchaComponent } from 'ng-recaptcha';
@Component({
templateUrl: './update-contact.component.html',
styleUrls: ['./update-contact.component.scss']
})
export class UpdateContactComponent implements OnInit, AfterViewInit {
@ViewChild('contactDetailsForm') private contactDetailsForm;
@ViewChild('captchaRef') captchaRef: RecaptchaComponent;
public telemetryImpression;
public tenantInfo: any = {};
public showOtpComp = false;
public showMergeConfirmation = false;
public userBlocked = false;
public userExist = false;
public disableSubmitBtn = true;
public otpData = {};
public userDetails: any = {};
public showError = false;
public custodianOrgDetails;
public isValidIdentifier;
public validationPattern = {
phone: /^[6-9]\d{9}$/,
email: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[a-z]{2,4}$/
};
public contactForm = {
value: '',
type: 'phone',
tncAccepted: false
};
tncLatestVersion: string;
termsAndConditionLink: string;
instance: string;
showTncPopup = false;
googleCaptchaSiteKey: string;
isP1CaptchaEnabled: any;
captchaValidationFailed = false;
constructor(public activatedRoute: ActivatedRoute, private tenantService: TenantService, public resourceService: ResourceService,
public userService: UserService, public otpService: OtpService, public toasterService: ToasterService,
public navigationHelperService: NavigationHelperService, private orgDetailsService: OrgDetailsService,
public utilService: UtilService, public signupService: SignupService,
public telemetryService: TelemetryService, public tncService: TncService) {
}
ngOnInit() {
this.instance = _.upperCase(this.resourceService.instance || 'SUNBIRD');
this.isP1CaptchaEnabled = (<HTMLInputElement>document.getElementById('p1reCaptchaEnabled'))
? (<HTMLInputElement>document.getElementById('p1reCaptchaEnabled')).value : 'true';
try {
this.googleCaptchaSiteKey = (<HTMLInputElement>document.getElementById('googleCaptchaSiteKey')).value;
} catch (error) {
this.googleCaptchaSiteKey = '';
}
this.fetchTncConfiguration();
this.setTenantInfo();
}
ngAfterViewInit () {
setTimeout(() => {
this.handleFormChangeEvent();
this.telemetryImpression = {
context: {
env: this.activatedRoute.snapshot.data.telemetry.env,
},
edata: {
type: this.activatedRoute.snapshot.data.telemetry.type,
pageid: this.activatedRoute.snapshot.data.telemetry.pageid,
uri: this.activatedRoute.snapshot.data.telemetry.uri,
duration: this.navigationHelperService.getPageLoadTime()
}
};
});
}
/**
* Toogles contact form value based on event data
* @param e event data
*/
toggleTncCheckBox(e) {
this.contactForm.tncAccepted = e.target.checked;
const cData = {
env: 'sso-signup',
cdata: [
{id: 'user:tnc:accept', type: 'Feature'},
{id: 'SB-16663', type: 'Task'}
]
};
const eData = {
id: 'user:tnc:accept',
type: 'click',
subtype: this.contactForm.tncAccepted ? 'selected' : 'unselected',
pageid: 'sso-signup'
};
this.generateInteractEvent(cData, eData);
}
/**
* Used to generate interact telemetry
* @param tncAcceptedStatus
*/
private generateInteractEvent(cData, eData) {
const interactData = {
context: cData,
edata: eData
};
this.telemetryService.interact(interactData);
}
/**
* Fetches tnc related configuration
*/
fetchTncConfiguration() {
this.tncService.getTncConfig().subscribe((data: ServerResponse) => {
this.telemetryLogEvents('fetch-terms-condition', true);
const response = _.get(data, 'result.response.value');
if (response) {
try {
const tncConfig = this.utilService.parseJson(response);
this.tncLatestVersion = _.get(tncConfig, 'latestVersion') || {};
this.termsAndConditionLink = tncConfig[this.tncLatestVersion].url;
} catch (e) {
this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
}
}
}, (err) => {
this.telemetryLogEvents('fetch-terms-condition', false);
this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
}
);
}
handleFormChangeEvent() {
if(this.contactDetailsForm){
this.contactDetailsForm.valueChanges.pipe(delay(1)).subscribe((data, data2) => {
if (_.get(this.contactDetailsForm, 'status') === 'VALID' &&
_.get(this.contactDetailsForm, 'controls.tncAccepted.value')) {
this.disableSubmitBtn = false;
this.isValidIdentifier = true;
this.userExist = false;
this.userBlocked = false;
} else {
this.disableSubmitBtn = true;
this.isValidIdentifier = _.get(this.contactDetailsForm, 'controls.value.status') === 'VALID'
&& this.validationPattern[this.contactForm.type].test(this.contactForm.value);
this.userExist = false;
this.userBlocked = false;
}
});
}
}
private checkUserExist(captchaResponse?) {
const uri = this.contactForm.type + '/' + this.contactForm.value + '?captchaResponse=' + captchaResponse;
combineLatest(this.userService.getUserByKey(uri), this.orgDetailsService.getCustodianOrgDetails())
.pipe(map(data => ({
userDetails: data[0], custOrgDetails: data[1]
})))
.subscribe(({userDetails, custOrgDetails}) => {
this.resetGoogleCaptcha();
if (_.get(userDetails, 'result.response.rootOrgId') === _.get(custOrgDetails, 'result.response.value')) {
this.userDetails = userDetails.result.response;
this.disableSubmitBtn = false;
this.userExist = false;
this.userBlocked = false;
this.generateOtp();
} else {
this.userExist = true;
this.userBlocked = false;
this.disableSubmitBtn = true;
}
}, err => {
this.userDetails = {};
if (_.get(err, 'error.params.status') && err.error.params.status === 'USER_ACCOUNT_BLOCKED') {
this.userBlocked = true;
this.userExist = false;
this.disableSubmitBtn = true;
return;
}
if (_.get(err, 'error.params.status') && err.error.params.status === 'I\'m a teapot') {
this.disableSubmitBtn = true;
this.captchaValidationFailed = true;
return;
}
this.generateOtp();
this.disableSubmitBtn = false;
this.userExist = false;
this.userBlocked = false;
});
}
public onFormUpdate(captchaResponse?) {
this.checkUserExist(captchaResponse);
const cData = {
env: 'sso-signup',
cdata: []
};
const eData = {
id: this.contactForm.type === 'email' ? 'submit-email' : 'submit-phone',
type: 'click',
pageid: 'sso-sign-in',
};
this.generateInteractEvent(cData, eData);
}
private generateOtp() {
const request = {
request: {
'key': this.contactForm.value,
'type': this.contactForm.type
}
};
this.otpService.generateOTP(request).subscribe((data) => {
this.prepareOtpData();
this.showOtpComp = true;
}, (err) => {
const errorMessage = (err.error.params.status === 'PHONE_ALREADY_IN_USE') || (err.error.params.status === 'EMAIL_IN_USE') ||
(err.error.params.status === 'ERROR_RATE_LIMIT_EXCEEDED') ? err.error.params.errmsg : this.resourceService.messages.fmsg.m0085;
this.toasterService.error(errorMessage);
}
);
}
resetForm(type = 'phone') {
this.disableSubmitBtn = true;
this.contactForm = {
value: '',
type: type,
tncAccepted: false
};
this.userDetails = {};
this.userExist = false;
this.userBlocked = false;
}
private prepareOtpData() {
this.otpData = {
type: this.contactForm.type,
value: this.contactForm.value,
instructions: this.contactForm.type === 'phone' ?
this.resourceService.frmelmnts.instn.t0083 : this.resourceService.frmelmnts.instn.t0084,
retryMessage: this.contactForm.type === 'phone' ?
this.resourceService.frmelmnts.lbl.unableToUpdateMobile : this.resourceService.frmelmnts.lbl.unableToUpdateEmail,
wrongOtpMessage: this.contactForm.type === 'phone' ? this.resourceService.frmelmnts.lbl.wrongPhoneOTP :
this.resourceService.frmelmnts.lbl.wrongEmailOTP
};
}
public handleOtpValidationFailed() {
this.showOtpComp = false;
this.resetForm();
setTimeout(() => this.handleFormChangeEvent(), 100);
}
getQueryParams(queryObj) {
return '?' + Object.keys(queryObj).filter(key => queryObj[key])
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryObj[key])}`)
.join('&');
}
public handleOtpValidationSuccess() {
const query: any = {
type: this.contactForm.type,
value: this.contactForm.value,
tncAccepted: this.contactForm.tncAccepted,
tncVersion: this.tncLatestVersion
};
if (!_.isEmpty(this.userDetails)) {
if (this.userDetails.id) {
this.showOtpComp = false;
this.showMergeConfirmation = true;
}
} else {
window.location.href = `/v1/sso/contact/verified` +
this.getQueryParams({...this.activatedRoute.snapshot.queryParams, ...query});
}
}
private setTenantInfo() {
this.tenantService.tenantData$.pipe(first()).subscribe(data => {
if (!data.err) {
this.tenantInfo = {
logo: data.tenantData.logo,
tenantName: data.tenantData.titleName
};
}
});
}
telemetryLogEvents(api: any, status: boolean) {
let level = 'ERROR';
let msg = api + ' failed';
if (status) {
level = 'SUCCESS';
msg = api + ' success';
}
const event = {
context: {
env: 'sso-signup'
},
edata: {
type: api,
level: level,
message: msg
}
};
this.telemetryService.log(event);
}
showAndHidePopup(mode: boolean) {
this.showTncPopup = mode;
}
/**
* @param {string} captchaResponse - reCaptcha token
* @description - Callback function for reCaptcha response
* @since - release-3.1.0
*/
resolved(captchaResponse: string) {
if (captchaResponse) {
this.onFormUpdate(captchaResponse);
}
}
/**
* @description - Function to reset reCaptcha
* @since - release-3.1.0
*/
resetGoogleCaptcha() {
const element: HTMLElement = document.getElementById('resetGoogleCaptcha') as HTMLElement;
element && element.click();
}
/**
* @description - Function to be called on form submit
* @since - release-3.1.0
*/
submitForm() {
if (this.isP1CaptchaEnabled === 'true') {
this.resetGoogleCaptcha();
this.captchaRef && this.captchaRef.execute();
} else {
this.onFormUpdate();
}
}
}
<app-modal-wrapper #modal [config]="{disableClose: true, size: 'large', panelClass: ['overflow-visible', 'material-modal']}">
<ng-template sbModalContent>
<div class="sb-mat__modal sb-mat__modal--fullscreen">
<div [appTelemetryImpression]="telemetryImpression" class="signup-background">
<div class="fullpage-background-image">
<div class="container-wrapper">
<div *ngIf="!showOtpComp">
<div class="d-flex flex-jc-center mb-24">
<img class="image centered" alt={{tenantInfo.tenantName}} height="45" src="{{tenantInfo.logo}}">
</div>
<p class="text-center mb-24">{{resourceService?.frmelmnts?.lbl?.mobileEmailInfoText |
interpolate:'{instance}': resourceService.instance}}</p>
<form class="sb-form borderd" #contactDetailsForm="ngForm">
<div class="inline fields d-flex flex-w-wrap mb-8" id="Mandatory">
<div class="ui radio checkbox mr-16" tabindex="0" (click)="resetForm('phone')">
<input type="radio" value="phone" [(ngModel)]="contactForm.type" name='type'>
<label for="phoneNumber">{{resourceService?.frmelmnts?.lbl?.phoneNumber}}</label>
</div>
<div class="ui radio checkbox" tabindex="0" (click)="resetForm('email')">
<input type="radio" value="email" [(ngModel)]="contactForm.type" name='type'>
<label for="email">{{resourceService?.frmelmnts?.lbl?.email}}</label>
</div>
</div>
<div *ngIf="contactForm.type === 'phone'" class="ui left icon input d-flex sb-field">
<input [(ngModel)]="contactForm.value" name="value"
placeholder="{{ resourceService?.frmelmnts?.lbl?.tenDigitPhone }}" class="sb-form-control" required>
<!-- pattern="validationPattern.phone" -->
<i class=" icon">+91-</i>
</div>
<label
*ngIf="!userBlocked && !isValidIdentifier && contactForm.type === 'phone' && contactDetailsForm.controls.value && disableSubmitBtn && contactDetailsForm.controls.value.touched"
class="sb-color-warning fxsmall mt-8">
{{ resourceService.frmelmnts?.lbl?.validPhone}}</label>
<div *ngIf="contactForm.type === 'email'" class="sb-field">
<input [(ngModel)]="contactForm.value" class="sb-form-control" name="value"
placeholder="{{ resourceService.frmelmnts?.lbl?.email }}" required>
<!-- pattern="validationPattern.email" -->
</div>
<label
*ngIf="!userBlocked && !isValidIdentifier && contactForm.type === 'email' && contactDetailsForm.controls.value && disableSubmitBtn && contactDetailsForm.controls.value.touched"
class="sb-color-warning fxsmall mt-8 pb-0">
{{resourceService.frmelmnts?.lbl?.validEmail}}</label>
<label *ngIf="disableSubmitBtn && userBlocked" class="sb-color-warning fxsmall mt-8">
{{resourceService.frmelmnts?.lbl?.blockedUserError}}</label>
<label *ngIf="disableSubmitBtn && userExist" class="sb-color-warning fxsmall mt-8">
{{ contactForm.type === 'phone' ? resourceService.frmelmnts?.lbl?.uniquePhone :
resourceService.frmelmnts?.lbl?.uniqueEmail}}</label>
<label *ngIf="disableSubmitBtn && captchaValidationFailed" class="sb-color-warning fxsmall mt-8">
{{resourceService.frmelmnts?.lbl?.captchaValidationFailed}}</label>
<div class="required sb-checkbox sb-checkbox-primary sb-field mt-16">
<input type="checkbox" id="tncAccepted" role="checkbox" [(ngModel)]="contactForm.tncAccepted"
(change)="toggleTncCheckBox($event)" name="tncAccepted" class="mr-10 cursor-pointer" required
#tncAccepted="ngModel">
<label for="tncAccepted" class="fsmall mr-0">{{resourceService?.frmelmnts?.lbl?.tncLabel}}
<a class="text-underline fsmall" href="javascript:void(0)" tabindex="0"
(click)="showAndHidePopup(true)">
{{resourceService?.frmelmnts?.lbl?.tncLabelLink | interpolate:'{instance}': instance}}
</a>
</label>
</div>
<re-captcha *ngIf="isP1CaptchaEnabled === 'true' && googleCaptchaSiteKey" #captchaRef="reCaptcha"
(resolved)="$event && resolved($event) && captchaRef.reset()" siteKey="{{googleCaptchaSiteKey}}"
size="invisible"></re-captcha>
<a tabindex="0" (click)="captchaRef.reset()" id="resetGoogleCaptcha"></a>
<div class="text-center">
<button [disabled]="disableSubmitBtn"
[ngClass]="{'sb-btn-disabled':disableSubmitBtn, 'sb-btn-primary':!disableSubmitBtn}"
class="sb-btn sb-btn-normal mt-24 w-100" tabindex="0"
(click)="submitForm()">{{resourceService.frmelmnts?.btn?.submitbtn}}</button>
</div>
</form>
</div>
<div *ngIf="showOtpComp">
<app-otp-popup [otpData]="otpData" [redirectToLogin]="true"
(verificationSuccess)="handleOtpValidationSuccess($event)"
(redirectToParent)="handleOtpValidationFailed($event)">
</app-otp-popup>
</div>
<div *ngIf="showMergeConfirmation">
<app-sso-merge-confirmation [userDetails]="userDetails" [identifierType]="contactForm.type"
[identifierValue]="contactForm.value" [isTncAccepted]="contactForm.tncAccepted"
[tncVersionAccepted]="tncLatestVersion">
</app-sso-merge-confirmation>
</div>
</div>
</div>
</div>
<app-tnc-popup (close)="showAndHidePopup(false)" [tncUrl]="termsAndConditionLink" #termsAndCondPopUp
*ngIf="showTncPopup">
</app-tnc-popup>
</div>
</ng-template>
</app-modal-wrapper>
./update-contact.component.scss
@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;
@use 'pages/sign_in_up' as *;
.ui.raised.shadow.container.segment {
margin: calculateRem(20px) auto !important;
padding: 0;
padding: calculateRem(10px);
width: calc(100% - 4%) !important;
min-height: 90vh;
}
@media only screen and (min-width: 1024px) {
.ui.raised.shadow.container.segment {
margin: calculateRem(35px) auto !important;
width: calculateRem(944px) !important;
padding: calculateRem(20px);
box-shadow: 0 calculateRem(2px) calculateRem(16px) 0 rgba(var(--rc-rgba-black), 0.2);
}
}
@media only screen and (max-width: 767px) {
.ui.form .fields {
flex-wrap: nowrap;
}
}
.ui.form.borderd input[type="text"],
.ui.form.borderd input[type="text"]:focus,
.ui.form.borderd input[type="email"],
.ui.form.borderd input[type="number"],
.ui.form.borderd input[type="tel"],
.ui.form.borderd input[type="password"],
.ui.form.borderd input[type="url"],
.ui.form.borderd input:not([type]),
.ui.form.borderd input:not([type]):focus {
border: calculateRem(1px) solid var(--gray-300);
padding: 0.8em 0 0.8em 1em;
border-radius: calculateRem(4px);
}
.grey.text {
color: var(--gray-400);
}
.orange.text {
color: var(--accessibility-red);
}
.orange-border {
border-color: var(--accessibility-red) !important;
}
.blue.text {
color: var(--primary-color);
}
.fullpage {
::ng-deep {
.ui.fullscreen.modal > .close {
opacity: 0;
pointer-events: none;
}
}
}
i.close.icon {
font-size: 1.5rem;
position: absolute;
right: calculateRem(6px);
top: calculateRem(12px);
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.ui.form .field > label {
font-size: 1rem !important;
font-weight: normal;
}
.width-300 {
width: calculateRem(300px) !important;
}
.ui[class*="left icon"].input > input {
padding-left: 3.37142857em !important;
}
.ui.left.icon.input .icon {
left: calculateRem(2px);
top: calculateRem(11px);
font-size: calculateRem(12px);
font-family: inherit;
}
.opacity-1 {
opacity: 1 !important;
}
.ui[class*="left icon"].input > input {
padding-left: calculateRem(32px) !important;
border: 0.0625rem solid var(--gray-200);
height: calculateRem(32px);
}
.ui.radio.checkbox input:checked ~ label:before {
border: calculateRem(2px) solid var(--primary-400);
}
.ui.radio.checkbox input:checked ~ label:after {
background: var(--primary-400);
}
::ng-deep {
.signup-background .fullpage-background-image {
.sb-mat__modal__content, .sb-mat__modal__actions {
background: inherit;
}
.sb-mat__modal__actions {
border-top: none;
}
}
}