Project

General

Profile

1
import {AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild} from "@angular/core";
2
import {Indicator, IndicatorPath, Stakeholder} from "../utils/entities/stakeholder";
3
import {IndicatorUtils, Option} from "../utils/indicator-utils";
4
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
5
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
6
import {StatisticsService} from "../utils/services/statistics.service";
7
import {HelperFunctions} from "../openaireLibrary/utils/HelperFunctions.class";
8
import {DomSanitizer} from "@angular/platform-browser";
9
import {StakeholderService} from "../services/stakeholder.service";
10
import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties";
11
import {Subscriber} from "rxjs";
12
import {LayoutService} from "../library/sharedComponents/sidebar/layout.service";
13

    
14
declare var UIkit;
15

    
16
@Component({
17
  selector: 'indicators',
18
  templateUrl: './indicators.component.html'
19
})
20
export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
21

    
22
  @Input()
23
  public properties: EnvProperties = null;
24
  @Input()
25
  public topicIndex: number = 0;
26
  @Input()
27
  public categoryIndex: number = 0;
28
  @Input()
29
  public subcategoryIndex: number = 0;
30
  @Input()
31
  public stakeholder: Stakeholder = null;
32
  public indicatorUtils: IndicatorUtils = new IndicatorUtils();
33
  public indicatorFb: FormGroup;
34
  /**
35
   * Editable indicator
36
   */
37
  public indicator: Indicator;
38
  public index: number;
39
  /**
40
   * Displayed chart and numbers base on Top filters
41
   */
42
  public displayCharts: Indicator[] = [];
43
  public displayNumbers: Indicator[] = [];
44
  /**
45
   * Top filters
46
   */
47
  public filters: FormGroup;
48
  public all: Option = {
49
    value: 'all',
50
    label: 'All'
51
  };
52
  /**
53
   * Grid or List View
54
   */
55
  public grid: boolean = true;
56
  private subscriptions: any[] = [];
57
  @ViewChild('editIndicatorModal') editIndicatorModal: AlertModal;
58
  @ViewChild('deleteIndicatorModal') deleteIndicatorModal: AlertModal;
59

    
60
  constructor(private layoutService: LayoutService,
61
              private stakeholderService: StakeholderService,
62
              private statisticsService: StatisticsService,
63
              private fb: FormBuilder,
64
              private sanitizer: DomSanitizer) {
65
  }
66

    
67
  ngOnInit(): void {
68
    this.buildFilters();
69
  }
70

    
71
  ngOnDestroy(): void {
72
    this.subscriptions.forEach(value => {
73
      if (value instanceof Subscriber) {
74
        value.unsubscribe();
75
      } else if(value instanceof Function){
76
        value();
77
      }
78
    });
79
  }
80

    
81
  ngAfterViewInit(): void {
82
    if (document !== undefined) {
83
      let callback = (list): void => {
84
        let items: HTMLCollection = list.current.children;
85
        let reordered = [];
86
        for (let i = 0; i < items.length; i++) {
87
          if (items.item(i).id) {
88
            reordered.push(items.item(i).id);
89
          }
90
        }
91
        this.reorderIndicators(list.current.id , reordered);
92
      };
93
      this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart', callback));
94
      this.subscriptions.push(UIkit.util.on(document, 'moved', '#number', callback));
95
    }
96
  }
97

    
98
  ngOnChanges(changes: SimpleChanges): void {
99
    if (this.canEdit) {
100
      this.buildFilters();
101
      this.filterCharts();
102
      this.filterNumbers();
103
    }
104
  }
105

    
106
  public toggleOpen(event = null) {
107
    if (!event) {
108
      this.layoutService.setOpen(!this.open);
109
    } else if (event && event['value'] === true) {
110
      this.layoutService.setOpen(false);
111
    }
112
  }
113

    
114
  public changeGrid(value) {
115
    this.grid = value;
116
  }
