Merge remote-tracking branch 'origin/monitor-admin-library' into angular-16
This commit is contained in:
commit
8647136803
|
@ -1,3 +1,5 @@
|
||||||
|
import {stakeholderTypes} from "../../monitor/entities/stakeholder";
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
email: string;
|
email: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
|
@ -98,23 +100,11 @@ export class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isMonitorCurator(user: User): boolean {
|
public static isMonitorCurator(user: User): boolean {
|
||||||
return this.isCommunityCurator(user) || this.isProjectCurator(user) || this.isFunderCurator(user) || this.isOrganizationCurator(user);
|
return stakeholderTypes.filter(stakeholderType => this.isTypeCurator(stakeholderType.value, user)).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isCommunityCurator(user: User): boolean {
|
public static isCommunityCurator(user: User): boolean {
|
||||||
return this.isTypeCurator("Community", user);
|
return this.isTypeCurator("community", user);
|
||||||
}
|
|
||||||
|
|
||||||
public static isFunderCurator(user: User): boolean {
|
|
||||||
return this.isTypeCurator("Funder", user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static isProjectCurator(user: User): boolean {
|
|
||||||
return this.isTypeCurator("Project", user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static isOrganizationCurator(user: User): boolean {
|
|
||||||
return this.isTypeCurator("Institution", user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static isTypeCurator(type: string, user: User): boolean {
|
private static isTypeCurator(type: string, user: User): boolean {
|
||||||
|
@ -122,16 +112,7 @@ export class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isCurator(type: string, user: User): boolean {
|
public static isCurator(type: string, user: User): boolean {
|
||||||
if (type == 'funder') {
|
return stakeholderTypes.find(stakeholderType => stakeholderType.value == type) && this.isTypeCurator(type, user);
|
||||||
return user && this.isFunderCurator(user);
|
|
||||||
} else if (type == 'ri' || type == 'community') {
|
|
||||||
return user && this.isCommunityCurator(user);
|
|
||||||
} else if (type == 'organization' || type == 'institution') {
|
|
||||||
return user && this.isOrganizationCurator(user);
|
|
||||||
} else if (type == 'project') {
|
|
||||||
return user && this.isProjectCurator(user);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static isPortalAdministrator(user: User): boolean {
|
public static isPortalAdministrator(user: User): boolean {
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
.uk-border-circle {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
max-width: 64px;
|
||||||
|
max-height: 64px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,443 @@
|
||||||
|
import {Component, Input, OnDestroy, ViewChild} from "@angular/core";
|
||||||
|
import {Stakeholder} from "../../../monitor/entities/stakeholder";
|
||||||
|
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
|
||||||
|
import {StakeholderUtils} from "../../utils/indicator-utils";
|
||||||
|
import {Option} from "../../../sharedComponents/input/input.component";
|
||||||
|
import {Subscription} from "rxjs";
|
||||||
|
import {EnvProperties} from "../../../utils/properties/env-properties";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
import {StakeholderService} from "../../../monitor/services/stakeholder.service";
|
||||||
|
import {UtilitiesService} from "../../../services/utilities.service";
|
||||||
|
import {Role, Session, User} from "../../../login/utils/helper.class";
|
||||||
|
import {UserManagementService} from "../../../services/user-management.service";
|
||||||
|
import {StringUtils} from "../../../utils/string-utils.class";
|
||||||
|
import {NotifyFormComponent} from "../../../notifications/notify-form/notify-form.component";
|
||||||
|
import {NotificationUtils} from "../../../notifications/notification-utils";
|
||||||
|
import {Notification} from "../../../notifications/notifications";
|
||||||
|
import {NotificationHandler} from "../../../utils/notification-handler";
|
||||||
|
import {StatsProfilesService} from "../../utils/services/stats-profiles.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'edit-stakeholder',
|
||||||
|
template: `
|
||||||
|
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
|
||||||
|
<div class="uk-grid uk-grid-large" uk-grid>
|
||||||
|
<div class="uk-width-1-2@m">
|
||||||
|
<div input id="name" [formInput]="stakeholderFb.get('name')"
|
||||||
|
placeholder="Name"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-2@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('alias')"
|
||||||
|
placeholder="URL Alias"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('index_id')"
|
||||||
|
placeholder="Index ID"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('index_name')"
|
||||||
|
placeholder="Index Name"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('index_shortName')"
|
||||||
|
placeholder="Index Short Name"></div>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div *ngIf="statsProfiles" input [formInput]="stakeholderFb.get('statsProfile')" [type]="'select'"
|
||||||
|
[options]="statsProfiles"
|
||||||
|
placeholder="Stats Profile"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('projectUpdateDate')" [type]="'date'"
|
||||||
|
placeholder="Last Project Update"></div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div input [formInput]="stakeholderFb.get('locale')" [type]="'select'"
|
||||||
|
[options]="stakeholderUtils.locales"
|
||||||
|
placeholder="Locale"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div input [type]="'textarea'" placeholder="Description"
|
||||||
|
[rows]="4" [formInput]="stakeholderFb.get('description')"></div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
|
||||||
|
<div *ngIf="!stakeholderFb.get('isUpload').value" class="uk-grid uk-grid-column-large" uk-grid>
|
||||||
|
<div class="uk-margin-top uk-width-auto@l uk-width-1-1">
|
||||||
|
<div class="uk-grid uk-grid-column-large uk-flex-middle" uk-grid>
|
||||||
|
<div class="uk-width-auto@l uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<button class="uk-button uk-button-primary uk-flex uk-flex-middle uk-flex-wrap"
|
||||||
|
(click)="file.click()">
|
||||||
|
<icon name="cloud_upload" [flex]="true"></icon>
|
||||||
|
<span class="uk-margin-small-left">Upload a file</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="uk-text-center uk-text-bold uk-width-expand">
|
||||||
|
OR
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-expand" type="logoURL" [placeholder]="'Link to the logo'"
|
||||||
|
[formInput]="stakeholderFb.get('logoUrl')"></div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="stakeholderFb.get('isUpload').value" class="uk-width-1-1 uk-flex uk-flex-middle">
|
||||||
|
<div class="uk-card uk-card-default uk-text-center uk-border-circle">
|
||||||
|
<img class="uk-position-center uk-blend-multiply" [src]="photo">
|
||||||
|
</div>
|
||||||
|
<div class="uk-margin-left">
|
||||||
|
<button (click)="remove()" class="uk-button-danger uk-icon-button uk-icon-button-small">
|
||||||
|
<icon [flex]="true" ratio="0.8" name="delete"></icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="uk-margin-small-left">
|
||||||
|
<button class="uk-button-secondary uk-icon-button uk-icon-button-small"
|
||||||
|
(click)="file.click()">
|
||||||
|
<icon [flex]="true" ratio="0.8" name="edit"></icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Full width error message -->
|
||||||
|
<div *ngIf="uploadError" class="uk-text-danger uk-margin-small-top uk-width-1-1">{{uploadError}}</div>
|
||||||
|
</div>
|
||||||
|
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
|
||||||
|
<div input [formInput]="stakeholderFb.get('visibility')"
|
||||||
|
[placeholder]="'Select a status'"
|
||||||
|
[options]="stakeholderUtils.statuses" type="select"></div>
|
||||||
|
</div>
|
||||||
|
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
|
||||||
|
<div input [formInput]="stakeholderFb.get('type')"
|
||||||
|
[placeholder]="'Select a type'"
|
||||||
|
[options]="types" type="select"></div>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="canChooseTemplate">
|
||||||
|
<div class="uk-width-1-3@m">
|
||||||
|
<div [placeholder]="'Select a template'"
|
||||||
|
input [formInput]="stakeholderFb.get('defaultId')"
|
||||||
|
[options]="defaultStakeholdersOptions" type="select"></div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form
|
||||||
|
class="uk-width-1-1 uk-margin-large-top uk-margin-medium-bottom"></div>
|
||||||
|
`,
|
||||||
|
styleUrls: ['edit-stakeholder.component.less']
|
||||||
|
})
|
||||||
|
export class EditStakeholderComponent implements OnDestroy {
|
||||||
|
@Input()
|
||||||
|
public disableAlias: boolean = false;
|
||||||
|
public stakeholderFb: UntypedFormGroup;
|
||||||
|
public secure: boolean = false;
|
||||||
|
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
|
||||||
|
public defaultStakeholdersOptions: Option[];
|
||||||
|
public defaultStakeholders: Stakeholder[];
|
||||||
|
public alias: string[];
|
||||||
|
public stakeholder: Stakeholder;
|
||||||
|
public isDefault: boolean;
|
||||||
|
public isNew: boolean;
|
||||||
|
public loading: boolean = false;
|
||||||
|
public types: Option[];
|
||||||
|
public statsProfiles: string[];
|
||||||
|
public properties: EnvProperties = properties;
|
||||||
|
private subscriptions: any[] = [];
|
||||||
|
/**
|
||||||
|
* Photo upload
|
||||||
|
* */
|
||||||
|
public file: File;
|
||||||
|
public photo: string | ArrayBuffer;
|
||||||
|
public uploadError: string;
|
||||||
|
public deleteCurrentPhoto: boolean = false;
|
||||||
|
private maxsize: number = 200 * 1024;
|
||||||
|
user: User;
|
||||||
|
@ViewChild('notify', {static: true}) notify: NotifyFormComponent;
|
||||||
|
private notification: Notification;
|
||||||
|
|
||||||
|
constructor(private fb: UntypedFormBuilder,
|
||||||
|
private stakeholderService: StakeholderService,
|
||||||
|
private statsProfileService: StatsProfilesService,
|
||||||
|
private utilsService: UtilitiesService, private userManagementService: UserManagementService,) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(stakeholder: Stakeholder, alias: string[], defaultStakeholders: Stakeholder[], isDefault: boolean, isNew: boolean) {
|
||||||
|
this.reset();
|
||||||
|
this.deleteCurrentPhoto = false;
|
||||||
|
this.stakeholder = stakeholder;
|
||||||
|
this.alias = alias;
|
||||||
|
this.defaultStakeholders = defaultStakeholders;
|
||||||
|
this.isDefault = isDefault;
|
||||||
|
this.isNew = isNew;
|
||||||
|
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
|
||||||
|
this.user = user;
|
||||||
|
if (this.isCurator) {
|
||||||
|
this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => {
|
||||||
|
this.statsProfiles = statsProfiles;
|
||||||
|
}, error => {
|
||||||
|
this.statsProfiles = [];
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.statsProfiles = [];
|
||||||
|
}
|
||||||
|
this.types = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
|
||||||
|
this.stakeholderFb = this.fb.group({
|
||||||
|
_id: this.fb.control(this.stakeholder._id),
|
||||||
|
defaultId: this.fb.control(this.stakeholder.defaultId),
|
||||||
|
name: this.fb.control(this.stakeholder.name, Validators.required),
|
||||||
|
description: this.fb.control(this.stakeholder.description),
|
||||||
|
index_name: this.fb.control(this.stakeholder.index_name, Validators.required),
|
||||||
|
index_id: this.fb.control(this.stakeholder.index_id, Validators.required),
|
||||||
|
index_shortName: this.fb.control(this.stakeholder.index_shortName, Validators.required),
|
||||||
|
statsProfile: this.fb.control(this.stakeholder.statsProfile, Validators.required),
|
||||||
|
locale: this.fb.control(this.stakeholder.locale, Validators.required),
|
||||||
|
projectUpdateDate: this.fb.control(this.stakeholder.projectUpdateDate),
|
||||||
|
creationDate: this.fb.control(this.stakeholder.creationDate),
|
||||||
|
alias: this.fb.control(this.stakeholder.alias,
|
||||||
|
[
|
||||||
|
Validators.required,
|
||||||
|
this.stakeholderUtils.aliasValidatorString(
|
||||||
|
this.alias.filter(alias => alias !== this.stakeholder.alias)
|
||||||
|
)]
|
||||||
|
),
|
||||||
|
isDefault: this.fb.control((this.isDefault)),
|
||||||
|
visibility: this.fb.control(this.stakeholder.visibility, Validators.required),
|
||||||
|
type: this.fb.control(this.stakeholder.type, Validators.required),
|
||||||
|
topics: this.fb.control(this.stakeholder.topics),
|
||||||
|
isUpload: this.fb.control(this.stakeholder.isUpload),
|
||||||
|
logoUrl: this.fb.control(this.stakeholder.logoUrl),
|
||||||
|
});
|
||||||
|
if (this.stakeholder.isUpload) {
|
||||||
|
this.stakeholderFb.get('logoUrl').clearValidators();
|
||||||
|
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
|
||||||
|
} else {
|
||||||
|
this.stakeholderFb.get('logoUrl').setValidators([StringUtils.urlValidator()]);
|
||||||
|
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
|
||||||
|
}
|
||||||
|
this.subscriptions.push(this.stakeholderFb.get('isUpload').valueChanges.subscribe(value => {
|
||||||
|
if (value == true) {
|
||||||
|
this.stakeholderFb.get('logoUrl').clearValidators();
|
||||||
|
this.stakeholderFb.updateValueAndValidity();
|
||||||
|
} else {
|
||||||
|
this.stakeholderFb.get('logoUrl').setValidators([StringUtils.urlValidator()]);
|
||||||
|
this.stakeholderFb.updateValueAndValidity();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.secure = (!this.stakeholderFb.get('logoUrl').value || this.stakeholderFb.get('logoUrl').value.includes('https://'));
|
||||||
|
this.subscriptions.push(this.stakeholderFb.get('logoUrl').valueChanges.subscribe(value => {
|
||||||
|
this.secure = (!value || value.includes('https://'));
|
||||||
|
}));
|
||||||
|
this.initPhoto();
|
||||||
|
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
|
||||||
|
this.onTypeChange(value, defaultStakeholders);
|
||||||
|
}));
|
||||||
|
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
|
||||||
|
if (!this.isNew) {
|
||||||
|
this.notification = NotificationUtils.editStakeholder(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
|
||||||
|
this.notify.reset(this.notification.message);
|
||||||
|
if (this.isAdmin) {
|
||||||
|
if (this.disableAlias) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stakeholderFb.get('alias').disable();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.isCurator) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stakeholderFb.get('statsProfile').disable();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stakeholderFb.get('alias').disable();
|
||||||
|
this.stakeholderFb.get('index_id').disable();
|
||||||
|
this.stakeholderFb.get('index_name').disable();
|
||||||
|
this.stakeholderFb.get('index_shortName').disable();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stakeholderFb.get('type').disable();
|
||||||
|
}, 0);
|
||||||
|
} else {
|
||||||
|
this.notification = NotificationUtils.createStakeholder(this.user.firstname + ' ' + this.user.lastname);
|
||||||
|
this.notify.reset(this.notification.message);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stakeholderFb.get('type').enable();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isAdmin() {
|
||||||
|
return Session.isPortalAdministrator(this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isCurator() {
|
||||||
|
return this.stakeholder && (this.isAdmin || Session.isCurator(this.stakeholder.type, this.user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public get disabled(): boolean {
|
||||||
|
return (this.stakeholderFb && this.stakeholderFb.invalid) ||
|
||||||
|
(this.stakeholderFb && this.stakeholderFb.pristine && !this.isNew && !this.file) ||
|
||||||
|
(this.uploadError && this.uploadError.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get dirty(): boolean {
|
||||||
|
return this.stakeholderFb && this.stakeholderFb.dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get canChooseTemplate(): boolean {
|
||||||
|
return this.isNew && this.stakeholderFb.get('type').valid && !!this.defaultStakeholdersOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.uploadError = null;
|
||||||
|
this.stakeholderFb = null;
|
||||||
|
this.subscriptions.forEach(subscription => {
|
||||||
|
if (subscription instanceof Subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onTypeChange(value, defaultStakeholders: Stakeholder[]) {
|
||||||
|
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
|
||||||
|
this.defaultStakeholdersOptions = [{
|
||||||
|
label: 'New blank profile',
|
||||||
|
value: '-1'
|
||||||
|
}];
|
||||||
|
defaultStakeholders.filter(stakeholder => stakeholder.type === value).forEach(stakeholder => {
|
||||||
|
this.defaultStakeholdersOptions.push({
|
||||||
|
label: 'Use ' + stakeholder.name + ' profile',
|
||||||
|
value: stakeholder._id
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public save(callback: Function, errorCallback: Function = null) {
|
||||||
|
this.loading = true;
|
||||||
|
if (this.file) {
|
||||||
|
this.subscriptions.push(this.utilsService.uploadPhoto(this.properties.utilsService + "/upload/" + encodeURIComponent(this.stakeholderFb.getRawValue().type) + "/" + encodeURIComponent(this.stakeholderFb.getRawValue().alias), this.file).subscribe(res => {
|
||||||
|
this.deletePhoto();
|
||||||
|
this.stakeholderFb.get('logoUrl').setValue(res.filename);
|
||||||
|
this.removePhoto();
|
||||||
|
this.saveStakeholder(callback, errorCallback);
|
||||||
|
}, error => {
|
||||||
|
this.uploadError = "An error has been occurred during upload your image. Try again later";
|
||||||
|
this.saveStakeholder(callback, errorCallback);
|
||||||
|
}));
|
||||||
|
} else if (this.deleteCurrentPhoto) {
|
||||||
|
this.deletePhoto();
|
||||||
|
this.saveStakeholder(callback, errorCallback);
|
||||||
|
} else {
|
||||||
|
this.saveStakeholder(callback, errorCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveStakeholder(callback: Function, errorCallback: Function = null) {
|
||||||
|
if (this.isNew) {
|
||||||
|
let defaultStakeholder = this.defaultStakeholders.find(value => value._id === this.stakeholderFb.getRawValue().defaultId);
|
||||||
|
this.stakeholderFb.setValue(this.stakeholderUtils.createFunderFromDefaultProfile(this.stakeholderFb.getRawValue(),
|
||||||
|
(defaultStakeholder ? defaultStakeholder.topics : []), this.stakeholderFb.getRawValue().isDefault));
|
||||||
|
this.removePhoto();
|
||||||
|
if(this.stakeholderFb.getRawValue().isDefault) {
|
||||||
|
this.stakeholderFb.get('defaultId').setValue(null);
|
||||||
|
}
|
||||||
|
this.subscriptions.push(this.stakeholderService.buildStakeholder(this.properties.monitorServiceAPIURL,
|
||||||
|
this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
|
||||||
|
this.notification.entity = stakeholder._id;
|
||||||
|
this.notification.stakeholder = stakeholder.alias;
|
||||||
|
this.notification.stakeholderType = stakeholder.type;
|
||||||
|
this.notification.groups = [Role.curator(stakeholder.type)];
|
||||||
|
this.notify.sendNotification(this.notification);
|
||||||
|
NotificationHandler.rise(stakeholder.name + ' has been <b>successfully created</b>');
|
||||||
|
callback(stakeholder);
|
||||||
|
this.loading = false;
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
|
||||||
|
if (errorCallback) {
|
||||||
|
errorCallback(error)
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
|
||||||
|
this.notification.entity = stakeholder._id;
|
||||||
|
this.notification.stakeholder = stakeholder.alias;
|
||||||
|
this.notification.stakeholderType = stakeholder.type;
|
||||||
|
this.notification.groups = [Role.curator(stakeholder.type), Role.manager(stakeholder.type, stakeholder.alias)];
|
||||||
|
this.notify.sendNotification(this.notification);
|
||||||
|
NotificationHandler.rise(stakeholder.name + ' has been <b>successfully saved</b>');
|
||||||
|
callback(stakeholder);
|
||||||
|
this.loading = false;
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
|
||||||
|
if (errorCallback) {
|
||||||
|
errorCallback(error)
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileChangeEvent(event) {
|
||||||
|
if (event.target.files && event.target.files[0]) {
|
||||||
|
this.file = event.target.files[0];
|
||||||
|
if (this.file.type !== 'image/png' && this.file.type !== 'image/jpeg') {
|
||||||
|
this.uploadError = 'You must choose a file with type: image/png or image/jpeg!';
|
||||||
|
this.stakeholderFb.get('isUpload').setValue(false);
|
||||||
|
this.stakeholderFb.get('isUpload').markAsDirty();
|
||||||
|
this.removePhoto();
|
||||||
|
} else if (this.file.size > this.maxsize) {
|
||||||
|
this.uploadError = 'File exceeds size\'s limit! Maximum resolution is 256x256 pixels.';
|
||||||
|
this.stakeholderFb.get('isUpload').setValue(false);
|
||||||
|
this.stakeholderFb.get('isUpload').markAsDirty();
|
||||||
|
this.removePhoto();
|
||||||
|
} else {
|
||||||
|
this.uploadError = null;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(this.file);
|
||||||
|
reader.onload = () => {
|
||||||
|
this.photo = reader.result;
|
||||||
|
this.stakeholderFb.get('isUpload').setValue(true);
|
||||||
|
this.stakeholderFb.get('isUpload').markAsDirty();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initPhoto() {
|
||||||
|
if (this.stakeholderFb.getRawValue().isUpload) {
|
||||||
|
this.photo = this.properties.utilsService + "/download/" + this.stakeholderFb.get('logoUrl').value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removePhoto() {
|
||||||
|
if (this.file) {
|
||||||
|
if (typeof document != 'undefined') {
|
||||||
|
(<HTMLInputElement>document.getElementById("photo")).value = "";
|
||||||
|
}
|
||||||
|
this.initPhoto();
|
||||||
|
this.file = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.stakeholderFb.get('isUpload').setValue(false);
|
||||||
|
this.stakeholderFb.get('isUpload').markAsDirty();
|
||||||
|
this.removePhoto();
|
||||||
|
this.stakeholderFb.get('logoUrl').setValue(null);
|
||||||
|
if (this.stakeholder.isUpload) {
|
||||||
|
this.deleteCurrentPhoto = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePhoto() {
|
||||||
|
if (this.stakeholder.logoUrl && this.stakeholder.isUpload) {
|
||||||
|
this.subscriptions.push(this.utilsService.deletePhoto(this.properties.utilsService + '/delete/' +
|
||||||
|
encodeURIComponent(this.stakeholder.type) + "/" + encodeURIComponent(this.stakeholder.alias) + "/" + this.stakeholder.logoUrl).subscribe());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {EditStakeholderComponent} from "./edit-stakeholder.component";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {InputModule} from "../../../sharedComponents/input/input.module";
|
||||||
|
import {ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {IconsModule} from "../../../utils/icons/icons.module";
|
||||||
|
import {NotifyFormModule} from "../../../notifications/notify-form/notify-form.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, InputModule, ReactiveFormsModule, IconsModule, NotifyFormModule],
|
||||||
|
declarations: [EditStakeholderComponent],
|
||||||
|
exports: [EditStakeholderComponent]
|
||||||
|
})
|
||||||
|
export class EditStakeholderModule {}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {PreviousRouteRecorder} from '../../utils/piwik/previousRouteRecorder.guard';
|
||||||
|
import {GeneralComponent} from "./general.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: GeneralComponent,
|
||||||
|
canDeactivate: [PreviousRouteRecorder],
|
||||||
|
data: {hasSidebar: true}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class GeneralRoutingModule {
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div page-content>
|
||||||
|
<div actions>
|
||||||
|
<sidebar-mobile-toggle class="uk-margin-top uk-hidden@m uk-display-block"></sidebar-mobile-toggle>
|
||||||
|
<div class="uk-section-xsmall uk-container uk-margin-top">
|
||||||
|
<div class="uk-flex uk-flex-center uk-flex-right@m">
|
||||||
|
<button class="uk-button uk-button-default uk-margin-right"
|
||||||
|
(click)="reset()" [class.uk-disabled]="loading || !editStakeholderComponent.dirty"
|
||||||
|
[disabled]="loading || !editStakeholderComponent.dirty">Reset
|
||||||
|
</button>
|
||||||
|
<button class="uk-button uk-button-primary" [class.uk-disabled]="loading || editStakeholderComponent.disabled"
|
||||||
|
(click)="save()" [disabled]="loading || editStakeholderComponent.disabled">Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div inner>
|
||||||
|
<div *ngIf="stakeholder" class="uk-container">
|
||||||
|
|
||||||
|
<div class="uk-position-relative" style="min-height: 60vh">
|
||||||
|
<div [class.uk-hidden]="loading" class="uk-section uk-section-small">
|
||||||
|
<edit-stakeholder #editStakeholderComponent [disableAlias]="true"></edit-stakeholder>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="loading" class="uk-position-center">
|
||||||
|
<loading *ngIf="loading"></loading>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||||
|
import {StakeholderService} from "../../monitor/services/stakeholder.service";
|
||||||
|
import {EnvProperties} from "../../utils/properties/env-properties";
|
||||||
|
import {Stakeholder} from "../../monitor/entities/stakeholder";
|
||||||
|
import { Subscription, zip} from "rxjs";
|
||||||
|
import {EditStakeholderComponent} from "./edit-stakeholder/edit-stakeholder.component";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
import {Title} from "@angular/platform-browser";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'general',
|
||||||
|
templateUrl: "./general.component.html"
|
||||||
|
})
|
||||||
|
export class GeneralComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
public stakeholder: Stakeholder;
|
||||||
|
public alias: string[];
|
||||||
|
public properties: EnvProperties = properties;
|
||||||
|
public defaultStakeholders: Stakeholder[];
|
||||||
|
public loading: boolean = false;
|
||||||
|
private subscriptions: any[] = [];
|
||||||
|
@ViewChild('editStakeholderComponent') editStakeholderComponent: EditStakeholderComponent;
|
||||||
|
|
||||||
|
constructor(private stakeholderService: StakeholderService,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private title: Title) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.loading = true;
|
||||||
|
this.subscriptions.push(this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => {
|
||||||
|
this.stakeholder = stakeholder;
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
if(this.stakeholder) {
|
||||||
|
this.title.setTitle(this.stakeholder.name + " | General");
|
||||||
|
let data = zip(
|
||||||
|
this.stakeholderService.getDefaultStakeholders(this.properties.monitorServiceAPIURL),
|
||||||
|
this.stakeholderService.getAlias(this.properties.monitorServiceAPIURL)
|
||||||
|
);
|
||||||
|
this.subscriptions.push(data.subscribe(res => {
|
||||||
|
this.defaultStakeholders = res[0];
|
||||||
|
this.alias = res[1];
|
||||||
|
this.reset();
|
||||||
|
this.loading = false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
this.editStakeholderComponent.init(this.stakeholder, this.alias, this.defaultStakeholders, this.stakeholder.defaultId == null, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public save() {
|
||||||
|
this.loading = true;
|
||||||
|
this.editStakeholderComponent.save((stakeholder) => {
|
||||||
|
this.stakeholder = stakeholder;
|
||||||
|
this.stakeholderService.setStakeholder(this.stakeholder);
|
||||||
|
this.reset();
|
||||||
|
this.loading = false;
|
||||||
|
}, (error) => {
|
||||||
|
console.error(error);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subscriptions.forEach(subscription => {
|
||||||
|
if(subscription instanceof Subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {GeneralComponent} from "./general.component";
|
||||||
|
import {GeneralRoutingModule} from "./general-routing.module";
|
||||||
|
import {PreviousRouteRecorder} from "../../utils/piwik/previousRouteRecorder.guard";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {RouterModule} from "@angular/router";
|
||||||
|
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||||
|
import {LoadingModule} from "../../utils/loading/loading.module";
|
||||||
|
import {AlertModalModule} from "../../utils/modal/alertModal.module";
|
||||||
|
import {ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {EditStakeholderModule} from "./edit-stakeholder/edit-stakeholder.module";
|
||||||
|
import {PageContentModule} from "../../dashboard/sharedComponents/page-content/page-content.module";
|
||||||
|
import {LogoUrlPipeModule} from "../../utils/pipes/logoUrlPipe.module";
|
||||||
|
import {
|
||||||
|
SidebarMobileToggleModule
|
||||||
|
} from "../../dashboard/sharedComponents/sidebar/sidebar-mobile-toggle/sidebar-mobile-toggle.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [GeneralComponent],
|
||||||
|
imports: [
|
||||||
|
GeneralRoutingModule,
|
||||||
|
CommonModule,
|
||||||
|
RouterModule,
|
||||||
|
InputModule,
|
||||||
|
LoadingModule,
|
||||||
|
AlertModalModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
EditStakeholderModule,
|
||||||
|
PageContentModule,
|
||||||
|
LogoUrlPipeModule,
|
||||||
|
SidebarMobileToggleModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
PreviousRouteRecorder,
|
||||||
|
],
|
||||||
|
exports: [GeneralComponent]
|
||||||
|
})
|
||||||
|
export class GeneralModule {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,490 @@
|
||||||
|
<div *ngIf="stakeholder && canEdit" class="uk-section">
|
||||||
|
<div *ngIf="numberSections">
|
||||||
|
<h5 class="uk-text-bold">Number Indicators</h5>
|
||||||
|
<div class="uk-grid uk-grid-large uk-child-width-1-1" uk-grid>
|
||||||
|
<div *ngFor="let number of numbers; let i=index">
|
||||||
|
<div class="section">
|
||||||
|
<div class="tools">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<a [class.uk-disabled]="editing" class="" (click)="createSection(i, 'number')"
|
||||||
|
uk-tooltip="Create a new section">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
</a>
|
||||||
|
<a *ngIf="!number.defaultId" [attr.uk-tooltip]="'Delete section'"
|
||||||
|
(click)="deleteSectionOpen(number, i, 'number', 'delete')">
|
||||||
|
<icon name="close" [flex]="true"></icon>
|
||||||
|
</a>
|
||||||
|
<!-- <ng-container *ngIf="!stakeholder.defaultId">-->
|
||||||
|
<!-- <button [disabled]="editing || number.defaultId " class="md-btn md-btn-mini"-->
|
||||||
|
<!-- [title]="(number.defaultId?'Default sections cannot be deleted':'Delete all related sections')"-->
|
||||||
|
<!-- (click)="deleteSectionOpen(number, i, 'number', 'delete')"><i class="material-icons">highlight_off</i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- <button [disabled]="editing || number.defaultId " class="md-btn md-btn-mini"-->
|
||||||
|
<!-- [title]="(number.defaultId?'Default sections cannot be deleted':'Delete section and disconnect related')"-->
|
||||||
|
<!-- (click)="deleteSectionOpen(number, i, 'number', 'disconnect')"><i class="material-icons">link_off</i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- </ng-container>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="numberSections.at(i)" class="uk-margin-medium-bottom">
|
||||||
|
<div input [formInput]="numberSections.at(i).get('title')"
|
||||||
|
(focusEmitter)="saveSection($event, numberSections.at(i), i, 'number')"
|
||||||
|
class="uk-width-1-3@m uk-width-1-1" placeholder="Title" inputClass="border-bottom"></div>
|
||||||
|
</div>
|
||||||
|
<div [id]="'number-' + number._id" class="uk-grid uk-grid-small uk-grid-match" uk-sortable="group: number" uk-grid>
|
||||||
|
<ng-template ngFor [ngForOf]="number.indicators" let-indicator let-j="index">
|
||||||
|
<div *ngIf="indicator" [id]="indicator._id"
|
||||||
|
[ngClass]="getNumberClassBySize(indicator.width)">
|
||||||
|
<div class="uk-card uk-card-default uk-padding-small number-card uk-position-relative">
|
||||||
|
<div *ngIf="!dragging"
|
||||||
|
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
|
||||||
|
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
|
||||||
|
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
|
||||||
|
<icon [flex]="true" name="more_vert"></icon>
|
||||||
|
</a>
|
||||||
|
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<li><a (click)="editNumberIndicatorOpen(number, indicator._id); hide(element)">Edit</a></li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||||
|
<li>
|
||||||
|
<a (click)="changeIndicatorStatus(number._id, indicator, v.value);hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
|
||||||
|
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
|
||||||
|
<li class="uk-nav-divider">
|
||||||
|
<li><a (click)="deleteIndicatorOpen(number, indicator._id, 'number', 'delete');hide(element)">Delete</a></li>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-text-small uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{indicator.name}}</div>
|
||||||
|
<div class="number uk-text-small uk-text-bold">
|
||||||
|
<span *ngIf="numberResults.get(i + '-' + j)" [innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j) | numberPercentage: stakeholder.locale))"></span>
|
||||||
|
<span *ngIf="!numberResults.get(i + '-' + j)">--</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isCurator" class="uk-margin-top">
|
||||||
|
<div class="uk-grid uk-grid-small" uk-grid>
|
||||||
|
<div [ngClass]="getNumberClassBySize('small')">
|
||||||
|
<a class="uk-card uk-card-default number-card uk-padding-small uk-flex uk-flex-middle uk-link-reset" (click)="editNumberIndicatorOpen(number)">
|
||||||
|
<div class="uk-text-background uk-margin-right">
|
||||||
|
<icon name="add" [flex]="true" ratio="1.5"></icon>
|
||||||
|
</div>
|
||||||
|
<div class="uk-text-bold uk-margin-remove">
|
||||||
|
Create a number Indicator
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ng-container *ngTemplateOutlet="new_section; context:{type: 'number'}"></ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="chartSections" class="uk-margin-large-top">
|
||||||
|
<h5 class="uk-text-bold">Chart Indicators</h5>
|
||||||
|
<div class="uk-grid uk-grid-large uk-child-width-1-1" uk-grid>
|
||||||
|
<div *ngFor="let chart of charts; let i=index">
|
||||||
|
<div class="section uk-margin-top uk-padding-small">
|
||||||
|
<div class="tools">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<a [class.uk-disabled]="editing" class="" (click)="createSection(i)"
|
||||||
|
title="Create a new section">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
</a>
|
||||||
|
<a *ngIf="!chart.defaultId" [attr.uk-tooltip]="'Delete section'"
|
||||||
|
(click)="deleteSectionOpen(chart, i, 'chart', 'delete')">
|
||||||
|
<icon name="close" [flex]="true"></icon>
|
||||||
|
</a>
|
||||||
|
<!-- <ng-container *ngIf="!stakeholder.defaultId">-->
|
||||||
|
<!-- <button [disabled]="editing || chart.defaultId " class="md-btn md-btn-mini"-->
|
||||||
|
<!-- [title]="(chart.defaultId?'Default sections cannot be deleted':'Delete all related sections')"-->
|
||||||
|
<!-- (click)="deleteSectionOpen(chart, i, 'chart', 'delete')"><i class="material-icons">highlight_off</i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- <button [disabled]="editing || chart.defaultId " class="md-btn md-btn-mini"-->
|
||||||
|
<!-- [title]="(chart.defaultId?'Default sections cannot be deleted':'Delete section and disconnect related')"-->
|
||||||
|
<!-- (click)="deleteSectionOpen(chart, i, 'chart', 'disconnect')"><i class="material-icons">link_off</i>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- </ng-container>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="chartSections.at(i)"
|
||||||
|
class="uk-margin-medium-bottom">
|
||||||
|
<div input [formInput]="chartSections.at(i).get('title')"
|
||||||
|
(focusEmitter)="saveSection($event, chartSections.at(i), i)"
|
||||||
|
class="uk-width-1-3@m uk-width-1-1" placeholder="Title" inputClass="border-bottom"></div>
|
||||||
|
</div>
|
||||||
|
<div [id]="'chart-' + chart._id" class="uk-grid uk-grid-small uk-grid-match" uk-sortable="group: chart" uk-grid>
|
||||||
|
<ng-template ngFor [ngForOf]="chart.indicators" let-indicator let-j="index">
|
||||||
|
<div *ngIf="indicator" [id]="indicator._id" [ngClass]="getChartClassBySize(indicator.width)">
|
||||||
|
<div class="uk-card uk-card-default uk-card-body uk-position-relative">
|
||||||
|
<div *ngIf="!dragging"
|
||||||
|
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
|
||||||
|
<a class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
|
||||||
|
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
|
||||||
|
<icon [flex]="true" name="more_vert"></icon>
|
||||||
|
</a>
|
||||||
|
<div #element class="uk-dropdown" uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<li><a (click)="editChartIndicatorOpen(chart, indicator._id); hide(element)">Edit</a></li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||||
|
<li>
|
||||||
|
<a (click)="changeIndicatorStatus(chart._id, indicator, v.value);">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
|
||||||
|
<icon *ngIf="indicator.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
|
||||||
|
<li class="uk-nav-divider">
|
||||||
|
<li><a (click)="deleteIndicatorOpen(chart, indicator._id, 'chart', 'delete');hide(element)">Delete</a></li>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div *ngIf="indicator.name" class="uk-text-center uk-text-bold uk-margin-small-bottom">
|
||||||
|
{{indicator.name}}
|
||||||
|
</div>
|
||||||
|
<iframe *ngIf="!properties.disableFrameLoad && indicator.indicatorPaths[0] && indicator.indicatorPaths[0].source !=='image' &&
|
||||||
|
safeUrls.get(indicatorUtils.getFullUrl(stakeholder, indicator.indicatorPaths[0]))"
|
||||||
|
allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"
|
||||||
|
[src]="safeUrls.get(indicatorUtils.getFullUrl(stakeholder, indicator.indicatorPaths[0]))"
|
||||||
|
class="uk-width-1-1" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
|
||||||
|
[class.uk-blend-multiply]="!isFullscreen"></iframe>
|
||||||
|
<div *ngIf="properties.disableFrameLoad && indicator.indicatorPaths &&
|
||||||
|
indicator.indicatorPaths.length > 0 && indicator.indicatorPaths[0].source !=='image'">
|
||||||
|
<img class="uk-width-1-1 uk-blend-multiply" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
|
||||||
|
src="assets/chart-placeholder.png">
|
||||||
|
</div>
|
||||||
|
<div *ngIf="indicator.indicatorPaths && indicator.indicatorPaths[0] &&
|
||||||
|
indicator.indicatorPaths[0].source === 'image'">
|
||||||
|
<img class="uk-width-1-1 uk-blend-multiply" [ngClass]="'uk-height-' + (indicator.height?indicator.height.toLowerCase():'medium')"
|
||||||
|
[src]="indicator.indicatorPaths[0].url">
|
||||||
|
</div>
|
||||||
|
<!--<ng-container *ngTemplateOutlet="description; context: {indicator:indicator}"></ng-container>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="isCurator" class="uk-margin-top">
|
||||||
|
<div class="uk-grid uk-grid-small uk-grid-match" uk-grid>
|
||||||
|
<div [ngClass]="getChartClassBySize('small')">
|
||||||
|
<div class=" uk-card uk-card-default uk-card-body clickable" (click)="editChartIndicatorOpen(chart)">
|
||||||
|
<h6 class="uk-text-bold uk-text-center">
|
||||||
|
Create a custom indicator
|
||||||
|
</h6>
|
||||||
|
<div class="uk-text-muted uk-text-small">
|
||||||
|
Use our advance tool to create a custom Indicator that suit the needs of your funding
|
||||||
|
KPI's.
|
||||||
|
</div>
|
||||||
|
<div class="uk-flex uk-flex-center uk-text-background uk-margin-medium-top">
|
||||||
|
<icon name="add" ratio="3" [flex]="true"></icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ng-container *ngTemplateOutlet="new_section; context:{type: 'chart'}"></ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<modal-alert #editNumberModal
|
||||||
|
[large]="true" classTitle="uk-background-primary uk-light"
|
||||||
|
(alertOutput)="saveIndicator()"
|
||||||
|
[okDisabled]="numberIndicatorFb && (numberIndicatorFb.invalid || numberIndicatorFb.pristine)">
|
||||||
|
<div *ngIf="editing" class="uk-position-relative uk-height-large">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
<div [class.uk-hidden]="editing" class="uk-padding-small">
|
||||||
|
<div *ngIf="numberIndicatorFb" class="uk-grid" [formGroup]="numberIndicatorFb" uk-grid>
|
||||||
|
<div input class="uk-width-1-1" [formInput]="numberIndicatorFb.get('name')" placeholder="Title"></div>
|
||||||
|
<div *ngIf="stakeholder.defaultId != '-1' && ( (indicator.description && indicator.description.length > 0) || !stakeholder.defaultId)"
|
||||||
|
input class="uk-width-1-1" [formInput]="numberIndicatorFb.get('description')" placeholder="Profile description" type="textarea">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-1" *ngIf="stakeholder.defaultId" [formInput]="numberIndicatorFb.get('additionalDescription')"
|
||||||
|
placeholder="Description" type="textarea">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('visibility')"
|
||||||
|
placeholder="Visibility" [options]="stakeholderUtils.visibility" type="select">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-2@m" [formInput]="numberIndicatorFb.get('width')"
|
||||||
|
placeholder="Number Size" [options]="indicatorUtils.indicatorSizes" type="select">
|
||||||
|
</div>
|
||||||
|
<div *ngIf="numberIndicatorPaths" formArrayName="indicatorPaths">
|
||||||
|
<div *ngFor="let indicatorPath of numberIndicatorPaths.controls; let i=index" [formGroupName]="i">
|
||||||
|
<div class="uk-grid" uk-grid>
|
||||||
|
<div class="uk-width-1-1">
|
||||||
|
<div class="uk-grid" uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-middle">
|
||||||
|
<div input class="uk-width-expand" [formInput]="indicatorPath.get('url')" placeholder="Number URL">
|
||||||
|
<div *ngIf="urlParameterizedMessage" warning>{{urlParameterizedMessage}}</div>
|
||||||
|
</div>
|
||||||
|
<div class='uk-padding-small'>
|
||||||
|
<a class="uk-link-reset" (click)="copyToClipboard(indicatorPath.get('url').value)"><icon [flex]="true" name="content_copy"></icon></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="showCheckForSchemaEnhancements" class="uk-width-1-1">
|
||||||
|
<div class="uk-alert uk-alert-warning">
|
||||||
|
There are schema enhancements that can be applied in this query.<a
|
||||||
|
(click)="indicatorPath.get('url').setValue(indicatorUtils.applySchemaEnhancements(indicatorPath.get('url').value)); indicatorPath.get('url').markAsDirty()">Apply
|
||||||
|
now</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-2@m">
|
||||||
|
<div input [formInput]="indicatorPath.get('source')" placeholder="Source"
|
||||||
|
[options]="isAdministrator?indicatorUtils.allSourceTypes:indicatorUtils.sourceTypes" type="select">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-2@m">
|
||||||
|
<div input [formInput]="indicatorPath.get('format')" placeholder="Format"
|
||||||
|
[options]="indicatorUtils.formats" type="select">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div formArrayName="jsonPath" class="uk-width-1-1">
|
||||||
|
<h6 class="uk-text-bold uk-margin-remove-bottom">
|
||||||
|
<span>JSON Path</span>
|
||||||
|
</h6>
|
||||||
|
<div *ngIf="numberIndicatorPaths.at(i).get('result').invalid && numberIndicatorPaths.at(i).get('result').errors.required">
|
||||||
|
<div class="uk-text-danger uk-text-small">
|
||||||
|
This JSON path is not valid or the result has not been calculated yet.
|
||||||
|
Please press calculate on box below to see the result.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-grid uk-child-width-1-3@m uk-child-width-1-1 uk-margin-top uk-flex-middle" uk-grid>
|
||||||
|
<div *ngFor="let jsonPath of getJsonPath(i).controls; let j=index" class="uk-flex uk-flex-middle">
|
||||||
|
<div input class="uk-width-1-1" [formInput]="jsonPath" [placeholder]="'Level ' + +(j + 1)"></div>
|
||||||
|
<a [class.uk-invisible]="getJsonPath(i).length === 1 || numberIndicatorFb.get('defaultId').value"
|
||||||
|
class="uk-margin-small-left uk-text-danger"
|
||||||
|
[class.uk-disabled]="getJsonPath(i).disabled"
|
||||||
|
(click)="removeJsonPath(i, j)">
|
||||||
|
<icon name="close"></icon>
|
||||||
|
</a>
|
||||||
|
<span [class.uk-invisible]="getJsonPath(i).disabled || j === (getJsonPath(i).controls.length - 1)" class="uk-text-center uk-margin-small-left">
|
||||||
|
<icon name="east"></icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="indicator.defaultId === null">
|
||||||
|
<button class="uk-icon-button uk-button-primary" (click)="addJsonPath(i)">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<div class="uk-flex uk-position-relative">
|
||||||
|
<span class="uk-padding number number-preview uk-flex uk-flex-column uk-flex-center uk-text-center">
|
||||||
|
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value !== 0"
|
||||||
|
[innerHTML]="(numberIndicatorPaths.at(i).get('format').value == 'NUMBER'?(numberIndicatorPaths.at(i).get('result').value | numberRound: 2:1:stakeholder.locale):(numberIndicatorPaths.at(i).get('result').value | numberPercentage: stakeholder.locale))">
|
||||||
|
</span>
|
||||||
|
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value === 0">
|
||||||
|
--
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div *ngIf="numberIndicatorPaths.at(i).get('result').invalid"
|
||||||
|
class="uk-width-1-1 uk-height-1-1 refresh-indicator">
|
||||||
|
<div class="uk-position-relative uk-height-1-1">
|
||||||
|
<a class="uk-position-center uk-text-center uk-text-small uk-link-reset"
|
||||||
|
[class.uk-disabled]="numberIndicatorPaths.at(i).get('url').invalid"
|
||||||
|
(click)="validateJsonPath(i, true)">
|
||||||
|
<div>
|
||||||
|
<icon name="refresh"></icon>
|
||||||
|
</div>
|
||||||
|
<span *ngIf="numberIndicatorPaths.at(i).get('result').errors.required">Calculate</span>
|
||||||
|
<span *ngIf="numberIndicatorPaths.at(i).get('result').errors.validating">Calculating...</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div #editNumberNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #editChartModal [large]="true" (alertOutput)="saveIndicator()" classTitle="uk-background-primary uk-light"
|
||||||
|
[okDisabled]="chartIndicatorFb && (chartIndicatorFb.invalid || chartIndicatorFb.pristine)">
|
||||||
|
<div *ngIf="editing" class="uk-position-relative uk-height-large">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
<div [class.uk-hidden]="editing" class="uk-padding-small">
|
||||||
|
<div *ngIf="chartIndicatorFb" [formGroup]="chartIndicatorFb" class="uk-grid" uk-grid>
|
||||||
|
<div input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('name')" placeholder="Title"></div>
|
||||||
|
<div *ngIf="stakeholder.defaultId != '-1' && ((indicator.description && indicator.description.length > 0) || !stakeholder.defaultId)"
|
||||||
|
input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('description')" placeholder="Default Description" type="textarea">
|
||||||
|
</div>
|
||||||
|
<div *ngIf="stakeholder.defaultId" input class="uk-width-1-1" [formInput]="chartIndicatorFb.get('additionalDescription')"
|
||||||
|
placeholder="Description" type="textarea">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('visibility')"
|
||||||
|
placeholder="Status" [options]="stakeholderUtils.visibility" type="select">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('width')" placeholder="Chart width"
|
||||||
|
[options]="indicatorUtils.indicatorSizes" type="select">
|
||||||
|
</div>
|
||||||
|
<div input class="uk-width-1-2@m" [formInput]="chartIndicatorFb.get('height')" placeholder="Chart height"
|
||||||
|
[options]="indicatorUtils.indicatorSizes" type="select">
|
||||||
|
</div>
|
||||||
|
<div *ngIf="chartIndicatorPaths" formArrayName="indicatorPaths" class="uk-width-1-1">
|
||||||
|
<div *ngFor="let indicatorPath of chartIndicatorPaths.controls; let i=index;"
|
||||||
|
[formGroupName]="i" class="uk-grid" uk-grid>
|
||||||
|
<div class="uk-width-1-1 uk-flex uk-flex-middle">
|
||||||
|
<div input class="uk-width-expand" [title]="indicatorPath.get('url').disabled?'Default chart URLs cannot change':''"
|
||||||
|
[formInput]="indicatorPath.get('url')" placeholder="Chart URL">
|
||||||
|
<div *ngIf="urlParameterizedMessage" warning>{{urlParameterizedMessage}}</div>
|
||||||
|
</div>
|
||||||
|
<div class='uk-padding-small'>
|
||||||
|
<a class="uk-link-reset" (click)="copyToClipboard(indicatorPath.get('url').value)"><icon [flex]="true" name="content_copy"></icon></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="showCheckForSchemaEnhancements" class=" uk-width-1-1 ">
|
||||||
|
<div class="uk-alert uk-alert-warning">
|
||||||
|
There are schema enhancements that can be applied in this query. <a
|
||||||
|
(click)="indicatorPath.get('url').setValue(indicatorUtils.applySchemaEnhancements(indicatorPath.get('url').value)); indicatorPath.get('url').markAsDirty()">Apply
|
||||||
|
now</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-1-1" formArrayName="parameters">
|
||||||
|
<div class="uk-grid" uk-grid>
|
||||||
|
<div *ngIf="getParameter(i, 'title')" input class="uk-width-1-1" [formInput]="getParameter(i, 'title').get('value')"
|
||||||
|
placeholder="Chart Title"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'subtitle')" input class="uk-width-1-1" placeholder="Chart Subtitle" [formInput]="getParameter(i, 'subtitle').get('value')" label="Chart Subtitle"></div>
|
||||||
|
<div *ngIf="!getParameter(i, 'type')" input class="uk-width-1-3@s" [formInput]="indicatorPath.get('type')" placeholder="Chart Type"
|
||||||
|
[options]="(indicatorPath.get('type').value == 'table' && getParameter(i, 'data_title_0'))?indicatorUtils.getChartTypes(indicatorPath.get('type').value):indicatorUtils.allChartTypes"
|
||||||
|
type="select"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'type')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'type').get('value')" placeholder="Chart Type"
|
||||||
|
[options]="indicatorUtils.getChartTypes(getParameter(i, 'type').get('value').value)" type="select"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'xAxisTitle')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'xAxisTitle').get('value')"
|
||||||
|
placeholder="X-Axis Title"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'yAxisTitle')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'yAxisTitle').get('value')"
|
||||||
|
placeholder="Y-Axis Title"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'data_title_0')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'data_title_0').get('value')"
|
||||||
|
placeholder="Data Title"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'data_title_1')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'data_title_1').get('value')"
|
||||||
|
placeholder="Data Title"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'start_year')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'start_year').get('value')"
|
||||||
|
placeholder="Year (From)"></div>
|
||||||
|
<div *ngIf="getParameter(i, 'end_year')" input class="uk-width-1-3@s" [formInput]="getParameter(i, 'end_year').get('value')"
|
||||||
|
placeholder="Year (To)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="indicator && indicator.indicatorPaths[i] && indicator.indicatorPaths[i].safeResourceUrl"
|
||||||
|
class="uk-margin-medium-top uk-position-relative uk-width-1-1 uk-flex uk-flex-center">
|
||||||
|
<div *ngIf="(hasDifference(i)) && !indicatorPath.invalid"
|
||||||
|
class="uk-width-1-1 uk-height-large refresh-indicator">
|
||||||
|
<div class="uk-position-relative uk-height-1-1">
|
||||||
|
<a class="uk-position-center uk-text-center uk-link-reset" (click)="refreshIndicator()">
|
||||||
|
<div>
|
||||||
|
<icon name="refresh"></icon>
|
||||||
|
</div>
|
||||||
|
<span>Click to refresh the graph view</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<iframe *ngIf="indicator.indicatorPaths[i].source !== 'image'" [class.uk-blend-multiply]="!isFullscreen"
|
||||||
|
[src]="indicator.indicatorPaths[i].safeResourceUrl" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"
|
||||||
|
class="uk-width-1-1 uk-height-large"></iframe>
|
||||||
|
<!-- <div *ngIf="properties.disableFrameLoad && indicator.indicatorPaths[i].source !== 'image'" class="uk-alert uk-alert-danger uk-text-center">I frames-->
|
||||||
|
<!-- preview is disabled</div>-->
|
||||||
|
<div *ngIf="indicator.indicatorPaths[i].source === 'image'">
|
||||||
|
<img class="uk-width-1-1 uk-height-large uk-blend-multiply" [src]="indicator.indicatorPaths[i].url">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div #editChartNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #deleteModal (alertOutput)="deleteIndicator()" [overflowBody]="false" classTitle="uk-background-primary uk-light">
|
||||||
|
<div [class.uk-invisible]="editing" class="uk-position-relative">
|
||||||
|
<div *ngIf="editing">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
|
||||||
|
"{{indicator.name ? indicator.name : (indicator.indicatorPaths[0]?.parameters?.title?indicator.indicatorPaths[0].parameters.title:'')}}"</span> indicator permanently.
|
||||||
|
<div *ngIf="indicatorChildrenActionOnDelete == 'delete'" class="uk-text-bold">
|
||||||
|
Indicators of all profiles based on this default indicator, will be deleted as well.
|
||||||
|
</div>
|
||||||
|
<!-- <span *ngIf="indicatorChildrenActionOnDelete == 'disconnect'" class="uk-text-bold">-->
|
||||||
|
<!-- Indicators of all profiles based on this default indicator, will not be marked as copied from default anymore.-->
|
||||||
|
<!-- </span>-->
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
<div #deleteNotify notify-form class="uk-width-1-1 uk-margin-medium-top"></div>
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<!--<modal-alert #deleteAllModal (alertOutput)="deleteIndicator('delete')">
|
||||||
|
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
|
||||||
|
"{{indicator.name ? indicator.name : indicator.indicatorPaths[0].parameters.title}}"</span> indicator permanently.
|
||||||
|
<span class="uk-text-bold">Indicators of all profiles based on this default indicator, will be deleted as well.</span>
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #deleteAndDisconnectModal (alertOutput)="deleteIndicator('disconnect')">
|
||||||
|
You are about to delete <span class="uk-text-bold" *ngIf="indicator && index !== -1">
|
||||||
|
"{{indicator.name ? indicator.name : indicator.indicatorPaths[0].parameters.title}}"</span> indicator permanently.
|
||||||
|
<span class="uk-text-bold">Indicators of all profiles based on this default indicator, will not be marked as copied from default anymore.</span>
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</modal-alert>-->
|
||||||
|
<modal-alert #deleteSectionModal (alertOutput)="deleteSection()" [overflowBody]="false" classTitle="uk-background-primary uk-light">
|
||||||
|
<div [class.uk-invisible]="editing" class="uk-position-relative">
|
||||||
|
<div *ngIf="editing">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
You are about to delete this section and its indicators permanently.
|
||||||
|
<div *ngIf="sectionChildrenActionOnDelete == 'delete' && !stakeholder.defaultId" class="uk-text-bold">
|
||||||
|
Sections of all profiles based on this default section and their contents, will be deleted as well.
|
||||||
|
</div>
|
||||||
|
<!-- <span *ngIf="sectionChildrenActionOnDelete == 'disconnect'" class="uk-text-bold">-->
|
||||||
|
<!-- Sections of all profiles based on this default section and their contents, will not be marked as copied from default anymore.-->
|
||||||
|
<!-- </span>-->
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<!--<modal-alert #deleteNumberSectionModal (alertOutput)="deleteSection('number')">
|
||||||
|
You are about to delete this section and its indicators permanently.
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #deleteChartSectionModal (alertOutput)="deleteSection()">
|
||||||
|
You are about to delete this section and its indicators permanently.
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</modal-alert>-->
|
||||||
|
<ng-template #new_section let-type="type">
|
||||||
|
<div class="section">
|
||||||
|
<div class="uk-flex uk-flex-center" (click)="createSection(-1, type)">
|
||||||
|
<button class="uk-button uk-button-primary uk-flex uk-flex-middle">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
<span class="uk-margin-small-left">New section</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
|
@ -0,0 +1,53 @@
|
||||||
|
@import (reference) "~src/assets/openaire-theme/less/_import-variables";
|
||||||
|
|
||||||
|
.number-preview {
|
||||||
|
border: @global-border-width solid @global-border;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
min-width: 100px;
|
||||||
|
min-height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-indicator {
|
||||||
|
background-color: @global-overlay-background;
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
position: absolute;
|
||||||
|
color: @global-inverse-color;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 60px 45px;
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
border: @global-border-width solid @global-border;
|
||||||
|
position: relative;
|
||||||
|
background: @global-inverse-color;
|
||||||
|
border-left: 5px @global-primary-background solid;
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -100%);
|
||||||
|
max-width: 50px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-image: @global-primary-gradient;
|
||||||
|
color: @global-inverse-color;
|
||||||
|
-webkit-clip-path: polygon(20% 5%, 80% 5%, 100% 100%, 0% 100%);
|
||||||
|
clip-path: polygon(20% 5%, 80% 5%, 100% 100%, 0% 100%);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.tools {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: currentColor;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {RouterModule} from '@angular/router';
|
||||||
|
import {PreviousRouteRecorder} from '../../utils/piwik/previousRouteRecorder.guard';
|
||||||
|
import {TopicComponent} from "./topic.component";
|
||||||
|
import {CanExitGuard} from "../../utils/can-exit.guard";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: TopicComponent,
|
||||||
|
canDeactivate: [PreviousRouteRecorder, CanExitGuard]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TopicRoutingModule {
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
<aside *ngIf="stakeholder" id="sidebar_main">
|
||||||
|
<div sidebar-content>
|
||||||
|
<div class="back">
|
||||||
|
<a [routerLink]="'/admin/' + stakeholder.alias" class="uk-flex uk-flex-middle uk-flex-center">
|
||||||
|
<div class="uk-width-auto">
|
||||||
|
<icon name="west" [flex]="true" ratio="1.3"></icon>
|
||||||
|
</div>
|
||||||
|
<span class="uk-width-expand uk-text-truncate uk-margin-left hide-on-close">Indicators</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="menu_section uk-margin-large-top">
|
||||||
|
<ul class="uk-list uk-nav uk-nav-default" transition-group [id]="'topics'"
|
||||||
|
uk-nav="duration: 400">
|
||||||
|
<li *ngFor="let topic of stakeholder.topics; let i=index" class="uk-parent" [class.uk-active]="topicIndex === i"
|
||||||
|
transition-group-item>
|
||||||
|
<a [routerLink]="'/admin/'+stakeholder.alias + '/indicators/' + topic.alias"
|
||||||
|
[title]="topic.name" class="uk-visible-toggle uk-flex uk-flex-middle">
|
||||||
|
<div *ngIf="topic.icon" class="uk-width-auto">
|
||||||
|
<icon class="menu-icon" [svg]="topic.icon" ratio="0.9" [flex]="true"></icon>
|
||||||
|
</div>
|
||||||
|
<span [class.hide-on-close]="topic.icon"
|
||||||
|
class="uk-width-expand uk-text-truncate uk-margin-small-left">
|
||||||
|
{{topic.name}}
|
||||||
|
</span>
|
||||||
|
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="topicIndex !== i"
|
||||||
|
(click)="$event.stopPropagation();$event.preventDefault()">
|
||||||
|
<a class="uk-link-reset uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
|
||||||
|
ratio="0.6"></icon>
|
||||||
|
<icon [flex]="true" name="more_vert"></icon>
|
||||||
|
</a>
|
||||||
|
<div #element
|
||||||
|
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<li>
|
||||||
|
<a (click)="editTopicOpen(i); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="edit" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Edit</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="i > 0 || i < stakeholder.topics.length - 1" class="uk-nav-divider"></li>
|
||||||
|
<li *ngIf="i > 0">
|
||||||
|
<a (click)="hide(element);moveTopic(i)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="north" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Up</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="i < stakeholder.topics.length - 1">
|
||||||
|
<a (click)="hide(element);moveTopic(i, i + 1)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="south" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Down</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||||
|
<li [class.uk-active]="topic.visibility === v.value">
|
||||||
|
<a (click)="openVisibilityModal(i, v.value, 'topic'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
|
||||||
|
<icon *ngIf="topic.visibility === v.value" [flex]="true" name="done"
|
||||||
|
class="uk-text-secondary" ratio="0.8"></icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container *ngIf="!topic.defaultId && isCurator">
|
||||||
|
<li class="uk-nav-divider">
|
||||||
|
<li>
|
||||||
|
<a (click)="deleteTopicOpen(i, 'delete'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="delete" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Delete</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<!--<ng-container *ngIf="!stakeholder.defaultId">
|
||||||
|
<a (click)="deleteTopicOpen(i, 'delete'); hide(element)">Delete from all profiles</a>
|
||||||
|
<a (click)="deleteTopicOpen(i, 'disconnect'); hide(element)">Delete and disconnect from all profiles</a>
|
||||||
|
</ng-container>-->
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="uk-nav-parent-icon hide-on-close"></span>
|
||||||
|
</a>
|
||||||
|
<ul *ngIf="isBrowser || topicIndex === i" class="uk-nav-sub" [id]="'categories-' + i.toString()"
|
||||||
|
transition-group>
|
||||||
|
<li *ngFor="let category of topic.categories; let j=index" transition-group-item class="uk-visible-toggle"
|
||||||
|
[class.uk-active]="categoryIndex == j">
|
||||||
|
<a (click)="chooseCategory(j)" [title]="category.name">
|
||||||
|
<div class="uk-flex uk-flex-middle uk-width-1-1">
|
||||||
|
<span class="uk-width-expand uk-text-truncate">{{category.name}}</span>
|
||||||
|
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="categoryIndex !== j"
|
||||||
|
(click)="$event.stopPropagation();$event.preventDefault()">
|
||||||
|
<a class="uk-link-reset uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
|
||||||
|
ratio="0.6"></icon>
|
||||||
|
<icon [flex]="true" name="more_vert"></icon>
|
||||||
|
</a>
|
||||||
|
<div #element
|
||||||
|
uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false; container: body">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<li>
|
||||||
|
<a (click)="editCategoryOpen(j); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="edit" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Edit</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="j > 0 || j < stakeholder.topics[topicIndex].categories.length - 1"
|
||||||
|
class="uk-nav-divider"></li>
|
||||||
|
<li *ngIf="j > 0">
|
||||||
|
<a (click)="hide(element);moveCategory(j)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="north" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Up</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="j < stakeholder.topics[topicIndex].categories.length - 1">
|
||||||
|
<a (click)="hide(element);moveCategory(j, j + 1)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="south" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Down</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||||
|
<li [class.uk-active]="category.visibility === v.value">
|
||||||
|
<a (click)="openVisibilityModal(j, v.value, 'category'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
|
||||||
|
<icon *ngIf="category.visibility === v.value" [flex]="true" name="done"
|
||||||
|
class="uk-text-secondary" ratio="0.8"></icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container *ngIf="!category.defaultId && isCurator">
|
||||||
|
<li class="uk-nav-divider">
|
||||||
|
<li>
|
||||||
|
<a (click)="deleteCategoryOpen(j, 'delete'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="delete" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Delete</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="isCurator">
|
||||||
|
<a (click)="editCategoryOpen(); $event.preventDefault()" class="uk-flex uk-flex-middle">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
<span class="hide-on-close">Create new category</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="isCurator" class="hide-on-close">
|
||||||
|
<a (click)="editTopicOpen(-1); $event.preventDefault()">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<div class="uk-width-auto">
|
||||||
|
<icon class="menu-icon" name="add" [flex]="true"></icon>
|
||||||
|
</div>
|
||||||
|
<span class="uk-width-expand uk-text-truncate uk-margin-small-left hide-on-close">Create new topic</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<div #pageContent *ngIf="stakeholder && filters" page-content>
|
||||||
|
<div actions>
|
||||||
|
<div *ngIf="stakeholder.topics.length > 0" class="uk-flex uk-flex-center uk-margin-medium-top uk-flex-right@m uk-width-1-1">
|
||||||
|
<button class="uk-button uk-button-primary uk-flex uk-flex-middle">
|
||||||
|
<icon name="visibility" [flex]="true"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-margin-small-right">Preview</span>
|
||||||
|
<icon name="expand_more" [flex]="true"></icon>
|
||||||
|
</button>
|
||||||
|
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; flip: false">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<li><a target="_blank"
|
||||||
|
[routerLink]="'/' + stakeholder.alias + '/' + stakeholder.topics[topicIndex].alias"
|
||||||
|
[queryParams]="{view: 'PUBLIC'}"
|
||||||
|
(click)="hide(element)">Public view</a>
|
||||||
|
</li>
|
||||||
|
<li><a target="_blank" [routerLink]="'/' + stakeholder.alias + '/' +
|
||||||
|
stakeholder.topics[topicIndex].alias"
|
||||||
|
[queryParams]="{view: 'RESTRICTED'}"
|
||||||
|
(click)="hide(element)">Restricted view</a>
|
||||||
|
</li>
|
||||||
|
<!--<li class="disabled"><a class="uk-disabled uk-text-muted"
|
||||||
|
uk-tooltip="Note: available only in administration dashboard"
|
||||||
|
(click)="hide(element)">Private view</a>
|
||||||
|
</li>-->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul *ngIf="stakeholder.topics.length > 0 && stakeholder.topics[topicIndex].categories.length > 0 && stakeholder.topics[topicIndex].categories[categoryIndex]"
|
||||||
|
transition-group class="uk-tab uk-margin-xsmall-top" [id]="'subCategories'">
|
||||||
|
<ng-template ngFor [ngForOf]=" stakeholder.topics[topicIndex].categories[categoryIndex].subCategories"
|
||||||
|
let-subCategory let-i="index">
|
||||||
|
<li class="uk-visible-toggle uk-flex" [class.uk-active]="subCategoryIndex === i" transition-group-item>
|
||||||
|
<a (click)="chooseSubcategory(i)">
|
||||||
|
<span class="uk-text-uppercase">{{subCategory.name}}</span>
|
||||||
|
</a>
|
||||||
|
<span class="uk-flex uk-flex-column uk-flex-center uk-margin-small-left"
|
||||||
|
[class.uk-invisible-hover]="subCategoryIndex !== i"
|
||||||
|
(click)="$event.stopPropagation();$event.preventDefault()">
|
||||||
|
<a class="uk-link-reset uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
|
||||||
|
ratio="0.6"></icon>
|
||||||
|
<icon [flex]="true" name="more_vert"></icon>
|
||||||
|
</a>
|
||||||
|
<div #element uk-dropdown="mode: click; pos: bottom-left; offset: 5; delay-hide: 0; container: body">
|
||||||
|
<ul class="uk-nav uk-dropdown-nav">
|
||||||
|
<ng-container *ngIf="isCurator">
|
||||||
|
<li>
|
||||||
|
<a (click)="editSubCategoryOpen(i); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="edit" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Edit</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="i > 0 || i < stakeholder.topics[topicIndex].categories[categoryIndex].subCategories.length - 1"
|
||||||
|
class="uk-nav-divider"></li>
|
||||||
|
<li *ngIf="i > 0">
|
||||||
|
<a (click)="hide(element);moveSubCategory(i)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="west" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Left</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="i < stakeholder.topics[topicIndex].categories[categoryIndex].subCategories.length - 1">
|
||||||
|
<a (click)="hide(element);moveSubCategory(i, i + 1)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="east" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Move Right</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
<li *ngIf="indicators">
|
||||||
|
<a (click)=" indicators.exportIndicators(i);hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="download" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Export indicators</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="indicators">
|
||||||
|
<a (click)="file.value = ''; this.index=i; file.click(); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="upload" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Import indicators</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="uk-nav-divider"></li>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||||
|
<li [class.uk-active]="subCategory.visibility === v.value">
|
||||||
|
<a (click)="openVisibilityModal(i, v.value, 'subcategory'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" [name]="v.icon" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">{{v.label}}</span>
|
||||||
|
<icon *ngIf="subCategory.visibility === v.value" [flex]="true" name="done"
|
||||||
|
class="uk-text-secondary" ratio="0.8"></icon>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<ng-container *ngIf="!subCategory.defaultId && isCurator">
|
||||||
|
<li class="uk-nav-divider">
|
||||||
|
<li>
|
||||||
|
<a (click)="deleteSubcategoryOpen(i, 'delete'); hide(element)">
|
||||||
|
<div class="uk-flex uk-flex-middle">
|
||||||
|
<icon [flex]="true" name="delete" ratio="0.6"></icon>
|
||||||
|
<span class="uk-margin-small-left uk-width-expand">Delete</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ng-container>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
<li *ngIf="isCurator">
|
||||||
|
<a (click)="editSubCategoryOpen(); $event.preventDefault()" class="uk-flex uk-flex-middle">
|
||||||
|
<icon name="add" [flex]="true"></icon>
|
||||||
|
<span class="uk-text-uppercase">Create new subcategory</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div inner>
|
||||||
|
<input #file id="import-file" type="file" class="uk-hidden"
|
||||||
|
(change)="indicators.fileChangeEvent($event, this.index)"/>
|
||||||
|
<indicators #indicators [topicIndex]="topicIndex" [categoryIndex]="categoryIndex"
|
||||||
|
[subcategoryIndex]="subCategoryIndex" [user]="user"
|
||||||
|
[stakeholder]="stakeholder" [changed]="change.asObservable()"></indicators>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<modal-alert #deleteModal classTitle="uk-background-primary uk-light" (alertOutput)="deleteElement()"
|
||||||
|
[overflowBody]="false">
|
||||||
|
<div [class.uk-invisible]="loading" class="uk-position-relative">
|
||||||
|
<div *ngIf="loading">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
You are about to delete <span class="uk-text-bold" *ngIf="element">"{{element.name}}"</span> {{type}} permanently.
|
||||||
|
<div *ngIf="elementChildrenActionOnDelete == 'delete'" class="uk-text-bold">
|
||||||
|
{{getPluralTypeName()}} of all profiles based on this default {{type}}, will be deleted as well.
|
||||||
|
</div>
|
||||||
|
Are you sure you want to proceed?
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #editModal classTitle="uk-background-primary uk-light" (alertOutput)="saveElement()"
|
||||||
|
[okDisabled]="form && (form.invalid || form.pristine)" [large]="true">
|
||||||
|
<div *ngIf="loading" class="uk-position-relative uk-height-large">
|
||||||
|
<loading class="uk-position-center"></loading>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="form" [class.uk-hidden]="loading"
|
||||||
|
class="uk-grid uk-padding uk-padding-remove-horizontal uk-child-width-1-1" [formGroup]="form" uk-grid>
|
||||||
|
<div input [formInput]="form.get('name')" class="uk-width-1-2@m" placeholder="Title"></div>
|
||||||
|
<div input [formInput]="form.get('visibility')" class="uk-width-1-2@m" placeholder="Status"
|
||||||
|
[options]="stakeholderUtils.visibility" type="select"></div>
|
||||||
|
<div input [formInput]="form.get('description')" placeholder="Description" type="textarea" rows="4"></div>
|
||||||
|
<div *ngIf="form.get('icon')" input [formInput]="form.get('icon')" placeholder="Icon(SVG)" type="textarea"></div>
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
||||||
|
<modal-alert #visibilityModal [large]="false" classTitle="uk-background-primary uk-light">
|
||||||
|
<div class="">
|
||||||
|
You have the option to change the visibility status of your {{type}}, with or without applying the changed status to
|
||||||
|
its contents.
|
||||||
|
</div>
|
||||||
|
<div class="uk-flex uk-flex-center uk-margin-medium-top uk-margin-medium-bottom">
|
||||||
|
<button class="uk-button uk-button-primary" (click)="changeElementStatus()">
|
||||||
|
Change {{type}} status
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="uk-flex uk-flex-center uk-margin-medium-bottom">
|
||||||
|
<button class="uk-button uk-button-primary" (click)="changeElementStatus(true)">
|
||||||
|
Change {{type}} and its contents' status
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="uk-text-small">
|
||||||
|
<p class="uk-text-bold uk-text-uppercase uk-margin-remove">Note:</p>
|
||||||
|
<div>
|
||||||
|
<span class="uk-text-bold uk-text-italic">The status of the {{type}} prevails the status of its contents.</span>
|
||||||
|
For example, if a {{type}}'s status is private, while it has
|
||||||
|
<span *ngIf="type == 'topic'">a category, subcategory or an indicator</span>
|
||||||
|
<span *ngIf="type == 'category'">a subcategory or an indicator</span>
|
||||||
|
<span *ngIf="type == 'subcategory'">an indicator</span>
|
||||||
|
that is public, the private status of the {{type}} dominates.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</modal-alert>
|
|
@ -0,0 +1,795 @@
|
||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component, Inject,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit, PLATFORM_ID,
|
||||||
|
QueryList,
|
||||||
|
ViewChild,
|
||||||
|
ViewChildren
|
||||||
|
} from '@angular/core';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
import {Title} from '@angular/platform-browser';
|
||||||
|
import {EnvProperties} from '../../utils/properties/env-properties';
|
||||||
|
import {Category, Stakeholder, SubCategory, Topic, Visibility} from "../../monitor/entities/stakeholder";
|
||||||
|
import {StakeholderService} from "../../monitor/services/stakeholder.service";
|
||||||
|
import {HelperFunctions} from "../../utils/HelperFunctions.class";
|
||||||
|
import {AlertModal} from "../../utils/modal/alert";
|
||||||
|
import {BehaviorSubject, Subject, Subscriber, Subscription} from "rxjs";
|
||||||
|
import {UntypedFormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
|
||||||
|
import {StakeholderUtils} from "../utils/indicator-utils";
|
||||||
|
import {StringUtils} from "../../utils/string-utils.class";
|
||||||
|
import {IDeactivateComponent} from "../../utils/can-exit.guard";
|
||||||
|
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
|
||||||
|
import {Option} from "../../sharedComponents/input/input.component";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
import {Session, User} from "../../login/utils/helper.class";
|
||||||
|
import {UserManagementService} from "../../services/user-management.service";
|
||||||
|
import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component";
|
||||||
|
import {NotificationHandler} from "../../utils/notification-handler";
|
||||||
|
|
||||||
|
declare var UIkit;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'topic',
|
||||||
|
templateUrl: './topic.component.html',
|
||||||
|
})
|
||||||
|
export class TopicComponent implements OnInit, OnDestroy, AfterViewInit, IDeactivateComponent {
|
||||||
|
private topicSubscriptions: any[] = [];
|
||||||
|
private subscriptions: any[] = [];
|
||||||
|
public properties: EnvProperties = properties;
|
||||||
|
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
|
||||||
|
public loading: boolean = false;
|
||||||
|
public stakeholder: Stakeholder;
|
||||||
|
public user: User;
|
||||||
|
/**
|
||||||
|
* Stakeholder change event
|
||||||
|
* */
|
||||||
|
public change: Subject<void> = new Subject<void>();
|
||||||
|
/**
|
||||||
|
* Current topic
|
||||||
|
**/
|
||||||
|
public topicIndexSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
public topicIndex: number = 0;
|
||||||
|
/**
|
||||||
|
* Current category
|
||||||
|
*/
|
||||||
|
public categoryIndexSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
public categoryIndex: number = 0;
|
||||||
|
/**
|
||||||
|
* Current Subcategory
|
||||||
|
*/
|
||||||
|
public subCategoryIndexSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
|
||||||
|
public subCategoryIndex: number = 0;
|
||||||
|
/**
|
||||||
|
* Current element and index of topic, category or subcategory to be deleted.
|
||||||
|
*/
|
||||||
|
public form: UntypedFormGroup;
|
||||||
|
public element: Topic | Category | SubCategory;
|
||||||
|
public type: 'topic' | 'category' | 'subcategory' = "topic";
|
||||||
|
public index: number = -1;
|
||||||
|
public visibility: Visibility;
|
||||||
|
|
||||||
|
@ViewChild('deleteModal', {static: true}) deleteModal: AlertModal;
|
||||||
|
@ViewChild('editModal', {static: true}) editModal: AlertModal;
|
||||||
|
@ViewChild('visibilityModal', {static: true}) visibilityModal: AlertModal;
|
||||||
|
@ViewChildren(TransitionGroupComponent) transitions: QueryList<TransitionGroupComponent>;
|
||||||
|
|
||||||
|
public elementChildrenActionOnDelete: string;
|
||||||
|
public filters: UntypedFormGroup;
|
||||||
|
public all: Option = {
|
||||||
|
value: 'all',
|
||||||
|
label: 'All'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private title: Title,
|
||||||
|
private fb: UntypedFormBuilder,
|
||||||
|
private stakeholderService: StakeholderService,
|
||||||
|
private userManagementService: UserManagementService,
|
||||||
|
private layoutService: LayoutService,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
@Inject(PLATFORM_ID) private platformId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
let subscription: Subscription;
|
||||||
|
this.subscriptions.push(this.topicIndexSubject.asObservable().subscribe(index => {
|
||||||
|
this.topicChanged(() => {
|
||||||
|
this.topicIndex = index;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
this.subscriptions.push(this.categoryIndexSubject.asObservable().subscribe(index => {
|
||||||
|
this.categoryChanged(() => {
|
||||||
|
this.categoryIndex = index;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
this.subscriptions.push(this.subCategoryIndexSubject.asObservable().subscribe(index => {
|
||||||
|
this.subCategoryChanged(() => {
|
||||||
|
this.subCategoryIndex = index;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
this.subscriptions.push(this.route.params.subscribe(params => {
|
||||||
|
if (subscription) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
subscription = this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => {
|
||||||
|
if (stakeholder) {
|
||||||
|
this.stakeholder = stakeholder;
|
||||||
|
if (params['topic']) {
|
||||||
|
this.chooseTopic(this.stakeholder.topics.findIndex(topic => topic.alias === params['topic']));
|
||||||
|
} else {
|
||||||
|
this.chooseTopic(0);
|
||||||
|
}
|
||||||
|
this.chooseCategory(0);
|
||||||
|
this.filters = this.fb.group({
|
||||||
|
chartType: this.fb.control('all'),
|
||||||
|
status: this.fb.control('all'),
|
||||||
|
keyword: this.fb.control('')
|
||||||
|
});
|
||||||
|
if (this.topicIndex === -1) {
|
||||||
|
this.navigateToError();
|
||||||
|
} else {
|
||||||
|
this.title.setTitle(stakeholder.name + " | Indicators");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.topicSubscriptions.push(subscription);
|
||||||
|
}));
|
||||||
|
this.topicSubscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
|
||||||
|
this.user = user;
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
if(this.topics) {
|
||||||
|
let activeIndex = UIkit.nav(this.topics.element.nativeElement).items.findIndex(item => item.classList.contains('uk-open'));
|
||||||
|
if(activeIndex !== this.topicIndex) {
|
||||||
|
setTimeout(() => {
|
||||||
|
UIkit.nav(this.topics.element.nativeElement).toggle(this.topicIndex, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get isBrowser() {
|
||||||
|
return this.platformId === 'browser';
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnDestroy() {
|
||||||
|
this.topicSubscriptions.forEach(value => {
|
||||||
|
if (value instanceof Subscriber) {
|
||||||
|
value.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.subscriptions.forEach(value => {
|
||||||
|
if (value instanceof Subscriber) {
|
||||||
|
value.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canExit(): boolean {
|
||||||
|
this.topicSubscriptions.forEach(value => {
|
||||||
|
if (value instanceof Subscriber) {
|
||||||
|
value.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.stakeholderService.setStakeholder(this.stakeholder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private findById(id: string) {
|
||||||
|
return this.transitions?this.transitions.find(item => item.id === id):null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get topics(): TransitionGroupComponent {
|
||||||
|
return this.findById('topics');
|
||||||
|
}
|
||||||
|
|
||||||
|
get categories(): TransitionGroupComponent {
|
||||||
|
return this.findById('categories-' + this.topicIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
get subCategories(): TransitionGroupComponent {
|
||||||
|
return this.findById('subCategories');
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(element: any) {
|
||||||
|
UIkit.dropdown(element).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
stakeholderChanged() {
|
||||||
|
this.change.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveElement() {
|
||||||
|
if (this.type === "topic") {
|
||||||
|
this.saveTopic();
|
||||||
|
} else if (this.type === "category") {
|
||||||
|
this.saveCategory();
|
||||||
|
} else {
|
||||||
|
this.saveSubCategory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteElement() {
|
||||||
|
if (this.type === "topic") {
|
||||||
|
this.deleteTopic();
|
||||||
|
} else if (this.type === "category") {
|
||||||
|
this.deleteCategory();
|
||||||
|
} else {
|
||||||
|
this.deleteSubcategory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeElementStatus(propagate: boolean = false) {
|
||||||
|
if (this.type === "topic") {
|
||||||
|
this.changeTopicStatus(propagate);
|
||||||
|
} else if (this.type === "category") {
|
||||||
|
this.changeCategoryStatus(propagate);
|
||||||
|
} else {
|
||||||
|
this.changeSubcategoryStatus(propagate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public chooseTopic(topicIndex: number) {
|
||||||
|
this.topicIndexSubject.next(topicIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildTopic(topic: Topic) {
|
||||||
|
let topics = this.stakeholder.topics.filter(element => element._id !== topic._id);
|
||||||
|
this.form = this.fb.group({
|
||||||
|
_id: this.fb.control(topic._id),
|
||||||
|
name: this.fb.control(topic.name, Validators.required),
|
||||||
|
description: this.fb.control(topic.description),
|
||||||
|
creationDate: this.fb.control(topic.creationDate),
|
||||||
|
alias: this.fb.control(topic.alias, [
|
||||||
|
Validators.required,
|
||||||
|
this.stakeholderUtils.aliasValidator(topics)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
visibility: this.fb.control(topic.visibility),
|
||||||
|
defaultId: this.fb.control(topic.defaultId),
|
||||||
|
categories: this.fb.control(topic.categories),
|
||||||
|
icon: this.fb.control(topic.icon)
|
||||||
|
});
|
||||||
|
this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => {
|
||||||
|
let i = 1;
|
||||||
|
value = this.stakeholderUtils.generateAlias(value);
|
||||||
|
this.form.controls['alias'].setValue(value);
|
||||||
|
while (this.form.get('alias').invalid) {
|
||||||
|
this.form.controls['alias'].setValue(value + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public editTopicOpen(index = -1) {
|
||||||
|
this.index = index;
|
||||||
|
this.type = 'topic';
|
||||||
|
if (index === -1) {
|
||||||
|
this.buildTopic(new Topic(null, null, null, "PUBLIC"));
|
||||||
|
} else {
|
||||||
|
this.buildTopic(this.stakeholder.topics[index]);
|
||||||
|
}
|
||||||
|
this.editOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveTopic() {
|
||||||
|
if (!this.form.invalid) {
|
||||||
|
let path = [this.stakeholder._id];
|
||||||
|
let callback = (topic: Topic): void => {
|
||||||
|
this.topicChanged(() => {
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.stakeholder.topics.push(topic);
|
||||||
|
} else {
|
||||||
|
this.stakeholder.topics[this.index] = HelperFunctions.copy(topic);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.save('Topic has been successfully created', path, this.form.value, callback);
|
||||||
|
} else {
|
||||||
|
this.save('Topic has been successfully saved', path, this.form.value, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeTopicStatus(propagate: boolean = false) {
|
||||||
|
let path = [
|
||||||
|
this.stakeholder._id,
|
||||||
|
this.stakeholder.topics[this.index]._id
|
||||||
|
];
|
||||||
|
let callback = (topic: Topic): void => {
|
||||||
|
this.topicChanged(() => {
|
||||||
|
this.stakeholder.topics[this.index] = HelperFunctions.copy(topic);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
this.changeStatus(this.stakeholder.topics[this.index], path, this.visibility, callback, propagate);
|
||||||
|
this.visibilityModal.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteTopicOpen(index = this.topicIndex, childrenAction: string = null) {
|
||||||
|
this.type = 'topic';
|
||||||
|
this.index = index;
|
||||||
|
this.element = this.stakeholder.topics[this.index];
|
||||||
|
this.deleteOpen(childrenAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteTopic() {
|
||||||
|
let path: string[] = [
|
||||||
|
this.stakeholder._id,
|
||||||
|
this.stakeholder.topics[this.index]._id
|
||||||
|
];
|
||||||
|
let callback = (): void => {
|
||||||
|
this.topicChanged(() => {
|
||||||
|
this.stakeholder.topics.splice(this.index, 1);
|
||||||
|
if(this.topicIndex === this.index) {
|
||||||
|
this.chooseTopic(Math.max(0, this.index - 1));
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
this.delete('Topic has been successfully be deleted', path, callback, (this.topicIndex === this.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public moveTopic(index: number, newIndex: number = index - 1) {
|
||||||
|
this.topics.init();
|
||||||
|
let path = [this.stakeholder._id];
|
||||||
|
let ids = this.stakeholder.topics.map(topic => topic._id);
|
||||||
|
HelperFunctions.swap(ids, index, newIndex);
|
||||||
|
this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => {
|
||||||
|
HelperFunctions.swap(this.stakeholder.topics, index, newIndex);
|
||||||
|
if(this.topicIndex === index) {
|
||||||
|
this.chooseTopic(newIndex);
|
||||||
|
} else if(this.topicIndex === newIndex) {
|
||||||
|
this.chooseTopic(index);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise(error.error.message)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public chooseCategory(index: number) {
|
||||||
|
this.categoryIndexSubject.next(index);
|
||||||
|
this.chooseSubcategory(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildCategory(category: Category) {
|
||||||
|
let categories = this.stakeholder.topics[this.topicIndex].categories.filter(element => element._id !== category._id);
|
||||||
|
this.form = this.fb.group({
|
||||||
|
_id: this.fb.control(category._id),
|
||||||
|
name: this.fb.control(category.name, Validators.required),
|
||||||
|
description: this.fb.control(category.description),
|
||||||
|
creationDate: this.fb.control(category.creationDate),
|
||||||
|
alias: this.fb.control(category.alias, [
|
||||||
|
Validators.required,
|
||||||
|
this.stakeholderUtils.aliasValidator(categories)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
visibility: this.fb.control(category.visibility),
|
||||||
|
defaultId: this.fb.control(category.defaultId),
|
||||||
|
subCategories: this.fb.control(category.subCategories)
|
||||||
|
});
|
||||||
|
this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => {
|
||||||
|
let i = 1;
|
||||||
|
value = this.stakeholderUtils.generateAlias(value);
|
||||||
|
this.form.controls['alias'].setValue(value);
|
||||||
|
while (this.form.get('alias').invalid) {
|
||||||
|
this.form.controls['alias'].setValue(value + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public editCategoryOpen(index: number = -1) {
|
||||||
|
this.index = index;
|
||||||
|
this.type = 'category';
|
||||||
|
if (index === -1) {
|
||||||
|
this.buildCategory(new Category(null, null, null, "PUBLIC"));
|
||||||
|
} else {
|
||||||
|
this.buildCategory(this.stakeholder.topics[this.topicIndex].categories[index]);
|
||||||
|
}
|
||||||
|
this.editOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveCategory() {
|
||||||
|
if (!this.form.invalid) {
|
||||||
|
let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id];
|
||||||
|
let callback = (category: Category): void => {
|
||||||
|
this.categoryChanged(() => {
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories.push(category);
|
||||||
|
this.categories.init();
|
||||||
|
} else {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.index] = HelperFunctions.copy(category);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.save('Category has been successfully created', path, this.form.value, callback);
|
||||||
|
} else {
|
||||||
|
this.save('Category has been successfully saved', path, this.form.value, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeCategoryStatus(propagate: boolean = false) {
|
||||||
|
let path = [
|
||||||
|
this.stakeholder._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex]._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.index]._id
|
||||||
|
];
|
||||||
|
let callback = (category: Category): void => {
|
||||||
|
this.categoryChanged(() => {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.index] = HelperFunctions.copy(category);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
this.changeStatus(this.stakeholder.topics[this.topicIndex].categories[this.index], path, this.visibility, callback, propagate);
|
||||||
|
this.visibilityModal.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteCategoryOpen(index: number, childrenAction: string = null) {
|
||||||
|
this.type = 'category';
|
||||||
|
this.index = index;
|
||||||
|
this.element = this.stakeholder.topics[this.topicIndex].categories[this.index];
|
||||||
|
this.deleteOpen(childrenAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteCategory() {
|
||||||
|
let path: string[] = [
|
||||||
|
this.stakeholder._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex]._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.index]._id
|
||||||
|
];
|
||||||
|
let callback = (): void => {
|
||||||
|
this.categoryChanged(() => {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories.splice(this.index, 1);
|
||||||
|
if(this.categoryIndex === this.index) {
|
||||||
|
this.chooseCategory(Math.max(0, this.index - 1));
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
this.delete('Category has been successfully be deleted', path, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public moveCategory(index: number, newIndex: number = index - 1) {
|
||||||
|
this.categories.init();
|
||||||
|
let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id];
|
||||||
|
let ids = this.stakeholder.topics[this.topicIndex].categories.map(category => category._id);
|
||||||
|
HelperFunctions.swap(ids, index, newIndex);
|
||||||
|
this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => {
|
||||||
|
HelperFunctions.swap(this.stakeholder.topics[this.topicIndex].categories, index, newIndex);
|
||||||
|
if(this.categoryIndex === index) {
|
||||||
|
this.chooseCategory(newIndex);
|
||||||
|
} else if(this.categoryIndex === newIndex) {
|
||||||
|
this.chooseCategory(index);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise(error.error.message)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chooseSubcategory(subcategoryIndex: number) {
|
||||||
|
this.subCategoryIndexSubject.next(subcategoryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildSubcategory(subCategory: SubCategory) {
|
||||||
|
let subCategories = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.filter(element => element._id !== subCategory._id);
|
||||||
|
this.form = this.fb.group({
|
||||||
|
_id: this.fb.control(subCategory._id),
|
||||||
|
name: this.fb.control(subCategory.name, Validators.required),
|
||||||
|
description: this.fb.control(subCategory.description),
|
||||||
|
creationDate: this.fb.control(subCategory.creationDate),
|
||||||
|
alias: this.fb.control(subCategory.alias, [
|
||||||
|
Validators.required,
|
||||||
|
this.stakeholderUtils.aliasValidator(subCategories)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
visibility: this.fb.control(subCategory.visibility),
|
||||||
|
defaultId: this.fb.control(subCategory.defaultId),
|
||||||
|
charts: this.fb.control(subCategory.charts),
|
||||||
|
numbers: this.fb.control(subCategory.numbers)
|
||||||
|
});
|
||||||
|
this.topicSubscriptions.push(this.form.get('name').valueChanges.subscribe(value => {
|
||||||
|
let i = 1;
|
||||||
|
value = this.stakeholderUtils.generateAlias(value);
|
||||||
|
this.form.controls['alias'].setValue(value);
|
||||||
|
while (this.form.get('alias').invalid) {
|
||||||
|
this.form.controls['alias'].setValue(value + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public editSubCategoryOpen(index: number = -1) {
|
||||||
|
this.index = index;
|
||||||
|
this.type = 'subcategory';
|
||||||
|
if (index === -1) {
|
||||||
|
this.buildSubcategory(new SubCategory(null, null, null, "PUBLIC"));
|
||||||
|
} else {
|
||||||
|
this.buildSubcategory(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[index]);
|
||||||
|
}
|
||||||
|
this.editOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveSubCategory() {
|
||||||
|
if (!this.form.invalid) {
|
||||||
|
let path: string[] = [
|
||||||
|
this.stakeholder._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex]._id,
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id
|
||||||
|
];
|
||||||
|
let callback = (subCategory: SubCategory): void => {
|
||||||
|
this.subCategoryChanged(() => {
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.push(subCategory);
|
||||||
|
} else {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index] = HelperFunctions.copy(subCategory);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.save('Subcategory has been successfully created', path, this.form.value, callback);
|
||||||
|
} else {
|
||||||
|
this.save('Subcategory has been successfully saved', path, this.form.value, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeSubcategoryStatus(propagate: boolean = false) {
|
||||||
|
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
|
||||||
|
];
|
||||||
|
let callback = (subcategory: SubCategory): void => {
|
||||||
|
this.subCategoryChanged(() => {
|
||||||
|
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index] = HelperFunctions.copy(subcategory);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
this.changeStatus(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index], path, this.visibility, callback, propagate);
|
||||||
|
this.visibilityModal.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public deleteSubcategoryOpen(index, childrenAction: string = null) {
|
||||||
|
this.type = 'subcategory';
|
||||||
|
this.index = index;
|
||||||
|
this.element = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index];
|
||||||
|
this.deleteOpen(childrenAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteSubcategory() {
|
||||||
|
let path: string[] = [
|
||||||
|
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
|
||||||
|
];
|
||||||
|
let callback = (): void => {
|
||||||
|
this.subCategoryChanged(() => {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
};
|
||||||
|
this.delete('Subcategory has been successfully be deleted', path, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public moveSubCategory(index: number, newIndex: number = index - 1) {
|
||||||
|
this.subCategories.init();
|
||||||
|
let path = [this.stakeholder._id, this.stakeholder.topics[this.topicIndex]._id, this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id];
|
||||||
|
let ids = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories.map(subCategory => subCategory._id);
|
||||||
|
HelperFunctions.swap(ids, index, newIndex);
|
||||||
|
this.stakeholderService.reorderElements(properties.monitorServiceAPIURL, path, ids).subscribe(() => {
|
||||||
|
HelperFunctions.swap(this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories, index, newIndex);
|
||||||
|
if(this.subCategoryIndex === index) {
|
||||||
|
this.chooseSubcategory(newIndex);
|
||||||
|
} else if(this.subCategoryIndex === newIndex) {
|
||||||
|
this.chooseSubcategory(index);
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise(error.error.message)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private navigateToError() {
|
||||||
|
this.router.navigate([this.properties.errorLink], {queryParams: {'page': this.router.url}});
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCurator(): boolean {
|
||||||
|
return Session.isPortalAdministrator(this.user) || Session.isCurator(this.stakeholder.type, this.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private editOpen() {
|
||||||
|
this.editModal.cancelButtonText = 'Cancel';
|
||||||
|
this.editModal.okButtonLeft = false;
|
||||||
|
this.editModal.alertMessage = false;
|
||||||
|
if (this.index === -1) {
|
||||||
|
this.editModal.alertTitle = 'Create a new ' + this.type;
|
||||||
|
this.editModal.okButtonText = 'Create';
|
||||||
|
} else {
|
||||||
|
this.editModal.alertTitle = 'Edit ' + this.type + '\'s information ';
|
||||||
|
this.editModal.okButtonText = 'Save';
|
||||||
|
}
|
||||||
|
this.editModal.stayOpen = true;
|
||||||
|
this.editModal.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private deleteOpen(childrenAction: string = null) {
|
||||||
|
this.elementChildrenActionOnDelete = null;
|
||||||
|
if (childrenAction == "delete") {
|
||||||
|
this.elementChildrenActionOnDelete = childrenAction;
|
||||||
|
} else if (childrenAction == "disconnect") {
|
||||||
|
this.elementChildrenActionOnDelete = childrenAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteModal.alertTitle = 'Delete ' + this.type;
|
||||||
|
this.deleteModal.cancelButtonText = 'No';
|
||||||
|
this.deleteModal.okButtonText = 'Yes';
|
||||||
|
// this.deleteModal.cancelButton = false;
|
||||||
|
// this.deleteModal.okButton = false;
|
||||||
|
this.deleteModal.stayOpen = true;
|
||||||
|
this.deleteModal.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private save(message: string, path: string[], saveElement: any, callback: Function, redirect = false) {
|
||||||
|
this.loading = true;
|
||||||
|
this.topicSubscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, saveElement, path).subscribe(saveElement => {
|
||||||
|
callback(saveElement);
|
||||||
|
this.stakeholderChanged();
|
||||||
|
this.loading = false;
|
||||||
|
this.editModal.cancel();
|
||||||
|
NotificationHandler.rise(message);
|
||||||
|
if (redirect) {
|
||||||
|
this.router.navigate(['../' + saveElement.alias], {
|
||||||
|
relativeTo: this.route
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.editModal.cancel();
|
||||||
|
NotificationHandler.rise(error.error.message, 'danger');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private delete(message: string, path: string[], callback: Function, redirect = false) {
|
||||||
|
this.loading = true;
|
||||||
|
this.topicSubscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.elementChildrenActionOnDelete).subscribe(() => {
|
||||||
|
callback();
|
||||||
|
this.stakeholderChanged();
|
||||||
|
this.loading = false;
|
||||||
|
this.deleteModal.cancel();
|
||||||
|
NotificationHandler.rise(message);
|
||||||
|
if (redirect) {
|
||||||
|
this.back();
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
this.deleteModal.cancel();
|
||||||
|
NotificationHandler.rise(error.error.message, 'danger');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private changeStatus(element: Topic | Category | SubCategory, path: string[], visibility: Visibility, callback: Function = null, propagate: boolean = false) {
|
||||||
|
this.topicSubscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility, propagate).subscribe(returnedElement => {
|
||||||
|
if(propagate) {
|
||||||
|
callback(returnedElement);
|
||||||
|
NotificationHandler.rise(StringUtils.capitalize(this.type) + ' has been <b>successfully changed</b> to ' + returnedElement.visibility.toLowerCase());
|
||||||
|
} else {
|
||||||
|
element.visibility = returnedElement.visibility;
|
||||||
|
NotificationHandler.rise(StringUtils.capitalize(this.type) + ' has been <b>successfully changed</b> to ' + element.visibility.toLowerCase());
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
NotificationHandler.rise(error.error.message, 'danger');
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
back() {
|
||||||
|
this.router.navigate(['../'], {
|
||||||
|
relativeTo: this.route
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPluralTypeName(): string {
|
||||||
|
if (this.type == "topic") {
|
||||||
|
return "Topics";
|
||||||
|
} else if (this.type == "category") {
|
||||||
|
return "Categories";
|
||||||
|
} else if (this.type == "subcategory") {
|
||||||
|
return "Subcategories";
|
||||||
|
} else {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isSmallScreen() {
|
||||||
|
return this.layoutService.isSmallScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get open() {
|
||||||
|
return this.layoutService.open;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleOpen(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.layoutService.setOpen(!this.open);
|
||||||
|
}
|
||||||
|
|
||||||
|
public openVisibilityModal(index: number, visibility: Visibility, type: any) {
|
||||||
|
this.index = index;
|
||||||
|
this.visibility = visibility;
|
||||||
|
this.type = type;
|
||||||
|
this.visibilityModal.alertTitle = 'Visibility Status';
|
||||||
|
this.visibilityModal.alertFooter = false;
|
||||||
|
this.visibilityModal.open();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
import {PreviousRouteRecorder} from '../../utils/piwik/previousRouteRecorder.guard';
|
||||||
|
|
||||||
|
import {PiwikService} from '../../utils/piwik/piwik.service';
|
||||||
|
import {TopicComponent} from "./topic.component";
|
||||||
|
import {TopicRoutingModule} from "./topic-routing.module";
|
||||||
|
import {RouterModule} from "@angular/router";
|
||||||
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {IndicatorsComponent} from "./indicators.component";
|
||||||
|
import {AlertModalModule} from "../../utils/modal/alertModal.module";
|
||||||
|
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||||
|
import {ClickModule} from "../../utils/click/click.module";
|
||||||
|
import {IconsService} from "../../utils/icons/icons.service";
|
||||||
|
import {earth, incognito, restricted} from "../../utils/icons/icons";
|
||||||
|
import {IconsModule} from "../../utils/icons/icons.module";
|
||||||
|
import {PageContentModule} from "../../dashboard/sharedComponents/page-content/page-content.module";
|
||||||
|
import {LoadingModule} from "../../utils/loading/loading.module";
|
||||||
|
import {NotifyFormModule} from "../../notifications/notify-form/notify-form.module";
|
||||||
|
import {LogoUrlPipeModule} from "../../utils/pipes/logoUrlPipe.module";
|
||||||
|
import {TransitionGroupModule} from "../../utils/transition-group/transition-group.module";
|
||||||
|
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";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule, TopicRoutingModule, ClickModule, RouterModule, FormsModule, AlertModalModule,
|
||||||
|
ReactiveFormsModule, InputModule, IconsModule, PageContentModule, LoadingModule, NotifyFormModule, LogoUrlPipeModule, TransitionGroupModule, NumberRoundModule, SideBarModule, SidebarMobileToggleModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
TopicComponent, IndicatorsComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
PreviousRouteRecorder,
|
||||||
|
PiwikService
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
TopicComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TopicModule {
|
||||||
|
constructor(private iconsService: IconsService) {
|
||||||
|
this.iconsService.registerIcons([earth, incognito, restricted]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||||
|
import {map, take, tap} from "rxjs/operators";
|
||||||
|
import {UserManagementService} from "../../services/user-management.service";
|
||||||
|
import {LoginErrorCodes} from "../../login/utils/guardHelper.class";
|
||||||
|
import {Session} from "../../login/utils/helper.class";
|
||||||
|
import {StakeholderService} from "../../monitor/services/stakeholder.service";
|
||||||
|
import {Observable, zip} from "rxjs";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AdminDashboardGuard {
|
||||||
|
|
||||||
|
constructor(private router: Router,
|
||||||
|
private stakeholderService: StakeholderService,
|
||||||
|
private userManagementService: UserManagementService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
check(path: string, alias: string): Observable<boolean> | boolean {
|
||||||
|
let errorCode = LoginErrorCodes.NOT_LOGIN;
|
||||||
|
return zip(
|
||||||
|
this.userManagementService.getUserInfo(), this.stakeholderService.getStakeholder(alias)
|
||||||
|
).pipe(take(1),map(res => {
|
||||||
|
if(res[0]) {
|
||||||
|
errorCode = LoginErrorCodes.NOT_ADMIN;
|
||||||
|
}
|
||||||
|
return res[0] && res[1] && (Session.isPortalAdministrator(res[0]) ||
|
||||||
|
Session.isCurator(res[1].type, res[0]) || Session.isManager(res[1].type, res[1].alias, res[0]))
|
||||||
|
}),tap(authorized => {
|
||||||
|
if(!authorized){
|
||||||
|
this.router.navigate(['/user-info'], {queryParams: {'errorCode': errorCode, 'redirectUrl':path}});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||||
|
return this.check(state.url, route.params.stakeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||||
|
return this.check(state.url, childRoute.params.stakeholder);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
@import (reference) "~src/assets/openaire-theme/less/_import-variables.less";
|
||||||
|
|
||||||
|
.cache-progress {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: @global-z-index;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import {Component, Inject, Input, OnChanges, OnDestroy, OnInit, PLATFORM_ID, SimpleChanges} from "@angular/core";
|
||||||
|
import {Report} from "./cache-indicators";
|
||||||
|
import {CacheIndicatorsService} from "./cache-indicators.service";
|
||||||
|
import {interval, Subject, Subscription} from "rxjs";
|
||||||
|
import {map, switchMap, takeUntil} from "rxjs/operators";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'cache-indicators',
|
||||||
|
template: `
|
||||||
|
<div *ngIf="report" class="cache-progress">
|
||||||
|
<div class="uk-position-relative" [attr.uk-tooltip]="'Caching indicators process for ' + alias">
|
||||||
|
<div class="uk-progress-circle" [attr.percentage]="report.percentage?report.percentage:0" [style]="'--percentage: ' + (report.percentage?report.percentage:0)"></div>
|
||||||
|
<button *ngIf="report.percentage === 100" (click)="clear()" class="uk-icon-button uk-icon-button-xsmall uk-button-default uk-position-top-right"><icon name="close" [flex]="true" ratio="0.8"></icon></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styleUrls: ['cache-indicators.component.less']
|
||||||
|
})
|
||||||
|
export class CacheIndicatorsComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
|
report: Report;
|
||||||
|
subscriptions: Subscription[] = [];
|
||||||
|
interval: number = 10000;
|
||||||
|
readonly destroy$ = new Subject();
|
||||||
|
@Input() alias: string;
|
||||||
|
|
||||||
|
constructor(private cacheIndicatorsService: CacheIndicatorsService,
|
||||||
|
@Inject(PLATFORM_ID) private platformId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if(changes.alias) {
|
||||||
|
this.getReport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getReport() {
|
||||||
|
this.clear();
|
||||||
|
this.subscriptions.push(this.cacheIndicatorsService.getReport(this.alias).subscribe(report => {
|
||||||
|
this.getReportInterval(report);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
getReportInterval(report: Report) {
|
||||||
|
if(this.isBrowser && (this.report || !report?.completed)) {
|
||||||
|
this.report = report;
|
||||||
|
this.subscriptions.push(interval(this.interval).pipe(
|
||||||
|
map(() => this.cacheIndicatorsService.getReport(this.alias)),
|
||||||
|
switchMap(report => report),
|
||||||
|
takeUntil(this.destroy$)).subscribe(report => {
|
||||||
|
console.log(this.alias);
|
||||||
|
this.report = report;
|
||||||
|
if(this.report.completed) {
|
||||||
|
this.destroy$.next();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.subscriptions.forEach(subscription => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
})
|
||||||
|
this.report = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get isBrowser() {
|
||||||
|
return this.platformId === 'browser';
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {CacheIndicatorsComponent} from "./cache-indicators.component";
|
||||||
|
import {IconsModule} from "../../../utils/icons/icons.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, IconsModule],
|
||||||
|
declarations: [CacheIndicatorsComponent],
|
||||||
|
exports: [CacheIndicatorsComponent]
|
||||||
|
})
|
||||||
|
export class CacheIndicatorsModule {}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {Injectable} from "@angular/core";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
import {CustomOptions} from "../../../services/servicesUtils/customOptions.class";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CacheIndicatorsService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
createReport(alias: string) {
|
||||||
|
return this.http.post<any>(properties.domain + properties.baseLink + '/cache/' + alias, {}, CustomOptions.registryOptions())
|
||||||
|
.pipe(map(res => res.report));
|
||||||
|
}
|
||||||
|
|
||||||
|
getReport(alias: string) {
|
||||||
|
return this.http.get<any>(properties.domain + properties.baseLink + '/cache/' + alias, CustomOptions.registryOptions())
|
||||||
|
.pipe(map(res => res.report));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
import {IndicatorType, Stakeholder} from "../../../monitor/entities/stakeholder";
|
||||||
|
import axios from "axios";
|
||||||
|
import {IndicatorUtils} from "../indicator-utils";
|
||||||
|
import {Composer} from "../../../utils/email/composer";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
|
||||||
|
|
||||||
|
export interface CacheItem {
|
||||||
|
reportId: string,
|
||||||
|
type: IndicatorType,
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Report {
|
||||||
|
creator: string;
|
||||||
|
name: string;
|
||||||
|
success: number;
|
||||||
|
errors: {
|
||||||
|
url: string,
|
||||||
|
status: number
|
||||||
|
}[];
|
||||||
|
total: number;
|
||||||
|
completed: boolean;
|
||||||
|
percentage: number
|
||||||
|
|
||||||
|
constructor(total: number, name: string, creator: string) {
|
||||||
|
this.creator = creator;
|
||||||
|
this.name = name;
|
||||||
|
this.success = 0;
|
||||||
|
this.errors = [];
|
||||||
|
this.total = total;
|
||||||
|
this.completed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPercentage() {
|
||||||
|
this.percentage = Math.floor((this.success + this.errors.length) / this.total * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CacheIndicators {
|
||||||
|
|
||||||
|
private static BATCH_SIZE = 10;
|
||||||
|
|
||||||
|
private reports: Map<string, Report> = new Map<string, Report>();
|
||||||
|
private queue: CacheItem[] = [];
|
||||||
|
private process: Promise<void>;
|
||||||
|
private isFinished: boolean = true;
|
||||||
|
|
||||||
|
stakeholderToCacheItems(stakeholder: Stakeholder) {
|
||||||
|
let cacheItems: CacheItem[] = [];
|
||||||
|
let indicatorUtils = new IndicatorUtils();
|
||||||
|
stakeholder.topics.forEach(topic => {
|
||||||
|
topic.categories.forEach(category => {
|
||||||
|
category.subCategories.forEach(subCategory => {
|
||||||
|
subCategory.numbers.forEach(section => {
|
||||||
|
section.indicators.forEach(indicator => {
|
||||||
|
indicator.indicatorPaths.forEach(indicatorPath => {
|
||||||
|
let url = indicatorUtils.getNumberUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath));
|
||||||
|
cacheItems.push({
|
||||||
|
reportId: stakeholder._id,
|
||||||
|
type: 'number',
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
subCategory.charts.forEach(section => {
|
||||||
|
section.indicators.forEach(indicator => {
|
||||||
|
indicator.indicatorPaths.forEach(indicatorPath => {
|
||||||
|
let url = indicatorUtils.getChartUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath));
|
||||||
|
cacheItems.push({
|
||||||
|
reportId: stakeholder._id,
|
||||||
|
type: 'chart',
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return cacheItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public exists(id: string) {
|
||||||
|
return this.reports.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public completed(id: string) {
|
||||||
|
return !this.exists(id) || this.reports.get(id).completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createReport(id: string, cacheItems: CacheItem[], name: string, creator: string) {
|
||||||
|
let report = new Report(cacheItems.length, name, creator);
|
||||||
|
this.reports.set(id, report);
|
||||||
|
this.addItemsToQueue(cacheItems);
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getReport(id: string) {
|
||||||
|
return this.reports.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processQueue() {
|
||||||
|
this.isFinished = false;
|
||||||
|
while (this.queue.length > 0) {
|
||||||
|
let batch = this.queue.splice(0, CacheIndicators.BATCH_SIZE);
|
||||||
|
await this.processBatch(batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processBatch(batch: CacheItem[]) {
|
||||||
|
let promises: Promise<any>[] = [];
|
||||||
|
let ids = new Set<string>();
|
||||||
|
batch.forEach(item => {
|
||||||
|
let promise;
|
||||||
|
ids.add(item.reportId);
|
||||||
|
if (item.type === 'chart') {
|
||||||
|
let [url, json] = item.url.split('?json=');
|
||||||
|
json = decodeURIComponent(json);
|
||||||
|
json = statsToolParser(JSON.parse(json));
|
||||||
|
promise = axios.post(url, json);
|
||||||
|
} else {
|
||||||
|
promise = axios.get(item.url);
|
||||||
|
}
|
||||||
|
promises.push(promise.then(response => {
|
||||||
|
let report = this.reports.get(item.reportId);
|
||||||
|
if (report) {
|
||||||
|
report.success++;
|
||||||
|
report.setPercentage();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}).catch(error => {
|
||||||
|
let report = this.reports.get(item.reportId);
|
||||||
|
if (report) {
|
||||||
|
report.errors.push({url: item.url, status: error.response.status});
|
||||||
|
report.setPercentage();
|
||||||
|
}
|
||||||
|
return error.response;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
await Promise.all(promises);
|
||||||
|
ids.forEach(id => {
|
||||||
|
let report = this.reports.get(id);
|
||||||
|
if (report?.percentage === 100) {
|
||||||
|
report.completed = true;
|
||||||
|
this.sendEmail(report);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addItemsToQueue(cacheItems: CacheItem[]) {
|
||||||
|
cacheItems.forEach(item => {
|
||||||
|
this.queue.push(item);
|
||||||
|
});
|
||||||
|
if (this.isFinished) {
|
||||||
|
this.processQueue().then(() => {
|
||||||
|
this.isFinished = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEmail(report: Report) {
|
||||||
|
let email = Composer.composeEmailToReportCachingProcess(report);
|
||||||
|
axios.post(properties.adminToolsAPIURL + "sendMail/", email).catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function statsToolParser(dataJSONobj: any): any {
|
||||||
|
let RequestInfoObj = Object.assign({});
|
||||||
|
switch (dataJSONobj.library) {
|
||||||
|
case "GoogleCharts":
|
||||||
|
//Pass the Chart library to ChartDataFormatter
|
||||||
|
RequestInfoObj.library = dataJSONobj.library;
|
||||||
|
RequestInfoObj.orderBy = dataJSONobj.orderBy;
|
||||||
|
|
||||||
|
//Create ChartInfo Object Array
|
||||||
|
RequestInfoObj.chartsInfo = [];
|
||||||
|
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
|
||||||
|
//along with the requested Chart type
|
||||||
|
RequestInfoObj.chartsInfo = dataJSONobj.chartDescription.queriesInfo;
|
||||||
|
break;
|
||||||
|
case "eCharts":
|
||||||
|
//Pass the Chart library to ChartDataFormatter
|
||||||
|
RequestInfoObj.library = dataJSONobj.library;
|
||||||
|
RequestInfoObj.orderBy = dataJSONobj.orderBy;
|
||||||
|
|
||||||
|
//Create ChartInfo Object Array
|
||||||
|
RequestInfoObj.chartsInfo = [];
|
||||||
|
|
||||||
|
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
|
||||||
|
//along with the requested Chart type
|
||||||
|
for (let index = 0; index < dataJSONobj.chartDescription.queries.length; index++) {
|
||||||
|
let element = dataJSONobj.chartDescription.queries[index];
|
||||||
|
var ChartInfoObj = Object.assign({});
|
||||||
|
|
||||||
|
if (element.type === undefined)
|
||||||
|
ChartInfoObj.type = dataJSONobj.chartDescription.series[index].type;
|
||||||
|
else
|
||||||
|
ChartInfoObj.type = element.type;
|
||||||
|
|
||||||
|
if (element.name === undefined)
|
||||||
|
ChartInfoObj.name = null;
|
||||||
|
else
|
||||||
|
ChartInfoObj.name = element.name;
|
||||||
|
|
||||||
|
ChartInfoObj.query = element.query;
|
||||||
|
RequestInfoObj.chartsInfo.push(ChartInfoObj);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "HighCharts":
|
||||||
|
RequestInfoObj.library = dataJSONobj.library;
|
||||||
|
RequestInfoObj.orderBy = dataJSONobj.orderBy;
|
||||||
|
//Pass the Chart type to ChartDataFormatter
|
||||||
|
var defaultType = dataJSONobj.chartDescription.chart.type;
|
||||||
|
//Create ChartInfo Object Array
|
||||||
|
RequestInfoObj.chartsInfo = [];
|
||||||
|
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
|
||||||
|
//along with the requested Chart type
|
||||||
|
dataJSONobj.chartDescription.queries.forEach(element => {
|
||||||
|
var ChartInfoObj = Object.assign({});
|
||||||
|
|
||||||
|
if (element.type === undefined)
|
||||||
|
ChartInfoObj.type = defaultType;
|
||||||
|
else
|
||||||
|
ChartInfoObj.type = element.type;
|
||||||
|
|
||||||
|
if (element.name === undefined)
|
||||||
|
ChartInfoObj.name = null;
|
||||||
|
else
|
||||||
|
ChartInfoObj.name = element.name;
|
||||||
|
|
||||||
|
ChartInfoObj.query = element.query;
|
||||||
|
RequestInfoObj.chartsInfo.push(ChartInfoObj);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "HighMaps":
|
||||||
|
RequestInfoObj.library = dataJSONobj.library;
|
||||||
|
//Create ChartInfo Object Array
|
||||||
|
RequestInfoObj.chartsInfo = [];
|
||||||
|
|
||||||
|
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
|
||||||
|
dataJSONobj.mapDescription.queries.forEach(element => {
|
||||||
|
var ChartInfoObj = Object.assign({});
|
||||||
|
|
||||||
|
if (element.name === undefined)
|
||||||
|
ChartInfoObj.name = null;
|
||||||
|
else
|
||||||
|
ChartInfoObj.name = element.name;
|
||||||
|
|
||||||
|
ChartInfoObj.query = element.query;
|
||||||
|
RequestInfoObj.chartsInfo.push(ChartInfoObj);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Unsupported Library: " + dataJSONobj.library);
|
||||||
|
}
|
||||||
|
return RequestInfoObj;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,24 @@
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {SourceType} from "../../../monitor/entities/stakeholder";
|
||||||
|
import {IndicatorUtils} from "../indicator-utils";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class StatisticsService {
|
||||||
|
|
||||||
|
indicatorsUtils = new IndicatorUtils();
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getNumbers(source: SourceType, url: string): Observable<any> {
|
||||||
|
if (source !== null) {
|
||||||
|
return this.http.get<any>(this.indicatorsUtils.getNumberUrl(source, url));
|
||||||
|
} else {
|
||||||
|
return this.http.get<any>(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import {Injectable} from "@angular/core";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {properties} from "src/environments/environment";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {map} from "rxjs/operators";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class StatsProfilesService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatsProfiles(): Observable<string[]> {
|
||||||
|
return this.http.get<any[]>(properties.monitorStatsFrameUrl + 'schema/profiles')
|
||||||
|
.pipe(map(profiles => profiles.map(profile => profile.name)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import {SafeResourceUrl} from "@angular/platform-browser";
|
import {SafeResourceUrl} from "@angular/platform-browser";
|
||||||
import {properties} from "../../../../environments/environment";
|
import {properties} from "../../../../environments/environment";
|
||||||
import {Session, User} from "../../login/utils/helper.class";
|
import {Session, User} from "../../login/utils/helper.class";
|
||||||
|
import {Option} from "../../sharedComponents/input/input.component";
|
||||||
|
|
||||||
export const ChartHelper = {
|
export const ChartHelper = {
|
||||||
prefix: "((__",
|
prefix: "((__",
|
||||||
|
@ -309,3 +310,10 @@ export enum StakeholderEntities {
|
||||||
ORGANIZATIONS = 'Research Institutions',
|
ORGANIZATIONS = 'Research Institutions',
|
||||||
PROJECTS = 'Projects'
|
PROJECTS = 'Projects'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export let stakeholderTypes: Option[] = [
|
||||||
|
{value: 'funder', label: StakeholderEntities.FUNDER},
|
||||||
|
{value: 'ri', label: StakeholderEntities.RI},
|
||||||
|
{value: 'project', label: StakeholderEntities.PROJECT},
|
||||||
|
{value: 'organization', label: StakeholderEntities.ORGANIZATION}
|
||||||
|
];
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {Meta, Title} from "@angular/platform-browser";
|
||||||
import {SEOService} from "../../sharedComponents/SEO/SEO.service";
|
import {SEOService} from "../../sharedComponents/SEO/SEO.service";
|
||||||
import {Breadcrumb} from "../../utils/breadcrumbs/breadcrumbs.component";
|
import {Breadcrumb} from "../../utils/breadcrumbs/breadcrumbs.component";
|
||||||
import {Subscriber} from "rxjs";
|
import {Subscriber} from "rxjs";
|
||||||
|
import {StakeholderEntities} from "../entities/stakeholder";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'indicator-themes-page',
|
selector: 'indicator-themes-page',
|
||||||
|
@ -53,9 +54,9 @@ import {Subscriber} from "rxjs";
|
||||||
This is the current set of indicator themes we cover. We’ll keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
|
This is the current set of indicator themes we cover. We’ll keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route">funders</a>,
|
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route" class="uk-text-lowercase">{{entities.FUNDERS}}</a>,
|
||||||
<a [routerLink]="['../organization']" [relativeTo]="route">research institutions</a> and
|
<a [routerLink]="['../organization']" [relativeTo]="route" class="uk-text-lowercase">{{entities.ORGANIZATIONS}}</a> and
|
||||||
<a [routerLink]="['../ri']" [relativeTo]="route">research initiatives</a>)
|
<a [routerLink]="['../ri']" [relativeTo]="route" class="uk-text-lowercase">{{entities.RIS}}</a>)
|
||||||
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics.
|
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,6 +68,7 @@ import {Subscriber} from "rxjs";
|
||||||
export class IndicatorThemesComponent implements OnInit, OnDestroy {
|
export class IndicatorThemesComponent implements OnInit, OnDestroy {
|
||||||
private subscriptions: any[] = [];
|
private subscriptions: any[] = [];
|
||||||
public properties = properties;
|
public properties = properties;
|
||||||
|
public entities = StakeholderEntities;
|
||||||
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Resources - Themes'}];
|
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Resources - Themes'}];
|
||||||
|
|
||||||
constructor(private router: Router,
|
constructor(private router: Router,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import {COOKIE} from '../../login/utils/helper.class';
|
|
||||||
import {HttpHeaders} from "@angular/common/http";
|
import {HttpHeaders} from "@angular/common/http";
|
||||||
|
|
||||||
export type MediaType = 'application/json' | 'text/plain'
|
export type MediaType = 'application/json' | 'text/plain'
|
||||||
|
|
Loading…
Reference in New Issue