489 lines
18 KiB
TypeScript
489 lines
18 KiB
TypeScript
import {ChangeDetectorRef, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
|
|
import {IndicatorStakeholderBaseComponent} from "../monitor-admin/utils/stakeholder-base.component";
|
|
import {DomSanitizer} from "@angular/platform-browser";
|
|
import {
|
|
Category,
|
|
Indicator, IndicatorPath,
|
|
IndicatorSize, Overlay,
|
|
Section,
|
|
Stakeholder,
|
|
SubCategory,
|
|
Topic,
|
|
Visibility
|
|
} from "./entities/stakeholder";
|
|
import {LayoutService} from "../dashboard/sharedComponents/sidebar/layout.service";
|
|
import {ClickEvent} from "../utils/click/click-outside-or-esc.directive";
|
|
import {Session, User} from "../login/utils/helper.class";
|
|
import {Filter, Value} from "../searchPages/searchUtils/searchHelperClasses.class";
|
|
import {RangeFilter} from "../utils/rangeFilter/rangeFilterHelperClasses.class";
|
|
import {RangeFilterComponent} from "../utils/rangeFilter/rangeFilter.component";
|
|
import {Dates, StringUtils} from "../utils/string-utils.class";
|
|
import {Params} from "@angular/router";
|
|
import {StatisticsService} from "../monitor-admin/utils/services/statistics.service";
|
|
import {SearchResearchResultsService} from "../services/searchResearchResults.service";
|
|
import {CustomFilterService} from "../shared/customFilter.service";
|
|
|
|
@Directive()
|
|
export abstract class MonitorIndicatorStakeholderBaseComponent extends IndicatorStakeholderBaseComponent implements OnInit {
|
|
/** Status */
|
|
public loading: boolean = true;
|
|
public isMobile: boolean = false;
|
|
public isFullscreen: boolean = false;
|
|
|
|
/** Variables */
|
|
public user: User;
|
|
public requireLogin: boolean = true;
|
|
public view: Visibility;
|
|
public stakeholder: Stakeholder;
|
|
public activeTopic: Topic = null;
|
|
public activeCategory: Category = null;
|
|
public activeSubCategory: SubCategory = null;
|
|
public filters: Filter[] = [];
|
|
public queryParams: any = {};
|
|
public periodFilter: RangeFilter = {
|
|
title: "Time range",
|
|
filterId: "year",
|
|
originalFilterIdFrom: null,
|
|
originalFilterIdTo: null,
|
|
selectedFromValue: null,
|
|
selectedToValue: null,
|
|
selectedFromAndToValues: ""
|
|
};
|
|
rangeFilter: RangeFilterComponent;
|
|
minYear = Dates.currentYear - 20;
|
|
maxYear = Dates.currentYear;
|
|
public numberResults: Map<string, number> = new Map<string, number>();
|
|
public clipboard;
|
|
|
|
/** Services */
|
|
protected sanitizer: DomSanitizer;
|
|
protected cdr: ChangeDetectorRef;
|
|
protected layoutService: LayoutService;
|
|
protected statisticsService: StatisticsService;
|
|
protected searchResearchResultsService: SearchResearchResultsService;
|
|
protected customFilterService: CustomFilterService;
|
|
|
|
@HostListener('fullscreenchange', ['$event'])
|
|
@HostListener('webkitfullscreenchange', ['$event'])
|
|
@HostListener('mozfullscreenchange', ['$event'])
|
|
@HostListener('MSFullscreenChange', ['$event'])
|
|
screenChange() {
|
|
this.isFullscreen = !this.isFullscreen;
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.layoutService.isMobile.subscribe(isMobile => {
|
|
this.isMobile = isMobile;
|
|
this.cdr.detectChanges();
|
|
});
|
|
this.createClipboard();
|
|
}
|
|
|
|
protected setView(params: Params) {
|
|
this.setSelectedFilters();
|
|
this.loading = false;
|
|
if (params['topic']) {
|
|
this.activeTopic = this.stakeholder.topics.find(topic => topic.alias === decodeURIComponent(params['topic']) && this.hasPermission(topic.visibility));
|
|
if (this.activeTopic) {
|
|
if (params['category']) {
|
|
this.activeCategory = this.activeTopic.categories.find(category =>
|
|
(category.alias === params['category']) && this.hasPermission(category.visibility));
|
|
if (!this.activeCategory) {
|
|
this.navigateToError();
|
|
return;
|
|
}
|
|
} else {
|
|
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
|
|
if (this.activeCategory) {
|
|
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
|
|
this.hasPermission(subCategory.visibility));
|
|
if (this.activeSubCategory) {
|
|
this.setIndicators();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (this.activeCategory) {
|
|
if (params['subCategory']) {
|
|
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
|
|
(subCategory.alias === params['subCategory'] && this.hasPermission(subCategory.visibility)));
|
|
if (!this.activeSubCategory) {
|
|
this.navigateToError();
|
|
return;
|
|
}
|
|
} else {
|
|
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
|
|
this.hasPermission(subCategory.visibility));
|
|
}
|
|
if (this.activeSubCategory) {
|
|
this.setIndicators();
|
|
} else {
|
|
this.navigateToError();
|
|
}
|
|
return;
|
|
} else {
|
|
this.activeSubCategory = null;
|
|
}
|
|
} else {
|
|
this.navigateToError();
|
|
return;
|
|
}
|
|
} else {
|
|
this.activeTopic = this.stakeholder.topics.find(topic => this.hasPermission(topic.visibility));
|
|
if (this.activeTopic) {
|
|
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
|
|
if (this.activeCategory) {
|
|
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory => this.hasPermission(subCategory.visibility));
|
|
if (this.activeSubCategory) {
|
|
this.setIndicators();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected handleQueryParams(queryParams, params) {
|
|
this.queryParams = Object.assign({}, queryParams);
|
|
this.initializeFilters();
|
|
this.setView(params);
|
|
if(this.requireLogin && !this.user && (this.filters.filter(filter => this.queryParams[filter.filterId]).length > 0 || this.queryParams['year'])) {
|
|
if(queryParams['view']) {
|
|
this._router.navigate([], {queryParams: {view: queryParams['view']}});
|
|
} else {
|
|
this._router.navigate([], {queryParams: {}});
|
|
}
|
|
}
|
|
this.view = queryParams['view'];
|
|
}
|
|
|
|
protected initializeFilters() {
|
|
this.periodFilter.selectedFromValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[0] : "";
|
|
this.periodFilter.selectedToValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[1] : "";
|
|
this.validateYearRange(false);
|
|
|
|
for (let filter of this.filters) {
|
|
if (this.queryParams[filter.filterId]) {
|
|
for (let value of filter.values) {
|
|
if (value.id == StringUtils.URIDecode(StringUtils.unquote(this.queryParams[filter.filterId]))) {
|
|
value.selected = true;
|
|
filter.countSelectedValues = 1;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
this.clearFilter(filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected validateYearRange(navigateTo: boolean = false) {
|
|
let validYears = true;
|
|
if (this.periodFilter.selectedToValue && (this.periodFilter.selectedToValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedToValue, this.minYear, this.maxYear))) {
|
|
this.periodFilter.selectedToValue = this.maxYear + "";
|
|
validYears = false;
|
|
}
|
|
if (this.periodFilter.selectedFromValue && (this.periodFilter.selectedFromValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedFromValue, this.minYear, this.maxYear))) {
|
|
this.periodFilter.selectedFromValue = this.minYear + "";
|
|
validYears = false;
|
|
}
|
|
if (this.periodFilter.selectedFromValue && this.periodFilter.selectedFromValue.length && this.periodFilter.selectedToValue && this.periodFilter.selectedToValue.length > 0 && parseInt(this.periodFilter.selectedFromValue, 10) > parseInt(this.periodFilter.selectedToValue, 10)) {
|
|
this.periodFilter.selectedFromValue = this.periodFilter.selectedToValue;
|
|
validYears = false;
|
|
}
|
|
if (!validYears || navigateTo) {
|
|
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
|
|
this.queryParams["year"] = 'range' + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : '') + ":" + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "");
|
|
} else {
|
|
delete this.queryParams["year"];
|
|
}
|
|
this._router.navigate([], {queryParams: this.queryParams});
|
|
this.setIndicators();
|
|
}
|
|
}
|
|
|
|
protected getFullUrl(indicatorPath: IndicatorPath) {
|
|
let fosValues = this.getSelectedFilterValues("fos");
|
|
return this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, indicatorPath,null, this.periodFilter.selectedFromValue, this.periodFilter.selectedToValue, false, fosValues?fosValues.lvl1:[],fosValues?fosValues.lvl2:[], this.getSelectedFilterValues("publiclyfunded"));
|
|
}
|
|
|
|
protected setIndicators() {
|
|
this.activeSubCategory.numbers = this.activeSubCategory.numbers.filter(section => section.indicators.length > 0);
|
|
this.activeSubCategory.charts = this.activeSubCategory.charts.filter(section => section.indicators.length > 0);
|
|
this.periodFilter.selectedFromAndToValues = (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue ? ((this.periodFilter.selectedFromValue && !this.periodFilter.selectedToValue ? "From " : "") + (!this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? "Until " : "") + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : "") +
|
|
(this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? " - " : "") + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "")) : "");
|
|
//clear numbers when filters change
|
|
this.numberResults.clear();
|
|
let urls: Map<string, [number, number, number][]> = new Map<string, [number, number, number][]>();
|
|
this.activeSubCategory.numbers.forEach((section, i) => {
|
|
section.indicators.forEach((number, j) => {
|
|
if (this.hasPermission(number.visibility)) {
|
|
number.indicatorPaths.forEach((indicatorPath, k) => {
|
|
let url = this.getFullUrl(indicatorPath);
|
|
const pair = JSON.stringify([indicatorPath.source, url]);
|
|
const indexes = urls.get(pair) ? urls.get(pair) : [];
|
|
indexes.push([i, j, k]);
|
|
urls.set(pair, indexes);
|
|
})
|
|
}
|
|
});
|
|
});
|
|
urls.forEach((indexes, pair) => {
|
|
pair = JSON.parse(pair);
|
|
let activeSubcategory = this.activeSubCategory._id;
|
|
this.subscriptions.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(pair[0]), pair[1]).subscribe(response => {
|
|
if(activeSubcategory === this.activeSubCategory._id) {
|
|
indexes.forEach(([i, j, k]) => {
|
|
if( this.activeSubCategory?.numbers[i]?.indicators[j]) {
|
|
let result = JSON.parse(JSON.stringify(response));
|
|
this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => {
|
|
if (result) {
|
|
result = result[jsonPath];
|
|
}
|
|
});
|
|
if (typeof result === 'string' || typeof result === 'number') {
|
|
result = Number(result);
|
|
if (result === Number.NaN) {
|
|
result = 0;
|
|
}
|
|
} else {
|
|
result = 0;
|
|
}
|
|
this.numberResults.set(i + '-' + j + '-' + k, result);
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
});
|
|
this.activeSubCategory.charts.forEach((section, i) => {
|
|
section.indicators.forEach((indicator, j) => {
|
|
indicator.indicatorPaths.forEach((indicatorPath, k) => {
|
|
indicator.indicatorPaths[k].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[k]);
|
|
});
|
|
});
|
|
});
|
|
if (this.cdr && !(this.cdr as ViewRef).destroyed) {
|
|
this.cdr.detectChanges();
|
|
}
|
|
}
|
|
|
|
public getUrlByStakeHolder(indicatorPath: IndicatorPath) {
|
|
return this.sanitizer.bypassSecurityTrustResourceUrl(
|
|
this.indicatorUtils.getChartUrl(indicatorPath.source, this.getFullUrl(indicatorPath)));
|
|
}
|
|
|
|
public getActiveIndicatorPath(indicator: Indicator) {
|
|
if(indicator.activePath) {
|
|
return indicator.indicatorPaths[indicator.activePath];
|
|
} else {
|
|
return indicator.indicatorPaths[0];
|
|
}
|
|
}
|
|
|
|
public filter() {
|
|
this.validateYearRange(true);
|
|
}
|
|
|
|
public filterChanged($event, navigate: boolean = true) {
|
|
let selected = [];
|
|
for (let value of $event.value.values) {
|
|
if (value.selected) {
|
|
selected.push( StringUtils.quote(StringUtils.URIEncode(value.id)));
|
|
}
|
|
}
|
|
if (selected.length > 0) {
|
|
this.queryParams[$event.value.filterId] = selected.join(",");
|
|
} else {
|
|
delete this.queryParams[$event.value.filterId];
|
|
}
|
|
if (navigate) {
|
|
this._router.navigate([], {queryParams: this.queryParams});
|
|
this.setIndicators();
|
|
}
|
|
|
|
}
|
|
|
|
public countSelectedFilters(): number {
|
|
let count = 0;
|
|
if (this.periodFilter.selectedFromAndToValues.length > 0) {
|
|
count += 1;
|
|
}
|
|
for (let filter of this.filters) {
|
|
count += filter.countSelectedValues;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public clearAll() {
|
|
for (let filter of this.filters) {
|
|
this.clearFilter(filter);
|
|
}
|
|
this.periodFilter.selectedFromValue = "";
|
|
this.periodFilter.selectedToValue = "";
|
|
this.validateYearRange(true)
|
|
}
|
|
|
|
public clearFilter(filter: Filter) {
|
|
filter.countSelectedValues = 0;
|
|
filter.radioValue = "";
|
|
for (let value of filter.values) {
|
|
if (value.selected) {
|
|
value.selected = false;
|
|
}
|
|
}
|
|
if (this.queryParams[filter.filterId]) {
|
|
delete this.queryParams[filter.filterId];
|
|
}
|
|
}
|
|
|
|
clearPeriodFilter() {
|
|
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
|
|
this.periodFilter.selectedFromValue = "";
|
|
this.periodFilter.selectedToValue = "";
|
|
if(this.rangeFilter) {
|
|
this.rangeFilter.clearFilter();
|
|
}
|
|
this.filter();
|
|
}
|
|
}
|
|
|
|
clearFilterValue(filter: Filter, value: Value) {
|
|
value.selected = false;
|
|
filter.radioValue = '';
|
|
filter.countSelectedValues = filter.countSelectedValues - 1;
|
|
this.filterChanged({
|
|
value:filter
|
|
});
|
|
}
|
|
|
|
public hasPermission(visibility: Visibility): boolean {
|
|
if(visibility === 'PUBLIC') {
|
|
return true;
|
|
} else if(visibility === 'RESTRICTED') {
|
|
return (!this.view || this.view === 'RESTRICTED') && this.isMember(this.stakeholder);
|
|
} else {
|
|
return !this.view && this.isManager(this.stakeholder);
|
|
}
|
|
}
|
|
|
|
public isMember(stakeholder: Stakeholder) {
|
|
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user)
|
|
|| Session.isManager(stakeholder.type, stakeholder.alias, this.user) || Session.isMember(stakeholder.type, stakeholder.alias, this.user));
|
|
}
|
|
|
|
public isManager(stakeholder: Stakeholder) {
|
|
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user) || Session.isManager(stakeholder.type, stakeholder.alias, this.user));
|
|
}
|
|
|
|
public countSubCategoriesToShow(category: Category): number {
|
|
return category.subCategories.filter(subCategory => this.hasPermission(subCategory.visibility)).length;
|
|
}
|
|
|
|
public countSectionsWithIndicatorsToShow(sections: Section[]):number {
|
|
return sections.map(section => this.countIndicatorsToShow(section.indicators)).reduce((sum, current) => sum + current, 0);
|
|
}
|
|
|
|
public countIndicatorsToShow(indicators: Indicator[]): number {
|
|
return indicators.filter(indicator => this.hasPermission(indicator.visibility)).length;
|
|
}
|
|
|
|
public getNumberClassBySize(size: IndicatorSize) {
|
|
if (size === 'small') {
|
|
return 'uk-width-medium@m uk-width-1-1';
|
|
} else if (size === 'medium') {
|
|
return 'uk-width-1-4@l uk-width-1-2@m uk-width-1-1';
|
|
} else {
|
|
return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
|
|
}
|
|
}
|
|
|
|
public getChartClassBySize(size: IndicatorSize): string {
|
|
if (size === 'small') {
|
|
return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
|
|
} else if (size === 'medium') {
|
|
return 'uk-width-1-2@l uk-width-1-1';
|
|
} else {
|
|
return 'uk-width-1-1';
|
|
}
|
|
}
|
|
|
|
public printReport() {
|
|
window.print();
|
|
}
|
|
|
|
changeOverlay(event, indicator: Indicator, overlay: Overlay) {
|
|
event.stopPropagation();
|
|
indicator.overlay = overlay;
|
|
}
|
|
|
|
closeOverlay(event: ClickEvent, indicator: Indicator) {
|
|
if(event.clicked && indicator.overlay) {
|
|
indicator.overlay = false;
|
|
}
|
|
}
|
|
|
|
private createClipboard() {
|
|
if (typeof window !== 'undefined') {
|
|
delete this.clipboard;
|
|
let Clipboard;
|
|
Clipboard = require('clipboard');
|
|
this.clipboard = new Clipboard('.clipboard_btn');
|
|
}
|
|
}
|
|
|
|
//Refine Type Filters
|
|
|
|
setSelectedFilters(){
|
|
for (var i = 0; i < this.filters.length; i++) {
|
|
var filter: Filter = this.filters[i];
|
|
filter.countSelectedValues = 0;
|
|
let parameterNames = Object.keys(this.queryParams);
|
|
if (parameterNames.indexOf(filter.filterId) != -1) {
|
|
let values = (decodeURIComponent(this.queryParams[filter.filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/, -1);
|
|
for (let filterValue of filter.values) {
|
|
if (values.indexOf(StringUtils.quote(filterValue.id)) > -1) {
|
|
filterValue.selected = true;
|
|
filter.countSelectedValues++;
|
|
} else {
|
|
filterValue.selected = false;
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
for (let filterValue of filter.values) {
|
|
filterValue.selected = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
getSelectedFilterValues(filterId){
|
|
let values = null;
|
|
for (let filter of this.filters) {
|
|
if(filterId == filter.filterId && filter.countSelectedValues > 0) {
|
|
values =filterId == "fos"?{lvl1:[],lvl2:[]}:[];
|
|
for (let filterValue of filter.values) {
|
|
if (filterValue.selected) {
|
|
if(filterId == "fos"){
|
|
let code = filterValue.id.split(" ")[0];
|
|
if(code.length == 2){
|
|
values.lvl1.push(filterValue.id)
|
|
}else{
|
|
values.lvl2.push(filterValue.id)
|
|
}
|
|
}else if(filterId == "publiclyfunded"){
|
|
// console.log("publiclyFunded", filterValue)
|
|
return filterValue.id;
|
|
}else{
|
|
values.push(filterValue.id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return values;
|
|
}
|
|
|
|
}
|