src/app/modules/search/components/user-search/user-search.component.ts
OnInit
AfterViewInit
OnDestroy
selector | app-user-search |
styleUrls | ./user-search.component.scss |
templateUrl | ./user-search.component.html |
Properties |
|
Methods |
constructor(searchService: SearchService, route: Router, ngZone: NgZone, activatedRoute: ActivatedRoute, paginationService: PaginationService, resourceService: ResourceService, toasterService: ToasterService, config: ConfigService, user: UserService, userSearchService: UserSearchService, permissionService: PermissionService, profileService: ProfileService, navigationhelperService: NavigationHelperService, layoutService: LayoutService)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Constructor to create injected service(s) object Default method of Draft Component class
Parameters :
|
downloadUser |
downloadUser()
|
Returns :
{}
|
initLayout |
initLayout()
|
Returns :
void
|
inview | ||||
inview(event)
|
||||
Parameters :
Returns :
void
|
navigateToPage | ||||||||
navigateToPage(page: number)
|
||||||||
This method helps to navigate to different pages. If page number is less than 1 or page number is greater than total number of pages is less which is not possible, then it returns.
Parameters :
Example :
Returns :
undefined | void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
populateLocationDetailsAndSetRoles |
populateLocationDetailsAndSetRoles()
|
Returns :
void
|
populateUserSearch |
populateUserSearch()
|
This method sets the make an api call to get all search data with page No and offset
Returns :
void
|
setInteractEventData |
setInteractEventData()
|
Returns :
void
|
Private activatedRoute |
Type : ActivatedRoute
|
To send activatedRoute.snapshot to router navigation service for redirection to parent component |
avatarConfig |
Type : literal type
|
closeIntractEdata |
Type : IInteractEventEdata
|
Public config |
Type : ConfigService
|
To get url, app configs |
csvOptions |
Type : object
|
Default value : {
fieldSeparator: ',',
quoteStrings: '"',
decimalseparator: '.',
showLabels: true,
headers: []
}
|
customStyle |
Type : object
|
Default value : {
backgroundColor: '#ffffff',
border: '1px solid #fff',
boxShadow: '0 0 6px 0 rgba(0,0,0,0.38)',
borderRadius: '50%',
color: '#024F9D',
fontWeight: 'bold',
fontFamily: 'inherit',
fontSize: '48px'
}
|
filterIntractEdata |
Type : IInteractEventEdata
|
inviewLogs |
Type : any
|
Default value : []
|
layoutConfiguration |
Type : any
|
Public layoutService |
Type : LayoutService
|
loaderMessage |
Type : any
|
loader message |
Public navigationhelperService |
Type : NavigationHelperService
|
noResult |
Default value : false
|
To show / hide no result message when no result found |
noResultMessage |
Type : INoResultMessage
|
no result message |
pageLimit |
Type : number
|
Contains page limit of outbox list |
pageNumber |
Type : number
|
Default value : 1
|
Current page number of inbox list |
pager |
Type : IPagination
|
Contains returned object of the pagination service which is needed to show the pagination on inbox view |
Private paginationService |
Type : PaginationService
|
For showing pagination on inbox list |
Public permissionService |
Type : PermissionService
|
Public profileService |
Type : ProfileService
|
queryParams |
Type : any
|
url value |
Private resourceService |
Type : ResourceService
|
rootOrgId |
Type : string
|
Private route |
Type : Router
|
To navigate to other pages |
searchList |
Type : Array<any>
|
Default value : []
|
Contains list of published course(s) of logged-in user |
Private searchService |
Type : SearchService
|
selectedRoles |
Type : Array<string>
|
showLoader |
Default value : true
|
This variable hepls to show and hide page loader. It is kept true by default as at first when we comes to a page the loader should be displayed before showing any data |
telemetryImpression |
Type : IImpressionEventInput
|
telemetryImpression |
Private toasterService |
Type : ToasterService
|
To show toaster(error, success etc) after any API calls |
totalCount |
Type : Number
|
totalCount of the list |
Public unsubscribe$ |
Default value : new Subject<void>()
|
Public user |
Type : UserService
|
To get user profile of logged-in user |
userDeleteIntractEdata |
Type : IInteractEventEdata
|
userEditIntractEdata |
Type : IInteractEventEdata
|
userProfile |
Type : any
|
Private userSearchService |
Type : UserSearchService
|
userViewIntractEdata |
Type : IInteractEventEdata
|
import {combineLatest as observableCombineLatest, Subject} from 'rxjs';
import {
ServerResponse, PaginationService, ResourceService, ConfigService, ToasterService, INoResultMessage,
NavigationHelperService, IPagination, LayoutService
} from '@sunbird/shared';
import { SearchService, UserService, PermissionService } from '@sunbird/core';
import {Component, OnInit, NgZone, AfterViewInit, OnDestroy} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import * as _ from 'lodash-es';
import { UserSearchService } from './../../services';
import { IInteractEventEdata, IImpressionEventInput } from '@sunbird/telemetry';
import { ProfileService } from '@sunbird/profile';
import {takeUntil} from 'rxjs/operators';
@Component({
selector: 'app-user-search',
templateUrl: './user-search.component.html',
styleUrls: ['./user-search.component.scss']
})
export class UserSearchComponent implements OnInit, AfterViewInit, OnDestroy {
private searchService: SearchService;
private resourceService: ResourceService;
/**
* telemetryImpression
*/
telemetryImpression: IImpressionEventInput;
closeIntractEdata: IInteractEventEdata;
userViewIntractEdata: IInteractEventEdata;
userDeleteIntractEdata: IInteractEventEdata;
userEditIntractEdata: IInteractEventEdata;
filterIntractEdata: IInteractEventEdata;
/**
* To get url, app configs
*/
public config: ConfigService;
/**
* To show toaster(error, success etc) after any API calls
*/
private toasterService: ToasterService;
private userSearchService: UserSearchService;
/**
* Contains list of published course(s) of logged-in user
*/
searchList: Array<any> = [];
/**
* To navigate to other pages
*/
private route: Router;
/**
* To send activatedRoute.snapshot to router navigation
* service for redirection to parent component
*/
private activatedRoute: ActivatedRoute;
/**
* For showing pagination on inbox list
*/
private paginationService: PaginationService;
/**
* To get user profile of logged-in user
*/
public user: UserService;
/**
* To show / hide no result message when no result found
*/
noResult = false;
/**
* no result message
*/
noResultMessage: INoResultMessage;
/**
* totalCount of the list
*/
totalCount: Number;
/**
* Current page number of inbox list
*/
pageNumber = 1;
/**
* Contains page limit of outbox list
*/
pageLimit: number;
/**
* This variable hepls to show and hide page loader.
* It is kept true by default as at first when we comes
* to a page the loader should be displayed before showing
* any data
*/
showLoader = true;
/**
* loader message
*/
loaderMessage: any;
/**
* Contains returned object of the pagination service
* which is needed to show the pagination on inbox view
*/
pager: IPagination;
/**
*url value
*/
queryParams: any;
rootOrgId: string;
userProfile: any;
inviewLogs: any = [];
selectedRoles: Array<string>;
customStyle = {
backgroundColor: '#ffffff',
border: '1px solid #fff',
boxShadow: '0 0 6px 0 rgba(0,0,0,0.38)',
borderRadius: '50%',
color: '#024F9D',
fontWeight: 'bold',
fontFamily: 'inherit',
fontSize: '48px'
};
csvOptions = {
fieldSeparator: ',',
quoteStrings: '"',
decimalseparator: '.',
showLabels: true,
headers: []
};
layoutConfiguration: any;
public unsubscribe$ = new Subject<void>();
avatarConfig: { size: any; view: any; isTitle:boolean };
/**
* Constructor to create injected service(s) object
* Default method of Draft Component class
* @param {SearchService} searchService Reference of SearchService
* @param {Router} route Reference of Router
* @param {PaginationService} paginationService Reference of PaginationService
* @param {ActivatedRoute} activatedRoute Reference of ActivatedRoute
* @param {ConfigService} config Reference of ConfigService
*/
constructor(searchService: SearchService, route: Router, private ngZone: NgZone,
activatedRoute: ActivatedRoute, paginationService: PaginationService,
resourceService: ResourceService, toasterService: ToasterService,
config: ConfigService, user: UserService, userSearchService: UserSearchService,
public permissionService: PermissionService, public profileService: ProfileService,
public navigationhelperService: NavigationHelperService, public layoutService: LayoutService) {
this.searchService = searchService;
this.route = route;
this.activatedRoute = activatedRoute;
this.paginationService = paginationService;
this.resourceService = resourceService;
this.toasterService = toasterService;
this.userSearchService = userSearchService;
this.config = config;
this.user = user;
this.avatarConfig = {
size: this.config.constants.SIZE.MEDIUM,
view: this.config.constants.VIEW.VERTICAL,
isTitle:false
};
}
/**
* This method sets the make an api call to get all search data with page No and offset
*/
populateUserSearch() {
this.showLoader = true;
this.pageLimit = this.config.appConfig.SEARCH.PAGE_LIMIT;
const searchParams = {
filters: {
'rootOrgId': this.rootOrgId,
'profileUserType.type': this.queryParams.Usertype,
'framework.medium': this.queryParams.medium,
'framework.gradeLevel': this.queryParams.gradeLevel,
'framework.subject': this.queryParams.subject
},
limit: this.pageLimit,
pageNumber: this.pageNumber,
query: this.queryParams.key
};
if (_.get(searchParams, 'filters["profileUserType.type"]')) {
const index = _.indexOf(searchParams.filters['profileUserType.type'], 'School head or officials');
if (index >= 0) {
searchParams.filters['profileUserType.type'][index] = 'administrator';
} else if (searchParams.filters['profileUserType.type'] === 'School head or officials') {
searchParams.filters['profileUserType.type'] = ['administrator'];
}
}
if (!_.isEmpty(this.selectedRoles)) { searchParams.filters['roles.role'] = this.selectedRoles; }
if (this.queryParams.School) {
searchParams.filters['organisations.organisationId'] = this.queryParams.School;
} else {
const locationArray = [];
if (this.queryParams.District) {
locationArray.push(typeof this.queryParams.District === 'string' ? this.queryParams.District : this.queryParams.District[0]);
}
if (this.queryParams.Block) {
locationArray.push(typeof this.queryParams.Block === 'string' ? this.queryParams.Block : this.queryParams.Block[0]);
}
if (!_.isEmpty(locationArray)) {
searchParams.filters['profileLocation.id'] = locationArray;
}
}
this.searchService.userSearch(searchParams).subscribe(
(apiResponse: ServerResponse) => {
if (apiResponse.result.response.count && apiResponse.result.response.content.length > 0) {
this.showLoader = false;
this.noResult = false;
this.searchList = apiResponse.result.response.content;
this.totalCount = apiResponse.result.response.count;
this.populateLocationDetailsAndSetRoles();
this.pager = this.paginationService.getPager(apiResponse.result.response.count, this.pageNumber, this.pageLimit);
} else {
this.noResult = true;
this.showLoader = false;
this.noResultMessage = {
'message': 'messages.stmsg.m0008',
'messageText': 'messages.stmsg.m0007'
};
}
},
err => {
this.showLoader = false;
this.noResult = true;
this.noResultMessage = {
'messageText': 'messages.fmsg.m0077'
};
this.toasterService.error(this.resourceService.messages.emsg.m0005);
}
);
}
populateLocationDetailsAndSetRoles() {
// Getting all location Ids
let locationArray = [];
_.each(this.searchList, (user) => {
if (_.get(this.userProfile, 'rootOrgAdmin') && this.userProfile.rootOrgAdmin === true) {
user.isEditableProfile = true;
}
_.each(user.locationIds, (location) => {
locationArray.push(location);
});
});
// Calling location search and setting location details to search list
if (!_.isEmpty(locationArray)) {
locationArray = _.uniq(locationArray);
const requestData = { 'filters': { id: locationArray } };
this.profileService.getUserLocation(requestData).subscribe(res => {
_.each(this.searchList, (user) => {
_.each(user.locationIds, (location) => {
const locations = _.find(res.result.response, (loc) => {
return loc.id === location;
});
if (locations) { user[locations.type] = locations; }
});
});
});
}
}
downloadUser() {
const downloadArray = [{
'firstName': 'First Name',
'lastName': 'Last Name',
'organisations': 'Organizations',
'location': 'Location',
'grades': 'Grades',
'language': 'Language',
'subject': 'Subject'
}];
_.each(this.searchList, (key, index) => {
downloadArray.push({
'firstName': key.firstName,
'lastName': key.lastName,
'organisations': 'test',
'location': key.location !== null ? key.location : '',
'grades': _.join(key.grade, ','),
'language': _.join(key.language, ','),
'subject': _.join(key.subject, ',')
});
});
return downloadArray;
}
/**
* This method helps to navigate to different pages.
* If page number is less than 1 or page number is greater than total number
* of pages is less which is not possible, then it returns.
*
* @param {number} page Variable to know which page has been clicked
*
* @example navigateToPage(1)
*/
navigateToPage(page: number): undefined | void {
if (page < 1 || page > this.pager.totalPages) {
return;
}
this.pageNumber = page;
this.route.navigate(['search/Users', this.pageNumber], {
queryParams: this.queryParams
});
}
initLayout() {
this.layoutConfiguration = this.layoutService.initlayoutConfig();
this.layoutService.switchableLayout().pipe(takeUntil(this.unsubscribe$)).subscribe(layoutConfig => {
if (layoutConfig != null) {
this.layoutConfiguration = layoutConfig.layout;
}
});
}
ngOnInit() {
this.initLayout();
this.user.userData$.subscribe(userdata => {
if (userdata && !userdata.err) {
this.userProfile = userdata.userProfile;
this.rootOrgId = this.userProfile.rootOrgId;
observableCombineLatest(this.activatedRoute.params, this.activatedRoute.queryParams,
(params: any, queryParams: any) => {
return {
params: params,
queryParams: queryParams
};
})
.subscribe(bothParams => {
if (bothParams.params.pageNumber) {
this.pageNumber = Number(bothParams.params.pageNumber);
}
this.queryParams = { ...bothParams.queryParams };
this.selectedRoles = [];
if (this.queryParams.Roles) {
this.permissionService.availableRoles$.subscribe(params => {
_.forEach(this.permissionService.allRoles, (role) => {
if (this.queryParams.Roles.includes(role.roleName)) { this.selectedRoles.push(role.role); }
});
this.populateUserSearch();
});
} else {
this.populateUserSearch();
}
});
}
});
this.setInteractEventData();
this.userSearchService.userDeleteEvent.subscribe(data => {
_.each(this.searchList, (key, index) => {
if (data && data === key.id) {
this.searchList[index].status = 0;
}
});
});
}
setInteractEventData() {
this.closeIntractEdata = {
id: 'user-search-close',
type: 'click',
pageid: 'user-search'
};
this.userViewIntractEdata = {
id: 'user-profile-view',
type: 'click',
pageid: 'user-search'
};
this.userEditIntractEdata = {
id: 'user-profile-edit',
type: 'click',
pageid: 'user-search'
};
this.userDeleteIntractEdata = {
id: 'user-profile-delete',
type: 'click',
pageid: 'user-search'
};
this.filterIntractEdata = {
id: 'filter',
type: 'click',
pageid: 'user-search'
};
}
ngAfterViewInit () {
setTimeout(() => {
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.route.url,
subtype: this.activatedRoute.snapshot.data.telemetry.subtype,
duration: this.navigationhelperService.getPageLoadTime()
}
};
this.inview({ inview: [] });
});
}
inview(event) {
_.forEach(event.inview, (inview, key) => {
const obj = _.find(this.inviewLogs, (o) => {
return o.objid === inview.data.identifier;
});
if (obj === undefined) {
this.inviewLogs.push({
objid: inview.data.identifier,
objtype: 'user',
index: inview.id
});
}
});
this.telemetryImpression.edata.visits = this.inviewLogs;
this.telemetryImpression.edata.subtype = 'pageexit';
this.telemetryImpression = Object.assign({}, this.telemetryImpression);
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}
<app-landing-section [noTitle]="true" [layoutConfiguration]="layoutConfiguration">
</app-landing-section>
<div [ngClass]="layoutConfiguration ? 'sbt-center-container sbt-user-profile relative9 sbt-fluid-content-bg pt-16':''" >
<app-user-filter></app-user-filter>
<div class="ui container mt-16">
<div [appTelemetryImpression]="telemetryImpression" class="ui grid mt-0">
<div class="twelve wide column">
<div>
<div class=" ui clearing segment content-player-header search-content-header" *ngIf="!showLoader && !noResult">
<div class="ui left floated header ">
<p class="serch-allresult">{{resourceService.frmelmnts?.lbl?.showingResults | translate}}
<span class="text-lowercase" *ngIf="this.queryParams.key" appHighlightText [config]="{'resourcePath': 'frmelmnts.lbl.forSearch', 'key': '{searchString}', 'value': queryParams?.key}"></span>
{{resourceService.frmelmnts?.lbl?.inUsers | translate}}
</p>
</div>
<!-- <h5 appTelemetryInteract [telemetryInteractEdata]="closeIntractEdata" class="ui right floated basic icon circular button " [routerLink]="['/profile']">
<i class="ui remove icon "></i>
</h5> -->
</div>
<div class=" ui clearing segment" *ngIf="!showLoader && !noResult && config.appConfig.SEARCH.USER.DOWNLOAD_BUTTON_VISIBILITY === 'TRUE'">
<!-- <angular2csv class="ui right floated primary button" [data]="downloadUser()" filename="Users" [options]="csvOptions">
{{resourceService.frmelmnts?.btn?.download | translate}} TODO: use Blob object to generate csv file
</angular2csv> -->
</div>
<div>
<div *ngIf="!showLoader && !noResult" [throttle]="[1000]" [trigger]="searchList">
<div [id]="i" [data]="user" class="ui segment mb-20" *ngFor="let user of searchList;let i = index;">
<div class="ui items">
<div class="item">
<div class="ui tiny image userSearchImage">
<sb-avatar class="my-avatar" [config]=avatarConfig initialsSize="1" [initial]="user?.firstName[0]"
[title]="user?.firstName">
</sb-avatar>
</div>
<div class="content userSearchContent">
<!--<a appTelemetryInteract [telemetryInteractEdata]="userViewIntractEdata"
[telemetryInteractObject]="{id:user.id,type:'user',ver:'1.0'}"
class="header " *ngIf="user.status===1" [queryParams]="queryParams" [routerLink]="['view/' + user.id]" tabindex="0" (click)="user.status===1 && userSearchService.userDetailsObject=user">{{user.firstName}} {{user.lastName}}</a>-->
<div class="header mb-5" *ngIf="user.status===1">{{user.firstName}} {{user.lastName}}</div>
<div class="header mb-5" *ngIf="user.status!==1">{{user.firstName}} {{user.lastName}}</div>
<a *ngIf="user.status===1 && user.isEditableProfile" class="right-float">
<!-- update user roles -->
<span appTelemetryInteract [telemetryInteractEdata]="userEditIntractEdata" [telemetryInteractObject]="{id:user.id,type:'user',ver:'1.0'}"
class="remove-outline" [queryParams]="queryParams" [routerLink]="['edit/' + user.id]" tabindex="0" (click)="user.status===1 && userSearchService.userDetailsObject=user">
<i class="edit large icon"></i>
</span>
<!-- delete user -->
<!-- <span appTelemetryInteract [telemetryInteractEdata]="userDeleteIntractEdata" [telemetryInteractObject]="{id:user.id,type:'user',ver:'1.0'}"
class="remove-outline mx-5">
<i class="trash outline large icon remove-outline" [queryParams]="queryParams" [routerLink]="['delete/' + user.id]" (click)="userSearchService.userDetailsObject=user"></i>
</span> -->
</a>
<!-- organization Name -->
<div class="meta">
<span class="cinema ">
<span *ngFor="let org of user.organisations; let last = last;">
<span *ngIf="org.orgName">{{org.orgName | titlecase}}</span>
<span *ngIf="!last && org.orgName">,</span>
</span>
</span>
</div>
<div class="extra ">
<!-- <div class="ui label " *ngIf="user.status===0">{{resourceService?.frmelmnts?.lbl?.inactive | translate}}</div>
<div class="ui label " *ngIf="user.status===1">{{resourceService?.frmelmnts?.lbl?.active | translate}}</div>
<div class="ui label " *ngIf="user.status===2">{{resourceService?.frmelmnts?.lbl?.blocked | translate}}</div>
<div class="ui label " *ngIf="user.status===3">{{resourceService?.frmelmnts?.lbl?.retired | translate}}</div> -->
<!-- updated code as per new design -->
<div class="ui horizontal bulleted list">
<span class="item" *ngIf="user.block"><span class="sunbird-portal-icon location-outline-icon"></span> {{user.block.name | titlecase}}</span>
<span class="item" *ngIf="user.district">{{user.district.name | titlecase}}</span>
</div>
</div>
</div>
</div>
<!-- Badging (Certifications and Awards) -->
<div class="ui middle aligned list" *ngIf="user.badgeAssertions && user.badgeAssertions.length > 0">
<div class="item" *ngFor="let badge of user.badgeAssertions">
<img class="ui avatar image" [src]="badge.badgeClassImage">
<div class="content">
<div class="header secondary-text-color-sunbird">{{badge.badgeClassName}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="noResult && !showLoader">
<app-no-result [data]="noResultMessage"></app-no-result>
</div>
</div>
</div>
<div class="ui grid mt-20">
<div class="twelve wide column pt-0">
<div class="pb-10" *ngIf="searchList && totalCount > config.appConfig.SEARCH.PAGE_LIMIT && !showLoader && !noResult">
<div class="ui pagination menu mt-10 right-floated pt-0" *ngIf="pager.pages.length">
<a [ngClass]="{disabled:pager.currentPage===1 }" class="item " tabindex="0" (click)="navigateToPage(1) ">First</a>
<a [ngClass]="{disabled:pager.currentPage===1 }" class="item " tabindex="0" (click)="navigateToPage(pager.currentPage - 1)">Previous</a>
<a *ngFor="let page of pager.pages" [ngClass]="{active:pager.currentPage===page}" tabindex="0" (click)="navigateToPage(page)" class="item">{{page}}</a>
<a [ngClass]="{disabled:pager.currentPage=== pager.totalPages}" tabindex="0" (click)="navigateToPage(pager.currentPage + 1)" class="item">Next</a>
<a [ngClass]="{disabled:pager.currentPage=== pager.totalPages}" tabindex="0" (click)="navigateToPage(pager.totalPages)" class="item ">Last</a>
</div>
</div>
</div>
</div>
<div class="twelve wide column" *ngIf="showLoader">
<app-loader [data]='loaderMessage'></app-loader>
</div>
</div>
</div>
</div>
<router-outlet></router-outlet>
</div>
./user-search.component.scss
::ng-deep {
app-user-filter .sb-prominent-filter .sb-prominent-filter-container .sb-prominent-filter-field .sb-mat__dropdown label {
margin-top: -0.5em;
}
}
::ng-deep {
.my-avatar .sb-members-list-item--medium .sb-member__img {
width: 5rem !important;
height: 5rem !important;
font-size: 3.125rem !important
}
.sb-members-list-item .sb-member .sb-member__name {
display: none
}
.my-avatar .sb-members-list-item .sb-member__img {
box-shadow: var(--sbt-box-shadow-6px) !important;
}
.sb-members-list-item .sb-member {
padding: 0px
}
}