Project

General

Profile

1
import {
2
  AfterViewInit,
3
  ChangeDetectorRef,
4
  Component,
5
  Input,
6
  OnChanges,
7
  OnDestroy,
8
  OnInit,
9
  SimpleChanges,
10
  ViewChild
11
} from "@angular/core";
12
import {
13
  Indicator,
14
  IndicatorPath,
15
  IndicatorType,
16
  Section,
17
  Stakeholder,
18
  Visibility
19
} from "../openaireLibrary/monitor/entities/stakeholder";
20
import {IndicatorUtils, StakeholderUtils} from "../utils/indicator-utils";
21
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
22
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
23
import {StatisticsService} from "../utils/services/statistics.service";
24
import {HelperFunctions} from "../openaireLibrary/utils/HelperFunctions.class";
25
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
26
import {StakeholderService} from "../openaireLibrary/monitor/services/stakeholder.service";
27
import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties";
28
import {Subscriber} from "rxjs";
29
import {LayoutService} from "../openaireLibrary/dashboard/sharedComponents/sidebar/layout.service";
30
import {Router} from "@angular/router";
31
import {Session} from "../openaireLibrary/login/utils/helper.class";
32
import {UserManagementService} from "../openaireLibrary/services/user-management.service";
33

    
34
declare var UIkit;
35

    
36
@Component({
37
  selector: 'indicators',
38
  templateUrl: './indicators.component.html',
39
  styleUrls: ['indicators.component.css']
40
})
41
export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
42
  
43
  @Input()
44
  public properties: EnvProperties = null;
45
  @Input()
46
  public topicIndex: number = 0;
47
  @Input()
48
  public categoryIndex: number = 0;
49
  @Input()
50
  public subcategoryIndex: number = 0;
51
  public stakeholder: Stakeholder = null;
52
  public user = null;
53
  public preview: string;
54
  public indicatorUtils: IndicatorUtils = new IndicatorUtils();
55
  public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
56
  public numberIndicatorFb: FormGroup;
57
  public chartIndicatorFb: FormGroup;
58
  public chartSections: FormArray;
59
  public numberSections: FormArray;
60
  /**
61
   * Editable indicator
62
   */
63
  public section: Section;
64
  public indicator: Indicator;
65
  public index: number = -1;
66
  /**
67
   * Displayed chart and numbers base on Top filters
68
   */
69
  public displayCharts: Section[] = [];
70
  public displayNumbers: Section[] = [];
71
  /**
72
   * Top filters
73
   */
74
  @Input()
75
  public filters: FormGroup;
76
  /**
77
   * Toggles
78
   */
79
    // public grid: boolean = true;
80
  public editing: boolean = false;
81
  /** Safe Urls*/
82
  public safeUrls: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>([]);
83
  public numberResults: Map<string, number> = new Map<string, number>();
84
  private subscriptions: any[] = [];
85
  private urlSubscriptions: any[] = [];
86
  @ViewChild('editChartModal') editChartModal: AlertModal;
87
  @ViewChild('editNumberModal') editNumberModal: AlertModal;
88
  @ViewChild('deleteModal') deleteModal: AlertModal;
89
  //@ViewChild('deleteAllModal') deleteAllModal: AlertModal;
90
  //@ViewChild('deleteAndDisconnectModal') deleteAndDisconnectModal: AlertModal;
91
  //@ViewChild('deleteChartSectionModal') deleteChartSectionModal: AlertModal;
92
  //@ViewChild('deleteNumberSectionModal') deleteNumberSectionModal: AlertModal;
93
  @ViewChild('deleteSectionModal') deleteSectionModal: AlertModal;
94
  public sectionTypeToDelete: string;
95
  public sectionChildrenActionOnDelete: string;
96
  public indicatorChildrenActionOnDelete: string;
97
  
98
  urlParameterizedMessage = "";
99
  
100
  constructor(private layoutService: LayoutService,
101
              private stakeholderService: StakeholderService,
102
              private statisticsService: StatisticsService,
103
              private userManagementService: UserManagementService,
104
              private fb: FormBuilder,
105
              private router: Router,
106
              private cdr: ChangeDetectorRef,
107
              private sanitizer: DomSanitizer) {
108
  }
109
  
110
  ngOnInit(): void {
111
    this.userManagementService.getUserInfo().subscribe(user => {
112
      this.user = user;
113
    });
114
    this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => {
115
      this.stakeholder = stakeholder;
116
      if (this.stakeholder) {
117
        this.buildFilters();
118
        this.buildSections();
119
        this.filterCharts();
120
        this.filterNumbers();
121
        this.setPreview();
122
      }
123
    });
124
  }
125
  
126
  ngOnDestroy(): void {
127
    this.subscriptions.forEach(value => {
128
      if (value instanceof Subscriber) {
129
        value.unsubscribe();
130
      } else if (value instanceof Function) {
131
        value();
132
      }
133
    });
134
  }
