Override Reference Stakeholders - Generate Stakeholders from file #55

Merged
k.triantafyllou merged 4 commits from angular-18-override-references into angular-18 2024-11-14 11:30:20 +01:00
12 changed files with 432 additions and 189 deletions

View File

@ -161,7 +161,6 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
public isFull: boolean;
public loading: boolean = false;
public typesByRole: Option[];
public statsProfiles: string[];
/**
* Photo upload
* */
@ -176,8 +175,9 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
constructor(private fb: UntypedFormBuilder,
private stakeholderService: StakeholderService,
private statsProfileService: StatsProfilesService,
private utilsService: UtilitiesService, private userManagementService: UserManagementService,) {
private utilsService: UtilitiesService,
private userManagementService: UserManagementService,
protected statsProfileService: StatsProfilesService) {
super();
}
@ -201,11 +201,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
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 = [];
}));
this.setStatsProfiles();
} else {
this.statsProfiles = [];
}
@ -297,6 +293,14 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
}));
}
public get isStandalone() {
return this.stakeholderCategory?.value !== 'dependent';
}
public get isUmbrella() {
return this.stakeholderCategory?.value === 'umbrella';
}
public get isDefault() {
return this.stakeholderCategory?.value === 'templates';
}
@ -340,7 +344,7 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
});
}
onTypeChange(value, defaultStakeholders: Stakeholder[]) {
onTypeChange(value: string, defaultStakeholders: Stakeholder[]) {
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew) ? [] : Validators.required));
this.defaultStakeholdersOptions = [{
label: 'New blank profile',
@ -384,8 +388,8 @@ export class EditStakeholderComponent extends StakeholderBaseComponent {
this.removePhoto();
this.subscriptions.push(this.stakeholderService.buildStakeholder(
this.stakeholderFb.getRawValue(), copyId,
this.stakeholderCategory.value !== 'dependent',
this.stakeholderCategory.value === 'umbrella')
this.isStandalone,
this.isUmbrella)
.subscribe(stakeholder => {
this.notification.entity = stakeholder._id;
this.notification.stakeholder = stakeholder.alias;

View File

@ -5,6 +5,9 @@
<a *ngIf="stakeholderCategory.tooltip" class="uk-margin-small-left uk-button uk-button-link" [attr.uk-tooltip]="stakeholderCategory.tooltip">
<icon name="info" [flex]="true" [ratio]="0.8"></icon>
</a>
<button *ngIf="!isDefault && stakeholderUtils.hasMultiBuild" class="uk-button uk-button-link uk-margin-small-left" (click)="buildMultiStakeholderOpen()">
<icon name="upload" [flex]="true"></icon>
</button>
</div>
<paging-no-load *ngIf="filteredStakeholders?.length > pageSize"
(pageChange)="updateCurrentPage($event)"
@ -26,7 +29,7 @@
<li>
<a (click)="editStakeholder(stakeholder); hide(element)">Edit</a>
</li>
<li *ngIf="isCurator && stakeholderUtils.isCachingIndicators">
<li *ngIf="isCurator && stakeholderUtils.hasCachingIndicators">
<a (click)="createReport(stakeholder);hide(element)">Cache Indicators</a>
</li>
<li *ngIf="showVisibility" class="uk-nav-divider"></li>
@ -76,6 +79,76 @@
</div>
</div>
</div>
<modal-alert *ngIf="stakeholderUtils.hasMultiBuild" #buildStakeholderModal [large]="true"
(cancelOutput)="clear()"
(alertOutput)="generate()"
[okDisabled]="stakeholderFb?.invalid || !indexFields"
classTitle="uk-background-primary uk-light">
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<div class="uk-height-large uk-position-relative" *ngIf="loading">
<loading class="uk-position-center"></loading>
</div>
<div *ngIf="!isDefault && stakeholderFb" class="uk-padding" [class.uk-hidden]="loading">
<div class="uk-margin-medium-bottom">
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid" uk-grid>
<div class="uk-width-1-1 uk-margin-medium-bottom uk-text-center">
<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"
(click)="file.value = ''; file.click()">
<icon name="cloud_upload" [flex]="true"></icon>
<span class="uk-margin-small-left">Upload a file</span>
</button>
</div>
<div *ngIf="indexFields && file" class="uk-margin-xsmall-top">{{file.files.item(0).name}}</div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_id')"
placeholder="Index ID Field"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_name')"
placeholder="Index Name Field"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_shortName')"
placeholder="Index Short Name Field"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('alias')"
[type]="'select'" [options]="['index_id', 'index_name', 'index_shortName']"
placeholder="URL Alias Generator Field"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('statsProfile')"
[type]="'select'" [options]="statsProfiles" [disabled]="statsProfiles?.length === 0">
placeholder="Stats Profile"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('locale')" [type]="'select'"
[options]="stakeholderUtils.locales"
placeholder="Locale"></div>
</div>
<div *ngIf="showVisibility" class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('visibility')"
[placeholder]="'Select a status'"
[options]="stakeholderUtils.visibilities" type="select"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('type')"
[placeholder]="'Select a type of ' + entities.stakeholder"
[options]="typesByRole" type="select"></div>
</div>
<div *ngIf="canChooseTemplate" class="uk-width-1-3@m">
<div [placeholder]="'Select a template'"
input [formInput]="stakeholderFb.get('defaultId')"
[options]="defaultStakeholdersOptions" type="select"></div>
</div>
</div>
</form>
</div>
</div>
</modal-alert>
<modal-alert #editStakeholderModal [large]="true" classTitle="uk-background-primary uk-light"
(alertOutput)="editStakeholderComponent.save(callback)"
(cancelOutput)="editStakeholderComponent.removePhoto()"

View File

@ -7,6 +7,10 @@ import {EditStakeholderComponent} from "../general/edit-stakeholder/edit-stakeho
import {CacheIndicatorsService} from "../utils/cache-indicators/cache-indicators.service";
import {NotificationHandler} from "../../utils/notification-handler";
import {FilteredStakeholdersBaseComponent} from "../shared/filtered-stakeholders-base.component";
import {FormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {UploadFileService} from "../../services/upload-file.service";
import {Option} from "../../sharedComponents/input/input.component";
import {StatsProfilesService} from "../utils/services/stats-profiles.service";
declare var UIkit;
@ -17,6 +21,7 @@ declare var UIkit;
styleUrls: ["manageStakeholders.component.less"]
})
export class ManageStakeholdersComponent extends FilteredStakeholdersBaseComponent {
filesToUpload: Array<File> = [];
@Input()
public defaultStakeholders: Stakeholder[];
/**
@ -37,28 +42,37 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
public user = null;
public callback: Function;
/**
* Grid or List View
*/
@ViewChild('editStakeholderModal', { static: true }) editStakeholderModal: AlertModal;
@ViewChild('deleteStakeholderModal', { static: true }) deleteStakeholderModal: AlertModal;
@ViewChild('editStakeholderComponent', { static: true }) editStakeholderComponent: EditStakeholderComponent;
* Build Multi Stakeholders
* */
public indexFields: any[];
public stakeholderFb: UntypedFormGroup;
public loading: boolean = false;
public typesByRole: Option[] = [];
public defaultStakeholdersOptions: Option[];
@ViewChild('editStakeholderModal', {static: true}) editStakeholderModal: AlertModal;
@ViewChild('buildStakeholderModal') buildStakeholderModal: AlertModal;
@ViewChild('deleteStakeholderModal', {static: true}) deleteStakeholderModal: AlertModal;
@ViewChild('editStakeholderComponent', {static: true}) editStakeholderComponent: EditStakeholderComponent;
constructor(private stakeholderService: StakeholderService,
private cacheIndicatorsService: CacheIndicatorsService,
protected cdr: ChangeDetectorRef) {
private fb: FormBuilder,
private uploadFileService: UploadFileService,
protected cdr: ChangeDetectorRef,
protected statsProfileService: StatsProfilesService) {
super();
}
ngOnInit(): void {
super.ngOnInit();
if(this.stakeholderCategory.value === 'templates') {
if (this.stakeholderCategory.value === 'templates') {
this.message = 'Create a new ' + this.stakeholderCategory.name + ' profile.';
} else {
this.message = 'Create a new ' + this.stakeholderCategory.name +
' profile by selecting a type and generate indicators based on a default or a blank profile.';
}
}
hide(element: any) {
UIkit.dropdown(element).hide();
}
@ -67,12 +81,83 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
this.aliasesChange.emit(this.aliases);
super.changed();
}
onTypeChange(value: string, defaultStakeholders: Stakeholder[]) {
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, 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 clear() {
this.stakeholderFb = null;
this.indexFields = null;
}
public buildMultiStakeholderOpen() {
this.setStatsProfiles();
this.stakeholder = new Stakeholder(null, null, null,
null, null, 'index_id', null, null);
this.typesByRole = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
this.stakeholderFb = this.fb.group({
alias: this.fb.control(this.stakeholder.alias, Validators.required),
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),
statsProfile: this.fb.control(this.stakeholder.statsProfile, Validators.required),
locale: this.fb.control(this.stakeholder.locale, Validators.required),
type: this.fb.control(this.stakeholder.type, Validators.required),
visibility: this.fb.control(this.stakeholder.visibility, Validators.required),
defaultId: this.fb.control(this.stakeholder.defaultId, Validators.required)
});
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
this.onTypeChange(value, this.defaultStakeholders);
}));
if(this.buildStakeholderModal) {
this.buildStakeholderModal.alertTitle = 'Generate Profiles from File';
this.buildStakeholderModal.cancelButtonText = 'Cancel';
this.buildStakeholderModal.okButtonLeft = false;
this.buildStakeholderModal.okButtonText = 'Generate';
this.buildStakeholderModal.alertMessage = false;
this.buildStakeholderModal.stayOpen = true;
this.buildStakeholderModal.open();
}
}
private validateIndexFields() {
let stakeholder = this.stakeholderFb.getRawValue();
let indexes = [stakeholder.index_id, stakeholder.index_name, stakeholder.index_shortName];
return this.indexFields?.filter(indexField => indexes.filter(index => index && !indexField[index]).length > 0).length === 0;
}
public generate() {
if(this.validateIndexFields()) {
this.subscriptions.push(this.stakeholderService.buildStakeholders(
this.stakeholderFb.getRawValue(), this.indexFields,
this.isStandalone,
this.isUmbrella).subscribe(stakeholders => {
stakeholders.forEach(stakeholder => {
this.stakeholders.push(stakeholder);
})
this.buildStakeholderModal.cancel();
}));
} else {
NotificationHandler.rise('Check index fields to match with the json from your file.', 'danger');
}
}
public editStakeholder(stakeholder: Stakeholder = null) {
this.index = (stakeholder) ? this.stakeholders.findIndex(value => value._id === stakeholder._id) : -1;
if (!stakeholder) {
this.stakeholder = new Stakeholder(null, null, null,
null, null, null, null, null);
null, null, null, null, null);
} else {
this.stakeholder = stakeholder;
}
@ -81,10 +166,10 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
this.callback = (stakeholder: Stakeholder) => {
let index: number = this.aliases.findIndex(value => value == this.stakeholders[this.index].alias);
this.stakeholders[this.index] = stakeholder;
if(index != -1) {
if (index != -1) {
this.aliases[index] = stakeholder.alias;
}
this.editStakeholderModal.cancel();
this.editStakeholderModal.cancel();
};
this.editStakeholderModal.alertTitle = 'Edit ' + this.stakeholder.name;
this.editStakeholderModal.okButtonText = 'Save Changes';
@ -93,7 +178,7 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
this.stakeholders.push(stakeholder);
this.aliases.push(stakeholder.alias)
this.changed();
this.editStakeholderModal.cancel();
this.editStakeholderModal.cancel();
};
this.editStakeholderModal.alertTitle = 'Create a new ' + this.stakeholderCategory.name + ' profile';
this.editStakeholderModal.okButtonText = 'Create';
@ -101,34 +186,34 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
this.editStakeholderModal.cancelButtonText = 'Cancel';
this.editStakeholderModal.okButtonLeft = false;
this.editStakeholderModal.alertMessage = false;
this.editStakeholderModal.stayOpen = true;
this.editStakeholderModal.stayOpen = true;
this.editStakeholderModal.open();
}
public createReport(stakeholder: Stakeholder) {
this.cacheIndicatorsService.createReport(stakeholder.alias).subscribe(report => {
NotificationHandler.rise('A caching process for ' + stakeholder.name + ' has been started.' )
NotificationHandler.rise('A caching process for ' + stakeholder.name + ' has been started.')
}, error => {
console.log(error);
NotificationHandler.rise(error.message(), 'danger');
});
}
public deleteStakeholderOpen(stakeholder: Stakeholder) {
this.stakeholder = stakeholder;
this.deleteStakeholderModal.alertTitle = 'Delete ' + this.stakeholder.index_name;
this.deleteStakeholderModal.cancelButtonText = 'No';
this.deleteStakeholderModal.okButtonText = 'Yes';
this.deleteStakeholderModal.alertMessage = false;
this.deleteStakeholderModal.stayOpen = true;
this.deleteStakeholderModal.alertMessage = false;
this.deleteStakeholderModal.stayOpen = true;
this.deleteStakeholderModal.open();
}
public deleteStakeholder() {
this.deleteLoading = true;
this.deleteLoading = true;
this.index = (this.stakeholder) ? this.stakeholders.findIndex(value => value._id === this.stakeholder._id) : -1;
this.subscriptions.push(this.stakeholderService.deleteElement([this.stakeholder._id]).subscribe(() => {
UIkit.notification(this.stakeholder.name+ ' has been <b>successfully deleted</b>', {
UIkit.notification(this.stakeholder.name + ' has been <b>successfully deleted</b>', {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
@ -136,26 +221,26 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
this.stakeholders.splice(this.index, 1);
this.aliases = this.aliases.filter(item => item !== this.stakeholder.alias);
this.changed();
this.deleteLoading = false;
this.deleteStakeholderModal.cancel();
this.deleteLoading = false;
this.deleteStakeholderModal.cancel();
}, error => {
UIkit.notification('An error has occurred. Please try again later', {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.deleteLoading = false;
this.deleteStakeholderModal.cancel();
this.deleteLoading = false;
this.deleteStakeholderModal.cancel();
}));
}
changeStakeholderStatus(stakeholder: Stakeholder, visibility: Visibility) {
let path = [
stakeholder._id
];
this.subscriptions.push(this.stakeholderService.changeVisibility(path, visibility).subscribe(returnedElement => {
stakeholder.visibility = returnedElement.visibility;
UIkit.notification(stakeholder.name+ '\'s status has been <b>successfully changed</b> to ' + stakeholder.visibility.toLowerCase(), {
UIkit.notification(stakeholder.name + '\'s status has been <b>successfully changed</b> to ' + stakeholder.visibility.toLowerCase(), {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
@ -169,6 +254,48 @@ export class ManageStakeholdersComponent extends FilteredStakeholdersBaseCompone
}));
}
fileChangeEvent(fileInput: any) {
this.loading = true;
this.filesToUpload = <Array<File>>fileInput.target.files;
this.upload();
}
upload() {
let observable = this.uploadFileService.upload(this.filesToUpload, 'json');
if (observable) {
observable.subscribe((json: any) => {
this.indexFields = json;
this.loading = false;
}, (error) => {
console.error("Error importing files", error);
UIkit.notification("Error importing files", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.loading = false;
});
} else {
this.loading = false;
}
}
public get canChooseTemplate(): boolean {
return this.stakeholderFb.get('type').valid && !!this.defaultStakeholdersOptions;
}
public get isStandalone() {
return this.stakeholderCategory?.value !== 'dependent';
}
public get isUmbrella() {
return this.stakeholderCategory?.value === 'umbrella';
}
public get isDefault() {
return this.stakeholderCategory?.value === 'templates';
}
public isManager(stakeholder: Stakeholder): boolean {
return this.isCurator() || (Session.isManager(stakeholder.type, stakeholder.alias, this.user));
}

View File

@ -31,27 +31,24 @@
<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">
<icon *ngIf="!isEditable && showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)"
<a *ngIf="isEditable || canEditVisibility(isEditable)"
class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
<icon *ngIf="showVisibility && !canEditVisibility(isEditable)" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<div #element *ngIf="isEditable || canEditVisibility(isEditable)"
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">
<ng-container *ngIf="isEditable">
<li>
<a (click)="editNumberIndicatorOpen(number, indicator._id); hide(element)">Edit</a>
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<li *ngIf="isEditable" class="uk-nav-divider"></li>
<li *ngFor="let v of stakeholderUtils.visibilities">
<a (click)="changeIndicatorStatus(number._id, indicator, v.value);hide(element)">
<div class="uk-flex uk-flex-middle">
@ -65,7 +62,7 @@
</a>
</li>
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<ng-container *ngIf="!indicator.defaultId && !editing && isEditable">
<li class="uk-nav-divider">
<li>
<a (click)="deleteIndicatorOpen(number, indicator._id, 'number', 'delete');hide(element)">Delete</a>
@ -76,14 +73,13 @@
</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 + '-' + 0)"
[innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j + '-' + 0) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j + '-' + 0) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="numberResults.get(i + '-' + j + '-' + 0)" [innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j + '-' + 0) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j + '-' + 0) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j + '-' + 0)">--</span>
</div>
</div>
</div>
</div>
<div *ngIf="isCurator && isEditable" class="uk-margin-top">
<div *ngIf="isEditable" 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"
@ -146,27 +142,24 @@
<!-- Dropdown -->
<div *ngIf="!dragging"
class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
<icon *ngIf="!isEditable && showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)"
<a *ngIf="isEditable || canEditVisibility(isEditable)"
class="uk-link-reset uk-flex uk-flex-middle" [class.uk-disabled]="editing">
<icon *ngIf="showVisibility" [flex]="true"
[name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
<icon *ngIf="showVisibility && !canEditVisibility(isEditable)" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(indicator.visibility)" ratio="0.6"></icon>
<div #element *ngIf="isEditable || canEditVisibility(isEditable)"
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">
<ng-container *ngIf="isEditable">
<li>
<a (click)="editChartIndicatorOpen(chart, indicator._id); hide(element)">Edit</a>
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<li *ngIf="isEditable" class="uk-nav-divider"></li>
<li *ngFor="let v of stakeholderUtils.visibilities">
<a (click)="changeIndicatorStatus(chart._id, indicator, v.value);">
<div class="uk-flex uk-flex-middle">
@ -180,7 +173,7 @@
</a>
</li>
</ng-container>
<ng-container *ngIf="!indicator.defaultId && !editing && isCurator">
<ng-container *ngIf="!indicator.defaultId && !editing && isEditable">
<li class="uk-nav-divider">
<li>
<a (click)="deleteIndicatorOpen(chart, indicator._id, 'chart', 'delete');hide(element)">Delete</a>
@ -228,7 +221,7 @@
</div>
</div>
</div>
<div *ngIf="isCurator && isEditable" class="uk-margin-top">
<div *ngIf="isEditable" 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"
@ -250,7 +243,7 @@
</div>
</div>
</div>
<div *ngIf="isEditable && isCurator">
<div *ngIf="isEditable">
<div class="section">
<div class="uk-flex uk-flex-center" (click)="createSection(-1)">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle">

View File

@ -1,6 +1,5 @@
import {
AfterViewInit,
ChangeDetectorRef,
Component,
HostListener,
Input,
@ -10,17 +9,18 @@ import {
ViewChild
} from "@angular/core";
import {
Format, ImportIndicators,
Indicator, IndicatorImport,
Format,
ImportIndicators,
Indicator,
IndicatorPath,
IndicatorSize,
IndicatorType,
Section,
Stakeholder,
Visibility
} from "../../monitor/entities/stakeholder";
import {
AbstractControl, FormArray, FormGroup,
AbstractControl,
FormGroup,
UntypedFormArray,
UntypedFormBuilder,
UntypedFormControl,
@ -46,6 +46,7 @@ import {IndicatorStakeholderBaseComponent} from "../utils/stakeholder-base.compo
import {properties} from "../../../../environments/environment";
import {StatsProfilesService} from "../utils/services/stats-profiles.service";
import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component";
import {UploadFileService} from "../../services/upload-file.service";
declare var UIkit;
declare var copy;
@ -55,7 +56,7 @@ declare var copy;
templateUrl: './indicators.component.html'
})
export class IndicatorsComponent extends IndicatorStakeholderBaseComponent implements OnInit, OnChanges, AfterViewInit {
filesToUpload: Array<File>;
filesToUpload: Array<File> = [];
errorMessage = "";
@Input()
public topicIndex: number = 0;
@ -122,13 +123,13 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
constructor(private layoutService: LayoutService,
private stakeholderService: StakeholderService,
private statisticsService: StatisticsService,
private statsProfileService: StatsProfilesService,
private notificationService: NotificationService,
private fb: UntypedFormBuilder,
private uploadFileService: UploadFileService,
private sanitizer: DomSanitizer,
protected _router: Router,
private sanitizer: DomSanitizer) {
super()
this.filesToUpload = [];
protected statsProfileService: StatsProfilesService) {
super();
}
ngOnInit(): void {
@ -142,11 +143,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
this.initReorder();
});
if (this.isCurator) {
this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => {
this.statsProfiles = [null].concat(statsProfiles);
}, error => {
this.statsProfiles = [];
}));
this.setStatsProfiles([null]);
} else {
this.statsProfiles = [];
}
@ -1435,57 +1432,38 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
upload() {
if (this.filesToUpload.length == 0) {
console.error("There is no selected file to upload.");
UIkit.notification("There is no selected file to upload.", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
let observable = this.uploadFileService.upload(this.filesToUpload, 'json');
if(observable) {
observable.subscribe((json: ImportIndicators) => {
if (json && Array.isArray(json)) {
UIkit.notification("This file is not supported any more. Please export indicators and try again!", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
} else if (!json || json?.indicators.length == 0) {
UIkit.notification("Importing file is empty", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
} else {
this.importIndicatorsAndSave(json);
}
}, (error) => {
console.error("Error importing files", error);
UIkit.notification("Error importing files", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
});
this.finish();
return;
} else {
if (this.filesToUpload[0].name.indexOf(".json") == -1 || (this.filesToUpload[0].type != "application/json")) {
console.error("No valid file type. The required type is JSON");
UIkit.notification("No valid file type. The required type is JSON", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
return;
}
}
this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
let json = JSON.parse(result);
// validate file
if (json && Array.isArray(json)) {
UIkit.notification("This file is not supported any more. Please export indicators and try again!", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
} else if (!json || json?.indicators.length == 0) {
UIkit.notification("Importing file is empty", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
} else {
this.importIndicatorsAndSave(json);
}
}, (error) => {
console.error("Error importing files", error);
UIkit.notification("Error importing files", {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.finish();
});
}
}
makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
@ -1524,7 +1502,7 @@ export class IndicatorsComponent extends IndicatorStakeholderBaseComponent imple
}
get isEditable(): boolean {
return this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1';
return this.isCurator && (this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1');
}
isLegendEnabled(indicator, i) {

View File

@ -28,17 +28,16 @@
</span>
<span class="uk-margin-xsmall-left hide-on-close" [class.uk-invisible-hover]="topicIndex !== i"
(click)="$event.stopPropagation();$event.preventDefault()">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<a *ngIf="isEditable || canEditVisibility(isEditable)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
<icon *ngIf="showVisibility && !canEditVisibility(isEditable)" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(topic.visibility)" ratio="0.6"></icon>
<div #element *ngIf="isEditable || canEditVisibility(isEditable)"
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">
<ng-container *ngIf="isEditable">
<li>
<a (click)="editTopicOpen(i); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -66,7 +65,7 @@
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<li *ngIf="isEditable" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="topic.visibility === v.value">
<a (click)="openVisibilityModal(i, v.value, 'topic'); hide(element)">
@ -80,7 +79,7 @@
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!topic.defaultId && isCurator">
<ng-container *ngIf="!topic.defaultId && isEditable">
<li class="uk-nav-divider">
<li>
<a (click)="deleteTopicOpen(i, 'delete'); hide(element)">
@ -109,17 +108,16 @@
<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()">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<a *ngIf="isEditable || canEditVisibility(isEditable)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
<icon *ngIf="showVisibility && !canEditVisibility(isEditable)" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(category.visibility)" ratio="0.6"></icon>
<div #element *ngIf="isEditable || canEditVisibility(isEditable)"
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">
<ng-container *ngIf="isEditable">
<li>
<a (click)="editCategoryOpen(j); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -149,7 +147,7 @@
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<li *ngIf="isEditable" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" let-v>
<li [class.uk-active]="category.visibility === v.value">
<a (click)="openVisibilityModal(j, v.value, 'category'); hide(element)">
@ -163,7 +161,7 @@
</li>
</ng-template>
</ng-container>
<ng-container *ngIf="!category.defaultId && isCurator">
<ng-container *ngIf="!category.defaultId && isEditable">
<li class="uk-nav-divider">
<li>
<a (click)="deleteCategoryOpen(j, 'delete'); hide(element)">
@ -180,7 +178,7 @@
</div>
</a>
</li>
<li *ngIf="isCurator && isEditable">
<li *ngIf="isEditable">
<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>
@ -188,7 +186,7 @@
</li>
</ul>
</li>
<li *ngIf="isCurator && isEditable" class="hide-on-close">
<li *ngIf="isEditable" class="hide-on-close">
<a (click)="editTopicOpen(-1); $event.preventDefault()">
<div class="uk-flex uk-flex-middle">
<div class="uk-width-auto">
@ -202,9 +200,9 @@
</div>
</div>
</aside>
<div #pageContent *ngIf="stakeholder && filters" class="uk-width-1-1" page-content>
<div *ngIf="stakeholder && filters" class="uk-width-1-1" page-content>
<div actions class="uk-margin-medium-top">
<div *ngIf="stakeholder.topics.length > 0 && showVisibility" class="uk-flex uk-flex-center uk-flex-right@m uk-width-1-1">
<div *ngIf="stakeholder.topics.length > 0 && canEditVisibility(isEditable)" class="uk-flex uk-flex-center 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>
@ -235,17 +233,16 @@
<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()">
<icon *ngIf="!isEditable && showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
ratio="0.6"></icon>
<a *ngIf="isEditable && (isCurator || showVisibility)" class="uk-link-reset uk-flex uk-flex-middle">
<a *ngIf="isEditable || canEditVisibility(isEditable)" class="uk-link-reset uk-flex uk-flex-middle">
<icon *ngIf="showVisibility" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)"
ratio="0.6"></icon>
<icon [flex]="true" name="more_vert"></icon>
</a>
<div #element *ngIf="isEditable && (isCurator || showVisibility)"
<icon *ngIf="showVisibility && !canEditVisibility(isEditable)" [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(subCategory.visibility)" ratio="0.6"></icon>
<div #element *ngIf="isEditable || canEditVisibility(isEditable)"
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">
<ng-container *ngIf="isEditable">
<li>
<a (click)="editSubCategoryOpen(i); hide(element)">
<div class="uk-flex uk-flex-middle">
@ -291,21 +288,21 @@
</li>
</ng-container>
<ng-container *ngIf="showVisibility">
<li *ngIf="isCurator" class="uk-nav-divider"></li>
<li *ngIf="isEditable" class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibilities" 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>
<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>
<ng-container *ngIf="!subCategory.defaultId && isCurator">
<ng-container *ngIf="!subCategory.defaultId && isEditable">
<li class="uk-nav-divider">
<li>
<a (click)="deleteSubcategoryOpen(i, 'delete'); hide(element)">
@ -321,7 +318,7 @@
</span>
</li>
</ng-container>
<li *ngIf="isCurator && isEditable">
<li *ngIf="isEditable">
<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>

View File

@ -785,6 +785,6 @@ export class TopicComponent extends StakeholderBaseComponent implements OnInit,
}
get isEditable(): boolean {
return this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1';
return this.isCurator && (this.stakeholder.copy || this.stakeholder.defaultId == null || this.stakeholder.defaultId == '-1');
}
}

View File

@ -53,6 +53,7 @@ export interface StakeholderCategory {
}
export class StakeholderConfiguration {
private static isDevelopment: boolean = properties.environment === 'development';
public static ENTITIES: Entities = new Entities();
public static STAKEHOLDER_CATEGORIES: StakeholderCategory[] = [
@ -88,6 +89,8 @@ export class StakeholderConfiguration {
public static FILTERS: FilterType[] = ['start_year', 'end_year', 'fundingL0', 'co-funded'];
public static CREDITS: string = 'Created by OpenAIRE via ' + ChartHelper.prefix + 'library' + ChartHelper.suffix;
public static CACHE_INDICATORS: boolean = true;
public static MULTI_BUILD: boolean = StakeholderConfiguration.isDevelopment;
public static OVERRIDE_REFERENCES: boolean = StakeholderConfiguration.isDevelopment;
public static openAccess: Map<string, OAIndicator> = new Map();
}
@ -124,10 +127,18 @@ export class StakeholderUtils {
return StakeholderConfiguration.CREDITS;
}
get isCachingIndicators() {
get hasCachingIndicators() {
return StakeholderConfiguration.CACHE_INDICATORS;
}
get hasMultiBuild() {
return StakeholderConfiguration.MULTI_BUILD;
}
get hasOverrideReferences() {
return StakeholderConfiguration.OVERRIDE_REFERENCES;
}
get openAccess(): Map<string, OAIndicator> {
return StakeholderConfiguration.openAccess;
}

View File

@ -2,14 +2,17 @@ import {Directive} from "@angular/core";
import {BaseComponent} from "../../sharedComponents/base/base.component";
import {IndicatorUtils, StakeholderUtils} from "./indicator-utils";
import {ConnectHelper} from "../../connect/connectHelper";
import {Indicator, IndicatorSize} from "../../monitor/entities/stakeholder";
import {statsToolParser} from "./cache-indicators/cache-indicators";
import {Indicator, IndicatorSize, Stakeholder} from "../../monitor/entities/stakeholder";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {ConfigurationService} from "../../utils/configuration/configuration.service";
import {StatsProfilesService} from "./services/stats-profiles.service";
@Directive()
export abstract class StakeholderBaseComponent extends BaseComponent {
stakeholderUtils: StakeholderUtils = new StakeholderUtils();
statsProfiles: string[] = [];
protected statsProfileService: StatsProfilesService;
get entities() {
return this.stakeholderUtils.entities;
@ -35,11 +38,15 @@ export abstract class StakeholderBaseComponent extends BaseComponent {
return this.stakeholderUtils.showField(this.stakeholderUtils.locales);
}
canEditVisibility(isEditable: boolean) {
return this.showVisibility && (isEditable || this.stakeholderUtils.hasOverrideReferences);
}
protected navigateToError() {
HelperFunctions.navigateToError(this._router, {"page": this._router.url});
}
setProperties(id, type = null, configurationService) {
setProperties(id: string, type = null, configurationService: ConfigurationService) {
this.properties.adminToolsCommunity = id;
if (type) {
this.properties.adminToolsPortalType = type;
@ -48,6 +55,16 @@ export abstract class StakeholderBaseComponent extends BaseComponent {
}
configurationService.initPortal(this.properties, this.properties.adminToolsCommunity);
}
setStatsProfiles(extra: string[] = []) {
if (this.statsProfileService) {
this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => {
this.statsProfiles = extra.concat(statsProfiles);
}, error => {
this.statsProfiles = [];
}));
}
}
}
@Directive()

View File

@ -1,10 +1,12 @@
import {ChangeDetectorRef, Component, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
import {ChangeDetectorRef, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
import {IndicatorStakeholderBaseComponent} from "../monitor-admin/utils/stakeholder-base.component";
import {DomSanitizer} from "@angular/platform-browser";
import {
Category, FilterType,
Indicator, IndicatorFilters, IndicatorPath,
IndicatorSize, Overlay,
Category,
Indicator,
IndicatorFilters,
IndicatorPath,
Overlay,
Section,
Stakeholder,
SubCategory,
@ -22,16 +24,6 @@ import {Params} from "@angular/router";
import {StatisticsService} from "../monitor-admin/utils/services/statistics.service";
import {SearchResearchResultsService} from "../services/searchResearchResults.service";
import {CustomFilterService} from "../shared/customFilter.service";
import {PageContentModule} from "../dashboard/sharedComponents/page-content/page-content.module";
import {CommonModule, NgIf} from "@angular/common";
import {SliderTabsModule} from "../sharedComponents/tabs/slider-tabs.module";
import {LoadingModule} from "../utils/loading/loading.module";
import {IconsModule} from "../utils/icons/icons.module";
import {ClickModule} from "../utils/click/click.module";
import {NumberRoundModule} from "../utils/pipes/number-round.module";
import {RangeFilterModule} from "../utils/rangeFilter/rangeFilter.module";
import {SearchFilterModule} from "../searchPages/searchUtils/searchFilter.module";
import {SliderUtilsModule} from "../sharedComponents/slider-utils/slider-utils.module";
@Directive()
export abstract class MonitorIndicatorStakeholderBaseComponent extends IndicatorStakeholderBaseComponent implements OnInit {

View File

@ -160,7 +160,19 @@ export class StakeholderService {
}));
}
changeVisibility(path: string[], visibility: Visibility, propagate: boolean = false): Observable<any> {
buildStakeholders(stakeholder: Stakeholder, indexFields: any, standalone: boolean = true, umbrella: boolean = false): Observable<Stakeholder[]> {
let buildStakeholder = {
stakeholder: stakeholder,
indexFields: indexFields,
umbrella: umbrella,
standalone: standalone
}
return this.http.post<Stakeholder[]>(properties.monitorServiceAPIURL + 'build-stakeholder', buildStakeholder, CustomOptions.registryOptions()).pipe(map(stakeholder => {
return HelperFunctions.copy(Stakeholder.checkIsUpload(stakeholder));
}));
}
changeVisibility(path: string[], visibility: Visibility, propagate: boolean = false): Observable<any> {
path.push('change-visibility');
return this.http.post<Visibility>(properties.monitorServiceAPIURL + path.join('/') + '?visibility=' + visibility + (propagate ? '&propagate=true' : ''), null, CustomOptions.registryOptions());
}

View File

@ -0,0 +1,39 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {properties} from "../../../environments/environment";
import {NotificationHandler} from "../utils/notification-handler";
@Injectable({
providedIn: 'root'
})
export class UploadFileService {
public types: Map<string, string> = new Map();
constructor(private http: HttpClient) {
this.types.set('csv', 'application/vnd.ms-excel');
this.types.set('json', 'application/json');
}
upload(files: Array<File>, type: 'json' | 'csv' = 'csv') {
let message: string;
if (files.length == 0) {
message = "There is no selected file to upload.";
} else {
if ([...files].filter(file => file.name.indexOf('.' + type) == -1 || file.type !== this.types.get(type)).length > 0) {
message = "No valid file type. The required type is " + type.toUpperCase();
}
}
if(message) {
console.error(message);
NotificationHandler.rise(message, 'danger');
return null;
} else {
let url = properties.utilsService + '/upload?type=' + type;
let formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append("uploads[]", files[i], files[i].name);
}
return this.http.post<any>(url, formData, {withCredentials: true});
}
}
}