diff --git a/dashboard/menu/menu.component.ts b/dashboard/menu/menu.component.ts index d756a77f..9f89224b 100644 --- a/dashboard/menu/menu.component.ts +++ b/dashboard/menu/menu.component.ts @@ -372,10 +372,8 @@ export class MenuComponent implements OnInit { } public valueChange() { - this.elements.disable(); this.cdr.detectChanges(); this.elements.init(); - this.elements.enable(); } public get displayMenuItems() { diff --git a/monitor-admin/topic/indicators.component.html b/monitor-admin/topic/indicators.component.html index 1f68e246..2b0a57c3 100644 --- a/monitor-admin/topic/indicators.component.html +++ b/monitor-admin/topic/indicators.component.html @@ -1,456 +1,657 @@
-
-
Number Indicators
-
-
-
-
- -
-
-
-
-
- -
-
-
- - - - -
- +
+ +
+ -
-
{{indicator.name}}
-
- - -- -
-
- -
- +
+ +
-
-
- -
-
-
-
Chart Indicators
-
-
-
-
- -
-
-
-
-
- -
-
-
- - - - -
- +
+
-
-
-
- {{indicator.name}} +
+
+
+
+
+ Create a custom indicator +
+
+ Use our advance tool to create a custom Indicator that suit the needs of your + funding + KPI's. +
+
+ +
+
+
+
- -
- -
-
- -
-
-
-
-
-
-
-
-
-
- Create a custom indicator -
-
- Use our advance tool to create a custom Indicator that suit the needs of your funding - KPI's. -
-
- -
-
-
-
+
+ +
-
-
- -
-
+ [large]="true" classTitle="uk-background-primary uk-light" + (alertOutput)="saveIndicator()" + [okDisabled]="numberIndicatorFb && (numberIndicatorFb.invalid || numberIndicatorFb.pristine)">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{urlParameterizedMessage}}
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
- JSON Path -
-
-
- This JSON path is not valid or the result has not been calculated yet. - Please press calculate on box below to see the result. -
-
-
-
-
- - - - - - -
-
- -
-
+
-
- +
+
+
+
+
+
+ +
+
+
+
+
+
+
{{ urlParameterizedMessage }}
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ JSON Path +
+
+
+ This JSON path is not valid or the result has not been calculated yet. + Please press calculate on box below to see the result. +
+
+
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
-
-
+
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{urlParameterizedMessage}}
+ [okDisabled]="chartIndicatorFb && (chartIndicatorFb.invalid || chartIndicatorFb.pristine)"> +
+ +
+
+
+
+
-
- +
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
- +
-
-
- -
-
- + +
+
+ +
+ You are about to delete + "{{ indicator.name ? indicator.name : (indicator.indicatorPaths[0]?.parameters?.title ? indicator.indicatorPaths[0].parameters.title : '') }} + " indicator permanently. +
+ Indicators of all profiles based on this default indicator, will be deleted as well. +
+ Are you sure you want to proceed? +
- You are about to delete - "{{indicator.name ? indicator.name : (indicator.indicatorPaths[0]?.parameters?.title?indicator.indicatorPaths[0].parameters.title:'')}}" indicator permanently. -
- Indicators of all profiles based on this default indicator, will be deleted as well. -
- Are you sure you want to proceed? -
-
- -
-
- + +
+
+ +
+ You are about to delete this section and its indicators permanently. +
+ Sections of all profiles based on this default section and their contents, will be deleted as well. +
+ Are you sure you want to proceed?
- You are about to delete this section and its indicators permanently. -
- Sections of all profiles based on this default section and their contents, will be deleted as well. -
- Are you sure you want to proceed? -
-
-
- +
+
+ +
-
diff --git a/monitor-admin/topic/indicators.component.ts b/monitor-admin/topic/indicators.component.ts index 62883ae6..42e85ddc 100644 --- a/monitor-admin/topic/indicators.component.ts +++ b/monitor-admin/topic/indicators.component.ts @@ -20,7 +20,7 @@ import { Visibility } from "../../monitor/entities/stakeholder"; import { - AbstractControl, + AbstractControl, FormArray, FormGroup, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, @@ -45,6 +45,7 @@ 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; @@ -101,6 +102,8 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple @ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent; @ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent; @ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent; + /* Transition Groups */ + @ViewChild('transitionGroup') transitionGroup: TransitionGroupComponent; public isFullscreen: boolean = false; @@ -266,7 +269,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple setNumbers() { this.numberSections = this.fb.array([]); this.numberResults.clear(); - let urls: Map = new Map(); + let urls: Map = new Map(); this.numbers.forEach((section, i) => { this.numberSections.push(this.fb.group({ _id: this.fb.control(section._id), @@ -278,11 +281,13 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple 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); + 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 => { @@ -304,10 +309,10 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple }); } - private calculateResults(response: any, indexes: [number, number][]) { - indexes.forEach(([i, j]) => { + 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[0].jsonPath.forEach(jsonPath => { + this.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => { if (result) { result = result[jsonPath]; } @@ -320,7 +325,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple } else { result = 0; } - this.numberResults.set(i + '-' + j, result); + this.numberResults.set(i + '-' + j + '-' + k, result); }); } @@ -367,6 +372,14 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple 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'; @@ -579,6 +592,65 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple } } + public removeNumberIndicatorPath(index: number) { + this.numberIndicatorPaths.removeAt(index); + this.indicator.indicatorPaths.splice(index, 1); + this.transitionGroup.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.transitionGroup.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, + indicatorPaths: FormArray, index: number, + newIndex: number = index - 1) { + this.transitionGroup.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.transitionGroup.init(); + } + this.indicator.activePath = index; + } + + public activeChartIndicatorPath(index: number) { + let paths = this.chartIndicatorPaths; + if(index == paths.length) { + this.addChartIndicatorPath(); + this.transitionGroup.init(); + } + this.indicator.activePath = index; + } + private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray { let jsonPath = this.fb.array([]); if (indicatorPath.jsonPath) { @@ -1273,25 +1345,29 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple if (chart.type == "chart") { indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(chart.url), chart.url, chart.type, 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; - } - } - } + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts.forEach((section: Section) => { + section.indicators.forEach(indicator => { + indicator.indicatorPaths.forEach(path => { + if (JSON.stringify(path.chartObject) == JSON.stringify(indicatorPath.chartObject)) { + duplicates++; + exists = true; + } + }); + }); + }); } else if (chart.type == "number") { indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, 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; - } - } - } + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers.forEach((section: Section) => { + section.indicators.forEach(indicator => { + indicator.indicatorPaths.forEach(path => { + if (JSON.stringify(path.chartObject) == JSON.stringify(indicatorPath.chartObject)) { + duplicates++; + exists = true; + } + }); + }); + }); } if (!exists) { 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), [indicatorPath]); diff --git a/monitor-admin/topic/topic.component.ts b/monitor-admin/topic/topic.component.ts index b178eb46..40d128d8 100644 --- a/monitor-admin/topic/topic.component.ts +++ b/monitor-admin/topic/topic.component.ts @@ -242,30 +242,18 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, } topicChanged(callback: Function, save: boolean = false) { - if(this.topics && save) { - this.topics.disable(); - } - if(this.categories) { - this.categories.disable(); - } - if(this.subCategories) { - this.subCategories.disable(); - } if(callback) { callback(); } this.cdr.detectChanges(); if(this.topics && save) { this.topics.init(); - this.topics.enable(); } if(this.categories) { this.categories.init(); - this.categories.enable(); } if(this.subCategories) { this.subCategories.init(); - this.subCategories.enable(); } } @@ -359,6 +347,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, this.stakeholder.topics.splice(this.index, 1); if(this.topicIndex === this.index) { this.chooseTopic(Math.max(0, this.index - 1)); + } else if(this.topicIndex > this.index) { + this.chooseTopic(this.topicIndex - 1); } }, true); }; @@ -388,23 +378,15 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, } categoryChanged(callback: Function, save: boolean = false) { - if(this.categories && save) { - this.categories.disable(); - } - if(this.subCategories) { - this.subCategories.disable(); - } if(callback) { callback(); } this.cdr.detectChanges(); if(this.categories && save) { this.categories.init(); - this.categories.enable(); } if(this.subCategories) { this.subCategories.init(); - this.subCategories.enable(); } } @@ -500,6 +482,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, this.stakeholder.topics[this.topicIndex].categories.splice(this.index, 1); if(this.categoryIndex === this.index) { this.chooseCategory(Math.max(0, this.index - 1)); + } else if(this.categoryIndex > this.index) { + this.chooseCategory(this.categoryIndex - 1); } }, true); }; @@ -528,16 +512,12 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, } subCategoryChanged(callback: Function, save: boolean = false) { - if(this.subCategories && save) { - this.subCategories.disable(); - } if(callback) { callback(); } this.cdr.detectChanges(); if(this.subCategories && save) { this.subCategories.init(); - this.subCategories.enable(); } } @@ -640,6 +620,8 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.splice(this.index, 1); if(this.subCategoryIndex === this.index) { this.chooseSubcategory(Math.max(0, this.index - 1)); + } else if(this.subCategoryIndex > this.index) { + this.chooseSubcategory(this.subCategoryIndex - 1); } }, true); }; diff --git a/monitor-admin/topic/topic.module.ts b/monitor-admin/topic/topic.module.ts index 71e9b9d9..4c887228 100644 --- a/monitor-admin/topic/topic.module.ts +++ b/monitor-admin/topic/topic.module.ts @@ -20,11 +20,12 @@ import {TransitionGroupModule} from "../../utils/transition-group/transition-gro import {NumberRoundModule} from "../../utils/pipes/number-round.module"; import {SideBarModule} from "../../dashboard/sharedComponents/sidebar/sideBar.module"; import {SidebarMobileToggleModule} from "../../dashboard/sharedComponents/sidebar/sidebar-mobile-toggle/sidebar-mobile-toggle.module"; +import {SliderTabsModule} from "../../sharedComponents/tabs/slider-tabs.module"; @NgModule({ imports: [ CommonModule, TopicRoutingModule, ClickModule, RouterModule, FormsModule, AlertModalModule, - ReactiveFormsModule, InputModule, IconsModule, PageContentModule, LoadingModule, NotifyFormModule, LogoUrlPipeModule, TransitionGroupModule, NumberRoundModule, SideBarModule, SidebarMobileToggleModule + ReactiveFormsModule, InputModule, IconsModule, PageContentModule, LoadingModule, NotifyFormModule, LogoUrlPipeModule, TransitionGroupModule, NumberRoundModule, SideBarModule, SidebarMobileToggleModule, SliderTabsModule ], declarations: [ TopicComponent, IndicatorsComponent diff --git a/monitor-admin/utils/indicator-utils.ts b/monitor-admin/utils/indicator-utils.ts index 9360c228..832b4894 100644 --- a/monitor-admin/utils/indicator-utils.ts +++ b/monitor-admin/utils/indicator-utils.ts @@ -55,6 +55,8 @@ export class StakeholderConfiguration { {icon: 'incognito', value: "PRIVATE", label: 'Private'}, ]; public static CACHE_INDICATORS: boolean = true; + public static NUMBER_MULTI_INDICATOR_PATHS = false; + public static CHART_MULTI_INDICATOR_PATHS = true; } export class StakeholderUtils { @@ -82,6 +84,14 @@ export class StakeholderUtils { return StakeholderConfiguration.CACHE_INDICATORS; } + get hasMultiNumberIndicatorPaths() { + return StakeholderConfiguration.NUMBER_MULTI_INDICATOR_PATHS; + } + + get hasMultiChartIndicatorPaths() { + return StakeholderConfiguration.CHART_MULTI_INDICATOR_PATHS; + } + visibilityIcon: Map = new Map(this.visibilities.map(option => [option.value, option.icon])); defaultValue(options: Option[]) { diff --git a/monitor/entities/stakeholder.ts b/monitor/entities/stakeholder.ts index 49c8e755..a33506ac 100644 --- a/monitor/entities/stakeholder.ts +++ b/monitor/entities/stakeholder.ts @@ -183,6 +183,7 @@ export class Indicator { visibility: Visibility; defaultId: string; indicatorPaths: IndicatorPath[]; + activePath: number = 0; overlay: Overlay = false; constructor(name: string, description: string, additionalDescription:string, type: IndicatorType, width: IndicatorSize,height: IndicatorSize, visibility: Visibility, indicatorPaths: IndicatorPath[], defaultId: string = null) { diff --git a/monitor/monitor-indicator-stakeholder-base.component.ts b/monitor/monitor-indicator-stakeholder-base.component.ts index 4573dee4..8838d202 100644 --- a/monitor/monitor-indicator-stakeholder-base.component.ts +++ b/monitor/monitor-indicator-stakeholder-base.component.ts @@ -214,15 +214,17 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator (this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? " - " : "") + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "")) : ""); //clear numbers when filters change this.numberResults.clear(); - let urls: Map = new Map(); + let urls: Map = new Map(); this.activeSubCategory.numbers.forEach((section, i) => { section.indicators.forEach((number, j) => { if (this.hasPermission(number.visibility)) { - let url = this.getFullUrl(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); + number.indicatorPaths.forEach((indicatorPath, k) => { + let url = this.getFullUrl(indicatorPath); + const pair = JSON.stringify([indicatorPath.source, url]); + const indexes = urls.get(pair) ? urls.get(pair) : []; + indexes.push([i, j, k]); + urls.set(pair, indexes); + }) } }); }); @@ -231,10 +233,10 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator let activeSubcategory = this.activeSubCategory._id; this.subscriptions.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(pair[0]), pair[1]).subscribe(response => { if(activeSubcategory === this.activeSubCategory._id) { - indexes.forEach(([i, j]) => { + indexes.forEach(([i, j, k]) => { if( this.activeSubCategory?.numbers[i]?.indicators[j]) { let result = JSON.parse(JSON.stringify(response)); - this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => { + this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => { if (result) { result = result[jsonPath]; } @@ -247,7 +249,7 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator } else { result = 0; } - this.numberResults.set(i + '-' + j, result); + this.numberResults.set(i + '-' + j + '-' + k, result); } }); } @@ -255,10 +257,10 @@ export abstract class MonitorIndicatorStakeholderBaseComponent extends Indicator }); this.activeSubCategory.charts.forEach((section, i) => { section.indicators.forEach((indicator, j) => { - if (indicator.indicatorPaths.length > 0) { - indicator.indicatorPaths[0].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[0]); - this.chartsActiveType.set(i + '-' + j, indicator.indicatorPaths[0]); - } + indicator.indicatorPaths.forEach((indicatorPath, k) => { + indicator.indicatorPaths[k].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[k]); + this.chartsActiveType.set(i + '-' + j + '-' + k, indicator.indicatorPaths[k]); + }); }); }); if (this.cdr && !(this.cdr as ViewRef).destroyed) { diff --git a/utils/transition-group/transition-group.component.ts b/utils/transition-group/transition-group.component.ts index 53530868..8cea910d 100644 --- a/utils/transition-group/transition-group.component.ts +++ b/utils/transition-group/transition-group.component.ts @@ -1,6 +1,6 @@ import {TransitionGroupItemDirective} from "./transition-group-item.directive"; import { - AfterViewInit, + AfterViewInit, ChangeDetectorRef, Component, ContentChildren, ElementRef, Input, @@ -29,15 +29,14 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy { @ContentChildren(TransitionGroupItemDirective) items: QueryList; @Input() public id: string; - private disabled: boolean = false; + public size: number; private subscription: Subscription; constructor(public element: ElementRef) {} ngAfterViewInit() { - this.init(); this.subscription = this.items.changes.subscribe(items => { - if(!this.disabled) { + if(items.length === this.size) { items.forEach(item => item.prevPos = item.newPos || item.prevPos); items.forEach(this.runCallback); this.refreshPosition('newPos'); @@ -80,6 +79,7 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy { init() { this.refreshPosition('prevPos'); this.refreshPosition('newPos'); + this.size = this.items.length; } runCallback(item: TransitionGroupItemDirective) { @@ -126,17 +126,17 @@ export class TransitionGroupComponent implements AfterViewInit, OnDestroy { /** * Enable transition - * + * @deprecated * */ enable() { - this.disabled = false; + console.debug('Deprecated') } /** * Disable transition - * + * @deprecated * */ disable() { - this.disabled = true; + console.debug('Deprecated') } }