135
  
136
  ngAfterViewInit(): void {
137
    this.initReorder();
138
  }
139
  
140
  ngOnChanges(changes: SimpleChanges): void {
141
    if (this.canEdit) {
142
      if (changes.topicIndex || changes.categoryIndex || changes.subcategoryIndex) {
143
        this.buildFilters();
144
        this.initReorder();
145
      }
146
      this.filterCharts();
147
      this.filterNumbers();
148
    }
149
    this.setPreview();
150
  }
151
  
152
  setNumberIndicators() {
153
    this.numberResults.clear();
154
    let urls: Map<string, [number, number][]> = new Map<string, [number, number][]>();
155
    this.numbers.forEach((section, i) => {
156
      section.indicators.forEach((number, j) => {
157
        let url = this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, number.indicatorPaths[0]);
158
        const pair = JSON.stringify([number.indicatorPaths[0].source, url]);
159
        const indexes = urls.get(pair) ? urls.get(pair) : [];
160
        indexes.push([i, j]);
161
        urls.set(pair, indexes);
162
      });
163
    });
164
    urls.forEach((indexes, pair) => {
165
      pair = JSON.parse(pair);
166
      this.statisticsService.getNumbers(this.statisticsService.getSourceType(pair[0]), pair[1]).subscribe(response => {
167
        indexes.forEach(([i, j]) => {
168
          let result = JSON.parse(JSON.stringify(response));
169
          this.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => {
170
            if (result) {
171
              result = result[jsonPath];
172
            }
173
          });
174
          if (typeof result == 'string') {
175
            this.numberResults.set(i + '-' + j, Number(result));
176
          }
177
        });
178
      });
179
    });
180
  }
181
  
182
  setPreview() {
183
    if (this.stakeholder) {
184
      this.preview = '/' + this.stakeholder.alias;
185
      if (this.stakeholder.topics[this.topicIndex]) {
186
        this.preview = '/' + this.stakeholder.alias + '/' + this.stakeholder.topics[this.topicIndex].alias;
187
        if (this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]) {
188
          this.preview += '/' + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].alias;
189
          if (this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
190
            this.preview += '/' + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].alias;
191
          }
192
        }
193
      }
194
    }
195
  }
196
  
197
  initReorder() {
198
    this.subscriptions.forEach(value => {
199
      if (value instanceof Function) {
200
        value();
201
      }
202
    });
203
    if (document !== undefined) {
204
      let callback = (list, type: IndicatorType): void => {
205
        let items: HTMLCollection = list.current.children;
206
        let reordered = [];
207
        for (let i = 0; i < items.length; i++) {
208
          if (items.item(i).id) {
209
            reordered.push(items.item(i).id);
210
          }
211
        }
212
        this.reorderIndicators(list.current.id.toString().split('-')[1], type, reordered);
213
      };
214
      this.numbers.forEach((section) => {
215
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#number-' + section._id, (list): void => {
216
          callback(list, "number");
217
        }));
218
        this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => {
219
          callback(list, "number");
220
        }));
221
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => {
222
          callback(list, "number");
223
        }));
224
      });
225
      this.charts.forEach((section) => {
226
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => {
227
          callback(list, "chart");
228
        }));
229
        this.subscriptions.push(UIkit.util.on(document, 'added', '#chart-' + section._id, (list): void => {
230
          callback(list, "chart");
231
        }));
232
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#chart-' + section._id, (list): void => {
233
          callback(list, "chart");
234
        }));
235
      });
236
    }
237
  }
238
  
239
  hide(element: any) {
240
    UIkit.dropdown(element).hide();
241
  }
242
  
243
  private buildFilters() {
244
    this.subscriptions.push(this.filters.get('chartType').valueChanges.subscribe(value => {
245
      this.onChartTypeChange(value);
246
    }));
247
    
248
    this.subscriptions.push(this.filters.get('status').valueChanges.subscribe(value => {
249
      this.onStatusChange(value);
250
    }));
251
    this.subscriptions.push(this.filters.get('keyword').valueChanges.subscribe(value => {
252
      this.onKeywordChange(value);
253
    }));
254
  }
255
  
256
  private buildSections() {
257
    this.numberSections = this.fb.array([]);
258
    this.numbers.forEach(section => {
259
      this.numberSections.push(this.fb.group({
260
        _id: this.fb.control(section._id),
261
        title: this.fb.control(section.title),
262
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
263
        defaultId: this.fb.control(section.defaultId),
264
        type: this.fb.control(section.type),
265
        indicators: this.fb.control(section.indicators)
266
      }));
267
    });
268
    this.chartSections = this.fb.array([]);
269
    this.charts.forEach(section => {
270
      this.chartSections.push(this.fb.group({
271
        _id: this.fb.control(section._id),
272
        title: this.fb.control(section.title),
273
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
274
        defaultId: this.fb.control(section.defaultId),
275
        type: this.fb.control(section.type),
276
        indicators: this.fb.control(section.indicators)
277
      }));
278
    });
279
  }