117

    
118
  private buildFilters() {
119
    this.filters = this.fb.group({
120
      chartType: this.fb.control('all'),
121
      privacy: this.fb.control('all'),
122
      status: this.fb.control('all'),
123
      keyword: this.fb.control('')
124
    });
125
    this.subscriptions.push(this.filters.get('chartType').valueChanges.subscribe( value => {
126
      this.onChartTypeChange(value);
127
    }));
128
    this.subscriptions.push(this.filters.get('privacy').valueChanges.subscribe( value => {
129
      this.onPrivacyChange(value);
130
    }));
131
    this.subscriptions.push(this.filters.get('status').valueChanges.subscribe( value => {
132
      this.onStatusChange(value);
133
    }));
134
    this.subscriptions.push(this.filters.get('keyword').valueChanges.subscribe( value => {
135
      this.onKeywordChange(value);
136
    }));
137
  }
138

    
139
  filterCharts() {
140
    this.displayCharts = this.filterChartType(this.filterPrivacy(
141
      this.filterStatus(this.filterByKeyword(this.charts, this.filters.value.keyword),
142
        this.filters.value.status),
143
      this.filters.value.privacy),
144
      this.filters.value.chartType
145
    );
146
  }
147

    
148
  filterNumbers() {
149
    this.displayNumbers = this.filterPrivacy(this.filterStatus(
150
      this.filterByKeyword(this.numbers, this.filters.value.keyword),
151
      this.filters.value.status),
152
      this.filters.value.privacy);
153
  }
154

    
155
  onChartTypeChange(value) {
156
    this.displayCharts = this.filterChartType(this.charts, value);
157
  }
158

    
159
  onPrivacyChange(value) {
160
    console.log(value);
161
    this.displayCharts = this.filterPrivacy(this.charts, value);
162
    this.displayNumbers = this.filterPrivacy(this.numbers, value);
163
  }
164

    
165
  onStatusChange(value) {
166
    this.displayCharts = this.filterStatus(this.charts, value);
167
    this.displayNumbers = this.filterStatus(this.numbers, value);
168
  }
169

    
170
  onKeywordChange(value) {
171
    this.displayCharts = this.filterByKeyword(this.charts, value);
172
    this.displayNumbers = this.filterByKeyword(this.numbers, value);
173
  }
174

    
175
  private filterChartType(indicators: Indicator[], value): Indicator[] {
176
    if (value === 'all') {
177
      return indicators;
178
    } else {
179
      return indicators.filter(indicator =>
180
        indicator.indicatorPaths.filter(indicatorPath => indicatorPath.type === value).length > 0);
181
    }
182
  }
183

    
184
  private filterPrivacy(indicators: Indicator[], value): Indicator[] {
185
    if (value === 'all') {
186
      return indicators;
187
    } else {
188
      return indicators.filter(indicator => indicator.isPublic === value);
189
    }
190
  }
191

    
192
  private filterStatus(indicators: Indicator[], value): Indicator[] {
193
    if (value === 'all') {
194
      return indicators;
195
    } else {
196
      return indicators.filter(indicator => indicator.isActive === value);
197
    }
198
  }
199

    
200
  private filterByKeyword(indicators: Indicator[], value): Indicator[] {
201
    if (value === null || value === '') {
202
      return indicators;
203
    } else {
204
      return indicators.filter(indicator => (indicator.name && indicator.name.toLowerCase().includes(value.toLowerCase()))
205
        || (indicator.description && indicator.description.toLowerCase().includes(value.toLowerCase())));
206
    }
207
  }
208

    
209
  get charts(): Indicator[] {
210
    return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
211
  }
212

    
213
  set charts(indicators: Indicator[]) {
214
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts = indicators;
215
  }
216

    
217
  get numbers(): Indicator[] {
218
    return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
219
  }
220

    
221
  set numbers(indicators: Indicator[]) {
222
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = indicators;
223
  }
224

    
225
  get open(): boolean {
226
    return this.layoutService.open;
227
  }
228

    
229
  get canNumbersReorder(): boolean {
230
    return this.displayNumbers.length === this.numbers.length;
231
  }
232

    
233
  get canChartsReorder(): boolean {
234
    return this.displayCharts.length === this.charts.length;
235
  }
