src/app/modules/shared-feature/components/profile-framework-popup/profile-framework-popup.component.ts
OnInit
OnDestroy
selector | app-popup |
styleUrls | ./profile-framework-popup.component.scss |
templateUrl | ./profile-framework-popup.component.html |
Properties |
|
Methods |
|
Inputs |
Outputs |
Accessors |
constructor(router: Router, userService: UserService, frameworkService: FrameworkService, formService: FormService, resourceService: ResourceService, cacheService: CacheService, toasterService: ToasterService, channelService: ChannelService, orgDetailsService: OrgDetailsService, popupControlService: PopupControlService, matDialog: MatDialog, profileService: ProfileService)
|
|||||||||||||||||||||||||||||||||||||||
Parameters :
|
buttonLabel | |
Type : string
|
|
dialogProps | |
Type : any
|
|
formInput | |
Type : any
|
|
Default value : {}
|
|
isClosable | |
Type : boolean
|
|
Default value : false
|
|
isGuestUser | |
Type : boolean
|
|
Default value : false
|
|
isPreference | |
Type : boolean
|
|
Default value : false
|
|
isStepper | |
Type : boolean
|
|
Default value : false
|
|
showCloseIcon | |
Type : boolean
|
|
close | |
Type : EventEmitter
|
|
submit | |
Type : EventEmitter
|
|
Private enableSubmitButton |
enableSubmitButton()
|
Returns :
void
|
Private getCustodianOrgData |
getCustodianOrgData()
|
Returns :
any
|
Private getCustodianOrgDataForGuest |
getCustodianOrgDataForGuest()
|
Returns :
any
|
Private getFormatedFilterDetails |
getFormatedFilterDetails()
|
Returns :
any
|
Private getFormDetails |
getFormDetails()
|
Returns :
any
|
Private getFormOptionsForCustodianOrg |
getFormOptionsForCustodianOrg()
|
Returns :
any
|
Private getFormOptionsForCustodianOrgForGuestUser |
getFormOptionsForCustodianOrgForGuestUser()
|
Returns :
any
|
Private getFormOptionsForOnboardedUser |
getFormOptionsForOnboardedUser()
|
Returns :
any
|
Private getUpdatedFilters | |||||||||
getUpdatedFilters(field, editMode)
|
|||||||||
Parameters :
Returns :
any
|
Public handleFieldChange | ||||||
handleFieldChange(event, field)
|
||||||
Parameters :
Returns :
void
|
Private isCustodianOrgUser |
isCustodianOrgUser()
|
Returns :
any
|
Private mergeBoard |
mergeBoard()
|
Returns :
void
|
Private navigateToLibrary |
navigateToLibrary()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onSubmitForm |
onSubmitForm()
|
Returns :
void
|
setInteractEventData |
setInteractEventData()
|
Returns :
void
|
Private _formFieldProperties |
Type : any
|
Public allowedFields |
Type : []
|
Default value : ['board', 'medium', 'gradeLevel', 'subject']
|
Private boardOptions |
Private categoryMasterList |
Type : any
|
Default value : {}
|
Private custodianOrg |
Default value : false
|
Private custodianOrgBoard |
Type : any
|
Default value : {}
|
Private custOrgFrameworks |
Type : any
|
dialogRef |
Type : MatDialogRef<any>
|
Private editMode |
Type : boolean
|
Public formFieldOptions |
Type : []
|
Default value : []
|
Private frameWorkId |
Type : string
|
guestUserHashTagId |
instance |
Type : string
|
Public popupControlService |
Type : PopupControlService
|
Public profileService |
Type : ProfileService
|
Public resourceService |
Type : ResourceService
|
Public selectedOption |
Type : any
|
Default value : {}
|
Public showButton |
Default value : false
|
submitInteractEdata |
Type : IInteractEventEdata
|
telemetryInteractObject |
Type : IInteractEventObject
|
Private unsubscribe |
Type : Subscription
|
formFieldProperties |
getformFieldProperties()
|
import { Component, OnInit, Input, EventEmitter, Output, OnDestroy } from '@angular/core';
import { FrameworkService, FormService, UserService, ChannelService, OrgDetailsService } from '@sunbird/core';
import { first, mergeMap, map, filter } from 'rxjs/operators';
import { of, throwError, Subscription } from 'rxjs';
import { ResourceService, ToasterService } from '@sunbird/shared';
import { Router } from '@angular/router';
import * as _ from 'lodash-es';
import { CacheService } from '../../../shared/services/cache-service/cache.service';
import { IInteractEventObject, IInteractEventEdata } from '@sunbird/telemetry';
import { PopupControlService } from '../../../../service/popup-control.service';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ProfileService } from '@sunbird/profile';
@Component({
selector: 'app-popup',
templateUrl: './profile-framework-popup.component.html',
styleUrls: ['./profile-framework-popup.component.scss']
})
export class ProfileFrameworkPopupComponent implements OnInit, OnDestroy {
@Input() showCloseIcon: boolean;
@Input() buttonLabel: string;
@Input() formInput: any = {};
@Input() isClosable = false;
@Input() isGuestUser = false;
@Input() isPreference = false;
@Output() submit = new EventEmitter<any>();
@Output() close = new EventEmitter<any>();
@Input() dialogProps;
@Input() isStepper: boolean = false;
public allowedFields = ['board', 'medium', 'gradeLevel', 'subject'];
private _formFieldProperties: any;
public formFieldOptions = [];
private custOrgFrameworks: any;
private categoryMasterList: any = {};
public selectedOption: any = {};
public showButton = false;
private unsubscribe: Subscription;
private frameWorkId: string;
private custodianOrg = false;
private custodianOrgBoard: any = {};
submitInteractEdata: IInteractEventEdata;
telemetryInteractObject: IInteractEventObject;
private editMode: boolean;
guestUserHashTagId;
instance: string;
dialogRef: MatDialogRef<any>;
private boardOptions;
constructor(private router: Router, private userService: UserService, private frameworkService: FrameworkService,
private formService: FormService, public resourceService: ResourceService, private cacheService: CacheService,
private toasterService: ToasterService, private channelService: ChannelService, private orgDetailsService: OrgDetailsService,
public popupControlService: PopupControlService, private matDialog: MatDialog, public profileService: ProfileService) {
this.instance = (<HTMLInputElement>document.getElementById('instance'))
? (<HTMLInputElement>document.getElementById('instance')).value.toUpperCase() : 'SUNBIRD';
}
ngOnInit() {
this.dialogRef = this.dialogProps && this.dialogProps.id && this.matDialog.getDialogById(this.dialogProps.id);
this.popupControlService.changePopupStatus(false);
this.selectedOption = _.pickBy(_.cloneDeep(this.formInput), 'length') || {}; // clone selected field inputs from parent
if (this.isGuestUser && !this.isStepper) {
this.orgDetailsService.getOrgDetails(this.userService.slug).subscribe((data: any) => {
this.guestUserHashTagId = data.hashTagId;
});
this.allowedFields = ['board', 'medium', 'gradeLevel'];
}
if (this.isGuestUser && this.isStepper) {
this.orgDetailsService.getCustodianOrgDetails().subscribe((custodianOrg) => {
this.guestUserHashTagId = custodianOrg.result.response.value;
});
this.allowedFields = ['board', 'medium', 'gradeLevel'];
}
this.editMode = _.some(this.selectedOption, 'length') || false;
this.unsubscribe = this.isCustodianOrgUser().pipe(
mergeMap((custodianOrgUser: boolean) => {
this.custodianOrg = custodianOrgUser;
if (this.isGuestUser) {
return this.getFormOptionsForCustodianOrgForGuestUser();
} else if (custodianOrgUser) {
return this.getFormOptionsForCustodianOrg();
} else {
return this.getFormOptionsForOnboardedUser();
}
}), first()).subscribe(data => {
this.formFieldOptions = data;
}, err => {
this.toasterService.warning(this.resourceService.messages.emsg.m0012);
this.navigateToLibrary();
});
this.setInteractEventData();
}
private getFormOptionsForCustodianOrgForGuestUser() {
return this.getCustodianOrgDataForGuest().pipe(mergeMap((data) => {
this.custodianOrgBoard = data;
const boardObj = _.cloneDeep(this.custodianOrgBoard);
boardObj.range = _.sortBy(boardObj.range, 'index');
const board = boardObj;
this.boardOptions = board;
if (_.get(this.selectedOption, 'board[0]')) { // update mode, get 1st board framework and update all fields
this.selectedOption.board = _.get(this.selectedOption, 'board[0]');
this.frameWorkId = _.get(_.find(this.custOrgFrameworks, { 'name': this.selectedOption.board }), 'identifier');
return this.getFormatedFilterDetails().pipe(map((formFieldProperties) => {
this._formFieldProperties = formFieldProperties;
this.mergeBoard(); // will merge board from custodian org and board from selected framework data
return this.getUpdatedFilters(board, true);
}));
} else {
let userType = localStorage.getItem('userType');
userType == "administrator" ? board.required = true : null;
const fieldOptions = [board,
{ code: 'medium', label: 'Medium', index: 2 },
{ code: 'gradeLevel', label: 'Class', index: 3 },
{ code: 'subject', label: 'Subject', index: 4 }];
return of(fieldOptions);
}
}));
}
private getCustodianOrgDataForGuest() {
return this.channelService.getFrameWork(this.guestUserHashTagId).pipe(map((channelData: any) => {
this.custOrgFrameworks = _.get(channelData, 'result.channel.frameworks') || [];
this.custOrgFrameworks = _.sortBy(this.custOrgFrameworks, 'index');
return {
range: this.custOrgFrameworks,
label: 'Board',
code: 'board',
index: 1
};
}));
}
private getFormOptionsForCustodianOrg() {
return this.getCustodianOrgData().pipe(mergeMap((data) => {
this.custodianOrgBoard = data;
const boardObj = _.cloneDeep(this.custodianOrgBoard);
boardObj.range = _.sortBy(boardObj.range, 'index');
const board = boardObj;
this.boardOptions = board;
if (_.get(this.selectedOption, 'board[0]')) { // update mode, get 1st board framework and update all fields
this.selectedOption.board = _.get(this.selectedOption, 'board[0]');
this.frameWorkId = _.get(_.find(this.custOrgFrameworks, { 'name': this.selectedOption.board }), 'identifier');
return this.getFormatedFilterDetails().pipe(map((formFieldProperties) => {
this._formFieldProperties = formFieldProperties;
this.mergeBoard(); // will merge board from custodian org and board from selected framework data
return this.getUpdatedFilters(board, true);
}));
} else {
const fieldOptions = [board,
{ code: 'medium', label: 'Medium', index: 2 },
{ code: 'gradeLevel', label: 'Class', index: 3 },
{ code: 'subject', label: 'Subject', index: 4 }];
return of(fieldOptions);
}
}));
}
private getFormOptionsForOnboardedUser() {
return this.getFormatedFilterDetails().pipe(map((formFieldProperties) => {
this._formFieldProperties = formFieldProperties;
this.boardOptions = _.find(formFieldProperties, { code: 'board' });
if (_.get(this.selectedOption, 'board[0]')) {
this.selectedOption.board = _.get(this.selectedOption, 'board[0]');
}
return this.getUpdatedFilters({ index: 0 }, this.editMode); // get filters for first field i.e index 0 incase of init
}));
}
private getFormatedFilterDetails() {
if (this.isGuestUser) {
this.frameworkService.initialize(this.frameWorkId, this.guestUserHashTagId);
} else {
this.frameworkService.initialize(this.frameWorkId);
}
return this.frameworkService.frameworkData$.pipe(
filter((frameworkDetails) => { // wait to get the framework name if passed as input
if (!frameworkDetails.err) {
const framework = this.frameWorkId ? this.frameWorkId : 'defaultFramework';
if (!_.get(frameworkDetails.frameworkdata, framework)) {
return false;
}
}
return true;
}),
mergeMap((frameworkDetails) => {
if (!frameworkDetails.err) {
const framework = this.frameWorkId ? this.frameWorkId : 'defaultFramework';
const frameworkData = _.get(frameworkDetails.frameworkdata, framework);
this.frameWorkId = frameworkData.identifier;
this.categoryMasterList = frameworkData.categories;
return this.getFormDetails();
} else {
return throwError(frameworkDetails.err);
}
}), map((formData: any) => {
const formFieldProperties = _.filter(formData, (formFieldCategory) => {
formFieldCategory.range = _.get(_.find(this.categoryMasterList, { code: formFieldCategory.code }), 'terms') || [];
return true;
});
return _.sortBy(_.uniqBy(formFieldProperties, 'code'), 'index');
}), first());
}
public handleFieldChange(event, field) {
if ((!this.isGuestUser || field.index !== 1) && (!this.custodianOrg || field.index !== 1)) { // no need to fetch data, just rearrange fields
this.formFieldOptions = this.getUpdatedFilters(field);
this.enableSubmitButton();
return;
}
if (_.get(this.boardOptions, 'range.length')) {
this.frameWorkId = _.get(_.find(this.boardOptions.range, { name: _.get(this.selectedOption, field.code) }), 'identifier');
} else {
this.frameWorkId = _.get(_.find(field.range, { name: _.get(this.selectedOption, field.code) }), 'identifier');
}
if (this.unsubscribe) { // cancel if any previous api call in progress
this.unsubscribe.unsubscribe();
}
this.unsubscribe = this.getFormatedFilterDetails().pipe().subscribe(
(formFieldProperties) => {
if (!formFieldProperties.length) {
} else {
this._formFieldProperties = formFieldProperties;
this.mergeBoard();
this.formFieldOptions = this.getUpdatedFilters(field);
this.enableSubmitButton();
}
}, (error) => {
this.toasterService.warning(this.resourceService.messages.emsg.m0012);
this.navigateToLibrary();
});
}
private mergeBoard() {
_.forEach(this._formFieldProperties, (field) => {
if (field.code === 'board') {
field.range = _.unionBy(_.concat(field.range, this.custodianOrgBoard.range), 'name');
}
});
}
private getUpdatedFilters(field, editMode = false) {
const targetIndex = field.index + 1; // only update next field if not editMode
const formFields = _.reduce(this.formFieldProperties, (accumulator, current) => {
if (current.index === targetIndex || editMode) {
const parentField: any = _.find(this.formFieldProperties, { index: current.index - 1 }) || {};
const parentAssociations = _.reduce(parentField.range, (collector, term) => {
const selectedFields = this.selectedOption[parentField.code] || [];
if ((selectedFields.includes(term.name) || selectedFields.includes(term.code))) {
const selectedAssociations = _.filter(term.associations, { category: current.code }) || [];
collector = _.concat(collector, selectedAssociations);
}
return collector;
}, []);
const updatedRange = _.filter(current.range, range => _.find(parentAssociations, { code: range.code }));
current.range = updatedRange.length ? updatedRange : current.range;
current.range = _.unionBy(current.range, 'identifier');
if (!editMode) {
this.selectedOption[current.code] = [];
}
accumulator.push(current);
} else {
if (current.index <= field.index) { // retain options for already selected fields
const updateField = current.code === 'board' ? (this.boardOptions || current) : _.find(this.formFieldOptions, { index: current.index });
accumulator.push(updateField);
} else { // empty filters and selection
current.range = [];
this.selectedOption[current.code] = [];
accumulator.push(current);
}
}
return accumulator;
}, []);
return formFields;
}
private getCustodianOrgData() {
return this.channelService.getFrameWork(this.userService.hashTagId).pipe(map((channelData: any) => {
this.custOrgFrameworks = _.get(channelData, 'result.channel.frameworks') || [];
this.custOrgFrameworks = _.sortBy(this.custOrgFrameworks, 'index');
return {
range: this.custOrgFrameworks,
label: 'Board',
code: 'board',
index: 1
};
}));
}
private getFormDetails() {
const formServiceInputParams = {
formType: 'user',
formAction: 'update',
contentType: 'framework',
framework: this.frameWorkId
};
let userType = localStorage.getItem('userType');
if (this.isGuestUser && userType == "administrator") {
formServiceInputParams.formAction = 'create',
formServiceInputParams.contentType= 'admin_framework'
delete formServiceInputParams.framework;
}
const hashTagId = this.isGuestUser ? this.guestUserHashTagId : _.get(this.userService, 'hashTagId');
return this.formService.getFormConfig(formServiceInputParams, hashTagId);
}
onSubmitForm() {
const selectedOption = _.cloneDeep(this.selectedOption);
selectedOption.board = _.get(this.selectedOption, 'board') ? [this.selectedOption.board] : [];
selectedOption.id = this.frameWorkId;
if (this.dialogRef && this.dialogRef.close) {
this.dialogRef.close();
}
// Process to handle in case of component rendered in stepper dialog
// API to be called for updation
if(this.isStepper && this.isGuestUser) {
const user: any = { name: 'guest', formatedName: 'Guest', framework: selectedOption };
const userType = localStorage.getItem('userType');
if (userType) {
user.role = userType;
}
this.userService.createGuestUser(user).subscribe(data => {
this.toasterService.success(_.get(this.resourceService, 'messages.smsg.m0058'));
}, error => {
this.toasterService.error(_.get(this.resourceService, 'messages.emsg.m0005'));
});
} else {
const req = {
framework: selectedOption
};
this.profileService.updateProfile(req).subscribe(res => {
this.userService.setUserFramework(selectedOption);
});
}
this.submit.emit(selectedOption);
}
private enableSubmitButton() {
const optionalFields = _.map(_.filter(this._formFieldProperties, formField => !_.get(formField, 'required')), 'code');
const enableSubmitButton = _.every(this.selectedOption, (value, index) => {
return _.includes(optionalFields, index) ? true : value.length;
});
if (enableSubmitButton) {
this.showButton = true;
} else {
this.showButton = false;
}
}
ngOnDestroy() {
this.close.emit();
this.popupControlService.changePopupStatus(true);
if (this.unsubscribe) {
this.unsubscribe.unsubscribe();
}
if (this.dialogRef && this.dialogRef.close) {
this.dialogRef.close();
}
}
private isCustodianOrgUser() {
return this.orgDetailsService.getCustodianOrgDetails().pipe(map((custodianOrg) => {
if (_.get(this.userService, 'userProfile.rootOrg.rootOrgId') === _.get(custodianOrg, 'result.response.value')) {
return true;
}
return false;
}));
}
get formFieldProperties() {
return _.cloneDeep(this._formFieldProperties);
}
private navigateToLibrary() {
if (this.dialogRef && this.dialogRef.close) {
this.dialogRef.close();
}
if (_.isEmpty(this.formInput)) {
this.router.navigate(['/resources']);
this.cacheService.set('showFrameWorkPopUp', 'installApp');
}
}
setInteractEventData() {
this.submitInteractEdata = {
id: 'submit-profile-framework-details',
type: 'click',
pageid: 'profile-read'
};
this.telemetryInteractObject = {
id: this.userService.userid,
type: 'User',
ver: '1.0'
};
}
}
<div class="sb-mat__modal" *ngIf="formFieldOptions.length">
<!--Header-->
<div mat-dialog-title>
<div class="title" role="heading" aria-level="2">
{{resourceService?.frmelmnts?.lbl?.profilePopup}}‎
</div>
<button aria-label="close dialog" mat-dialog-close class="close-btn" *ngIf="showCloseIcon"></button>
</div>
<!--/Header-->
<!--Content-->
<mat-dialog-content>
<div class="sb-mat__modal__content">
<div *ngIf="isPreference">
<div class="d-flex">
<label class="fs-1 mb-0"> {{resourceService?.frmelmnts?.lbl?.yourPreferences}}</label>
<div class="hr-preference-line ml-24"></div>
</div>
<p class="fsmall font-weight-normal mt-8 mb-0">{{resourceService?.frmelmnts?.lbl?.preferencesheader | transposeTerms: 'frmelmnts.lbl.preferencesheader' : resourceService?.selectedLang
| interpolate:'{instance}': instance}}</p>
<div class="hr-border py-8"></div>
</div>
<form class="ui form">
<div *ngFor="let field of formFieldOptions">
<div class="required sb-field-form-group"
*ngIf="field.code === 'board' && allowedFields.includes(field.code)">
<div class="sb-field">
<mat-form-field id={{field.code}} appearance="fill" class="sb-mat__dropdown w-100 mb-8">
<mat-label>{{( field?.translation || field?.label) | frameworkCatLabelTranslate | transposeTerms: ( field?.translation || field?.label) : resourceService?.selectedLang}}
<span class="mr-8 sb-color-red" *ngIf="field.required"> * </span>
</mat-label>
<mat-select name={{field.code}} role="listbox" aria-label="field?.label"
class="selection" [(ngModel)]="selectedOption[field.code]" (selectionChange)="handleFieldChange($event, field)">
<mat-option class="mat-dropdown__options" role="option" *ngFor="let option of field.range | sortBy:'name':'asc'" [value]="option?.name"
attr.aria-label="{{option?.name}}">{{option?.name}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="sb-field-form-group mt-8" *ngIf="field.code !== 'board' && allowedFields.includes(field.code)"
[ngClass]="field.code === 'subject' ? '' : 'required'">
<div class="sb-field">
<mat-form-field *ngIf="field.code === 'gradeLevel'" id={{field.code}} appearance="fill" class="sb-mat__dropdown w-100 mb-8">
<mat-label>{{( field?.translation || field?.label) | frameworkCatLabelTranslate | transposeTerms: ( field?.translation || field?.label) : resourceService?.selectedLang}}
<span class="mr-8 sb-color-red" *ngIf="field.required"> * </span>
</mat-label>
<mat-select multiple name={{field.code}} role="listbox" aria-label="field?.label"
class="selection" [(ngModel)]="selectedOption[field.code]" (selectionChange)="handleFieldChange($event, field)">
<!-- <mat-select-trigger>
{{selectedOption[field.code] ? selectedOption[field.code][0] : ''}}
<span *ngIf="selectedOption[field.code]?.length > 1" class="example-additional-selection">
(+{{selectedOption[field.code].length - 1}} {{selectedOption[field.code]?.length === 2 ? 'other' : 'others'}})
</span>
</mat-select-trigger> -->
<mat-option class="mat-dropdown__options" role="option" *ngFor="let option of field.range" [value]="option?.name"
attr.aria-label="{{option?.name}}">{{option?.name}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="field.code !== 'gradeLevel'" id={{field.code}} appearance="fill" class="sb-mat__dropdown w-100 mb-8">
<mat-label>{{( field?.translation || field?.label) | frameworkCatLabelTranslate | transposeTerms: (field?.translation || field?.label) : resourceService?.selectedLang}}
<span class="mr-8 sb-color-red" *ngIf="field.required"> * </span>
</mat-label>
<mat-select multiple name={{field.code}} role="listbox" aria-label="field?.label"
class="selection" [(ngModel)]="selectedOption[field.code]" (selectionChange)="handleFieldChange($event, field)">
<!-- <mat-select-trigger>
{{selectedOption[field.code] ? selectedOption[field.code][0] : ''}}
<span *ngIf="selectedOption[field.code]?.length > 1" class="example-additional-selection">
(+{{selectedOption[field.code].length - 1}} {{selectedOption[field.code]?.length === 2 ? 'other' : 'others'}})
</span>
</mat-select-trigger> -->
<mat-option class="mat-dropdown__options" role="option" *ngFor="let option of field.range | sortBy:'name':'asc'" [value]="option?.name"
attr.aria-label="{{option?.name}}">{{option?.name}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</form>
</div>
</mat-dialog-content>
<!--/Content-->
<!--Actions-->
<mat-dialog-actions align="end" class="mb-0 sb-mat__modal__actions">
<button appTelemetryInteract [telemetryInteractObject]="telemetryInteractObject"
[telemetryInteractEdata]="submitInteractEdata" class="sb-btn sb-btn-normal sb-btn-primary"
[disabled]="!showButton" tabindex="0" [ngClass]="{'sb-btn-disabled': !showButton}" (click)="onSubmitForm()">
{{buttonLabel}}
</button>
</mat-dialog-actions>
<!--/Actions-->
</div>
<div *ngIf="!formFieldOptions || !formFieldOptions.length" class="sb-mat__modal">
<app-loader></app-loader>
</div>
./profile-framework-popup.component.scss