280
  
281
  filterCharts() {
282
    this.displayCharts = this.filterChartType(
283
      this.filterStatus(this.filterByKeyword(HelperFunctions.copy(this.charts), this.filters.value.keyword),
284
        this.filters.value.status),
285
      this.filters.value.chartType
286
    );
287
    this.displayCharts.forEach(section => {
288
      section.indicators.forEach(indicator => {
289
        indicator.indicatorPaths.forEach(indicatorPath => {
290
          let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
291
          if (!this.safeUrls.get('url')) {
292
            indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
293
            this.safeUrls.set(url, indicatorPath.safeResourceUrl);
294
          }
295
        });
296
      })
297
    });
298
    this.buildSections();
299
  }
300
  
301
  filterNumbers() {
302
    this.displayNumbers = this.filterStatus(
303
      this.filterByKeyword(HelperFunctions.copy(this.numbers), this.filters.value.keyword),
304
      this.filters.value.status);
305
    this.buildSections();
306
    this.setNumberIndicators();
307
  }
308
  
309
  onChartTypeChange(value) {
310
    this.displayCharts = this.filterChartType(HelperFunctions.copy(this.charts), value);
311
  }
312
  
313
  
314
  onStatusChange(value) {
315
    this.displayCharts = this.filterStatus(HelperFunctions.copy(this.charts), value);
316
    this.displayNumbers = this.filterStatus(HelperFunctions.copy(this.numbers), value);
317
  }
318
  
319
  onKeywordChange(value) {
320
    this.displayCharts = this.filterByKeyword(HelperFunctions.copy(this.charts), value);
321
    this.displayNumbers = this.filterByKeyword(HelperFunctions.copy(this.numbers), value);
322
  }
323
  
324
  private filterChartType(sections: Section[], value): Section[] {
325
    if (value !== 'all') {
326
      sections.forEach(section =>
327
        section.indicators = section.indicators.filter(indicator =>
328
          indicator.indicatorPaths.filter(indicatorPath => indicatorPath.type === value).length > 0));
329
    }
330
    return sections;
331
  }
332
  
333
  
334
  private filterStatus(sections: Section[], value): Section[] {
335
    if (value !== 'all') {
336
      sections.forEach(section =>
337
        section.indicators = section.indicators.filter(indicator => indicator.visibility === value));
338
    }
339
    return sections;
340
  }
341
  
342
  private filterByKeyword(sections: Section[], value): Section[] {
343
    if (value !== null && value !== '') {
344
      sections.forEach(section =>
345
        section.indicators = section.indicators.filter(indicator => (indicator.name && indicator.name.toLowerCase().includes(value.toLowerCase()))
346
          || (indicator.description && indicator.description.toLowerCase().includes(value.toLowerCase()))
347
          || (indicator.additionalDescription && indicator.additionalDescription.toLowerCase().includes(value.toLowerCase()))
348
          || indicator.indicatorPaths.filter(indicatorPath => (indicatorPath.parameters && indicatorPath.parameters.title &&
349
            indicatorPath.parameters.title.includes(value.toLowerCase()))).length > 0));
350
    }
351
    return sections;
352
  }
353
  
354
  get charts(): Section[] {
355
    if (this.stakeholder.topics[this.topicIndex] &&
356
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
357
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
358
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
359
    } else {
360
      return [];
361
    }
362
  }
363
  
364
  set charts(sections: Section[]) {
365
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts = sections;
366
  }
367
  
368
  get numbers(): Section[] {
369
    if (this.stakeholder.topics[this.topicIndex] &&
370
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
371
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
372
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
373
    } else {
374
      return [];
375
    }
376
  }
377
  
378
  set numbers(sections: Section[]) {
379
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = sections;
380
  }
381
  
382
  get open(): boolean {
383
    return this.layoutService.open;
384
  }
385
  
386
  get canReorder(): boolean {
387
    return this.filters.value.chartType === 'all' &&
388
      this.filters.value.status === 'all' && this.filters.value.keyword === '' && !this.editing;
389
  }
390
  
391
  get canEdit() {
392
    return this.stakeholder &&
393
      this.stakeholder.topics[this.topicIndex] &&
394
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
395
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex];
396
  }
397
  
398
  public get numberIndicatorPaths(): FormArray {
399
    return this.numberIndicatorFb.get('indicatorPaths') as FormArray;
400
  }
401
  
402
  public get chartIndicatorPaths(): FormArray {
403
    return this.chartIndicatorFb.get('indicatorPaths') as FormArray;
404
  }
405
  