236

    
237
  get canEdit() {
238
    return this.stakeholder &&
239
      this.stakeholder.topics[this.topicIndex] &&
240
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
241
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex];
242
  }
243

    
244
  public get urls(): FormArray {
245
    return this.indicatorFb.get('urls') as FormArray;
246
  }
247

    
248
  public get indicatorPaths(): FormArray {
249
    return this.indicatorFb.get('indicatorPaths') as FormArray;
250
  }
251

    
252
  public getParameters(index: number): FormArray {
253
    return this.indicatorPaths.at(index).get('parameters') as FormArray;
254
  }
255

    
256
  public getParameter(index: number, key: string): FormControl {
257
    return this.getParameters(index).controls.filter(control => control.value.key === key)[0] as FormControl;
258
  }
259

    
260
  private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
261
    return this.sanitizer.bypassSecurityTrustResourceUrl(
262
      this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(indicatorPath)));
263
  }
264

    
265
  public addUrl() {
266
    this.urls.push(this.fb.control('', [Validators.required,
267
      Validators.pattern('https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.' +
268
        '[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.' +
269
        '[a-zA-Z0-9]+\.[^\s]{2,}')])
270
    );
271
  }
272

    
273
  public createIndicatorOpen() {
274
    this.indicator = null;
275
    this.indicatorFb = this.fb.group({
276
      id: this.fb.control(null),
277
      name: this.fb.control('', Validators.required),
278
      description: this.fb.control(''),
279
      isPublic: this.fb.control(false),
280
      isActive: this.fb.control(false),
281
      urls: this.fb.array([]),
282
      width: this.fb.control('small', Validators.required),
283
    });
284
    this.addUrl();
285
    this.editIndicatorModal.alertTitle = 'Create a new chart Indicator';
286
    this.editIndicatorModal.cancelButtonText = 'Cancel';
287
    this.editIndicatorModal.okButtonText = 'Create';
288
    this.editIndicatorModal.okButtonLeft = false;
289
    this.editIndicatorModal.alertMessage = false;
290
    this.editIndicatorModal.stayOpen = true;
291
    this.editIndicatorModal.open();
292
  }
293

    
294
  public createIndicator() {
295
    this.indicator = new Indicator(
296
      this.indicatorFb.value.name,
297
      this.indicatorFb.value.description,
298
      'chart',
299
      this.indicatorFb.value.width,
300
      this.indicatorFb.value.isActive,
301
      this.indicatorFb.value.isPublic,
302
      []
303
    );
304
    this.indicatorFb.value.urls.forEach(url => {
305
      this.indicator.indicatorPaths.push(
306
        this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(url), url));
307
    });
308
    this.editIndicatorOpen();
309
  }
310

    
311
  public editIndicatorOpen(index: number = -1) {
312
    this.index = index;
313
    if (this.index !== -1) {
314
      this.indicator = HelperFunctions.copy(this.charts[index]);
315
    }
316
    let indicatorPaths = this.fb.array([]);
317
    this.indicator.indicatorPaths.forEach(indicatorPath => {
318
      let parameters = this.fb.array([]);
319
      if (indicatorPath.parameters) {
320
        Object.keys(indicatorPath.parameters).forEach(key => {
321
          if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) {
322
            if (this.indicatorUtils.parametersValidators.has(key)) {
323
              parameters.push(this.fb.group({
324
                key: this.fb.control(key),
325
                value: this.fb.control(indicatorPath.parameters[key], this.indicatorUtils.parametersValidators.get(key))
326
              }));
327
            } else {
328
              parameters.push(this.fb.group({
329
                key: this.fb.control(key),
330
                value: this.fb.control(indicatorPath.parameters[key], Validators.required)
331
              }));
332
            }
333
          }
334
        });
335
      }
336
      indicatorPaths.push(this.fb.group({
337
        parameters: parameters
338
      }));
339
    });
