import { AfterViewInit, ChangeDetectorRef, Component, HostListener, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from "@angular/core"; import { Format, Indicator, IndicatorPath, IndicatorSize, IndicatorType, Section, Stakeholder, Visibility } from "../../monitor/entities/stakeholder"; import { AbstractControl, FormArray, FormGroup, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from "@angular/forms"; import {AlertModal} from "../../utils/modal/alert"; import {StatisticsService} from "../utils/services/statistics.service"; import {HelperFunctions} from "../../utils/HelperFunctions.class"; import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser"; import {MoveIndicator, SectionInfo, StakeholderService} from "../../monitor/services/stakeholder.service"; import {BehaviorSubject, Observable, Subscriber} from "rxjs"; import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service"; import {Router} from "@angular/router"; import {Role, Session, User} from "../../login/utils/helper.class"; import {StringUtils} from "../../utils/string-utils.class"; import {Notification} from "../../notifications/notifications"; import {NotificationUtils} from "../../notifications/notification-utils"; import {NotifyFormComponent} from "../../notifications/notify-form/notify-form.component"; import {NotificationService} from "../../notifications/notification.service"; import {NotificationHandler} from "../../utils/notification-handler"; import {IndicatorStakeholderBaseComponent} from "../utils/stakeholder-base.component"; import {properties} from "../../../../environments/environment"; import {StatsProfilesService} from "../utils/services/stats-profiles.service"; import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component"; declare var UIkit; declare var copy; @Component({ selector: 'indicators', templateUrl: './indicators.component.html' }) export class IndicatorsComponent extends IndicatorStakeholderBaseComponent implements OnInit, OnChanges, AfterViewInit { filesToUpload: Array; errorMessage = ""; @Input() public topicIndex: number = 0; @Input() public categoryIndex: number = 0; @Input() public subcategoryIndex: number = 0; @Input() public stakeholder: Stakeholder = null; @Input() public changed: Observable; @Input() public user: User = null; public preview: string; public statsProfiles = []; public numberIndicatorFb: UntypedFormGroup; public chartIndicatorFb: UntypedFormGroup; public chartSections: UntypedFormArray; public numberSections: UntypedFormArray; /** * Editable indicator */ public section: Section; public indicator: Indicator; public index: number = -1; public editing: boolean = false; public dragging: boolean = false; /* Reorder indicators */ public to: BehaviorSubject = new BehaviorSubject(null); /** Caches */ public safeUrls: Map = new Map([]); public numberResponses: Map = new Map(); public numberResults: Map = new Map(); public loading: boolean = false; @ViewChild('editChartModal', {static: true}) editChartModal: AlertModal; @ViewChild('editNumberModal', {static: true}) editNumberModal: AlertModal; @ViewChild('deleteModal', {static: true}) deleteModal: AlertModal; @ViewChild('deleteSectionModal', {static: true}) deleteSectionModal: AlertModal; public sectionTypeToDelete: string; public sectionChildrenActionOnDelete: string; public indicatorChildrenActionOnDelete: string; private notification: Notification; @ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent; @ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent; @ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent; /* Transition Groups */ @ViewChild('numbersTransition') numbersTransition: TransitionGroupComponent; @ViewChild('chartsTransition') chartsTransition: TransitionGroupComponent; public isFullscreen: boolean = false; @HostListener('fullscreenchange', ['$event']) @HostListener('webkitfullscreenchange', ['$event']) @HostListener('mozfullscreenchange', ['$event']) @HostListener('MSFullscreenChange', ['$event']) screenChange(event) { this.isFullscreen = !this.isFullscreen; } /** * Subscriptions **/ private urlSubscriptions: any[] = []; private numberSubscription: any[] = []; constructor(private layoutService: LayoutService, private stakeholderService: StakeholderService, private statisticsService: StatisticsService, private statsProfileService: StatsProfilesService, private notificationService: NotificationService, private fb: UntypedFormBuilder, protected _router: Router, private sanitizer: DomSanitizer) { super() this.filesToUpload = []; } ngOnInit(): void { if (this.stakeholder) { this.setCharts(); this.setNumbers(); } this.changed.subscribe(() => { this.setCharts(); this.setNumbers(); this.initReorder(); }); if (this.isCurator) { this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => { this.statsProfiles = [null].concat(statsProfiles); }, error => { this.statsProfiles = []; })); } else { this.statsProfiles = []; } } ngOnDestroy(): void { super.ngOnDestroy(); this.urlSubscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); this.numberSubscription.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); } ngAfterViewInit(): void { this.initReorder(); } ngOnChanges(changes: SimpleChanges): void { if (this.canEdit) { if (changes.topicIndex || changes.categoryIndex || changes.subcategoryIndex) { this.initReorder(); this.setCharts(); this.setNumbers(); } } } initReorder() { this.subscriptions.forEach(value => { if (value instanceof Function) { value(); } }); if (document !== undefined) { let callback = (list): string[] => { let items: HTMLCollection = list.current.children; let reordered = []; for (let i = 0; i < items.length; i++) { if (items.item(i).id) { reordered.push(items.item(i).id); } } return reordered; }; this.numbers.forEach((section) => { this.subscriptions.push(UIkit.util.on(document, 'start', '#number-' + section._id, (): void => { this.dragging = true; })); this.subscriptions.push(UIkit.util.on(document, 'stop', '#number-' + section._id, (): void => { this.dragging = false; })); this.subscriptions.push(UIkit.util.on(document, 'moved', '#number-' + section._id, (list): void => { this.reorderIndicators(section._id, 'number', callback(list)); })); this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => { this.to.next({id: section._id, indicators: callback(list)}); })); this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => { let sub = this.to.asObservable().subscribe(to => { if (to) { let from: SectionInfo = {id: section._id, indicators: callback(list)}; this.moveIndicator({target: list.detail[1].id, from: from, to: to}); setTimeout(() => { sub.unsubscribe(); }) } }) })); }); this.charts.forEach((section) => { this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => { this.reorderIndicators(section._id, 'chart', callback(list)); })); this.subscriptions.push(UIkit.util.on(document, 'added', '#chart-' + section._id, (list): void => { //callback(list, "chart", 'added'); })); this.subscriptions.push(UIkit.util.on(document, 'removed', '#chart-' + section._id, (list): void => { // callback(list, "chart", 'removed'); })); }); } } hide(element: any) { UIkit.dropdown(element).hide(); } setCharts() { this.chartSections = this.fb.array([]); this.charts.forEach(section => { this.chartSections.push(this.fb.group({ _id: this.fb.control(section._id), title: this.fb.control(section.title), creationDate: this.fb.control(section.creationDate), stakeholderAlias: this.fb.control(section.stakeholderAlias), defaultId: this.fb.control(section.defaultId), type: this.fb.control(section.type), indicators: this.fb.control(section.indicators) })); section.indicators.forEach(indicator => { indicator.indicatorPaths.forEach(indicatorPath => { let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath); if (!this.safeUrls.get('url')) { indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath); this.safeUrls.set(url, indicatorPath.safeResourceUrl); } }); }) }); } setNumbers() { this.numberSections = this.fb.array([]); this.numberResults.clear(); let urls: Map = new Map(); this.numbers.forEach((section, i) => { this.numberSections.push(this.fb.group({ _id: this.fb.control(section._id), title: this.fb.control(section.title), creationDate: this.fb.control(section.creationDate), stakeholderAlias: this.fb.control(section.stakeholderAlias), defaultId: this.fb.control(section.defaultId), type: this.fb.control(section.type), indicators: this.fb.control(section.indicators) })); section.indicators.forEach((number, j) => { number.indicatorPaths.forEach((indicatorPath, k) => { let url = this.indicatorUtils.getFullUrl(this.stakeholder, 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); }); }); }); this.numberSubscription.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); urls.forEach((indexes, pair) => { let parsed = JSON.parse(pair); let response = this.numberResponses.get(pair); if (response) { this.calculateResults(response, indexes); } else { this.numberSubscription.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(parsed[0]), parsed[1]).subscribe(response => { this.calculateResults(response, indexes); this.numberResponses.set(pair, response); })); } }); } private calculateResults(response: any, indexes: [number, number, number][]) { indexes.forEach(([i, j, k]) => { let result = JSON.parse(JSON.stringify(response)); this.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); }); } get charts(): Section[] { if (this.stakeholder.topics[this.topicIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) { return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts; } else { return []; } } get numbers(): Section[] { if (this.stakeholder.topics[this.topicIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) { return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers; } else { return []; } } set numbers(sections: Section[]) { this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = sections; } get open(): boolean { return this.layoutService.open; } get canEdit() { return this.stakeholder && this.stakeholder.topics[this.topicIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] && this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] && !this.loading; } public get numberIndicatorPaths(): UntypedFormArray { return this.numberIndicatorFb.get('indicatorPaths') as UntypedFormArray; } public get chartIndicatorPaths(): UntypedFormArray { return this.chartIndicatorFb.get('indicatorPaths') as UntypedFormArray; } public getActiveIndicatorPath(indicator: Indicator) { if (indicator.activePath) { return indicator.indicatorPaths[indicator.activePath]; } else { return indicator.indicatorPaths[0]; } } 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) { 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 addJsonPath(index: number) { if (index == 0 && this.getJsonPath(index).getRawValue()[index].indexOf(",") != -1) { //if in the first path there are more than one paths comma separated, split them and autogenerate the forms let paths = this.getJsonPath(index).getRawValue()[index].split(","); for (let i = 0; i < paths.length; i++) { if (i != 0) { this.getJsonPath(index).push(this.fb.control('', Validators.required)); } } this.getJsonPath(index).setValue(paths) } else { this.getJsonPath(index).push(this.fb.control('', Validators.required)); } } public removeJsonPath(i: number, j: number) { if (this.getJsonPath(i).enabled) { this.getJsonPath(i).removeAt(j); } } public validateJsonPath(index: number, dirty: boolean = false) { let indicatorPath: UntypedFormGroup = this.numberIndicatorPaths.at(index); if (this.indicator.defaultId === null) { this.getJsonPath(index).disable(); } indicatorPath.get('result').setErrors({validating: true}); this.subscriptions.push(this.statisticsService.getNumbers(indicatorPath.get('source').value, this.indicatorUtils.getFullUrl(this.stakeholder, this.indicator.indicatorPaths[index])).subscribe(response => { let result = JSON.parse(JSON.stringify(response)); this.getJsonPath(index).controls.forEach(jsonPath => { if (result) { result = result[jsonPath.value]; } }); setTimeout(() => { if (this.indicator.defaultId === null) { this.getJsonPath(index).enable(); if (dirty) { this.getJsonPath(index).markAsDirty(); } } indicatorPath.get('result').setErrors(null); if (typeof result === 'string' || typeof result === 'number') { result = Number(result); if (result !== Number.NaN) { indicatorPath.get('result').setValue(result); } else { indicatorPath.get('result').setValue(0); } } else { indicatorPath.get('result').setValue(0); } }, 500); }, error => { setTimeout(() => { if (this.indicator.defaultId === null) { this.getJsonPath(index).enable(); if (dirty) { this.getJsonPath(index).markAsDirty(); } } indicatorPath.get('result').setErrors(null); indicatorPath.get('result').setValue(0); }, 500); })); } public getJsonPath(index: number): UntypedFormArray { return this.numberIndicatorPaths.at(index).get('jsonPath') as UntypedFormArray; } public getCurrentJsonPath(index: number): string[] { return this.section.indicators[this.index].indicatorPaths[index].jsonPath; } public getParameters(index: number, type: IndicatorType = 'chart'): UntypedFormArray { if (type === 'chart') { return this.chartIndicatorPaths.at(index).get('parameters') as UntypedFormArray; } else { return this.numberIndicatorPaths.at(index).get('parameters') as UntypedFormArray; } } public getParameter(index: number, key: string, type: IndicatorType = 'chart'): UntypedFormControl { return this.getParameters(index, type).controls.filter(control => control.value.key === key)[0] as UntypedFormControl; } private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) { return this.sanitizer.bypassSecurityTrustResourceUrl( this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath))); } private getUrlByStakeHolder(indicatorPath: IndicatorPath) { return this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)); } public addNumberIndicatorPath(url: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), source: string = 'stats-tool', jsonPath: UntypedFormArray = new UntypedFormArray([]), format: Format = "NUMBER") { if (jsonPath.length === 0) { jsonPath.push(this.fb.control('', Validators.required)); } this.numberIndicatorPaths.push(this.fb.group({ url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]), jsonPath: jsonPath, result: this.fb.control(0, Validators.required), source: this.fb.control(source, Validators.required), parameters: parameters, format: this.fb.control(format, Validators.required) } )); let index = this.numberIndicatorPaths.length - 1; if (this.numberIndicatorPaths.at(index).get('url').valid) { this.validateJsonPath(index); } if (this.indicator.defaultId === null) { this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => { this.numberIndicatorPaths.at(index).get('result').setValue(null); if (this.numberIndicatorPaths.at(index).get('url').valid) { let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(value))); if (this.indicator.indicatorPaths[index]) { this.indicator.indicatorPaths[index] = indicatorPath; } else { this.indicator.indicatorPaths.push(indicatorPath); } if (indicatorPath.source) { this.numberIndicatorPaths.at(index).get('source').setValue(indicatorPath.source); } (this.numberIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath)); if (indicatorPath.jsonPath.length > 1 && this.getJsonPath(index).length == 1) { let paths = indicatorPath.jsonPath; for (let i = 0; i < paths.length; i++) { if (i == this.getJsonPath(index).length) { this.getJsonPath(index).push(this.fb.control('', Validators.required)); } } this.getJsonPath(index).setValue(paths) } } }) ); this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => { if (this.indicator.indicatorPaths[index]) { this.indicator.indicatorPaths[index].jsonPath = value; } this.numberIndicatorPaths.at(index).get('result').setValue(null); }) ); this.subscriptions.push(this.numberIndicatorPaths.at(index).get('source').valueChanges.subscribe(value => { if (this.indicator.indicatorPaths[index]) { this.indicator.indicatorPaths[index].source = value; } }) ); } else { this.numberIndicatorPaths.at(index).get('url').disable(); this.numberIndicatorPaths.at(index).get('jsonPath').disable(); this.numberIndicatorPaths.at(index).get('source').disable(); } } public addChartIndicatorPath(value: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), disableUrl: boolean = false, type: string = null) { this.chartIndicatorPaths.push(this.fb.group({ url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]), parameters: parameters, type: this.fb.control(type) } )); let index = this.chartIndicatorPaths.length - 1; if (disableUrl) { this.chartIndicatorPaths.at(index).get('url').disable(); } else { this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => { if (this.chartIndicatorPaths.at(index).get('url').valid) { let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder); (this.chartIndicatorPaths.at(index) as UntypedFormGroup).get('type').setValue(indicatorPath.type); (this.chartIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath)); if (!this.indicator.indicatorPaths[index]) { this.indicator.indicatorPaths[index] = indicatorPath; this.indicator.indicatorPaths[index].safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath); } else { indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl; this.indicator.indicatorPaths[index] = indicatorPath; } } })); } } public removeNumberIndicatorPath(index: number) { this.numberIndicatorPaths.removeAt(index); this.indicator.indicatorPaths.splice(index, 1); this.numbersTransition.init(); if (this.indicator.activePath === index) { this.activeNumberIndicatorPath(Math.max(0, index - 1)); } else if (this.indicator.activePath > index) { this.activeNumberIndicatorPath(this.indicator.activePath - 1); } this.numberIndicatorFb.markAsDirty(); } public removeChartIndicatorPath(index: number) { this.chartIndicatorPaths.removeAt(index); this.indicator.indicatorPaths.splice(index, 1); this.chartsTransition.init(); if (this.indicator.activePath === index) { this.activeChartIndicatorPath(Math.max(0, index - 1)); } else if (this.indicator.activePath > index) { this.activeChartIndicatorPath(this.indicator.activePath - 1); } this.chartIndicatorFb.markAsDirty(); } public moveIndicatorPath(form: FormGroup, type: 'number' | 'chart', index: number, newIndex: number = index - 1) { let indicatorPaths = type == 'number'?this.numberIndicatorPaths:this.chartIndicatorPaths; if(type == 'number') { this.numbersTransition.init(); } else { this.chartsTransition.init(); } let a = indicatorPaths.at(index); let b = indicatorPaths.at(newIndex); indicatorPaths.setControl(index, b); indicatorPaths.setControl(newIndex, a); HelperFunctions.swap(this.indicator.indicatorPaths, index, newIndex); if (this.indicator.activePath === index) { this.indicator.activePath = newIndex; } else if (this.indicator.activePath === newIndex) { this.indicator.activePath = index; } form.markAsDirty(); } public activeNumberIndicatorPath(index: number) { let paths = this.numberIndicatorPaths; if (index == paths.length) { this.addNumberIndicatorPath(); this.numbersTransition.init(); } this.indicator.activePath = index; } public activeChartIndicatorPath(index: number) { let paths = this.chartIndicatorPaths; if (index == paths.length) { this.addChartIndicatorPath(); this.chartsTransition.init(); } this.indicator.activePath = index; } private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray { let jsonPath = this.fb.array([]); if (indicatorPath.jsonPath) { indicatorPath.jsonPath.forEach(path => { jsonPath.push(this.fb.control(path, Validators.required)); }); } return jsonPath; } private getParametersAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray { let parameters = this.fb.array([]); if (indicatorPath.parameters) { if(!indicatorPath.parameters.statsProfile) { indicatorPath.parameters.statsProfile = null; } Object.keys(indicatorPath.parameters).forEach(key => { if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) { if (this.indicatorUtils.parametersValidators.has(key)) { parameters.push(this.fb.group({ key: this.fb.control(key), value: this.fb.control(indicatorPath.parameters[key], this.indicatorUtils.parametersValidators.get(key)) })); } else { parameters.push(this.fb.group({ key: this.fb.control(key), value: this.fb.control(indicatorPath.parameters[key]) })); } } }); } return parameters; } public editNumberIndicatorOpen(section: Section, id = null) { this.editNumberModal.cancelButtonText = 'Cancel'; this.editNumberModal.okButtonLeft = false; this.editNumberModal.alertMessage = false; if (this.index === -1) { this.editNumberModal.alertTitle = 'Create a new number indicator'; this.editNumberModal.okButtonText = 'Save'; this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name); this.editNumberNotify.reset(this.notification.message); } else { this.editNumberModal.alertTitle = 'Edit number indicator\'s information'; this.editNumberModal.okButtonText = 'Save Changes'; this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name); this.editNumberNotify.reset(this.notification.message); } this.editNumberModal.stayOpen = true; this.section = section; this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1; if (this.index !== -1) { this.indicator = HelperFunctions.copy(this.section.indicators[this.index]); this.numberIndicatorFb = this.fb.group({ _id: this.fb.control(this.indicator._id), name: this.fb.control(this.indicator.name, Validators.required), description: this.fb.control(this.indicator.description), creationDate: this.fb.control(this.indicator.creationDate), additionalDescription: this.fb.control(this.indicator.additionalDescription), visibility: this.fb.control(this.indicator.visibility), indicatorPaths: this.fb.array([], Validators.required), type: this.fb.control(this.indicator.type), width: this.fb.control(this.indicator.width), height: this.fb.control(this.indicator.height), defaultId: this.fb.control(this.indicator.defaultId) }); this.indicator.indicatorPaths.forEach(indicatorPath => { this.addNumberIndicatorPath(this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), this.getParametersAsFormArray(indicatorPath), indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath), indicatorPath.format); }); } else { this.indicator = new Indicator('', '', '', 'number', 'small', 'small', "PUBLIC", []); this.numberIndicatorFb = this.fb.group({ _id: this.fb.control(this.indicator._id), name: this.fb.control(this.indicator.name, Validators.required), description: this.fb.control(this.indicator.description), additionalDescription: this.fb.control(this.indicator.additionalDescription), visibility: this.fb.control(this.indicator.visibility), indicatorPaths: this.fb.array([], Validators.required), type: this.fb.control(this.indicator.type), width: this.fb.control(this.indicator.width), height: this.fb.control(this.indicator.height), defaultId: this.fb.control(this.indicator.defaultId) }); this.addNumberIndicatorPath(); } if (this.indicator.defaultId) { setTimeout(() => { this.numberIndicatorFb.get('description').disable(); }, 0); } this.editNumberModal.open(); } public editChartIndicatorOpen(section: Section, id = null) { this.editChartModal.cancelButtonText = 'Cancel'; this.editChartModal.okButtonLeft = false; this.editChartModal.alertMessage = false; if (this.index === -1) { this.editChartModal.alertTitle = 'Create a new chart indicator'; this.editChartModal.okButtonText = 'Save'; this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name); this.editChartNotify.reset(this.notification.message); } else { this.editChartModal.alertTitle = 'Edit chart indicator\'s information'; this.editChartModal.okButtonText = 'Save Changes'; this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name); ; this.editChartNotify.reset(this.notification.message); } this.editChartModal.stayOpen = true; this.urlSubscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } }); this.section = section; this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1; if (this.index !== -1) { this.indicator = HelperFunctions.copy(this.section.indicators[this.index]); this.chartIndicatorFb = this.fb.group({ _id: this.fb.control(this.indicator._id), name: this.fb.control(this.indicator.name), creationDate: this.fb.control(this.indicator.creationDate), description: this.fb.control(this.indicator.description), additionalDescription: this.fb.control(this.indicator.additionalDescription), visibility: this.fb.control(this.indicator.visibility), indicatorPaths: this.fb.array([]), width: this.fb.control(this.indicator.width), height: this.fb.control(this.indicator.height), defaultId: this.fb.control(this.indicator.defaultId) }); this.indicator.indicatorPaths.forEach(indicatorPath => { this.addChartIndicatorPath(this.getUrlByStakeHolder(indicatorPath), this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type); indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath); }); } else { this.indicator = new Indicator('', '', '', 'chart', 'medium', 'medium', "PUBLIC", []); this.chartIndicatorFb = this.fb.group({ _id: this.fb.control(this.indicator._id), name: this.fb.control(this.indicator.name), description: this.fb.control(this.indicator.description), additionalDescription: this.fb.control(this.indicator.additionalDescription), visibility: this.fb.control(this.indicator.visibility), indicatorPaths: this.fb.array([]), width: this.fb.control(this.indicator.width, Validators.required), height: this.fb.control(this.indicator.height, Validators.required), defaultId: this.fb.control(this.indicator.defaultId) }); this.addChartIndicatorPath(); } if (this.indicator.defaultId) { setTimeout(() => { this.chartIndicatorFb.get('description').disable(); }, 0); } this.editChartModal.open(); } saveIndicator() { this.editing = true; if (this.indicator.type === 'chart') { this.chartIndicatorFb.get('description').enable(); this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type); this.section = this.charts.find(section => section._id === this.section._id); } else { this.numberIndicatorFb.get('description').enable(); this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type); this.section = this.numbers.find(section => section._id === this.section._id); } let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id, this.section._id ]; this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.indicator, path).subscribe(indicator => { if (this.index !== -1) { this.section.indicators[this.index] = indicator; } else { this.section.indicators.push(indicator); } this.notification.entity = indicator._id; this.notification.stakeholder = this.stakeholder.alias; this.notification.stakeholderType = this.stakeholder.type; this.notification.groups = [Role.curator(this.stakeholder.type)]; if (this.stakeholder.defaultId) { this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias)); if (this.indicator.type === "chart") { this.setCharts(); this.chartIndicatorFb = null; this.editChartNotify.sendNotification(this.notification); } else { this.setNumbers(); this.numberIndicatorFb = null; this.editNumberNotify.sendNotification(this.notification); } } else { this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => { stakeholders.forEach(value => { this.notification.groups.push(Role.manager(value.type, value.alias)) }); if (this.indicator.type === "chart") { this.setCharts(); this.chartIndicatorFb = null; this.editChartNotify.sendNotification(this.notification); } else { this.setNumbers(); this.numberIndicatorFb = null; this.editNumberNotify.sendNotification(this.notification); } }); } UIkit.notification('Indicator has been successfully saved', { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.editing = false; if (this.indicator.type === "chart") { this.editChartModal.cancel(); } else { this.editNumberModal.cancel(); } }, error => { this.chartIndicatorFb = null; UIkit.notification(error.error.message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; if (this.indicator.type === "chart") { this.editChartModal.cancel(); } else { this.editNumberModal.cancel(); } })); } saveIndicators(sections) { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id ]; this.subscriptions.push(this.stakeholderService.saveBulkElements(this.properties.monitorServiceAPIURL, sections, path).subscribe(stakeholder => { this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts; this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers; this.setCharts(); this.setNumbers(); this.initReorder(); if (properties.notificationsAPIURL) { this.notification = NotificationUtils.importIndicators(this.user.fullname, this.stakeholder.alias); this.notification.entity = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id; this.notification.name = this.user.firstname; this.notification.surname = this.user.lastname; this.notification.stakeholder = this.stakeholder.alias; this.notification.stakeholderType = this.stakeholder.type; this.notification.groups = [Role.curator(this.stakeholder.type)]; if (this.stakeholder.defaultId) { this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias)); this.notificationService.sendNotification(this.notification).subscribe(notification => { UIkit.notification('A notification has been sent successfully', { status: 'success', timeout: 6000, pos: 'bottom-right' }); }, error => { UIkit.notification('An error has occurred. Please try again later', { status: 'danger', timeout: 6000, pos: 'bottom-right' }); }); } else { this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => { stakeholders.forEach(value => { this.notification.groups.push(Role.manager(value.type, value.alias)) }); this.notificationService.sendNotification(this.notification).subscribe(notification => { NotificationHandler.rise('A notification has been sent successfully'); }, error => { NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); }); }); } } this.finish(); NotificationHandler.rise('Indicators have been imported successfully!'); }, error => { this.chartIndicatorFb = null; NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); this.finish(); })); } finish() { this.editing = false; this.loading = false; } moveIndicator(moveIndicator: MoveIndicator) { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id ]; this.subscriptions.push(this.stakeholderService.moveIndicator(this.properties.monitorServiceAPIURL, path, moveIndicator).subscribe(subCategory => { this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] = subCategory; this.setCharts(); this.setNumbers(); this.editing = false; })); } reorderIndicators(sectionId: string, type: IndicatorType, indicators: string[]) { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id, sectionId ]; this.subscriptions.push(this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, indicators).subscribe(indicators => { if (type === 'chart') { this.charts.find(section => section._id === sectionId).indicators = indicators; this.setCharts(); } else { this.numbers.find(section => section._id === sectionId).indicators = indicators; this.setNumbers(); } this.editing = false; })); } hasDifference(index: number, type: IndicatorType = 'chart'): boolean { let hasDifference = false; if (type === 'chart') { this.chartIndicatorPaths.at(index).value.parameters.forEach(parameter => { if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) { hasDifference = true; return; } }); return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !== this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString(); } else if (type === 'number') { let indicatorPath = this.numberIndicatorPaths.at(index).value; indicatorPath.parameters.forEach(parameter => { if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) { hasDifference = true; return; } }); } return hasDifference; } public get isAdministrator(): boolean { return Session.isPortalAdministrator(this.user); } public get isCurator(): boolean { return this.isAdministrator || Session.isCurator(this.stakeholder.type, this.user); } refreshIndicator(type: IndicatorType = 'chart') { if (type === 'chart') { this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart'); this.indicator.indicatorPaths.forEach(indicatorPath => { indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath); }); } else if (type === 'number') { this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, 'number'); this.indicator.indicatorPaths.forEach((indicatorPath, index) => { this.validateJsonPath(index); }); } } deleteIndicatorOpen(section: Section, indicatorId: string, type: string, childrenAction: string = null) { this.indicatorChildrenActionOnDelete = null; if (childrenAction == "delete") { this.indicatorChildrenActionOnDelete = childrenAction; } else if (childrenAction == "disconnect") { this.indicatorChildrenActionOnDelete = childrenAction; } this.section = section; if (type === 'chart') { this.index = this.charts.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId); } else { this.index = this.numbers.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId); } this.indicator = section.indicators.find(value => value._id == indicatorId); this.deleteModal.alertTitle = 'Delete indicator'; this.deleteModal.cancelButtonText = 'No'; this.deleteModal.okButtonText = 'Yes'; this.notification = NotificationUtils.deleteIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name); this.deleteNotify.reset(this.notification.message); this.deleteModal.stayOpen = true; this.deleteModal.open(); } deleteIndicator() { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id, this.section._id, this.indicator._id ]; this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.indicatorChildrenActionOnDelete).subscribe(() => { if (this.indicator.type === 'chart') { this.charts.find(section => section._id === this.section._id).indicators.splice(this.index, 1); this.setCharts(); } else { this.numbers.find(section => section._id === this.section._id).indicators.splice(this.index, 1); this.setNumbers(); } UIkit.notification('Indicator has been successfully deleted', { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.notification.entity = this.indicator._id; this.notification.stakeholder = this.stakeholder.alias; this.notification.stakeholderType = this.stakeholder.type; this.notification.groups = [Role.curator(this.stakeholder.type)]; if (this.stakeholder.defaultId) { this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias)); this.deleteNotify.sendNotification(this.notification); } else { this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => { stakeholders.forEach(value => { this.notification.groups.push(Role.manager(value.type, value.alias)) }); this.deleteNotify.sendNotification(this.notification); }); } this.editing = false; this.deleteModal.cancel(); }, error => { UIkit.notification(error.error.message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.deleteModal.cancel(); })); } changeIndicatorStatus(sectionId: string, indicator: Indicator, visibility: Visibility) { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id, sectionId, indicator._id ]; this.subscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(returnedElement => { indicator.visibility = returnedElement.visibility; UIkit.notification('Indicator has been successfully changed to ' + indicator.visibility.toLowerCase(), { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.editing = false; }, error => { UIkit.notification('An error has been occurred. Try again later', { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; })); } saveSection(focused: boolean, sectionControl: AbstractControl, index: number, type: IndicatorType = "chart") { if (!focused && sectionControl.dirty) { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id ]; this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, sectionControl.value, path, index).subscribe(section => { if (type === 'chart') { this.charts[index] = section; this.setCharts(); } else { this.numbers[index] = section; this.setNumbers(); } this.initReorder(); UIkit.notification('Section has been successfully saved', { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.editing = false; }, error => { UIkit.notification(error.error.message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; })); } } createSection(index = -1, type: IndicatorType = 'chart') { this.editing = true; this.section = new Section(type, null, null, this.stakeholder.alias); let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id ]; this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, this.section, path, index).subscribe(section => { if (type === 'chart') { if (index !== -1) { this.charts.splice(index, 0, section); } else { this.charts.push(section); } this.setCharts(); } else { if (index !== -1) { this.numbers.splice(index, 0, section); } else { this.numbers.push(section); } this.setNumbers(); } this.initReorder(); UIkit.notification('Section has been successfully created', { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.editing = false; }, error => { UIkit.notification(error.error.message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; })); } deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) { if (!this.editing && !section.defaultId) { this.sectionTypeToDelete = type; this.sectionChildrenActionOnDelete = null; if (childrenAction == "delete") { this.sectionChildrenActionOnDelete = childrenAction; } else if (childrenAction == "disconnect") { this.sectionChildrenActionOnDelete = childrenAction; } this.section = section; this.index = index; this.deleteSectionModal.alertTitle = 'Delete Section'; this.deleteSectionModal.cancelButtonText = 'No'; this.deleteSectionModal.okButtonText = 'Yes'; this.deleteSectionModal.stayOpen = true; this.deleteSectionModal.open(); } } deleteSection() { this.editing = true; let path = [ this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id, this.section._id ]; this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.sectionChildrenActionOnDelete).subscribe(() => { if (this.sectionTypeToDelete === "chart") { this.charts.splice(this.index, 1); this.setCharts(); } else { this.numbers.splice(this.index, 1); this.setNumbers(); } this.initReorder(); UIkit.notification('Section has been successfully deleted', { status: 'success', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.deleteSectionModal.cancel(); }, error => { UIkit.notification(error.error.message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.deleteSectionModal.cancel(); })); } migrateFromOldImportJsonFile(charts) { // first section contains numbers // second contains charts let hasNumbers = false; for (let chart of charts) { if (chart['type'] == 'number') { hasNumbers = true; break; } } let chartsSection = (hasNumbers ? 1 : 0); // no numbers: first sections contains charts for (let chart of charts) { if (chart['sectionIndex'] == null) { chart['sectionIndex'] = chart['type'] == 'chart' ? chartsSection : 0; } if (chart.url && chart.jsonPath) { chart.indicatorPaths = [{url: chart.url, jsonPath: chart.jsonPath}]; } else if(chart.url) { chart.indicatorPaths = [{url: chart.url}]; } } return charts; } importIndicatorsAndSave(stakeholder: Stakeholder, charts: any[]) { let sectionsToSave: Section[] = []; let countIndicators = 0; if (stakeholder.type !== this.stakeholder.type) { UIkit.notification("The type of this profile is not the same with the file's one!", { status: 'warning', timeout: 6000, pos: 'bottom-right' }); this.finish(); return; } // name description additionalDescription, height, width, visibility let duplicates = 0; charts = this.migrateFromOldImportJsonFile(charts); for (let chart of charts) { chart.visibility = this.showVisibility ? chart.visibility : this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities); if (!sectionsToSave[chart['sectionIndex']]) { let sectionToSave = new Section(chart['sectionType'] ? chart['sectionType'] : chart['type'], chart['sectionTitle']); sectionToSave.indicators = []; sectionsToSave[chart['sectionIndex']] = sectionToSave; } let exists = false; let indicatorPaths: IndicatorPath[] = []; // validate indicators' schema from file let invalid_file_message; if (!chart.type) { invalid_file_message = "No indicator type is specified. Type should be chart or number."; } else if (chart.type != "chart" && chart.type != "number") { invalid_file_message = "Invalid indicator type. Type should be chart or number."; } else if (chart.indicatorPaths.length === 0) { invalid_file_message = "No indicator paths are specified." } else if (chart.type == "number" && chart.indicatorPaths.filter(path => !path.jsonPath).length > 0) { invalid_file_message = "No jsonPath is specified for number indicator." } else if (chart.indicatorPaths.filter(path => !path.url).length > 0) { invalid_file_message = "No indicator url is specified."; } if (invalid_file_message) { UIkit.notification(invalid_file_message, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); break; } if (chart.type == "chart") { indicatorPaths = chart.indicatorPaths.map(path => this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(path.url), path.url, chart.type, stakeholder)); this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts.forEach((section: Section) => { section.indicators.forEach(indicator => { indicator.indicatorPaths.forEach(path => { let size = indicatorPaths.length; indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject)) if (indicatorPaths.length < size) { duplicates = duplicates + (size - indicatorPaths.length); exists = true; } }); }); }); } else if (chart.type == "number") { indicatorPaths = chart.indicatorPaths.map(path => this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(path.url), path.url, stakeholder, path.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(path.url)))); this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers.forEach((section: Section) => { section.indicators.forEach(indicator => { indicator.indicatorPaths.forEach(path => { let size = indicatorPaths.length; indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject)) if (indicatorPaths.length < size) { duplicates = duplicates + (size - indicatorPaths.length); exists = true; } }); }); }); } if (indicatorPaths.length > 0) { let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, this.showVisibility ? "RESTRICTED" : this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities), indicatorPaths); sectionsToSave[chart['sectionIndex']].indicators.push(i); countIndicators++; } } if (duplicates > 0) { UIkit.notification(duplicates + " urls already exist and will not be imported!", { status: 'warning', timeout: 6000, pos: 'bottom-right' }); } if (sectionsToSave.length > 0 && countIndicators > 0) { this.saveIndicators(sectionsToSave.filter(section => !!section)); } if (sectionsToSave.length == 0 || countIndicators == 0) { UIkit.notification(" No urls imported!", { status: 'warning', timeout: 6000, pos: 'bottom-right' }); this.finish(); } } public exportIndicators(subcategoryIndex) { this.editing = true; let indicators = []; let index: number = 0; let indexIndicator: number = 0; this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].numbers.forEach(section => { section.indicators.forEach(indicator => { indicators[indexIndicator] = { "indicatorPaths": indicator.indicatorPaths.map(path => { return { jsonPath: path.jsonPath, url: this.indicatorUtils.getNumberUrl(path.source, this.indicatorUtils.getFullUrl(this.stakeholder, path)) } }), "type": indicator.type, "name": indicator.name, "description": indicator.description, "additionalDescription": indicator.additionalDescription, "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height, "sectionTitle": section.title, "sectionType": section.type, "sectionIndex": index }; indexIndicator++; }); index++; }); this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].charts.forEach(section => { section.indicators.forEach(indicator => { indicators[indexIndicator] = { "indicatorPaths": indicator.indicatorPaths.map(path => { return { url: this.getUrlByStakeHolder(path) } }), "type": indicator.type, "name": indicator.name, "description": indicator.description, "additionalDescription": indicator.additionalDescription, "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height, "sectionTitle": section.title, "sectionType": section.type, "sectionIndex": index }; indexIndicator++; }); index++; }); let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null; let category = topic ? topic.categories[this.categoryIndex] : null; let subCategory = category ? category.subCategories[subcategoryIndex] : null; let json = { stakeholder: HelperFunctions.copy(this.stakeholder), indicators: indicators } delete json.stakeholder.topics; var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(json)], {type: 'application/json'})); var a = window.document.createElement('a'); window.document.body.appendChild(a); a.setAttribute('style', 'display: none'); a.href = jsonFileUrl; a.download = this.stakeholder.alias + "_" + topic.alias + "_" + category.alias + "_" + subCategory.alias + ".json"; a.click(); window.URL.revokeObjectURL(jsonFileUrl); a.remove(); // remove the element this.finish(); } fileChangeEvent(fileInput: any, index) { this.index = index; this.editing = true; this.loading = true; this.filesToUpload = >fileInput.target.files; this.upload(); } upload() { if (this.filesToUpload.length == 0) { console.error("There is no selected file to upload."); UIkit.notification("There is no selected file to upload.", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); return; } else { if (this.filesToUpload[0].name.indexOf(".json") == -1 || (this.filesToUpload[0].type != "application/json")) { console.error("No valid file type. The required type is JSON"); UIkit.notification("No valid file type. The required type is JSON", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); return; } } this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => { let json = JSON.parse(result); // validate file if (json && Array.isArray(json)) { UIkit.notification("This file is not supported any more. Please export indicators and try again!", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); } else if (!json || json?.indicators.length == 0) { UIkit.notification("Importing file is empty", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); } else { this.importIndicatorsAndSave(json.stakeholder, json.indicators); } }, (error) => { console.error("Error importing files", error); UIkit.notification("Error importing files", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.finish(); }); } makeFileRequest(url: string, params: Array, files: Array) { return new Promise((resolve, reject) => { const formData: any = new FormData(); const xhr = new XMLHttpRequest(); for (let i = 0; i < files.length; i++) { formData.append("uploads[]", files[i], files[i].name); } xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status == 200) { resolve(xhr.response); } else { reject(xhr.response); } } }; xhr.open("POST", url, true); xhr.send(formData); }); } copyToClipboard(value) { const tempBox = document.createElement('div'); tempBox.id = 'tempBox'; tempBox.style.position = 'fixed'; tempBox.style.left = '0'; tempBox.style.top = '0'; tempBox.style.opacity = '0'; tempBox.innerHTML = value.toString(); document.body.appendChild(tempBox); copy.exec('tempBox'); document.body.removeChild(tempBox); NotificationHandler.rise('Copied to clipboard'); } get isEditable(): boolean { return this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1'; } }