406
  public addJsonPath(index: number) {
407
    if (index == 0 && this.getJsonPath(index).getRawValue()[index].indexOf(",") != -1) {
408
      //if in the first path there are more than one paaths comma separated, split them and autogenerate the forms
409
      let paths = this.getJsonPath(index).getRawValue()[index].split(",");
410
      for (let i = 0; i < paths.length; i++) {
411
        if (i != 0) {
412
          this.getJsonPath(index).push(this.fb.control('', Validators.required));
413
        }
414
      }
415
      this.getJsonPath(index).setValue(paths)
416
      return;
417
    }
418
    this.getJsonPath(index).push(this.fb.control('', Validators.required));
419
  }
420
  
421
  public removeJsonPath(i: number, j: number) {
422
    this.getJsonPath(i).removeAt(j);
423
    if (this.getJsonPath(i).length !== this.getCurrentJsonPath(i).length) {
424
      this.getJsonPath(i).markAsDirty();
425
    }
426
  }
427
  
428
  public validateJsonPath(index: number) {
429
    let indicatorPath: FormGroup = <FormGroup>this.numberIndicatorPaths.at(index);
430
    this.getJsonPath(index).disable();
431
    this.statisticsService.getNumbers(null, indicatorPath.get('url').value).subscribe(response => {
432
      let result = JSON.parse(JSON.stringify(response));
433
      this.getJsonPath(index).controls.forEach(jsonPath => {
434
        if (result) {
435
          result = result[jsonPath.value];
436
        }
437
      });
438
      setTimeout( () => {
439
        if (typeof result == 'string') {
440
          this.getJsonPath(index).enable();
441
          this.getJsonPath(index).setErrors(null);
442
        } else {
443
          this.getJsonPath(index).enable();
444
          this.getJsonPath(index).setErrors({invalid: true});
445
        }
446
      }, 1000);
447
    });
448
  }
449
  
450
  public getJsonPath(index: number): FormArray {
451
    return this.numberIndicatorPaths.at(index).get('jsonPath') as FormArray;
452
  }
453
  
454
  public getCurrentJsonPath(index: number): string[] {
455
    return this.section.indicators[this.index].indicatorPaths[index].jsonPath;
456
  }
457
  
458
  public getParameters(index: number): FormArray {
459
    return this.chartIndicatorPaths.at(index).get('parameters') as FormArray;
460
  }
461
  
462
  public getParameter(index: number, key: string): FormControl {
463
    return this.getParameters(index).controls.filter(control => control.value.key === key)[0] as FormControl;
464
  }
465
  
466
  private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) {
467
    return this.sanitizer.bypassSecurityTrustResourceUrl(
468
      this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
469
  }
470
  
471
  private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
472
    return this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath));
473
  }
474
  
475
  public addNumberIndicatorPath(url: string = '', parameters: FormArray = new FormArray([]), source: string = 'search', jsonPath: FormArray = new FormArray([])) {
476
    if (jsonPath.length === 0) {
477
      jsonPath.push(this.fb.control('', Validators.required));
478
    }
479
    this.numberIndicatorPaths.push(this.fb.group({
480
        url: this.fb.control(url, Validators.required),
481
        jsonPath: jsonPath,
482
        // parameters: parameters,
483
        source: this.fb.control(source, Validators.required)
484
      }
485
    ));
486
    
487
    for (let index = 0; index < this.numberIndicatorPaths.length; index++) {
488
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
489
          if (this.numberIndicatorPaths.at(index).get('url').valid) {
490
            let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.statisticsService.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value);
491
            if ((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)
492
              || (!indicatorPath.chartObject && indicatorPath.url.indexOf("index_id") == -1 && indicatorPath.url.indexOf("index_name") == -1 && (indicatorPath.url).indexOf("index_shortName") == -1)) {
493
              // default profile
494
              if (this.stakeholder.defaultId == null) {
495
                this.urlParameterizedMessage = "This indicator couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
496
              } else {
497
                this.urlParameterizedMessage = "This indicator couldn't be generated properly. Please make sure chart data is for the current stakeholder."
498
              }
499
            } else {
500
              this.urlParameterizedMessage = "";
501
            }
502
            /*  if(value != indicatorPath.url) {
503
                (this.numberIndicatorPaths.at(index) as FormGroup).get('url').setValue( indicatorPath.url);
504
              }*/
505
            this.indicator.indicatorPaths[index] = indicatorPath;
506
            (this.numberIndicatorPaths.at(index) as FormGroup).get('source').setValue(indicatorPath.source);
507
          }
508
        })
509
      );
510
      
511
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => {
512
          this.indicator.indicatorPaths[index].jsonPath = value;
513
          //this.getJsonPath(index).setErrors({invalid: true});
514
        })
515
      );
516
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('source').valueChanges.subscribe(value => {
517
          this.indicator.indicatorPaths[index].source = value;
518
        })
519
      );
520
    }
521
  }
522
  
