import { AfterViewInit, ChangeDetectorRef, Component, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from "@angular/core"; import { Format, Indicator, IndicatorPath, IndicatorSize, IndicatorType, Section, Stakeholder, Visibility } from "../../monitor/entities/stakeholder"; import {IndicatorUtils, StakeholderUtils} from "../utils/indicator-utils"; import { AbstractControl, 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 {Reorder, StakeholderService} from "../../monitor/services/stakeholder.service"; import {EnvProperties} from "../../utils/properties/env-properties"; import {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 {properties} from "src/environments/environment"; import {NotificationHandler} from "../../utils/notification-handler"; declare var UIkit; @Component({ selector: 'indicators', templateUrl: './indicators.component.html' }) export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { filesToUpload: Array; errorMessage = ""; public properties: EnvProperties = properties; @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 indicatorUtils: IndicatorUtils = new IndicatorUtils(); public stakeholderUtils: StakeholderUtils = new StakeholderUtils(); 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; /** Caches */ public safeUrls: Map = new Map([]); public numberResponses: Map = new Map(); public numberResults: Map = new Map(); /** Import / Export Indicators */ importLoading: 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; urlParameterizedMessage = null; showCheckForSchemaEnhancements: boolean = false; private notification: Notification; @ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent; @ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent; @ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent; public isFullscreen: boolean = false; @HostListener('fullscreenchange', ['$event']) @HostListener('webkitfullscreenchange', ['$event']) @HostListener('mozfullscreenchange', ['$event']) @HostListener('MSFullscreenChange', ['$event']) screenChange(event) { this.isFullscreen = !this.isFullscreen; } /** * Subscriptions **/ private subscriptions: any[] = []; private urlSubscriptions: any[] = []; private numberSubscription: any[] = []; constructor(private layoutService: LayoutService, private stakeholderService: StakeholderService, private statisticsService: StatisticsService, private notificationService: NotificationService, private fb: UntypedFormBuilder, private router: Router, private cdr: ChangeDetectorRef, private sanitizer: DomSanitizer) { this.filesToUpload = []; } ngOnInit(): void { if (this.stakeholder) { this.setCharts(); this.setNumbers(); } this.changed.subscribe(() => { this.setCharts(); this.setNumbers(); this.initReorder(); }) } ngOnDestroy(): void { this.subscriptions.forEach(value => { if (value instanceof Subscriber) { value.unsubscribe(); } else if (value instanceof Function) { value(); } }); 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, type: IndicatorType, action: 'moved' | 'added' | 'removed'): void => { 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); } } let reorder: Reorder = { action: action, target: list.detail[1].id, ids: reordered } this.reorderIndicators(list.current.id.toString().split('-')[1], type, reorder); }; 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 => { callback(list, "number", 'moved'); })); this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => { callback(list, "number", 'added'); })); this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => { callback(list, "number", 'removed'); })); }); this.charts.forEach((section) => { this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => { callback(list, "chart", 'moved'); })); 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) => { let url = this.indicatorUtils.getFullUrl(this.stakeholder, number.indicatorPaths[0]); const pair = JSON.stringify([number.indicatorPaths[0].source, url]); const indexes = urls.get(pair) ? urls.get(pair) : []; indexes.push([i, j]); 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][]) { indexes.forEach(([i, j]) => { let result = JSON.parse(JSON.stringify(response)); this.numbers[i].indicators[j].indicatorPaths[0].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, 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]; } public get numberIndicatorPaths(): UntypedFormArray { return this.numberIndicatorFb.get('indicatorPaths') as UntypedFormArray; } public get chartIndicatorPaths(): UntypedFormArray { return this.chartIndicatorFb.get('indicatorPaths') as UntypedFormArray; } 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(null, indicatorPath.get('url').value).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): UntypedFormArray { return this.chartIndicatorPaths.at(index).get('parameters') as UntypedFormArray; } public getParameter(index: number, key: string): UntypedFormControl { return this.getParameters(index).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), format: this.fb.control(format, Validators.required) } )); let index = this.numberIndicatorPaths.length - 1; if (this.numberIndicatorPaths.at(index).get('url').valid) { this.validateJsonPath(index); this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value); } 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.isStakeholderParametersValid(indicatorPath)) { // default profile if (this.stakeholder.defaultId == null) { this.urlParameterizedMessage = "This indicator couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly." } else { this.urlParameterizedMessage = "This indicator couldn't be generated properly. Please make sure chart data is for the current stakeholder." } } else { this.urlParameterizedMessage = null; } this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').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); } 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) } } else { this.urlParameterizedMessage = null; } }) ); 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.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value); 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); if (!this.isStakeholderParametersValid(indicatorPath)) { // default profile if (this.stakeholder.defaultId == null) { this.urlParameterizedMessage = "This chart couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly." } else { this.urlParameterizedMessage = "This chart couldn't be generated properly. Please make sure chart data is for the current stakeholder." } } else { this.urlParameterizedMessage = null; } this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value); (this.chartIndicatorPaths.at(index) as UntypedFormGroup).get('type').setValue(indicatorPath.type); let parameters = this.getParametersAsFormArray(indicatorPath); (this.chartIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', parameters); 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; } } else { this.urlParameterizedMessage = null; } })); } } private isStakeholderParametersValid(indicatorPath: IndicatorPath) { return !((indicatorPath.chartObject && Object.keys(indicatorPath.parameters).indexOf("index_id") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_name") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_shortName") == -1) || (!indicatorPath.chartObject && indicatorPath.url.indexOf("index_id") == -1 && indicatorPath.url.indexOf("index_name") == -1 && (indicatorPath.url).indexOf("index_shortName") == -1)); } 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) { 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.urlParameterizedMessage = null; 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)), indicatorPath.parameters, 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.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.editNumberModal.open(); } public editChartIndicatorOpen(section: Section, id = null) { this.urlParameterizedMessage = null; 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.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.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, true); 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, false); 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(); 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.editing = false; this.importLoading = false; NotificationHandler.rise('Indicators have been imported successfully!'); }, error => { this.chartIndicatorFb = null; NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); this.editing = false; this.importLoading = false; })); } reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) { 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, reorder, type).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): boolean { let hasDifference = false; 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(); } public get isAdministrator(): boolean { return Session.isPortalAdministrator(this.user); } public get isCurator(): boolean { return this.isAdministrator || Session.isCurator(this.stakeholder.type, this.user); } refreshIndicator() { this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart', true); this.indicator.indicatorPaths.forEach(indicatorPath => { indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath); }); } 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; })); } // deleteNumberSectionOpen(section: Section, index: number) { // this.section = section; // this.index = index; // this.deleteNumberSectionModal.alertTitle = 'Delete Section'; // this.deleteNumberSectionModal.cancelButtonText = 'No'; // this.deleteNumberSectionModal.okButtonText = 'Yes'; // this.deleteNumberSectionModal.open(); // } // // deleteChartSectionOpen(section: Section, index: number) { // this.section = section; // this.index = index; // this.deleteChartSectionModal.alertTitle = 'Delete Section'; // this.deleteChartSectionModal.cancelButtonText = 'No'; // this.deleteChartSectionModal.okButtonText = 'Yes'; // this.deleteChartSectionModal.open(); // } 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(); })); } private checkForSchemaEnhancements(url: string) { this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url); } 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; } } return charts; } importIndicatorsAndSave(charts: any[]) { let sectionsToSave: Section[] = []; let countIndicators = 0; // name description additionalDescription, height, width, visibility let noValidParams = 0; let duplicates = 0; charts = this.migrateFromOldImportJsonFile(charts); for (let chart of charts) { 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 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.type == "number" && !chart.jsonPath) { invalid_file_message = "No jsonPath is specified for number indicator." } else if (!chart.url) { 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.editing = false; this.importLoading = false; break; } if (chart.type == "chart") { indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(chart.url), chart.url, chart.type, this.stakeholder); for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts) { for (let chart of section.indicators) { if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) { duplicates++; exists = true; } } } } else if (chart.type == "number") { indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, this.stakeholder, chart.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(chart.url))); for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers) { for (let chart of section.indicators) { if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) { duplicates++; exists = true; } } } } if (!this.isStakeholderParametersValid(indicatorPath)) { noValidParams++; } if (!exists) { let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, "RESTRICTED", [indicatorPath]); 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 (noValidParams > 0) { let noValidMessage = "Some indicators couldn't be generated properly. Please make sure chart data is for the current stakeholder."; if (this.stakeholder.defaultId == null) { noValidMessage = "Some indicators couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."; } UIkit.notification(noValidMessage, { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.importLoading = false; } else 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.editing = false; this.importLoading = false; } } 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 => { indicator.indicatorPaths.forEach(indicatorPath => { indicators[indexIndicator] = { "type": indicator.type, "name": indicator.name, "jsonPath": indicatorPath.jsonPath, "description": indicator.description, "additionalDescription": indicator.additionalDescription, "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height, "url": this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), "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 => { indicator.indicatorPaths.forEach(indicatorPath => { indicators[indexIndicator] = { "type": indicator.type, "name": indicator.name, "description": indicator.description, "additionalDescription": indicator.additionalDescription, "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height, "url": this.getUrlByStakeHolder(indicatorPath), "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[this.subcategoryIndex] : null; var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(indicators)], {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.editing = false; } fileChangeEvent(fileInput: any, index) { this.index = index; this.editing = true; this.importLoading = 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.editing = false; this.importLoading = false; 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.editing = false; this.importLoading = false; return; } } this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => { let json_result = JSON.parse(result); // validate file if (!json_result || json_result.length == 0) { UIkit.notification("Importing file is empty", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.importLoading = false; } else { this.importIndicatorsAndSave(json_result); } }, (error) => { console.error("Error importing files", error); UIkit.notification("Error importing files", { status: 'danger', timeout: 6000, pos: 'bottom-right' }); this.editing = false; this.importLoading = false; }); } 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('textarea'); tempBox.style.position = 'fixed'; tempBox.style.left = '0'; tempBox.style.top = '0'; tempBox.style.opacity = '0'; tempBox.value = value; document.body.appendChild(tempBox); tempBox.focus(); tempBox.select(); tempBox.setSelectionRange(0, 99999); document.execCommand('copy'); document.body.removeChild(tempBox); NotificationHandler.rise('Copied to clipboard'); } }