340
    this.indicatorFb = this.fb.group({
341
      id: this.fb.control(this.indicator._id),
342
      name: this.fb.control(this.indicator.name, Validators.required),
343
      description: this.fb.control(this.indicator.description),
344
      isPublic: this.fb.control(this.indicator.isPublic),
345
      isActive: this.fb.control(this.indicator.isActive),
346
      indicatorPaths: indicatorPaths,
347
      width: this.fb.control(this.indicator.width, Validators.required),
348
    });
349
    if(index === -1) {
350
      this.editIndicatorModal.okButtonText = 'Save';
351
      this.editIndicatorModal.stayOpen = false;
352
    } else {
353
      this.editIndicatorModal.cancelButtonText = 'Cancel';
354
      this.editIndicatorModal.okButtonText = 'Save Changes';
355
      this.editIndicatorModal.okButtonLeft = false;
356
      this.editIndicatorModal.alertMessage = false;
357
      this.editIndicatorModal.open();
358
    }
359
    this.indicator.indicatorPaths.forEach(indicatorPath => {
360
      indicatorPath.safeResourceUrl = this.getUrlByStakeHolder(indicatorPath)
361
    });
362
  }
363

    
364
  saveIndicator() {
365
    if(this.indicator) {
366
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.indicatorFb.value, this.indicator.indicatorPaths);
367
      let path = [
368
        this.stakeholder._id,
369
        this.stakeholder.topics[this.topicIndex]._id,
370
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
371
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
372
      ];
373
      this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.indicator, path).subscribe(indicator => {
374
        if (this.index !== -1) {
375
          this.charts[this.index] = indicator;
376
        } else {
377
          this.charts.push(indicator);
378
        }
379
        this.filterCharts();
380
        this.stakeholderService.setStakeholder(this.stakeholder);
381
        this.indicatorFb = null;
382
      }, error => {
383
        this.indicatorFb = null;
384
      });
385
    } else {
386
      this.createIndicator();
387
    }
388
  }
389

    
390
  reorderIndicators(type: string, indicatorIds: string[]) {
391
    let path = [
392
      this.stakeholder._id,
393
      this.stakeholder.topics[this.topicIndex]._id,
394
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
395
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
396
    ];
397
    this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, indicatorIds, type).subscribe(indicators => {
398
      if (type === 'chart') {
399
        this.charts = indicators;
400
        this.filterCharts();
401
      } else {
402
        this.numbers = indicators;
403
        this.filterNumbers();
404
      }
405
      this.stakeholderService.setStakeholder(this.stakeholder);
406
    });
407
  }
408

    
409
  refreshIndicator(index: number) {
410
    this.indicator = this.indicatorUtils.generateIndicatorByForm(this.indicatorFb.value, this.indicator.indicatorPaths);
411
    this.indicator.indicatorPaths.forEach(indicatorPath => {
412
      indicatorPath.safeResourceUrl = this.getUrlByStakeHolder(indicatorPath)
413
    });
414
    this.indicatorPaths.at(index).markAsPristine({onlySelf: true});
415
  }
416

    
417
  deleteIndicatorOpen(index: number, type: string = 'chart') {
418
    this.index = index;
419
    if(type === 'chart') {
420
      this.indicator = this.charts[index];
421
    } else {
422
      this.indicator = this.numbers[index];
423
    }
424
    this.deleteIndicatorModal.alertTitle = 'Delete ' + this.indicator.name;
425
    this.deleteIndicatorModal.cancelButtonText = 'No';
426
    this.deleteIndicatorModal.okButtonText = 'Yes';
427
    this.deleteIndicatorModal.message = 'This indicator will permanently be deleted. Are you sure you want to proceed?';
428
    this.deleteIndicatorModal.open();
429
  }
430

    
431
  deleteIndicator() {
432
    let path = [
433
      this.stakeholder._id,
434
      this.stakeholder.topics[this.topicIndex]._id,
435
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
436
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
437
      this.indicator._id
438
    ];
439
    this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path).subscribe( () => {
440
      if(this.indicator.type === 'chart') {
441
        this.charts.splice(this.index, 1);
442
      } else {
443
        this.numbers.splice(this.index, 1);
444
      }
445
      this.stakeholderService.setStakeholder(this.stakeholder);
446
    });
447
  }
448
}
(2-2/6)