523
  public addChartIndicatorPath(value: string = '', parameters: FormArray = new FormArray([]), disableUrl: boolean = false, type: string = null) {
524
    this.chartIndicatorPaths.push(this.fb.group({
525
        url: this.fb.control(value, [Validators.required,
526
          Validators.pattern('https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.' +
527
            '[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.' +
528
            '[a-zA-Z0-9]+\.[^\s]{2,}')]),
529
        parameters: parameters,
530
        type: this.fb.control(type)
531
      }
532
    ));
533
    if (disableUrl) {
534
      for (let index = 0; index < this.chartIndicatorPaths.length; index++) {
535
        this.chartIndicatorPaths.at(index).get('url').disable();
536
      }
537
    } else {
538
      for (let index = 0; index < this.chartIndicatorPaths.length; index++) {
539
        this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
540
            if (this.chartIndicatorPaths.at(index).get('url').valid) {
541
              let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
542
              if (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) {
543
                // default profile
544
                if (this.stakeholder.defaultId == null) {
545
                  this.urlParameterizedMessage = "This chart couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
546
                } else {
547
                  this.urlParameterizedMessage = "This chart couldn't be generated properly. Please make sure chart data is for the current stakeholder."
548
                }
549
              } else {
550
                this.urlParameterizedMessage = "";
551
              }
552
              (this.chartIndicatorPaths.at(index) as FormGroup).get('type').setValue(indicatorPath.type);
553
              let parameters = this.getParametersAsFormArray(indicatorPath);
554
              (this.chartIndicatorPaths.at(index) as FormGroup).setControl('parameters', parameters);
555
              if (!this.indicator.indicatorPaths[index]) {
556
                this.indicator.indicatorPaths[index] = indicatorPath;
557
                this.indicator.indicatorPaths[index].safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
558
              } else {
559
                indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl;
560
                this.indicator.indicatorPaths[index] = indicatorPath;
561
              }
562
            }
563
          })
564
        );
565
      }
566
    }
567
  }
568
  
569
  private getJsonPathAsFormArray(indicatorPath: IndicatorPath): FormArray {
570
    let jsonPath = this.fb.array([]);
571
    if (indicatorPath.jsonPath) {
572
      indicatorPath.jsonPath.forEach(path => {
573
        jsonPath.push(this.fb.control(path, Validators.required));
574
      });
575
    }
576
    return jsonPath;
577
  }
578
  
579
  private getParametersAsFormArray(indicatorPath: IndicatorPath): FormArray {
580
    let parameters = this.fb.array([]);
581
    if (indicatorPath.parameters) {
582
      Object.keys(indicatorPath.parameters).forEach(key => {
583
        if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) {
584
          if (this.indicatorUtils.parametersValidators.has(key)) {
585
            parameters.push(this.fb.group({
586
              key: this.fb.control(key),
587
              value: this.fb.control(indicatorPath.parameters[key], this.indicatorUtils.parametersValidators.get(key))
588
            }));
589
          } else {
590
            parameters.push(this.fb.group({
591
              key: this.fb.control(key),
592
              value: this.fb.control(indicatorPath.parameters[key])
593
            }));
594
          }
595
        }
596
      });
597
    }
598
    return parameters;
599
  }
600
  
601
  public editNumberIndicatorOpen(section: Section, id = null) {
602
    this.urlParameterizedMessage = "";
603
    this.section = section;
604
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
605
    if (this.index !== -1) {
606
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
607
      this.numberIndicatorFb = this.fb.group({
608
        _id: this.fb.control(this.indicator._id),
609
        name: this.fb.control(this.indicator.name, Validators.required),
610
        description: this.fb.control(this.indicator.description),
611
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
612
        visibility: this.fb.control(this.indicator.visibility),
613
        indicatorPaths: this.fb.array([], Validators.required),
614
        type: this.fb.control(this.indicator.type),
615
        width: this.fb.control(this.indicator.width),
616
        defaultId: this.fb.control(this.indicator.defaultId)
617
      });
618
      this.indicator.indicatorPaths.forEach(indicatorPath => {
619
        this.addNumberIndicatorPath(this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), indicatorPath.parameters, indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath));
620
      });
621
    } else {
622
      this.indicator = new Indicator('', '', '', 'number', 'small', "PUBLIC", []);
623
      this.numberIndicatorFb = this.fb.group({
624
        _id: this.fb.control(this.indicator._id),
625
        name: this.fb.control(this.indicator.name, Validators.required),
626
        description: this.fb.control(this.indicator.description),
627
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
628
        visibility: this.fb.control(this.indicator.visibility),
629
        indicatorPaths: this.fb.array([], Validators.required),
630
        type: this.fb.control(this.indicator.type),
631
        width: this.fb.control(this.indicator.width),
632
        defaultId: this.fb.control(this.indicator.defaultId)
633
      });
634
      this.addNumberIndicatorPath();
635
    }
636
    if(this.indicator.defaultId) {
637
      setTimeout(() => {
638
        this.numberIndicatorFb.get('additionalDescription').disable();
639
      }, 0);
640
    }
641
    this.editNumberModal.cancelButtonText = 'Cancel';
642
    this.editNumberModal.okButtonLeft = false;
643
    this.editNumberModal.alertMessage = false;
644
    if (this.index === -1) {
645
      this.editNumberModal.alertTitle = 'Create a new number indicator';
646
      this.editNumberModal.okButtonText = 'Save';
647
    } else {
648
      this.editNumberModal.alertTitle = 'Edit number indicator\'s information';
649
      this.editNumberModal.okButtonText = 'Save Changes';
650
    }
651
    this.editNumberModal.open();
652
  }
653
  
654
  public editChartIndicatorOpen(section: Section, id = null) {
655
    this.urlParameterizedMessage = "";
656
    this.urlSubscriptions.forEach(value => {
657
      if (value instanceof Subscriber) {
658
        value.unsubscribe();
659
      }
660
    });
661
    this.section = section;
662
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
663
    if (this.index !== -1) {
664
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
665
      this.chartIndicatorFb = this.fb.group({
666
        _id: this.fb.control(this.indicator._id),
667
        name: this.fb.control(this.indicator.name),
668
        description: this.fb.control(this.indicator.description),
669
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
670
        visibility: this.fb.control(this.indicator.visibility),
671
        indicatorPaths: this.fb.array([]),
672
        width: this.fb.control(this.indicator.width),
673
        defaultId: this.fb.control(this.indicator.defaultId)
674
      });
675
      this.indicator.indicatorPaths.forEach(indicatorPath => {
676
        this.addChartIndicatorPath(this.getUrlByStakeHolder(indicatorPath),
677
          this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type);
678
        indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
679
      });
680
    } else {
681
      this.indicator = new Indicator('', '', '', 'chart', 'medium', "PUBLIC", []);
682
      this.chartIndicatorFb = this.fb.group({
683
        _id: this.fb.control(this.indicator._id),
684
        name: this.fb.control(this.indicator.name),
685
        description: this.fb.control(this.indicator.description),
686
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
687
        visibility: this.fb.control(this.indicator.visibility),
688
        indicatorPaths: this.fb.array([]),
689
        width: this.fb.control(this.indicator.width, Validators.required),
690
        defaultId: this.fb.control(this.indicator.defaultId)
691
      });
692
      this.addChartIndicatorPath();
693
    }
694
    if(this.indicator.defaultId) {
695
      setTimeout(() => {
696
        this.chartIndicatorFb.get('additionalDescription').disable();
697
      }, 0);
698
    }
699
    this.editChartModal.cancelButtonText = 'Cancel';
700
    this.editChartModal.okButtonLeft = false;
701
    this.editChartModal.alertMessage = false;
702
    if (this.index === -1) {
703
      this.editChartModal.alertTitle = 'Create a new chart indicator';
704
      this.editChartModal.okButtonText = 'Save';
705
    } else {
706
      this.editChartModal.alertTitle = 'Edit chart indicator\'s information';
707
      this.editChartModal.okButtonText = 'Save Changes';
708
    }
709
    this.editChartModal.open();
710
  }
711
  
712
  saveIndicator() {
713
    this.editing = true;
714
    if (this.indicator.type === 'chart') {
715
      this.chartIndicatorFb.get('additionalDescription').enable();
716
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type, true);
717
      this.section = this.charts.find(section => section._id === this.section._id);
718
    } else {
719
      this.numberIndicatorFb.get('additionalDescription').enable();
720
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type, false);
721
      this.section = this.numbers.find(section => section._id === this.section._id);
722
    }
723
    let path = [
724
      this.stakeholder._id,
725
      this.stakeholder.topics[this.topicIndex]._id,
726
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
727
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
728
      this.section._id
729
    ];
730
    this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.indicator, path).subscribe(indicator => {
731
      if (this.index !== -1) {
732
        this.section.indicators[this.index] = indicator;
733
      } else {
734
        this.section.indicators.push(indicator);
735
      }
736
      if (this.indicator.type === "chart") {
737
        this.filterCharts();
738
        this.chartIndicatorFb = null;
739
      } else {
740
        this.filterNumbers();
741
        this.numberIndicatorFb = null;
742
      }
743
      UIkit.notification('Indicator has been successfully saved', {
744
        status: 'success',
745
        timeout: 3000,
746
        pos: 'top-left'
747
      });
748
      this.editing = false;
749
    }, error => {
750
      this.chartIndicatorFb = null;
751
      UIkit.notification(error.error.message, {
752
        status: 'danger',
753
        timeout: 3000,
754
        pos: 'top-left'
755
      });
756
      this.editing = false;
757
    });
758
  }
759
  
760
  reorderIndicators(sectionId: string, type: IndicatorType, indicatorIds: string[]) {
761
    this.editing = true;
762
    let path = [
763
      this.stakeholder._id,
764
      this.stakeholder.topics[this.topicIndex]._id,
765
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
766
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
767
      sectionId
768
    ];
769
    this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, indicatorIds, type).subscribe(indicators => {
770
      if (type === 'chart') {
771
        this.charts.find(section => section._id === sectionId).indicators = indicators;
772
        this.filterCharts();
773
      } else {
774
        this.numbers.find(section => section._id === sectionId).indicators = indicators;
775
        this.filterNumbers();
776
      }
777
      this.editing = false;
778
    });
779
  }
780
  
781
  hasDifference(index: number): boolean {
782
    let hasDifference = false;
783
    this.chartIndicatorPaths.at(index).value.parameters.forEach((parameter) => {
784
      if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
785
        hasDifference = true;
786
        return;
787
      }
788
    });
789
    return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !==
790
      this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString();
791
  }
792
  
793
  public get isAdministrator(): boolean {
794
    return Session.isPortalAdministrator(this.user) || Session.isMonitorCurator(this.user);
795
  }
796
  
797
  refreshIndicator() {
798
    this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart', true);
799
    this.indicator.indicatorPaths.forEach(indicatorPath => {
800
      indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
801
    });
802
  }
803
  
804
  deleteIndicatorOpen(section: Section, indicatorId: string, type: string, childrenAction: string = null) {
805
    this.indicatorChildrenActionOnDelete = null;
806
    if (childrenAction == "delete") {
807
      this.indicatorChildrenActionOnDelete = childrenAction;
808
    } else if (childrenAction == "disconnect") {
809
      this.indicatorChildrenActionOnDelete = childrenAction;
810
    }
811
    
812
    this.section = section;
813
    if (type === 'chart') {
814
      this.index = this.charts.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
815
    } else {
816
      this.index = this.numbers.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
817
    }
818
    this.indicator = section.indicators.find(value => value._id == indicatorId);
819
    this.deleteModal.alertTitle = 'Delete indicator';
820
    this.deleteModal.cancelButtonText = 'No';
821
    this.deleteModal.okButtonText = 'Yes';
822
    this.deleteModal.open();
823
  }
824
  
825
  deleteIndicator() {
826
    this.editing = true;
827
    let path = [
828
      this.stakeholder._id,
829
      this.stakeholder.topics[this.topicIndex]._id,
830
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
831
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
832
      this.section._id,
833
      this.indicator._id
834
    ];
835
    this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.indicatorChildrenActionOnDelete).subscribe(() => {
836
      if (this.indicator.type === 'chart') {
837
        this.charts.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
838
        this.filterCharts();
839
      } else {
840
        this.numbers.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
841
        this.filterNumbers();
842
      }
843
      UIkit.notification('Indicator has been successfully deleted', {
844
        status: 'success',
845
        timeout: 3000,
846
        pos: 'top-left'
847
      });
848
      this.editing = false;
849
    }, error => {
850
      UIkit.notification(error.error.message, {
851
        status: 'danger',
852
        timeout: 3000,
853
        pos: 'top-left'
854
      });
855
      this.editing = false;
856
    });
857
  }
858
  
859
  changeIndicatorStatus(sectionId: string, indicator: Indicator, visibility: Visibility) {
860
    this.editing = true;
861
    let path = [
862
      this.stakeholder._id,
863
      this.stakeholder.topics[this.topicIndex]._id,
864
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
865
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
866
      sectionId,
867
      indicator._id
868
    ];
869
    this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(visibility => {
870
      indicator.visibility = visibility;
871
      UIkit.notification('Indicator has been successfully changed to ' + indicator.visibility.toLowerCase(), {
872
        status: 'success',
873
        timeout: 3000,
874
        pos: 'top-left'
875
      });
876
      this.editing = false;
877
    }, error => {
878
      UIkit.notification('An error has been occurred. Try again later', {
879
        status: 'danger',
880
        timeout: 3000,
881
        pos: 'top-left'
882
      });
883
      this.editing = false;
884
    });
885
  }
886
  
887
  /*toggleIndicatorAccess(sectionId: string, indicator: Indicator) {
888
    this.editing = true;
889
    let path = [
890
      this.stakeholder._id,
891
      this.stakeholder.topics[this.topicIndex]._id,
892
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
893
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
894
      sectionId,
895
      indicator._id
896
    ];
897
    this.stakeholderService.toggleAccess(this.properties.monitorServiceAPIURL, path).subscribe(visibility => {
898
      indicator.visibility = visibility;
899
      UIkit.notification('Indicator has been successfully changed to ' + (visibility?'public':'private'), {
900
        status: 'success',
901
        timeout: 3000,
902
        pos: 'top-left'
903
      });
904
      this.editing = false;
905
    }, error => {
906
      UIkit.notification(error.error.message, {
907
        status: 'danger',
908
        timeout: 3000,
909
        pos: 'top-left'
910
      });
911
      this.editing = false;
912
    });
913
  }*/
914
  
915
  saveSection(section: Section, index: number, type: IndicatorType = "chart") {
916
    this.editing = true;
917
    let path = [
918
      this.stakeholder._id,
919
      this.stakeholder.topics[this.topicIndex]._id,
920
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
921
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
922
    ];
923
    this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, section, path, index).subscribe(section => {
924
      if (type === 'chart') {
925
        this.charts[index] = section;
926
        this.filterCharts();
927
      } else {
928
        this.numbers[index] = section;
929
        this.filterNumbers();
930
      }
931
      this.initReorder();
932
      UIkit.notification('Section has been successfully saved', {
933
        status: 'success',
934
        timeout: 3000,
935
        pos: 'top-left'
936
      });
937
      this.editing = false;
938
    }, error => {
939
      UIkit.notification(error.error.message, {
940
        status: 'danger',
941
        timeout: 3000,
942
        pos: 'top-left'
943
      });
944
      this.editing = false;
945
    });
946
  }
947
  
948
  createSection(index = -1, type: IndicatorType = 'chart') {
949
    this.editing = true;
950
    this.section = new Section(type, null, null, this.stakeholder.alias);
951
    let path = [
952
      this.stakeholder._id,
953
      this.stakeholder.topics[this.topicIndex]._id,
954
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
955
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
956
    ];
957
    this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, this.section, path, index).subscribe(section => {
958
      if (type === 'chart') {
959
        if (index !== -1) {
960
          this.charts.splice(index, 0, section);
961
        } else {
962
          this.charts.push(section);
963
        }
964
        this.filterCharts();
965
      } else {
966
        if (index !== -1) {
967
          this.numbers.splice(index, 0, section);
968
        } else {
969
          this.numbers.push(section);
970
        }
971
        this.filterNumbers();
972
      }
973
      this.initReorder();
974
      UIkit.notification('Section has been successfully created', {
975
        status: 'success',
976
        timeout: 3000,
977
        pos: 'top-left'
978
      });
979
      this.editing = false;
980
    }, error => {
981
      UIkit.notification(error.error.message, {
982
        status: 'danger',
983
        timeout: 3000,
984
        pos: 'top-left'
985
      });
986
      this.editing = false;
987
    });
988
  }
989
  
990
  // deleteNumberSectionOpen(section: Section, index: number) {
991
  //   this.section = section;
992
  //   this.index = index;
993
  //   this.deleteNumberSectionModal.alertTitle = 'Delete Section';
994
  //   this.deleteNumberSectionModal.cancelButtonText = 'No';
995
  //   this.deleteNumberSectionModal.okButtonText = 'Yes';
996
  //   this.deleteNumberSectionModal.open();
997
  // }
998
  //
999
  // deleteChartSectionOpen(section: Section, index: number) {
1000
  //   this.section = section;
1001
  //   this.index = index;
1002
  //   this.deleteChartSectionModal.alertTitle = 'Delete Section';
1003
  //   this.deleteChartSectionModal.cancelButtonText = 'No';
1004
  //   this.deleteChartSectionModal.okButtonText = 'Yes';
1005
  //   this.deleteChartSectionModal.open();
1006
  // }
1007
  
1008
  deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) {
1009
    if (!this.editing && !section.defaultId) {
1010
      this.sectionTypeToDelete = type;
1011
      this.sectionChildrenActionOnDelete = null;
1012
      if (childrenAction == "delete") {
1013
        this.sectionChildrenActionOnDelete = childrenAction;
1014
      } else if (childrenAction == "disconnect") {
1015
        this.sectionChildrenActionOnDelete = childrenAction;
1016
      }
1017
      
1018
      this.section = section;
1019
      this.index = index;
1020
      this.deleteSectionModal.alertTitle = 'Delete Section';
1021
      this.deleteSectionModal.cancelButtonText = 'No';
1022
      this.deleteSectionModal.okButtonText = 'Yes';
1023
      this.deleteSectionModal.open();
1024
    }
1025
  }
1026
  
1027
  deleteSection() {
1028
    this.editing = true;
1029
    let path = [
1030
      this.stakeholder._id,
1031
      this.stakeholder.topics[this.topicIndex]._id,
1032
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1033
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
1034
      this.section._id
1035
    ];
1036
    this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.sectionChildrenActionOnDelete).subscribe(() => {
1037
      if (this.sectionTypeToDelete === "chart") {
1038
        this.charts.splice(this.index, 1);
1039
        this.filterCharts();
1040
      } else {
1041
        this.numbers.splice(this.index, 1);
1042
        this.filterNumbers();
1043
      }
1044
      this.initReorder();
1045
      UIkit.notification('Section has been successfully deleted', {
1046
        status: 'success',
1047
        timeout: 3000,
1048
        pos: 'top-left'
1049
      });
1050
      this.editing = false;
1051
    }, error => {
1052
      UIkit.notification(error.error.message, {
1053
        status: 'danger',
1054
        timeout: 3000,
1055
        pos: 'top-left'
1056
      });
1057
      this.editing = false;
1058
    });
1059
  }
1060
}
(3-3/7)