Production release November 2023 #20
|
@ -1,6 +1,5 @@
|
|||
import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
|
||||
import {Injectable, Inject, PLATFORM_ID, TransferState} from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
|
||||
import { TransferState } from '@angular/platform-browser';
|
||||
import { properties } from "../../environments/environment";
|
||||
|
||||
@Injectable({
|
||||
|
|
|
@ -2,13 +2,12 @@ import {Component, Input, ViewChild} from '@angular/core';
|
|||
import {Router} from '@angular/router';
|
||||
import {ContextsService} from './service/contexts.service';
|
||||
import {ClaimEntity, ShowOptions} from './claimHelper.class';
|
||||
import {Session} from '../../login/utils/helper.class';
|
||||
import {LoginErrorCodes} from '../../login/utils/guardHelper.class';
|
||||
import {EnvProperties} from '../../utils/properties/env-properties';
|
||||
import {Subscriber} from "rxjs";
|
||||
import {OpenaireEntities} from "../../utils/properties/searchFields";
|
||||
import {CommunitiesService} from "../../connect/communities/communities.service";
|
||||
import {UserManagementService} from "../../services/user-management.service";
|
||||
import {Session} from "../../login/utils/helper.class";
|
||||
|
||||
declare var UIkit: any;
|
||||
|
||||
|
@ -183,10 +182,6 @@ export class ClaimContextSearchFormComponent {
|
|||
}
|
||||
|
||||
getCommunities() {
|
||||
if (!Session.isLoggedIn()) {
|
||||
this.saveStateAndRedirectLogin();
|
||||
|
||||
} else {
|
||||
this.loading = true;
|
||||
this.subscriptions.push(this._contextService.getCommunitiesByState().subscribe(
|
||||
data => {
|
||||
|
@ -220,18 +215,14 @@ export class ClaimContextSearchFormComponent {
|
|||
this.error = true;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getCategories() {
|
||||
this.loading = true;
|
||||
// this.categories=[];
|
||||
if (this.selectedCommunityId != '0') {
|
||||
if (!Session.isLoggedIn()) {
|
||||
this.saveStateAndRedirectLogin();
|
||||
|
||||
|
||||
} else {
|
||||
if (this.categories[this.selectedCommunityId]) {
|
||||
this.loading = false;
|
||||
if(this.categories[this.selectedCommunityId].length > 0){
|
||||
|
@ -259,7 +250,7 @@ export class ClaimContextSearchFormComponent {
|
|||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
displaySubcategory(id) {
|
||||
|
@ -272,9 +263,6 @@ export class ClaimContextSearchFormComponent {
|
|||
}
|
||||
|
||||
browseConcepts(categoryId) {
|
||||
if (!Session.isLoggedIn()) {
|
||||
this.saveStateAndRedirectLogin();
|
||||
} else {
|
||||
if (this.conceptsClass[categoryId] != null) {
|
||||
this.conceptsClassDisplay[categoryId] = !this.conceptsClassDisplay[categoryId];
|
||||
return;
|
||||
|
@ -304,8 +292,6 @@ export class ClaimContextSearchFormComponent {
|
|||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
browseSubConcepts(categoryId, conceptId) {
|
||||
|
||||
this.conceptsCategoryLoading[categoryId] = true;
|
||||
|
@ -325,24 +311,6 @@ export class ClaimContextSearchFormComponent {
|
|||
|
||||
}
|
||||
|
||||
|
||||
saveStateAndRedirectLogin() {
|
||||
|
||||
if (this.results != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "results", JSON.stringify(this.results));
|
||||
}
|
||||
if (this.sources != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "sources", JSON.stringify(this.sources));
|
||||
}
|
||||
|
||||
this.router.navigate(['/user-info'], {
|
||||
queryParams: {
|
||||
"errorCode": LoginErrorCodes.NOT_VALID,
|
||||
"redirectUrl": this.router.url
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static handleError(message: string, error) {
|
||||
console.error("Claim context search form (component): " + message, error);
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
import {Component, Input, ViewChild} from '@angular/core';
|
||||
import {ShowOptions} from './claimHelper.class';
|
||||
import {MatSelect} from "@angular/material/select";
|
||||
import {OpenaireEntities} from "../../utils/properties/searchFields";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'claim-enities-selection',
|
||||
|
||||
template:`
|
||||
<span *ngIf="showOptions && showOptions.linkToEntities && showOptions.linkToEntities.length > 0"
|
||||
class=" entitiesSelection portal-box uk-text-small clickable" style=""
|
||||
(click)="open()">
|
||||
<mat-select [(value)]="showOptions.show"
|
||||
[disableOptionCentering]="true" >
|
||||
|
||||
<mat-option *ngIf="showOptions.linkToEntities.indexOf('result')!=-1" value="result">{{openaireEntities.RESULTS}}</mat-option>
|
||||
<mat-option *ngIf="showOptions.linkToEntities.indexOf('project')!=-1" value="project">{{openaireEntities.PROJECTS}}</mat-option>
|
||||
<mat-option *ngIf="showOptions.linkToEntities.indexOf('context')!=-1" value="context">{{openaireEntities.COMMUNITIES}}</mat-option>
|
||||
|
||||
</mat-select>
|
||||
|
||||
|
||||
</span>
|
||||
`,
|
||||
|
||||
})
|
||||
export class ClaimEntitiesSelectionComponent{
|
||||
@ViewChild(MatSelect) matSelect: MatSelect;
|
||||
@Input() showOptions:ShowOptions = new ShowOptions();
|
||||
public openaireEntities = OpenaireEntities;
|
||||
|
||||
open() {
|
||||
if (this.matSelect && !this.matSelect.focused) {
|
||||
this.matSelect.open();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
|
||||
import { SharedModule } from '../../../openaireLibrary/shared/shared.module';
|
||||
import {SharedModule} from '../../shared/shared.module';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
import {ClaimProjectsSearchFormComponent} from './claimProjectSearchForm.component';
|
||||
// import {LoadingModalModule} from '../../utils/modal/loadingModal.module';
|
||||
|
||||
import {ProjectServiceModule} from '../../landingPages/project/projectService.module';
|
||||
import {ProjectsServiceModule} from '../../services/projectsService.module';
|
||||
import {EntitiesAutocompleteModule} from '../../utils/entitiesAutoComplete/entitiesAutoComplete.module';
|
||||
|
@ -16,8 +14,6 @@ import {ClaimResultsModule} from './claimResults.module';
|
|||
import {PagingModule} from '../../utils/paging.module';
|
||||
import {SearchFilterModule} from '../../searchPages/searchUtils/searchFilter.module';
|
||||
import {RangeFilterModule} from "../../utils/rangeFilter/rangeFilter.module";
|
||||
import {ClaimEntitiesSelectionComponent} from "./claimEntitiesSelection.component";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {AdvancedSearchInputModule} from "../../sharedComponents/advanced-search-input/advanced-search-input.module";
|
||||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
import {DropdownFilterModule} from "../../utils/dropdown-filter/dropdown-filter.module";
|
||||
|
@ -27,15 +23,13 @@ import {DropdownFilterModule} from "../../utils/dropdown-filter/dropdown-filter.
|
|||
SharedModule, CommonModule,
|
||||
// LoadingModalModule,
|
||||
ProjectServiceModule, ProjectsServiceModule, EntitiesAutocompleteModule, HelperModule,
|
||||
PagingModule, SearchFilterModule, ClaimResultsModule, RangeFilterModule, MatSelectModule, AdvancedSearchInputModule, InputModule, DropdownFilterModule
|
||||
PagingModule, SearchFilterModule, ClaimResultsModule, RangeFilterModule, AdvancedSearchInputModule, InputModule, DropdownFilterModule
|
||||
],
|
||||
providers:[
|
||||
],
|
||||
declarations: [
|
||||
ClaimProjectsSearchFormComponent,
|
||||
ClaimEntitiesSelectionComponent
|
||||
|
||||
],
|
||||
exports: [ClaimProjectsSearchFormComponent, ClaimEntitiesSelectionComponent]
|
||||
exports: [ClaimProjectsSearchFormComponent]
|
||||
})
|
||||
export class ClaimProjectsSearchFormModule { }
|
||||
|
|
|
@ -10,7 +10,7 @@ export class ClaimsService {
|
|||
}
|
||||
|
||||
private getClaimRequest(size : number, page : number, url :string, fromCache:boolean):any {
|
||||
return this.http.get(url, CustomOptions.getAuthOptions());
|
||||
return this.http.get(url, CustomOptions.getAuthOptionsWithBody());
|
||||
}
|
||||
getClaims( size : number, page : number, keyword:string, sortby: string, descending: boolean, types: string, apiUrl:string):any {
|
||||
let url = apiUrl +"claims"+"?offset="+(size*(page-1) + "&limit="+size)+"&keyword="+keyword+"&sortby="+sortby+"&descending="+descending+(types.length>0?"&"+types:types);
|
||||
|
@ -44,10 +44,7 @@ export class ClaimsService {
|
|||
deleteClaimById(claimId:string , apiUrl:string):any{
|
||||
//console.warn('Trying to delete claim with id : '+claimId);
|
||||
let url = apiUrl +"claims/"+claimId;
|
||||
// let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
// let options = new RequestOptions({ headers: headers });
|
||||
return this.http.delete( url, CustomOptions.getAuthOptionsWithBody())//.map(request => <any> request.json())
|
||||
// .do(request => console.info("After delete" ))
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
|
@ -61,10 +58,7 @@ export class ClaimsService {
|
|||
}
|
||||
url= apiUrl +"claims/bulk?"+url;
|
||||
|
||||
// let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
// let options = new RequestOptions({ headers: headers });
|
||||
return this.http.delete( url, CustomOptions.getAuthOptions())//.map(request => <any> request.json())
|
||||
// .do(request => console.info("After delete" ))
|
||||
return this.http.delete( url, CustomOptions.getAuthOptionsWithBody())//.map(request => <any> request.json())
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
|
@ -72,37 +66,24 @@ export class ClaimsService {
|
|||
// console.warn('Trying toinsert claims : '+claims);
|
||||
let url = apiUrl +"claims/bulk";
|
||||
let body = JSON.stringify( claims );
|
||||
//console.warn('Json body: : '+body);
|
||||
// let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
// let options = new RequestOptions({ headers: headers });
|
||||
return this.http.post(url, body, CustomOptions.getAuthOptionsWithBody())
|
||||
//.map(res => res.json())
|
||||
//.do(request => console.info("Insert Response:"+request.status) )
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
insertClaim(claim, apiUrl:string):any{
|
||||
//console.warn('Trying toinsert claim : '+claim);
|
||||
let url = apiUrl +"claims";
|
||||
let body = JSON.stringify( claim );
|
||||
// let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
// let options = new RequestOptions({ headers: headers });
|
||||
return this.http.post(url, body, CustomOptions.getAuthOptionsWithBody())
|
||||
//.map(res => res.json())
|
||||
//.do(request => console.info("Insert Response:"+request.status) )
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
insertDirectRecords(records, apiUrl:string):any{
|
||||
//console.warn('Trying to feedrecords : '+records);
|
||||
let url = apiUrl +"feed/bulk";
|
||||
let body = JSON.stringify( records );
|
||||
//console.warn('Json body: : '+body);
|
||||
// let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
// let options = new RequestOptions({ headers: headers });
|
||||
return this.http.post(url, body, CustomOptions.getAuthOptionsWithBody())
|
||||
//.map(res => res.json())
|
||||
//.do(request => console.info("Insert Response:"+request) )
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
getStatus(jobId, apiUrl:string):any{
|
||||
|
||||
let url = apiUrl +"jobStatus/" + jobId;
|
||||
|
||||
return this.http.get(url,CustomOptions.getAuthOptionsWithBody())
|
||||
.pipe(catchError(this.handleError));
|
||||
|
||||
}
|
||||
|
@ -113,22 +94,4 @@ export class ClaimsService {
|
|||
return observableThrowError(error || 'Server error');
|
||||
}
|
||||
|
||||
// getClaim(id:string, apiUrl:string):any {
|
||||
// let url = apiUrl+"claims/"+id;
|
||||
// return new Promise((resolve, reject) => {
|
||||
// this.http.get(url)
|
||||
// //.map(res => res.json())
|
||||
// .subscribe(
|
||||
// data => {
|
||||
// resolve(data['data']);
|
||||
// },
|
||||
// err => {
|
||||
// reject(err);
|
||||
// }
|
||||
// )
|
||||
// ;
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -18,26 +18,12 @@ export class ClaimsByTokenService {
|
|||
|
||||
let key = url;
|
||||
|
||||
return this.http.get(url, CustomOptions.getAuthOptions());
|
||||
//.map(res => <any> res.text())
|
||||
//.map(request => <any> request.json());
|
||||
return this.http.get(url, CustomOptions.getAuthOptionsWithBody());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
getClaims(email: string, token: string, user_token: string):any {
|
||||
let url = OpenaireProperties.getClaimsAPIURL(); // What else?
|
||||
let body = JSON.stringify( {"email": email, "token": token} );
|
||||
console.warn('Json body: : '+body);
|
||||
let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||
let options = new RequestOptions({ headers: headers });
|
||||
return this.http.post(url, body, options)
|
||||
.map(res => res.json())
|
||||
.do(request => console.info("Insert Response:"+request.status) )
|
||||
.catch(this.handleError);
|
||||
}
|
||||
*/
|
||||
|
||||
updateClaimsCuration( selectedRight: Set<string>, selectedWrong: Set<string>, apiURL:string) {
|
||||
let url = apiURL + "curate/bulk";
|
||||
|
|
|
@ -14,7 +14,8 @@ import {
|
|||
Message
|
||||
} from "../../claim-utils/claimHelper.class";
|
||||
import {UserManagementService} from "../../../services/user-management.service";
|
||||
import {Subscriber} from "rxjs";
|
||||
import {Subscriber, timer} from "rxjs";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: 'claim-insert',
|
||||
|
@ -30,7 +31,19 @@ import {Subscriber} from "rxjs";
|
|||
<div class="uk-width-expand uk-margin-small-left">CONFIRM LINKING</div>
|
||||
</button>
|
||||
</div>
|
||||
<modal-loading [message]="'Please wait...'"></modal-loading>
|
||||
<modal-loading [message]="'Please wait...'">
|
||||
|
||||
<div *ngIf="claimsJob">
|
||||
<div *ngIf="claimsJob && claimsJob.insertedIds.length <1" class="uk-text-meta uk-text-small">
|
||||
Initiating process....</div>
|
||||
<div *ngIf="claimsJob && claimsJob.insertedIds.length >0" class="uk-text-meta uk-text-small">
|
||||
{{claimsJob.insertedIds.length}} out of {{claims2Insert}} links created.</div>
|
||||
<div *ngIf="feedRecordsJob && feedRecordsJob.insertedIds.length >0" class="uk-text-meta uk-text-small">
|
||||
{{feedRecordsJob.insertedIds.length}} out of {{records2Insert}} records added in the index...</div>
|
||||
<div *ngIf="claimsJob.status != 'COMPLETE'" class="uk-text-meta uk-text-small">
|
||||
Please don't close the window, process is ongoing...</div>
|
||||
</div>
|
||||
</modal-loading>
|
||||
<modal-alert (alertOutput)="confirmClose()">
|
||||
<h4 class="modal-title uk-text-bold " id="myModalLabel">Confirmation notice</h4>
|
||||
<p>All the links you provided will be published in the OpenAIRE platform. <br>
|
||||
|
@ -60,6 +73,32 @@ export class ClaimInsertComponent {
|
|||
this.subscriptions.push(this.route.queryParams.subscribe(params => {
|
||||
this.params = params;
|
||||
}));
|
||||
if(localStorage.getItem(this.localStoragePrefix + "claimsJob")){
|
||||
this.claimsJob = JSON.parse(localStorage.getItem(this.localStoragePrefix + "claimsJob"));
|
||||
this.feedRecordsJob = JSON.parse(localStorage.getItem(this.localStoragePrefix + "feedRecordsJob"));
|
||||
if(this.claimsJob.status != "COMPLETE"){
|
||||
this.claiming = true;
|
||||
let loadingTimerSubscription = timer(0, 1000).pipe(
|
||||
map(() => {
|
||||
if(this.loading) {
|
||||
this.loading.open();
|
||||
loadingTimerSubscription.unsubscribe();
|
||||
}
|
||||
})
|
||||
).subscribe();
|
||||
this.subscriptions.push(loadingTimerSubscription);
|
||||
|
||||
let timerSubscription = timer(0, 10000).pipe(
|
||||
map(() => {
|
||||
this.getStatus(); // load data contains the http request
|
||||
})
|
||||
).subscribe();
|
||||
this.subscriptions.push(timerSubscription);
|
||||
}else{
|
||||
this.claimsJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
params = {};
|
||||
|
@ -84,15 +123,15 @@ export class ClaimInsertComponent {
|
|||
private errorInClaims: ClaimRecord2Insert[] = [];
|
||||
private insertedRecords = [];
|
||||
private errorInRecords = [];
|
||||
|
||||
public claimsJob;
|
||||
public feedRecordsJob;
|
||||
public claims2Insert;
|
||||
public records2Insert
|
||||
public insert() {
|
||||
this.confirmOpen();
|
||||
}
|
||||
saveAndNavigate(){
|
||||
localStorage.setItem(this.localStoragePrefix + "results", JSON.stringify(this.results));
|
||||
if (this.sources != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "sources", JSON.stringify(this.sources));
|
||||
}
|
||||
this.saveLocalStorage();
|
||||
this._router.navigate(['/user-info'], {
|
||||
queryParams: {
|
||||
"errorCode": LoginErrorCodes.NOT_VALID,
|
||||
|
@ -177,12 +216,15 @@ export class ClaimInsertComponent {
|
|||
//first call direct index service - when call is done (success or error) call isertBulkClaims method to insert claims in DB
|
||||
// console.log("directclaims");
|
||||
// console.log(directclaims);
|
||||
if (directclaims.length > 0 && this.properties.environment != "development"){
|
||||
if (directclaims.length > 0/* && this.properties.environment != "development"*/){
|
||||
this.subscriptions.push(this.claimService.insertDirectRecords(directclaims, this.properties.claimsAPIURL).subscribe(
|
||||
data => {
|
||||
this.insertedRecords = data.insertedIds;
|
||||
|
||||
this.errorInRecords = data.errorInClaims;
|
||||
this.feedRecordsJob = data.data;
|
||||
this.records2Insert = directclaims.length;
|
||||
console.log(data);
|
||||
// this.insertedRecords = data.insertedIds;
|
||||
//
|
||||
// this.errorInRecords = data.errorInClaims;
|
||||
this.isertBulkClaims(claims);
|
||||
},
|
||||
err => {
|
||||
|
@ -190,12 +232,12 @@ export class ClaimInsertComponent {
|
|||
if (err.code && err.code == 403) {
|
||||
this.saveAndNavigate();
|
||||
}
|
||||
if (err.insertedIds && err.insertedIds.length > 0) {
|
||||
/* if (err.insertedIds && err.insertedIds.length > 0) {
|
||||
this.insertedRecords = err.insertedIds;
|
||||
}
|
||||
if (err.errorInClaims && err.errorInClaims.length > 0) {
|
||||
this.errorInRecords = err.errorInClaims;
|
||||
}
|
||||
}*/
|
||||
this.isertBulkClaims(claims);
|
||||
|
||||
ClaimInsertComponent.handleError("Error inserting direct records: " + JSON.stringify(directclaims), err);
|
||||
|
@ -209,30 +251,18 @@ export class ClaimInsertComponent {
|
|||
}
|
||||
|
||||
private isertBulkClaims(claims: ClaimRecord2Insert[]) {
|
||||
console.log("claims");
|
||||
console.log(claims);
|
||||
this.errors.splice(0, this.errors.length);
|
||||
this.subscriptions.push(this.claimService.insertBulkClaims(claims, this.properties.claimsAPIURL).subscribe(
|
||||
data => {
|
||||
this.insertedClaims = data.insertedIds;
|
||||
this.errorInClaims = data.errorInClaims;
|
||||
//TODO remove - testing having errors in claims
|
||||
// this.insertedClaims.pop();
|
||||
// this.insertedClaims.pop();
|
||||
// this.errorInClaims.push(claims[1]);
|
||||
// this.insertedClaims.splice(0,this.insertedClaims.length);
|
||||
// this.errorInClaims = claims;
|
||||
//remove till here
|
||||
|
||||
if (claims.length != this.insertedClaims.length) {
|
||||
let error: ClaimsErrorMessage = new ClaimsErrorMessage();
|
||||
error.type = "claimServiceFail2Insert";
|
||||
error.inserted = this.insertedClaims.length;
|
||||
error.failed = this.errorInClaims.length;
|
||||
this.createErrorMessagesPerEntity((this.insertedClaims.length == 0));
|
||||
this.errors.push(error);
|
||||
}
|
||||
this.afterclaimsInsertion();
|
||||
this.claims2Insert = claims.length;
|
||||
this.claimsJob = data.data;
|
||||
this.saveLocalStorage();
|
||||
let timerSubscription = timer(0, 10000).pipe(
|
||||
map(() => {
|
||||
this.getStatus(); // load data contains the http request
|
||||
})
|
||||
).subscribe();
|
||||
this.subscriptions.push(timerSubscription);
|
||||
},
|
||||
err => {
|
||||
err = err && err.error?err.error:err;
|
||||
|
@ -335,13 +365,16 @@ export class ClaimInsertComponent {
|
|||
}
|
||||
|
||||
private afterclaimsInsertion() {
|
||||
|
||||
this.loading.close();
|
||||
this.claiming = false;
|
||||
this.loading.close();
|
||||
|
||||
|
||||
if (this.errorInClaims.length == 0 && this.insertedClaims.length > 0) {
|
||||
localStorage.removeItem(this.localStoragePrefix + "sources");
|
||||
localStorage.removeItem(this.localStoragePrefix + "results");
|
||||
localStorage.removeItem(this.localStoragePrefix + "claimsJob");
|
||||
localStorage.removeItem(this.localStoragePrefix + "feedRecordsJob");
|
||||
|
||||
this._router.navigate(['/myclaims'], {queryParams: this.params});
|
||||
}
|
||||
}
|
||||
|
@ -520,5 +553,74 @@ export class ClaimInsertComponent {
|
|||
}
|
||||
return buttonClass + "uk-text-center ";
|
||||
}
|
||||
|
||||
getStatus(){
|
||||
if(this.feedRecordsJob && ! (this.feedRecordsJob.status == "COMPLETE" || this.feedRecordsJob.status == "ERROR") ) {
|
||||
this.subscriptions.push(this.claimService.getStatus(this.feedRecordsJob.id, this.properties.claimsAPIURL).subscribe(data => {
|
||||
console.log("feed", data);
|
||||
this.feedRecordsJob = data.data;
|
||||
if (this.feedRecordsJob.status == "COMPLETE" || data.data.status == "ERROR") {
|
||||
this.insertedRecords = this.feedRecordsJob.insertedIds;
|
||||
this.errorInRecords = this.feedRecordsJob.errorInClaims;
|
||||
}
|
||||
}, err => {
|
||||
let error: ClaimsErrorMessage = new ClaimsErrorMessage();
|
||||
error.type = "jobError";
|
||||
this.createErrorMessagesPerEntity((this.insertedClaims.length == 0));
|
||||
this.errors.push(error);
|
||||
this.afterclaimsInsertion();
|
||||
this.feedRecordsJob = null;
|
||||
localStorage.removeItem(this.localStoragePrefix + "feedRecordsJob");
|
||||
}
|
||||
|
||||
));
|
||||
}
|
||||
if(this.claimsJob) {
|
||||
this.subscriptions.push(this.claimService.getStatus(this.claimsJob.id, this.properties.claimsAPIURL).subscribe(data => {
|
||||
console.log("claim", data);
|
||||
this.claimsJob = data.data;
|
||||
if ((this.claimsJob.status == "COMPLETE" || data.data.status == "ERROR") && ( !this.feedRecordsJob || !(this.feedRecordsJob.status == "COMPLETE" || data.data.status == "ERROR")) ) {
|
||||
this.insertedClaims = this.claimsJob.insertedIds;
|
||||
this.errorInClaims = this.claimsJob.errorInClaims;
|
||||
|
||||
if (this.claims2Insert != this.insertedClaims.length) {
|
||||
let error: ClaimsErrorMessage = new ClaimsErrorMessage();
|
||||
error.type = "claimServiceFail2Insert";
|
||||
error.inserted = this.insertedClaims.length;
|
||||
error.failed = this.errorInClaims.length;
|
||||
this.createErrorMessagesPerEntity((this.insertedClaims.length == 0));
|
||||
this.errors.push(error);
|
||||
}
|
||||
this.afterclaimsInsertion();
|
||||
|
||||
|
||||
}
|
||||
}, err => {
|
||||
let error: ClaimsErrorMessage = new ClaimsErrorMessage();
|
||||
error.type = "jobError";
|
||||
this.createErrorMessagesPerEntity((this.insertedClaims.length == 0));
|
||||
this.errors.push(error);
|
||||
this.afterclaimsInsertion();
|
||||
this.claimsJob = null;
|
||||
localStorage.removeItem(this.localStoragePrefix + "claimsJob");
|
||||
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
saveLocalStorage(){
|
||||
if (this.results != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "results", JSON.stringify(this.results));
|
||||
}
|
||||
if (this.sources != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "sources", JSON.stringify(this.sources));
|
||||
}
|
||||
if (this.claimsJob != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "claimsJob", JSON.stringify(this.claimsJob));
|
||||
}
|
||||
if (this.feedRecordsJob != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "feedRecordsJob", JSON.stringify(this.feedRecordsJob));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!inlineEntity" uk-sticky="offset: 65; bottom: #pageBottom; media: @m" class="uk-blur-background">
|
||||
<div *ngIf="!inlineEntity" uk-sticky="offset: 65; end: #pageBottom; media: @m" class="uk-blur-background">
|
||||
<div class="uk-section-xsmall">
|
||||
<stepper>
|
||||
<step [status]="stepStatus('source')" stepId="source" stepNumber="1"
|
||||
|
@ -110,7 +110,7 @@
|
|||
</div>
|
||||
<!-- Basket-->
|
||||
<div *ngIf="showOptions.show != 'claim'" class="uk-width-1-3">
|
||||
<div id="basket" uk-sticky="offset: 220; bottom: !*; media: @m" style="z-index: 0!important;">
|
||||
<div id="basket" uk-sticky="offset: 220; end: !*; media: @m" style="z-index: 0!important;">
|
||||
<div class="uk-card uk-card-default linkingBasket">
|
||||
<div class="uk-card-body uk-padding-small">
|
||||
<div class="uk-margin-right">
|
||||
|
|
|
@ -16,6 +16,8 @@ import {OpenaireEntities} from "../../utils/properties/searchFields";
|
|||
import {StringUtils} from "../../utils/string-utils.class";
|
||||
import {RouterHelper} from "../../utils/routerHelper.class";
|
||||
import { Location } from '@angular/common';
|
||||
import {LoginErrorCodes} from "../../login/utils/guardHelper.class";
|
||||
import {UserManagementService} from "../../services/user-management.service";
|
||||
|
||||
@Component({
|
||||
selector: 'linking-generic',
|
||||
|
@ -51,11 +53,16 @@ export class LinkingGenericComponent {
|
|||
constructor (private _router: Router, private route: ActivatedRoute, private entitySearch:EntitiesSearchService,
|
||||
private _meta: Meta, private _title: Title, private _piwikService:PiwikService,
|
||||
private seoService: SEOService, private helper: HelperService, private cdr: ChangeDetectorRef,
|
||||
private location: Location) {
|
||||
private location: Location, private userManagementService: UserManagementService) {
|
||||
}
|
||||
subscriptions = [];
|
||||
|
||||
ngOnInit() {
|
||||
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
|
||||
if (!user) {
|
||||
this.saveStateAndRedirectLogin();
|
||||
}
|
||||
}));
|
||||
if(this.breadcrumbs.length === 0) {
|
||||
this.breadcrumbs.push({name: 'home', route: '/'});
|
||||
this.breadcrumbs.push({name: "Link", route: null});
|
||||
|
@ -94,6 +101,11 @@ export class LinkingGenericComponent {
|
|||
if(localStorage.getItem(this.localStoragePrefix + "sources")){
|
||||
this.sources = JSON.parse(localStorage.getItem(this.localStoragePrefix + "sources"));
|
||||
}
|
||||
if(localStorage.getItem(this.localStoragePrefix + "claimsJob")){
|
||||
let job = JSON.parse(localStorage.getItem(this.localStoragePrefix + "claimsJob"));
|
||||
if(job.status != "COMPLETE"){
|
||||
this.showOptions.show = 'claim'; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,4 +218,20 @@ export class LinkingGenericComponent {
|
|||
this.location.back();
|
||||
}
|
||||
}
|
||||
saveStateAndRedirectLogin() {
|
||||
|
||||
if (this.results != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "results", JSON.stringify(this.results));
|
||||
}
|
||||
if (this.sources != null) {
|
||||
localStorage.setItem(this.localStoragePrefix + "sources", JSON.stringify(this.sources));
|
||||
}
|
||||
|
||||
this._router.navigate(['/user-info'], {
|
||||
queryParams: {
|
||||
"errorCode": LoginErrorCodes.NOT_VALID,
|
||||
"redirectUrl": this._router.url
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,6 +250,14 @@
|
|||
None of the links saved.
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="message.type == 'jobError'">
|
||||
|
||||
<div class="">
|
||||
<span class=" uk-text-bold">The saving status of your links can't be fetched. Please check your links or start over. </span>
|
||||
<br>
|
||||
<a routerLinkActive="router-link-active" routerLink="/myclaims">Manage your links here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="warnings.length > 0"
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
import {filter, map, mergeMap, take} from 'rxjs/operators';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
CanActivateChild,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {Session} from '../../login/utils/helper.class';
|
||||
import {LoginErrorCodes} from '../../login/utils/guardHelper.class';
|
||||
import {UserManagementService} from "../../services/user-management.service";
|
||||
|
||||
@Injectable()
|
||||
export class ConnectAdminLoginGuard implements CanActivate, CanActivateChild {
|
||||
export class ConnectAdminLoginGuard {
|
||||
|
||||
constructor(private router: Router,
|
||||
private userManagementService: UserManagementService) {
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
Router,
|
||||
CanActivate,
|
||||
ActivatedRouteSnapshot,
|
||||
RouterStateSnapshot,
|
||||
CanLoad, Route, UrlSegment, CanActivateChild, UrlTree
|
||||
} from '@angular/router';
|
||||
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
import {ConnectHelper} from '../connectHelper';
|
||||
|
@ -14,7 +8,7 @@ import {CommunityService} from "../community/community.service";
|
|||
import {map} from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
export class IsCommunity implements CanActivate, CanActivateChild {
|
||||
export class IsCommunity {
|
||||
|
||||
constructor(private router: Router,
|
||||
private communityService: CommunityService) {
|
||||
|
|
|
@ -9,7 +9,7 @@ export class ConnectHelper {
|
|||
if(properties.environment == "development" &&
|
||||
(properties.adminToolsPortalType == "connect" || properties.adminToolsPortalType == "community"
|
||||
|| properties.adminToolsPortalType == "aggregator" || properties.adminToolsPortalType == "eosc")) {
|
||||
domain = "test.openaire.eu"; //for testing
|
||||
domain = "covid-19.openaire.eu"; //for testing
|
||||
}
|
||||
domain = domain.indexOf("//") != -1? domain.split("//")[1]:domain; //remove https:// prefix
|
||||
if (domain.indexOf('eosc-portal.eu') != -1) {
|
||||
|
|
|
@ -16,7 +16,8 @@ export class ZenodoCommunitiesService {
|
|||
//.map(res => <any> res.json())
|
||||
.pipe(map(res => [this.parseZenodoCommunities(res['hits'].hits),res['hits'].total]));
|
||||
}
|
||||
getZenodoCommunityById(properties:EnvProperties, url: string) {
|
||||
getZenodoCommunityById(properties:EnvProperties, id: string) {
|
||||
let url = properties.zenodoCommunities + "/" + id;
|
||||
return this.http.get((properties.useLongCache)? (properties.cacheUrl+encodeURIComponent(url)) : url)
|
||||
//.map(res => <any> res.json())
|
||||
.pipe(map(res => {
|
||||
|
@ -39,14 +40,14 @@ export class ZenodoCommunitiesService {
|
|||
|
||||
parseZenodoCommunity(resData:any):ZenodoCommunityInfo {
|
||||
var result: ZenodoCommunityInfo = new ZenodoCommunityInfo();
|
||||
|
||||
result['title'] = resData.title;
|
||||
let metadata = resData["metadata"];
|
||||
result['title'] = metadata.title;
|
||||
result['id'] = resData.id;
|
||||
result['description'] = resData.description;
|
||||
result['link'] = resData.links.html;
|
||||
result['logoUrl'] = resData.logo_url;
|
||||
result['description'] = metadata.description;
|
||||
result['link'] = resData.links.self_html;
|
||||
result['logoUrl'] = resData.links.logo;
|
||||
result['date'] = resData.updated;
|
||||
result['page'] = resData.page;
|
||||
result['page'] = metadata.page;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
|
|
@ -6,14 +6,6 @@ import {AlertModalModule} from '../../utils/modal/alertModal.module';
|
|||
import {DivIdsComponent} from './divIds.component';
|
||||
import {AdminToolServiceModule} from "../../services/adminToolService.module";
|
||||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
|
||||
|
||||
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||
import { MatCheckboxModule } from "@angular/material/checkbox";
|
||||
import { MatFormFieldModule } from "@angular/material/form-field";
|
||||
|
||||
|
||||
import {MatChipsModule} from '@angular/material/chips';
|
||||
import {AdminTabsModule} from "../sharedComponents/admin-tabs/admin-tabs.module";
|
||||
import {PageContentModule} from "../sharedComponents/page-content/page-content.module";
|
||||
import {ClassesRoutingModule} from "./classes-routing.module";
|
||||
|
@ -24,8 +16,8 @@ import {LoadingModule} from "../../utils/loading/loading.module";
|
|||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, RouterModule, FormsModule,
|
||||
AlertModalModule, ReactiveFormsModule, AdminToolServiceModule, InputModule, MatAutocompleteModule, MatFormFieldModule, MatChipsModule,
|
||||
MatCheckboxModule, AdminTabsModule, PageContentModule, ClassesRoutingModule, SearchInputModule, IconsModule, LoadingModule
|
||||
AlertModalModule, ReactiveFormsModule, AdminToolServiceModule, InputModule,
|
||||
AdminTabsModule, PageContentModule, ClassesRoutingModule, SearchInputModule, IconsModule, LoadingModule
|
||||
],
|
||||
declarations: [DivIdsComponent],
|
||||
exports: [DivIdsComponent]
|
||||
|
|
|
@ -8,21 +8,20 @@ import {PageContentFormComponent} from './page-help-content-form.component';
|
|||
import {PageHelpContentFormRoutingModule} from './page-help-content-form-routing.module';
|
||||
import {AdminToolServiceModule} from '../../services/adminToolService.module';
|
||||
import {InputModule} from '../../sharedComponents/input/input.module';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import {IconsModule} from '../../utils/icons/icons.module';
|
||||
import {PageContentModule} from '../sharedComponents/page-content/page-content.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {LoadingModule} from '../../utils/loading/loading.module';
|
||||
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, RouterModule,
|
||||
SafeHtmlPipeModule, CKEditorModule,
|
||||
AlertModalModule, ReactiveFormsModule, PageHelpContentFormRoutingModule, AdminToolServiceModule, InputModule, MatSlideToggleModule, IconsModule, PageContentModule, LoadingModule
|
||||
],
|
||||
declarations: [
|
||||
PageContentFormComponent
|
||||
AlertModalModule, ReactiveFormsModule, PageHelpContentFormRoutingModule, AdminToolServiceModule, InputModule, IconsModule, PageContentModule, LoadingModule, MatSlideToggleModule
|
||||
],
|
||||
declarations: [PageContentFormComponent],
|
||||
exports: [PageContentFormComponent]
|
||||
})
|
||||
export class PageHelpContentFormModule {}
|
||||
export class PageHelpContentFormModule {
|
||||
}
|
||||
|
|
|
@ -80,9 +80,6 @@ export class PageContentComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
if(this.isBrowser) {
|
||||
this.stickyBugWorkaround();
|
||||
}
|
||||
this.subscriptions.push(this.layoutService.isMobile.subscribe(isMobile => {
|
||||
this.isMobile = isMobile;
|
||||
if(this.isBrowser) {
|
||||
|
@ -132,41 +129,7 @@ export class PageContentComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
initFooter() {
|
||||
let footer_offset = this.calcStickyFooterOffset(this.sticky_footer.nativeElement);
|
||||
this.sticky.footer = UIkit.sticky(this.sticky_footer.nativeElement, {bottom: true, offset: footer_offset});
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for sticky not update bug when sidebar is toggled.
|
||||
* TODO when UIKit will be updated => remove
|
||||
*
|
||||
* */
|
||||
stickyBugWorkaround() {
|
||||
let sidebarOffset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--dashboard-sidebar-width')) -
|
||||
Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--dashboard-sidebar-mini-width'));
|
||||
let transitionDelay = Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--dashboard-transition-delay')) * 1000;
|
||||
this.subscriptions.push(this.layoutService.isOpen.subscribe(isOpen => {
|
||||
if (this.sticky.header) {
|
||||
if (isOpen) {
|
||||
this.sticky.header.$el.style.width = Number.parseInt(this.sticky.header.$el.style.width) - sidebarOffset + 'px';
|
||||
} else {
|
||||
this.sticky.header.$el.style.width = Number.parseInt(this.sticky.header.$el.style.width) + sidebarOffset + 'px';
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.sticky.header.$emit();
|
||||
}, transitionDelay);
|
||||
}
|
||||
if (this.sticky.footer) {
|
||||
if (isOpen) {
|
||||
this.sticky.footer.$el.style.width = Number.parseInt(this.sticky.footer.$el.style.width) - sidebarOffset + 'px';
|
||||
} else {
|
||||
this.sticky.footer.$el.style.width = Number.parseInt(this.sticky.footer.$el.style.width) + sidebarOffset + 'px';
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.sticky.footer.$emit();
|
||||
}, transitionDelay);
|
||||
}
|
||||
this.cdr.detectChanges();
|
||||
}));
|
||||
this.sticky.footer = UIkit.sticky(this.sticky_footer.nativeElement, {end: true, offset: footer_offset});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -79,6 +79,10 @@ export class LayoutService {
|
|||
* Add hasMenuSearchBar: false/ nothing on data of route config, if the search bar in the menu should not appear, otherwise true.
|
||||
*/
|
||||
private hasMenuSearchBarSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
/**
|
||||
* Add hasStickyHeaderOnMobile: true in order to activate uk-sticky in header of mobile/tablet devices.
|
||||
* */
|
||||
private hasStickyHeaderOnMobileSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private subscriptions: any[] = [];
|
||||
|
||||
|
@ -127,7 +131,7 @@ export class LayoutService {
|
|||
data['hasHeader'] === false) {
|
||||
this.setHasHeader(false);
|
||||
if (typeof document !== "undefined") {
|
||||
document.documentElement.style.setProperty('--header-height', '0');
|
||||
document.documentElement.style.setProperty('--header-height', '0px');
|
||||
}
|
||||
} else {
|
||||
this.setHasHeader(true);
|
||||
|
@ -177,6 +181,12 @@ export class LayoutService {
|
|||
} else {
|
||||
this.setHasMenuSearchBar(false);
|
||||
}
|
||||
if (data['hasStickyHeaderOnMobile'] !== undefined &&
|
||||
data['hasStickyHeaderOnMobile'] === true) {
|
||||
this.setHasStickyHeaderOnMobile(true);
|
||||
} else {
|
||||
this.setHasStickyHeaderOnMobile(false);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.setObserver();
|
||||
|
@ -311,4 +321,12 @@ export class LayoutService {
|
|||
setHasMenuSearchBar(value: boolean) {
|
||||
this.hasMenuSearchBarSubject.next(value);
|
||||
}
|
||||
|
||||
get hasStickyHeaderOnMobile(): Observable<boolean> {
|
||||
return this.hasStickyHeaderOnMobileSubject.asObservable();
|
||||
}
|
||||
|
||||
setHasStickyHeaderOnMobile(value: boolean) {
|
||||
this.hasStickyHeaderOnMobileSubject.next(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<aside id="sidebar_main" class="uk-visible@m">
|
||||
<div id="sidebar_main" uk-sticky="end: .sidebar_main_swipe;" [attr.offset]="offset" class="uk-visible@m">
|
||||
<div sidebar-content>
|
||||
<ng-container *ngTemplateOutlet="menu; context: {mobile: false}"></ng-container>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
<div class="uk-hidden@m">
|
||||
<div id="sidebar_offcanvas" #sidebar_offcanvas [attr.uk-offcanvas]="'overlay: true'">
|
||||
<div class="uk-offcanvas-bar uk-padding-remove">
|
||||
|
@ -29,7 +29,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div *ngIf="items.length > 0" class="menu_section uk-margin-large-top" [class.mobile]="mobile" style="min-height: 30vh">
|
||||
<ul #nav class="uk-list uk-nav uk-nav-parent-icon" [class.uk-list-large]="mobile"
|
||||
<ul #nav class="uk-list uk-nav" [class.uk-list-large]="mobile"
|
||||
[class.uk-nav-default]="!mobile" [class.uk-nav-primary]="mobile" uk-nav="duration: 400">
|
||||
<ng-template ngFor [ngForOf]="items" let-item>
|
||||
<li [class.uk-active]="item.isActive" [ngClass]="item.customClass"
|
||||
|
@ -40,6 +40,7 @@
|
|||
<icon class="menu-icon" [customClass]="item.icon.class" [name]="item.icon.name" ratio="0.9" [svg]="item.icon.svg" [flex]="true"></icon>
|
||||
</div>
|
||||
<span [class.hide-on-close]="item.icon" class="uk-width-expand@l uk-text-truncate uk-margin-small-left">{{item.title}}</span>
|
||||
<span *ngIf="item.items.length > 0" class="uk-nav-parent-icon hide-on-close"></span>
|
||||
</a>
|
||||
<ul *ngIf="item.items?.length > 0 && (isBrowser || item.isActive)" class="uk-nav-sub">
|
||||
<li *ngFor="let subItem of item.items" [ngClass]="subItem.customClass"
|
||||
|
|
|
@ -30,6 +30,7 @@ export class SideBarComponent implements OnInit, AfterViewInit, OnDestroy, OnCha
|
|||
@Input() queryParamsHandling;
|
||||
@ViewChild("nav") nav: ElementRef;
|
||||
@ViewChild("sidebar_offcanvas") sidebar_offcanvas: ElementRef;
|
||||
public offset: number;
|
||||
public properties = properties;
|
||||
private subscriptions: any[] = [];
|
||||
private init: boolean = false;
|
||||
|
@ -45,6 +46,9 @@ export class SideBarComponent implements OnInit, AfterViewInit, OnDestroy, OnCha
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (typeof document !== "undefined") {
|
||||
this.offset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height'));
|
||||
}
|
||||
this.setActiveMenuItem();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,24 +4,18 @@ import {Observable} from 'rxjs';
|
|||
|
||||
import {properties} from "../../../environments/environment";
|
||||
|
||||
@Injectable()
|
||||
export class isDevelopmentGuard implements CanActivate, CanLoad {
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class isDevelopmentGuard {
|
||||
constructor(private router: Router) {
|
||||
}
|
||||
|
||||
check(): Observable<boolean> | boolean {
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree{
|
||||
if(properties.environment == 'development') {
|
||||
return true;
|
||||
} else {
|
||||
this.router.navigate([properties.errorLink]);
|
||||
}
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree{
|
||||
return this.check();
|
||||
}
|
||||
|
||||
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
return this.check()
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import {Observable} from 'rxjs';
|
||||
import {take, tap} from 'rxjs/operators';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, CanActivate, Data, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, Data, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import {ConfigurationService} from '../utils/configuration/configuration.service';
|
||||
import {ConnectHelper} from '../connect/connectHelper';
|
||||
import {properties} from "../../../environments/environment";
|
||||
|
||||
@Injectable()
|
||||
export class IsRouteEnabled implements CanActivate {
|
||||
export class IsRouteEnabled {
|
||||
|
||||
constructor(private router: Router,
|
||||
private config: ConfigurationService) {
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<div class="uk-margin-top uk-padding-small">
|
||||
<div id="parentContainer" class="uk-grid uk-grid-large" uk-grid>
|
||||
<div class="uk-width-1-4@m uk-visible@m">
|
||||
<div class="uk-sticky" uk-sticky="bottom: !#parentContainer; offset: 200;">
|
||||
<div class="uk-sticky" uk-sticky="end: !#parentContainer; offset: 200;">
|
||||
<ul *ngIf="!keyword" class="uk-tab uk-tab-left">
|
||||
<li *ngFor="let item of fos; index as i" [class.uk-active]="activeSection === item.id"
|
||||
class="uk-margin-small-bottom uk-text-capitalize">
|
||||
|
|
|
@ -2,7 +2,7 @@ import {Injectable, Inject, PLATFORM_ID, Optional} from '@angular/core';
|
|||
import {HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpHeaders} from '@angular/common/http';
|
||||
import {Observable, of} from 'rxjs';
|
||||
import {tap} from 'rxjs/operators';
|
||||
import {TransferState, makeStateKey, StateKey} from '@angular/platform-browser';
|
||||
import {TransferState, makeStateKey, StateKey} from '@angular/core';
|
||||
import {isPlatformServer} from '@angular/common';
|
||||
import {properties} from "../../environments/environment";
|
||||
import {REQUEST} from "./utils/tokens";
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<!-- left box - actions -->
|
||||
<!-- <div id="landing-left-sidebar" *ngIf="dataProviderInfo" class="uk-visible@s uk-padding-remove-horizontal">
|
||||
<div class="uk-flex uk-flex-column uk-flex-between uk-flex-center uk-sticky"
|
||||
uk-sticky="bottom: true" [attr.offset]="offset">
|
||||
uk-sticky="end: true" [attr.offset]="offset">
|
||||
<div class="uk-align-center uk-text-center uk-margin-medium-top uk-flex uk-flex-column uk-flex-between">
|
||||
<ng-container *ngIf="dataProviderInfo && hasMetrics">
|
||||
<metrics [pageViews]="pageViews"
|
||||
|
@ -48,7 +48,8 @@
|
|||
<div id="landing-center-content" class="uk-width-expand uk-padding-remove uk-background-default">
|
||||
|
||||
<ng-template #graph_and_feedback_template>
|
||||
<div class="uk-container uk-container-xlarge uk-flex uk-flex-between uk-flex-wrap uk-margin-small-top uk-margin-small-bottom" [class.uk-invisible]="!dataProviderInfo">
|
||||
<div class="uk-padding-xsmall">
|
||||
<div class="uk-container uk-container-xlarge uk-flex uk-flex-between uk-flex-wrap" [class.uk-invisible]="!dataProviderInfo">
|
||||
<!-- Last Index Info-->
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@l">
|
||||
<img src="assets/common-assets/openaire-badge-1.png" alt="Powered by OpenAIRE graph" style="height: 15px;">
|
||||
|
@ -63,10 +64,11 @@
|
|||
<a (click)="showFeedback = true; scroll()" class="uk-text-xsmall">Give us feedback</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div #graph_and_feedback id="graph_and_feedback" class="uk-blur-background uk-text-xsmall uk-visible@m"
|
||||
uk-sticky="bottom: true;" [attr.offset]="graph_offset">
|
||||
uk-sticky="end: true;" [attr.offset]="graph_offset">
|
||||
<ng-container *ngTemplateOutlet="graph_and_feedback_template"></ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -229,7 +231,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="main-tabs-div" class="uk-sticky uk-blur-background"
|
||||
uk-sticky="bottom: true; media: @m" [attr.offset]="offset">
|
||||
uk-sticky="end: true; media: @m" [attr.offset]="offset">
|
||||
<div class="uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom">
|
||||
<landing-header [ngClass]="stickyHeader ? 'uk-visible@m' : 'uk-invisible'"
|
||||
[properties]="properties" [title]="dataProviderInfo.title.name"
|
||||
|
|
|
@ -434,7 +434,7 @@ export class DataProviderComponent {
|
|||
},
|
||||
err => {
|
||||
//console.log(err);
|
||||
this.handleError("Error getting " + this.type + " for " + (this.datasourceId ? ("id: " + this.datasourceId) : ("pid: " + this.identifier.id + " ("+this.identifier.class+")")), err);
|
||||
this.handleError("Error getting " + this.type + " for " + (this.datasourceId ? ("id: " + this.datasourceId) : ("pid: " + this.identifier.id)), err);
|
||||
if (err.status == 404) {
|
||||
this._router.navigate([this.properties.errorLink], {
|
||||
queryParams: {
|
||||
|
|
|
@ -24,7 +24,7 @@ export class DataProviderService {
|
|||
if (id) {
|
||||
return properties.searchAPIURLLAst + typePathParam + "/" + id + '?format=json';
|
||||
} else if (identifier) {
|
||||
return properties.searchAPIURLLAst + "resources2?pid="+encodeURIComponent(identifier.id) + "&pidtype=" + identifier.class + "&type="+typePathParam+"&format=json";
|
||||
return properties.searchAPIURLLAst + "resources2?query=(pid exact \""+encodeURIComponent(identifier.id) + "\")&type="+typePathParam+"&format=json";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import {CommonModule} from "@angular/common";
|
|||
import {FeedbackComponent} from "./feedback.component";
|
||||
import {LandingHeaderModule} from "../landing-utils/landing-header/landing-header.module";
|
||||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {AlertModalModule} from "../../utils/modal/alertModal.module";
|
||||
import {EmailService} from "../../utils/email/email.service";
|
||||
import {RecaptchaModule} from "ng-recaptcha";
|
||||
|
@ -11,7 +10,7 @@ import {IconsModule} from "../../utils/icons/icons.module";
|
|||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, LandingHeaderModule, ReactiveFormsModule, MatSelectModule, AlertModalModule, RecaptchaModule, IconsModule, InputModule],
|
||||
imports: [CommonModule, LandingHeaderModule, ReactiveFormsModule, AlertModalModule, RecaptchaModule, IconsModule, InputModule],
|
||||
declarations: [FeedbackComponent],
|
||||
providers: [EmailService],
|
||||
exports: [FeedbackComponent]
|
||||
|
|
|
@ -40,10 +40,11 @@ import {RouterHelper} from "../../utils/routerHelper.class";
|
|||
<icon [flex]="true" [name]="(isOpen?'arrow_drop_up':'arrow_drop_down')"></icon>
|
||||
</span>
|
||||
</a>
|
||||
<div #dropElement uk-drop="mode: click; pos: bottom-left;"
|
||||
class="uk-drop download-drop uk-card uk-card-default uk-padding-small uk-padding-remove-horizontal uk-text-small uk-height-max-large uk-overflow-auto">
|
||||
<div #dropElement uk-drop="mode: click; pos: bottom-left; flip: false; shift: false" class="uk-drop download-drop">
|
||||
<div class="uk-card uk-card-default uk-padding-small uk-padding-remove-horizontal uk-text-small uk-height-max-large uk-overflow-auto">
|
||||
<ng-container *ngTemplateOutlet="availableOnList"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isMobile">
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
import {
|
||||
Component,
|
||||
Inject,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
RendererFactory2,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import {Component, Inject, Input, OnDestroy, OnInit, RendererFactory2, ViewEncapsulation} from '@angular/core';
|
||||
import {Citation, CitationData} from './citation.class';
|
||||
import {ResultLandingInfo} from "../../../utils/entities/resultLandingInfo";
|
||||
import {DOCUMENT} from "@angular/common";
|
||||
import {EnvProperties} from "../../../utils/properties/env-properties";
|
||||
import {properties} from "../../../../../environments/environment";
|
||||
import {PiwikService} from "../../../utils/piwik/piwik.service";
|
||||
import {ResultPreview} from "../../../utils/result-preview/result-preview";
|
||||
|
||||
declare var Cite: any;
|
||||
// Based on https://citation.js.org/api/tutorial-getting_started.html browser release
|
||||
|
|
|
@ -3,13 +3,12 @@ import {CommonModule} from '@angular/common';
|
|||
import {FormsModule} from '@angular/forms';
|
||||
|
||||
import {CiteThisComponent} from './citeThis.component';
|
||||
import { MatSelectModule } from "@angular/material/select";
|
||||
import {InputModule} from "../../../sharedComponents/input/input.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
||||
CommonModule, FormsModule, MatSelectModule, InputModule
|
||||
CommonModule, FormsModule, InputModule
|
||||
],
|
||||
declarations: [
|
||||
CiteThisComponent
|
||||
|
|
|
@ -888,7 +888,7 @@ export class ParsingFunctions {
|
|||
counts.push({name: 'downloads', icon: 'download', value: element.count, order: 1});
|
||||
measure.downloads = element.count;
|
||||
}
|
||||
if (element.id == 'influence_alt') {
|
||||
if (element.id == 'influence_alt' || element.id == 'citation_count') {
|
||||
bip.push({name: 'citations', icon: 'cite', value: element.score, order: 2});
|
||||
measure.citations = element.score;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<!-- left column -->
|
||||
<!-- <div id="landing-left-sidebar" *ngIf="organizationInfo" class="uk-visible@s uk-padding-remove-horizontal">
|
||||
<div class="uk-flex uk-flex-column uk-flex-right uk-sticky"
|
||||
uk-sticky="bottom: true" [attr.offset]="offset">
|
||||
uk-sticky="end: true" [attr.offset]="offset">
|
||||
<div class="uk-margin-large-bottom uk-align-center">
|
||||
<div class="uk-text-meta uk-text-uppercase">Actions</div>
|
||||
<ul class="uk-list">
|
||||
|
@ -47,6 +47,7 @@
|
|||
|
||||
<!-- Graph and feedback -->
|
||||
<ng-template #graph_and_feedback_template>
|
||||
<div class="uk-padding-xsmall">
|
||||
<div class="uk-container uk-container-xlarge uk-flex uk-flex-between uk-flex-wrap uk-margin-small-top uk-margin-small-bottom" [class.uk-invisible]="!organizationInfo">
|
||||
<!-- Last Index Info-->
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@l">
|
||||
|
@ -62,10 +63,11 @@
|
|||
<a (click)="showFeedback = true; scroll()" class="uk-text-xsmall">Give us feedback</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div #graph_and_feedback id="graph_and_feedback" class="uk-blur-background uk-text-xsmall uk-visible@m"
|
||||
uk-sticky="bottom: true;" [attr.offset]="graph_offset">
|
||||
uk-sticky="end: true;" [attr.offset]="graph_offset">
|
||||
<ng-container *ngTemplateOutlet="graph_and_feedback_template"></ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -171,7 +173,7 @@
|
|||
</div>
|
||||
<!-- Tabs section -->
|
||||
<div id="main-tabs-div" class="uk-sticky uk-blur-background"
|
||||
uk-sticky="bottom: true; media: @m" [attr.offset]="offset">
|
||||
uk-sticky="end: true; media: @m" [attr.offset]="offset">
|
||||
<div class="uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom">
|
||||
<!-- <showTitle *ngIf="stickyHeader" [titleName]="organizationInfo.title.name" classNames="uk-margin-remove-bottom" class="uk-visible@m"></showTitle>-->
|
||||
<landing-header [ngClass]="stickyHeader ? 'uk-visible@m' : 'uk-invisible'"
|
||||
|
|
|
@ -23,7 +23,6 @@ import {HelperModule} from "../../utils/helper/helper.module";
|
|||
import {OrganizationsDeletedByInferenceModule} from "./deletedByInference/deletedByInference.module";
|
||||
import {LandingHeaderModule} from "../landing-utils/landing-header/landing-header.module";
|
||||
import {FeedbackModule} from "../feedback/feedback.module";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {TabsModule} from "../../utils/tabs/tabs.module";
|
||||
import {SearchTabModule} from "../../utils/tabs/contents/search-tab.module";
|
||||
import {LoadingModule} from '../../utils/loading/loading.module';
|
||||
|
@ -50,7 +49,6 @@ import {EntityActionsModule} from "../../utils/entity-actions/entity-actions.mod
|
|||
ProjectsServiceModule,
|
||||
Schema2jsonldModule, SEOServiceModule, HelperModule,
|
||||
OrganizationsDeletedByInferenceModule, LandingHeaderModule, FeedbackModule,
|
||||
MatSelectModule,
|
||||
TabsModule, SearchTabModule, LoadingModule, IconsModule, InputModule, FullScreenModalModule, EGIDataTransferModule, EntityActionsModule
|
||||
],
|
||||
declarations: [
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<!-- left box - actions -->
|
||||
<!-- <div id="landing-left-sidebar" *ngIf="projectInfo" class="uk-visible@s uk-padding-remove-horizontal">
|
||||
<div class="uk-flex uk-flex-column uk-flex-between uk-flex-center uk-sticky"
|
||||
uk-sticky="bottom: true" [attr.offset]="offset">
|
||||
uk-sticky="end: true" [attr.offset]="offset">
|
||||
<div class="uk-align-center uk-text-center uk-margin-medium-top uk-flex uk-flex-column uk-flex-between">
|
||||
<ng-container *ngIf="projectInfo && hasMetrics">
|
||||
<metrics [pageViews]="pageViews"
|
||||
|
@ -85,6 +85,7 @@
|
|||
<div id="landing-center-content" class="uk-width-expand uk-padding-remove uk-background-default">
|
||||
|
||||
<ng-template #graph_and_feedback_template>
|
||||
<div class="uk-padding-xsmall">
|
||||
<div class="uk-container uk-container-xlarge uk-flex uk-flex-between uk-flex-wrap uk-margin-small-top uk-margin-small-bottom" [class.uk-invisible]="!projectInfo">
|
||||
<!-- Last Index Info-->
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@l">
|
||||
|
@ -100,10 +101,11 @@
|
|||
<a (click)="showFeedback = true; scroll()" class="uk-text-xsmall">Give us feedback</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div #graph_and_feedback id="graph_and_feedback" class="uk-blur-background uk-text-xsmall uk-visible@m"
|
||||
uk-sticky="bottom: true;" [attr.offset]="graph_offset">
|
||||
uk-sticky="end: true;" [attr.offset]="graph_offset">
|
||||
<ng-container *ngTemplateOutlet="graph_and_feedback_template"></ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -363,7 +365,7 @@
|
|||
</div>
|
||||
|
||||
<div id="main-tabs-div" class="uk-sticky uk-blur-background"
|
||||
uk-sticky="bottom: true; media: @m" [attr.offset]="offset">
|
||||
uk-sticky="end: true; media: @m" [attr.offset]="offset">
|
||||
<div class="uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom">
|
||||
<!-- <showTitle *ngIf="stickyHeader" [titleName]="projectName" classNames="uk-margin-remove-bottom" class="uk-visible@m"></showTitle>-->
|
||||
<landing-header [ngClass]="stickyHeader ? 'uk-visible@m' : 'uk-invisible'"
|
||||
|
|
|
@ -2,7 +2,6 @@ import {NgModule} from '@angular/core';
|
|||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
|
||||
import {ProjectComponent} from './project.component';
|
||||
import {ProjectServiceModule} from './projectService.module';
|
||||
|
@ -41,7 +40,7 @@ import {EntityActionsModule} from "../../utils/entity-actions/entity-actions.mod
|
|||
IFrameModule, ReportsServiceModule,
|
||||
SearchResearchResultsServiceModule, ProjectServiceModule,
|
||||
Schema2jsonldModule, SEOServiceModule, HelperModule,
|
||||
LandingHeaderModule, MatSelectModule, FeedbackModule, AltMetricsModule,
|
||||
LandingHeaderModule, FeedbackModule, AltMetricsModule,
|
||||
TabsModule, SearchTabModule, LoadingModule, IconsModule, InputModule,
|
||||
FullScreenModalModule, SafeHtmlPipeModule, EGIDataTransferModule, EntityActionsModule
|
||||
],
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<!-- left box/sidebar - actions -->
|
||||
<!-- <div id="landing-left-sidebar" *ngIf="resultLandingInfo" class="uk-visible@s uk-padding-remove-horizontal">
|
||||
<div class="uk-flex uk-flex-column uk-flex-between uk-flex-center uk-sticky"
|
||||
uk-sticky="bottom: true" [attr.offset]="offset">
|
||||
uk-sticky="end: true" [attr.offset]="offset">
|
||||
<div class="uk-align-center uk-text-center uk-margin-medium-top uk-flex uk-flex-column uk-flex-between">
|
||||
<ng-container *ngIf="resultLandingInfo && (hasAltMetrics || hasMetrics)">
|
||||
<metrics *ngIf="hasMetrics" class="uk-margin-bottom"
|
||||
|
@ -81,6 +81,7 @@
|
|||
<div id="landing-center-content" class="uk-width-expand uk-padding-remove uk-background-default">
|
||||
|
||||
<ng-template #graph_and_feedback_template>
|
||||
<div class="uk-padding-xsmall">
|
||||
<div class="uk-container uk-container-xlarge uk-flex uk-flex-between uk-flex-wrap uk-margin-small-top uk-margin-small-bottom" [class.uk-invisible]="!resultLandingInfo">
|
||||
<!-- Last Index Info-->
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@l">
|
||||
|
@ -96,10 +97,11 @@
|
|||
<a (click)="feedbackClicked()" class="uk-text-xsmall">Give us feedback</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div #graph_and_feedback id="graph_and_feedback" class="uk-blur-background uk-text-xsmall uk-visible@m"
|
||||
uk-sticky="bottom: true;" [attr.offset]="graph_offset">
|
||||
uk-sticky="end: true;" [attr.offset]="graph_offset">
|
||||
<ng-container *ngTemplateOutlet="graph_and_feedback_template"></ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -269,7 +271,7 @@
|
|||
</div>
|
||||
|
||||
<div id="main-tabs-div" class="uk-sticky uk-blur-background"
|
||||
uk-sticky="bottom: true; media: @m" [attr.offset]="offset">
|
||||
uk-sticky="end: true; media: @m" [attr.offset]="offset">
|
||||
<div class="uk-padding uk-padding-remove-horizontal uk-padding-remove-bottom">
|
||||
<landing-header [ngClass]="stickyHeader ? 'uk-visible@m' : 'uk-invisible'"
|
||||
[properties]="properties" [title]="resultLandingInfo.title"
|
||||
|
@ -349,7 +351,7 @@
|
|||
<ng-container *ngIf="resultLandingInfo && hasRightSidebarInfo">
|
||||
<div id="landing-right-sidebar" *ngIf="!rightSidebarOffcanvasClicked" class="uk-width-1-3 uk-width-1-4@l uk-padding-remove uk-text-small uk-visible@m"
|
||||
[class.uk-animation-right]="viewAll">
|
||||
<div class="uk-sticky" uk-sticky="bottom: true" [attr.offset]="offset">
|
||||
<div class="uk-sticky" uk-sticky="end: true" [attr.offset]="offset">
|
||||
<div class="uk-overflow-auto uk-height-1-1">
|
||||
<ng-container *ngTemplateOutlet="right_column"></ng-container>
|
||||
</div>
|
||||
|
@ -360,11 +362,11 @@
|
|||
|
||||
<!-- right box/ sidebar-->
|
||||
<ng-container *ngIf="!showFeedback && resultLandingInfo && hasRightSidebarInfo">
|
||||
<div id="landing-right-sidebar-switcher" uk-toggle href="#right-column-offcanvas"
|
||||
class="uk-offcanvas-switcher uk-flex uk-flex-center uk-flex-middle uk-hidden@m"
|
||||
<a id="landing-right-sidebar-switcher" uk-toggle href="#right-column-offcanvas"
|
||||
class="uk-offcanvas-switcher uk-flex uk-link-reset uk-flex-center uk-flex-middle uk-hidden@m"
|
||||
(click)="rightSidebarOffcanvasClicked = true;">
|
||||
<icon name="more" [ratio]="1.5" customClass="uk-text-primary" [flex]="true" visuallyHidden="sidebar"></icon>
|
||||
</div>
|
||||
</a>
|
||||
<div id="right-column-offcanvas" class="uk-offcanvas" uk-offcanvas="flip: true; overlay: true;">
|
||||
<div class="uk-offcanvas-bar">
|
||||
<button class="uk-offcanvas-close uk-close uk-icon" type="button"
|
||||
|
|
|
@ -604,7 +604,7 @@ export class ResultLandingComponent {
|
|||
}
|
||||
},
|
||||
err => {
|
||||
this.handleError("Error getting " + this.type + " for " + (this.id ? ("id: " + this.id) : ("pid: " + this.identifier.id + " ("+this.identifier.class+")")), err);
|
||||
this.handleError("Error getting " + this.type + " for " + (this.id ? ("id: " + this.id) : ("pid: " + this.identifier.id)), err);
|
||||
if (err.status == 404) {
|
||||
this._router.navigate([this.properties.errorLink], {queryParams: {"page": this._location.path(true), "page_type": this.type}});
|
||||
}else if(err.name == "TimeoutError"){
|
||||
|
|
|
@ -28,8 +28,6 @@ import {FeedbackModule} from "../feedback/feedback.module";
|
|||
import {TabsModule} from "../../utils/tabs/tabs.module";
|
||||
import {LoadingModule} from "../../utils/loading/loading.module";
|
||||
import {OrcidModule} from "../../orcid/orcid.module";
|
||||
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {IconsModule} from "../../utils/icons/icons.module";
|
||||
import {IconsService} from "../../utils/icons/icons.service";
|
||||
import {cite, fire, graph, landmark, link, link_to, quotes, rocket, versions} from "../../utils/icons/icons";
|
||||
|
@ -48,7 +46,7 @@ import {EntityActionsModule} from "../../utils/entity-actions/entity-actions.mod
|
|||
AltMetricsModule, Schema2jsonldModule, SEOServiceModule,
|
||||
DeletedByInferenceModule, ShowAuthorsModule, HelperModule, ResultLandingUtilsModule, AlertModalModule,
|
||||
AnnotationModule, LandingHeaderModule, NoLoadPaging, ResultPreviewModule, FeedbackModule, TabsModule, LoadingModule,
|
||||
OrcidModule, MatFormFieldModule, MatSelectModule, IconsModule, InputModule, EGIDataTransferModule, RecaptchaModule,
|
||||
OrcidModule, IconsModule, InputModule, EGIDataTransferModule, RecaptchaModule,
|
||||
SdgFosSuggestModule, FullScreenModalModule, SafeHtmlPipeModule, EntityActionsModule
|
||||
],
|
||||
declarations: [
|
||||
|
|
|
@ -39,7 +39,7 @@ export class ResultLandingService {
|
|||
} else if (identifier) {
|
||||
// pid = "10.3389/fphys.2014.00466";
|
||||
let url = properties.searchAPIURLLAst + "resources2";
|
||||
url += "?pid=" + encodeURIComponent(identifier.id) + "&pidtype=" + identifier.class + "&type=";
|
||||
url += "?query=(pid exact \"" + encodeURIComponent(identifier.id) + "\")&type=";
|
||||
if (type === 'publication') {
|
||||
url += 'publications';
|
||||
} else if (type === 'dataset') {
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
CanActivateChild, Data,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, Data, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import {Observable} from 'rxjs';
|
||||
import {Session} from './utils/helper.class';
|
||||
import {LoginErrorCodes} from './utils/guardHelper.class';
|
||||
|
@ -16,7 +9,7 @@ import {map, tap} from "rxjs/operators";
|
|||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AdminLoginGuard implements CanActivate, CanActivateChild {
|
||||
export class AdminLoginGuard {
|
||||
|
||||
constructor(private router: Router,
|
||||
private userManagementService: UserManagementService) {
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
Router,
|
||||
CanActivate,
|
||||
ActivatedRouteSnapshot,
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import {Observable} from 'rxjs';
|
||||
import {LoginErrorCodes} from './utils/guardHelper.class';
|
||||
|
||||
@Injectable()
|
||||
export class FreeGuard implements CanActivate {
|
||||
export class FreeGuard {
|
||||
|
||||
constructor(private router: Router) {
|
||||
}
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
CanActivateChild,
|
||||
CanLoad,
|
||||
Route,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import { ActivatedRouteSnapshot, Route, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import {Observable} from 'rxjs';
|
||||
import {LoginErrorCodes} from './utils/guardHelper.class';
|
||||
import {map, tap} from "rxjs/operators";
|
||||
|
@ -17,7 +8,7 @@ import {UserManagementService} from "../services/user-management.service";
|
|||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LoginGuard implements CanActivate, CanLoad, CanActivateChild {
|
||||
export class LoginGuard {
|
||||
|
||||
constructor(private router: Router,
|
||||
private userManagementService: UserManagementService) {
|
||||
|
|
|
@ -83,12 +83,13 @@ declare var UIkit;
|
|||
{{user.fullname}}
|
||||
</h5>
|
||||
</div>
|
||||
<ul class="uk-nav uk-nav-primary uk-list uk-margin-top uk-nav-parent-icon" uk-nav>
|
||||
<ul class="uk-nav uk-nav-primary uk-list uk-margin-top" uk-nav>
|
||||
<ng-container *ngFor="let item of userMenuItems ">
|
||||
<li *ngIf="item.needsAuthorization && isAuthorized || !item.needsAuthorization" [ngClass]="item.customClass">
|
||||
<a *ngIf="item.route" [routerLink]="item.route" (click)="closeCanvas(account)">{{item.title}}</a>
|
||||
<a *ngIf="item.route" [routerLink]="item.route" (click)="closeCanvas(account)">
|
||||
{{item.title}}<span *ngIf="item.items.length > 0" class="uk-nav-parent-icon"></span></a>
|
||||
<a *ngIf="!item.route && item.url" (click)="closeCanvas(account)" [href]="item.url" [class.custom-external]="item.target != '_self'"
|
||||
[target]="item.target">{{item.title}}</a>
|
||||
[target]="item.target">{{item.title}}<span *ngIf="item.items.length > 0" class="uk-nav-parent-icon"></span></a>
|
||||
</li>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="notificationConfiguration">
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {stakeholderTypes} from "../../monitor/entities/stakeholder";
|
||||
|
||||
export class User {
|
||||
email: string;
|
||||
firstname: string;
|
||||
|
@ -98,23 +100,11 @@ export class Session {
|
|||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
return this.isTypeCurator("community", user);
|
||||
}
|
||||
|
||||
private static isTypeCurator(type: string, user: User): boolean {
|
||||
|
@ -122,16 +112,7 @@ export class Session {
|
|||
}
|
||||
|
||||
public static isCurator(type: string, user: User): boolean {
|
||||
if (type == 'funder') {
|
||||
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;
|
||||
return (type === 'community' || stakeholderTypes.find(stakeholderType => stakeholderType.value == type)) && this.isTypeCurator(type, user);
|
||||
}
|
||||
|
||||
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,18 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {PreviousRouteRecorder} from '../../utils/piwik/previousRouteRecorder.guard';
|
||||
import {ManageStakeholdersComponent} from "./manageStakeholders.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild([
|
||||
{
|
||||
path: '',
|
||||
component: ManageStakeholdersComponent,
|
||||
canDeactivate: [PreviousRouteRecorder]
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
||||
export class ManageStakeholdersRoutingModule {
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
<div page-content>
|
||||
<div header>
|
||||
<sidebar-mobile-toggle class="uk-margin-top uk-hidden@m uk-display-block"></sidebar-mobile-toggle>
|
||||
<div *ngIf="isCurator()" class="uk-margin-remove-bottom uk-margin-medium-top">
|
||||
<slider-tabs [type]="'dynamic'" (activeEmitter)="tab = $event">
|
||||
<slider-tab tabTitle="All" [tabId]="'all'" [active]="tab === 'all'"></slider-tab>
|
||||
<slider-tab tabTitle="Profile templates" [tabId]="'templates'" [active]="tab === 'templates'"></slider-tab>
|
||||
<slider-tab tabTitle="Profiles" [tabId]="'profiles'" [active]="tab === 'profiles'"></slider-tab>
|
||||
</slider-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<div actions>
|
||||
<div class="uk-section-xsmall">
|
||||
<div class="uk-flex uk-flex-right@m uk-flex-center uk-flex-wrap uk-flex-middle">
|
||||
<div search-input [searchControl]="filters.get('keyword')" [expandable]="true" placeholder="Search Profiles" searchInputClass="outer"
|
||||
class="uk-width-1-3@xl uk-width-2-5@l uk-width-1-2@m uk-width-1-1 uk-flex uk-flex-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div inner>
|
||||
<div *ngIf="loading" class="uk-margin-medium-top uk-padding-large">
|
||||
<loading></loading>
|
||||
</div>
|
||||
<div *ngIf="!loading" uk-height-match="target: .titleContainer; row: false">
|
||||
<div uk-height-match="target: .logoContainer; row: false">
|
||||
<div *ngIf="tab != 'profiles' && isCurator()" class="uk-section">
|
||||
<h4>Profile Templates</h4>
|
||||
<div class="uk-grid uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1 uk-grid-match" uk-grid>
|
||||
<ng-template ngFor [ngForOf]="displayDefaultStakeholders" let-stakeholder>
|
||||
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="!loading && isCurator()">
|
||||
<ng-container *ngTemplateOutlet="newBox; context: {text:'Create a new default profile.', isDefault:true}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!isManager()" class="message">
|
||||
<h4 class="uk-text-center">
|
||||
No profiles to manage yet
|
||||
</h4>
|
||||
</div>
|
||||
<div *ngIf="tab != 'templates' && isManager()" class="uk-section">
|
||||
<h4>Profiles</h4>
|
||||
<div class="uk-grid uk-grid-match uk-child-width-1-3@l uk-child-width-1-2@m uk-child-width-1-1" uk-grid>
|
||||
<ng-template ngFor [ngForOf]="displayStakeholders" let-stakeholder>
|
||||
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="!loading && isCurator()">
|
||||
<ng-container *ngTemplateOutlet="newBox; context: {text:'Create a new profile by selecting the type ('+typesAsString+') and ' +
|
||||
'select indicators based on a default or a blank profile.', isDefault:false}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #stakeholderBox let-stakeholder="stakeholder">
|
||||
<div *ngIf="stakeholder">
|
||||
<div class="uk-card uk-card-default uk-card-body uk-position-relative" [ngClass]="stakeholder.type">
|
||||
<div class="uk-position-top-right uk-margin-small-right uk-margin-small-top">
|
||||
<a class="uk-link-reset uk-flex uk-flex-middle">
|
||||
<icon [flex]="true" [name]="stakeholderUtils.visibilityIcon.get(stakeholder.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">
|
||||
<li>
|
||||
<a (click)="editStakeholder(stakeholder, !stakeholder.defaultId); hide(element)">Edit</a>
|
||||
</li>
|
||||
<li *ngIf="isCurator">
|
||||
<a (click)="createReport(stakeholder);hide(element)">Cache Indicators</a>
|
||||
</li>
|
||||
<li class="uk-nav-divider"></li>
|
||||
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
|
||||
<li [class.uk-active]="stakeholder.visibility === v.value">
|
||||
<a (click)="changeStakeholderStatus(stakeholder, 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="stakeholder.visibility === v.value" [flex]="true" name="done" class="uk-text-secondary" ratio="0.8"></icon>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
<hr *ngIf="isProfileManager(stakeholder)" class="uk-nav-divider">
|
||||
<li *ngIf="isProfileManager(stakeholder)"><a
|
||||
(click)="deleteStakeholderOpen(stakeholder);hide(element)">Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<a class="uk-display-block uk-text-center uk-link-reset" [routerLink]="'/admin/' + stakeholder.alias">
|
||||
<div class="titleContainer uk-h6 uk-margin-remove-bottom uk-margin-top multi-line-ellipsis lines-2">
|
||||
<p *ngIf="stakeholder.name" class="uk-margin-remove">
|
||||
{{stakeholder.name}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="logoContainer uk-margin-top uk-flex uk-flex-column uk-flex-center uk-flex-middle">
|
||||
<img [src]="stakeholder | logoUrl" class="uk-blend-multiply" style="max-height: 80px;">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #newBox let-text="text" let-isDefault="isDefault">
|
||||
<ng-container *ngIf="!loading">
|
||||
<div class="uk-card uk-card-default uk-text-center uk-card-body clickable" (click)="editStakeholder(null, isDefault)">
|
||||
<div class="uk-text-small uk-text-muted">
|
||||
{{text}}
|
||||
</div>
|
||||
<div class="uk-margin-medium-top uk-margin-small-bottom">
|
||||
<span class="uk-text-secondary">
|
||||
<icon name="add" [ratio]="3"></icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<modal-alert #editStakeholderModal [large]="true" classTitle="uk-background-primary uk-light"
|
||||
(alertOutput)="editStakeholderComponent.save(callback)" (cancelOutput)="editStakeholderComponent.removePhoto()"
|
||||
[okDisabled]="editStakeholderComponent.disabled">
|
||||
<div class="uk-height-large uk-position-relative" *ngIf="editStakeholderComponent.loading">
|
||||
<loading class="uk-position-center"></loading>
|
||||
</div>
|
||||
<div class="uk-padding" [class.uk-hidden]="editStakeholderComponent.loading">
|
||||
<edit-stakeholder #editStakeholderComponent></edit-stakeholder>
|
||||
</div>
|
||||
</modal-alert>
|
||||
<modal-alert #deleteStakeholderModal [overflowBody]="false" (alertOutput)="deleteStakeholder()">
|
||||
<div class="uk-height-medium uk-position-relative" *ngIf="deleteLoading">
|
||||
<loading class="uk-position-center"></loading>
|
||||
</div>
|
||||
<div *ngIf="!deleteLoading">
|
||||
This stakeholder will permanently be deleted. Are you sure you want to proceed?
|
||||
</div>
|
||||
</modal-alert>
|
|
@ -0,0 +1,23 @@
|
|||
@import (reference) "~src/assets/openaire-theme/less/color.less";
|
||||
|
||||
.setType(@color) {
|
||||
border-bottom: 4px solid fade(@color, 30%);
|
||||
|
||||
& .type {
|
||||
color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
.uk-card {
|
||||
&.funder {
|
||||
.setType(@funder-color);
|
||||
}
|
||||
|
||||
&.ri {
|
||||
.setType(@ri-color);
|
||||
}
|
||||
|
||||
&.organization {
|
||||
.setType(@organization-color);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
import {Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||
import {StakeholderService} from "../../monitor/services/stakeholder.service";
|
||||
import {EnvProperties} from "../../utils/properties/env-properties";
|
||||
import {Stakeholder, StakeholderEntities, Visibility} from "../../monitor/entities/stakeholder";
|
||||
import {Subscriber, zip} from "rxjs";
|
||||
import {StakeholderUtils} from "../utils/indicator-utils";
|
||||
import {UntypedFormBuilder, UntypedFormGroup} from "@angular/forms";
|
||||
import {AlertModal} from "../../utils/modal/alert";
|
||||
import {Option} from "../../sharedComponents/input/input.component";
|
||||
import {Title} from "@angular/platform-browser";
|
||||
import {UserManagementService} from "../../services/user-management.service";
|
||||
import {Session} from "../../login/utils/helper.class";
|
||||
import {EditStakeholderComponent} from "../general/edit-stakeholder/edit-stakeholder.component";
|
||||
import {properties} from "src/environments/environment";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {CacheIndicatorsService} from "../utils/cache-indicators/cache-indicators.service";
|
||||
import {NotificationHandler} from "../../utils/notification-handler";
|
||||
|
||||
type Tab = 'all' | 'templates'| 'profiles';
|
||||
|
||||
declare var UIkit;
|
||||
|
||||
@Component({
|
||||
selector: 'home',
|
||||
templateUrl: "./manageStakeholders.component.html",
|
||||
styleUrls: ["./manageStakeholders.component.less"]
|
||||
})
|
||||
export class ManageStakeholdersComponent implements OnInit, OnDestroy {
|
||||
|
||||
public properties: EnvProperties;
|
||||
public loading: boolean = true;
|
||||
public deleteLoading: boolean = false;
|
||||
public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
|
||||
public defaultStakeholders: Stakeholder[];
|
||||
public stakeholders: Stakeholder[];
|
||||
public alias: string[];
|
||||
public stakeholder: Stakeholder;
|
||||
public index: number;
|
||||
public user = null;
|
||||
public tab: Tab = 'all';
|
||||
/**
|
||||
* Filtered Stakeholders
|
||||
*/
|
||||
public displayDefaultStakeholders: Stakeholder[];
|
||||
public displayStakeholders: Stakeholder[];
|
||||
/**
|
||||
* Top filters
|
||||
*/
|
||||
public filters: UntypedFormGroup;
|
||||
public all: Option = {
|
||||
value: 'all',
|
||||
label: 'All'
|
||||
};
|
||||
|
||||
public callback: Function;
|
||||
|
||||
/**
|
||||
* Grid or List View
|
||||
*/
|
||||
private subscriptions: any[] = [];
|
||||
@ViewChild('editStakeholderModal', { static: true }) editStakeholderModal: AlertModal;
|
||||
@ViewChild('deleteStakeholderModal', { static: true }) deleteStakeholderModal: AlertModal;
|
||||
@ViewChild('editStakeholderComponent', { static: true }) editStakeholderComponent: EditStakeholderComponent;
|
||||
|
||||
constructor(private stakeholderService: StakeholderService,
|
||||
private cacheIndicatorsService: CacheIndicatorsService,
|
||||
private userManagementService: UserManagementService,
|
||||
private route: ActivatedRoute,
|
||||
private title: Title,
|
||||
private fb: UntypedFormBuilder) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.buildFilters();
|
||||
this.properties = properties;
|
||||
this.title.setTitle('Manage profiles');
|
||||
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
|
||||
this.user = user;
|
||||
}));
|
||||
let data = zip(
|
||||
this.stakeholderService.getDefaultStakeholders(this.properties.monitorServiceAPIURL),
|
||||
this.stakeholderService.getMyStakeholders(this.properties.monitorServiceAPIURL),
|
||||
this.stakeholderService.getAlias(this.properties.monitorServiceAPIURL)
|
||||
);
|
||||
this.subscriptions.push(data.subscribe(res => {
|
||||
this.defaultStakeholders = res[0];
|
||||
this.stakeholders = res[1];
|
||||
this.displayDefaultStakeholders = res[0];
|
||||
this.displayStakeholders = res[1];
|
||||
this.alias = res[2];
|
||||
this.loading = false;
|
||||
}, error => {
|
||||
this.loading = false;
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(value => {
|
||||
if (value instanceof Subscriber) {
|
||||
value.unsubscribe();
|
||||
} else if (value instanceof Function) {
|
||||
value();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hide(element: any) {
|
||||
UIkit.dropdown(element).hide();
|
||||
}
|
||||
|
||||
|
||||
private buildFilters() {
|
||||
this.filters = this.fb.group({
|
||||
status: this.fb.control('all'),
|
||||
keyword: this.fb.control('')
|
||||
});
|
||||
this.subscriptions.push(this.filters.get('status').valueChanges.subscribe(value => {
|
||||
this.onStatusChange(value);
|
||||
}));
|
||||
this.subscriptions.push(this.filters.get('keyword').valueChanges.subscribe(value => {
|
||||
this.onKeywordChange(value);
|
||||
}));
|
||||
}
|
||||
|
||||
onStatusChange(value) {
|
||||
this.displayDefaultStakeholders = this.filterStatus(this.defaultStakeholders, value);
|
||||
this.displayStakeholders = this.filterStatus(this.stakeholders, value);
|
||||
}
|
||||
|
||||
onKeywordChange(value) {
|
||||
this.displayDefaultStakeholders = this.filterByKeyword(this.defaultStakeholders, value);
|
||||
this.displayStakeholders = this.filterByKeyword(this.stakeholders, value);
|
||||
}
|
||||
|
||||
private filterStatus(stakeholders: Stakeholder[], value): Stakeholder[] {
|
||||
if (value === 'all') {
|
||||
return stakeholders;
|
||||
} else {
|
||||
return stakeholders.filter(stakeholder => stakeholder.visibility == value);
|
||||
}
|
||||
}
|
||||
|
||||
private filterByKeyword(stakeholders: Stakeholder[], value): Stakeholder[] {
|
||||
if (!value) {
|
||||
return stakeholders;
|
||||
} else {
|
||||
return stakeholders.filter(stakeholder =>
|
||||
stakeholder.name && stakeholder.name.toLowerCase().includes(value.toLowerCase()) ||
|
||||
stakeholder.type && stakeholder.type.toLowerCase().includes(value.toLowerCase()) ||
|
||||
stakeholder.index_id && stakeholder.index_id.toLowerCase().includes(value.toLowerCase()) ||
|
||||
stakeholder.index_shortName && stakeholder.index_shortName.toLowerCase().includes(value.toLowerCase()) ||
|
||||
stakeholder.index_name && stakeholder.index_name.toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public editStakeholder(stakeholder: Stakeholder = null, isDefault: boolean = false) {
|
||||
if (isDefault) {
|
||||
this.index = (stakeholder) ? this.defaultStakeholders.findIndex(value => value._id === stakeholder._id) : -1;
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
this.stakeholder = stakeholder;
|
||||
}
|
||||
this.editStakeholderComponent.init(this.stakeholder, this.alias, this.defaultStakeholders, isDefault, this.index === -1);
|
||||
if (this.index !== -1) {
|
||||
this.callback = (stakeholder: Stakeholder) => {
|
||||
let index: number;
|
||||
if (stakeholder.defaultId == null) {
|
||||
index = this.alias.findIndex(value => value == this.defaultStakeholders[this.index].alias);
|
||||
this.defaultStakeholders[this.index] = stakeholder;
|
||||
} else {
|
||||
index = this.alias.findIndex(value => value == this.stakeholders[this.index].alias);
|
||||
this.stakeholders[this.index] = stakeholder;
|
||||
}
|
||||
if(index != -1) {
|
||||
this.alias[index] = stakeholder.alias;
|
||||
}
|
||||
this.editStakeholderModal.cancel();
|
||||
};
|
||||
this.editStakeholderModal.alertTitle = 'Edit ' + this.stakeholder.name;
|
||||
this.editStakeholderModal.okButtonText = 'Save Changes';
|
||||
} else {
|
||||
this.callback = (stakeholder: Stakeholder) => {
|
||||
if (stakeholder.defaultId === null) {
|
||||
this.defaultStakeholders.push(stakeholder);
|
||||
} else {
|
||||
this.stakeholders.push(stakeholder);
|
||||
}
|
||||
this.alias.push(stakeholder.alias);
|
||||
this.editStakeholderModal.cancel();
|
||||
};
|
||||
this.editStakeholderModal.alertTitle = 'Create a new ' + (isDefault?'Default ':'') + 'Profile';
|
||||
this.editStakeholderModal.okButtonText = 'Create';
|
||||
}
|
||||
this.editStakeholderModal.cancelButtonText = 'Cancel';
|
||||
this.editStakeholderModal.okButtonLeft = false;
|
||||
this.editStakeholderModal.alertMessage = false;
|
||||
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.' )
|
||||
}, 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.open();
|
||||
}
|
||||
|
||||
public deleteStakeholder() {
|
||||
this.deleteLoading = true;
|
||||
if (!this.stakeholder.defaultId) {
|
||||
this.index = (this.stakeholder) ? this.defaultStakeholders.findIndex(value => value._id === this.stakeholder._id) : -1;
|
||||
} else {
|
||||
this.index = (this.stakeholder) ? this.stakeholders.findIndex(value => value._id === this.stakeholder._id) : -1;
|
||||
}
|
||||
this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, [this.stakeholder._id]).subscribe(() => {
|
||||
UIkit.notification(this.stakeholder.name+ ' has been <b>successfully deleted</b>', {
|
||||
status: 'success',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
if (!this.stakeholder.defaultId) {
|
||||
this.defaultStakeholders.splice(this.index, 1);
|
||||
} else {
|
||||
this.stakeholders.splice(this.index, 1);
|
||||
}
|
||||
this.alias = this.alias.filter(item => item !== this.stakeholder.alias);
|
||||
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();
|
||||
}));
|
||||
}
|
||||
|
||||
changeStakeholderStatus(stakeholder: Stakeholder, visibility: Visibility) {
|
||||
let path = [
|
||||
stakeholder._id
|
||||
];
|
||||
this.subscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(returnedElement => {
|
||||
stakeholder.visibility = returnedElement.visibility;
|
||||
UIkit.notification(stakeholder.name+ '\'s status has been <b>successfully changed</b> to ' + stakeholder.visibility.toLowerCase(), {
|
||||
status: 'success',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
}, error => {
|
||||
UIkit.notification('An error has occurred. Please try again later', {
|
||||
status: 'danger',
|
||||
timeout: 6000,
|
||||
pos: 'bottom-right'
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
public isManager(): boolean {
|
||||
return this.isCurator() || (Session.isKindOfMonitorManager(this.user));
|
||||
}
|
||||
|
||||
public isProfileManager(stakeholder: Stakeholder): boolean {
|
||||
return this.isCurator() || (Session.isManager(stakeholder.type, stakeholder.alias, this.user));
|
||||
}
|
||||
|
||||
public isCurator(): boolean {
|
||||
return this.isAdmin() || Session.isMonitorCurator(this.user);
|
||||
}
|
||||
|
||||
public isAdmin(): boolean {
|
||||
return Session.isPortalAdministrator(this.user);
|
||||
}
|
||||
|
||||
get typesAsString() {
|
||||
return this.stakeholderUtils.types.slice(0, this.stakeholderUtils.types.length - 1).map(type => type.label).join(', ') +
|
||||
' or ' + this.stakeholderUtils.types[this.stakeholderUtils.types.length - 1].label
|
||||
}
|
||||
|
||||
private isTab(tab: Tab): boolean {
|
||||
switch (tab) {
|
||||
case "all":
|
||||
return true;
|
||||
case "profiles":
|
||||
return true;
|
||||
case "templates":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {ManageStakeholdersComponent} from "./manageStakeholders.component";
|
||||
import {ManageStakeholdersRoutingModule} from "./manageStakeholders-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 {IconsModule} from "../../utils/icons/icons.module";
|
||||
import {IconsService} from "../../utils/icons/icons.service";
|
||||
import {earth, incognito, restricted} from "../../utils/icons/icons";
|
||||
import {PageContentModule} from "../../dashboard/sharedComponents/page-content/page-content.module";
|
||||
import {LogoUrlPipeModule} from "../../utils/pipes/logoUrlPipe.module";
|
||||
import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module";
|
||||
import {
|
||||
SidebarMobileToggleModule
|
||||
} from "../../dashboard/sharedComponents/sidebar/sidebar-mobile-toggle/sidebar-mobile-toggle.module";
|
||||
import {SliderTabsModule} from "../../sharedComponents/tabs/slider-tabs.module";
|
||||
import {EditStakeholderModule} from "../general/edit-stakeholder/edit-stakeholder.module";
|
||||
|
||||
@NgModule({
|
||||
declarations: [ManageStakeholdersComponent],
|
||||
imports: [
|
||||
ManageStakeholdersRoutingModule,
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
InputModule,
|
||||
LoadingModule,
|
||||
AlertModalModule,
|
||||
ReactiveFormsModule,
|
||||
EditStakeholderModule,
|
||||
IconsModule,
|
||||
PageContentModule,
|
||||
LogoUrlPipeModule,
|
||||
SearchInputModule,
|
||||
SidebarMobileToggleModule,
|
||||
SliderTabsModule
|
||||
],
|
||||
providers: [
|
||||
PreviousRouteRecorder,
|
||||
],
|
||||
exports: [ManageStakeholdersComponent]
|
||||
})
|
||||
export class ManageStakeholdersModule {
|
||||
constructor(private iconsService: IconsService) {
|
||||
this.iconsService.registerIcons([earth, incognito, restricted]);
|
||||
}
|
||||
}
|
|
@ -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,383 @@
|
|||
<div class="uk-flex">
|
||||
<aside *ngIf="stakeholder" uk-sticky="end: .sidebar_main_swipe;" [attr.offset]="offset" 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" class="uk-width-1-1" 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>
|
||||
</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,799 @@
|
|||
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 offset: number;
|
||||
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() {
|
||||
if (typeof document !== "undefined") {
|
||||
this.offset = Number.parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height'));
|
||||
}
|
||||
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 {properties} from "../../../../environments/environment";
|
||||
import {Session, User} from "../../login/utils/helper.class";
|
||||
import {Option} from "../../sharedComponents/input/input.component";
|
||||
|
||||
export const ChartHelper = {
|
||||
prefix: "((__",
|
||||
|
@ -309,3 +310,10 @@ export enum StakeholderEntities {
|
|||
ORGANIZATIONS = 'Research Institutions',
|
||||
PROJECTS = 'Projects'
|
||||
}
|
||||
|
||||
export let stakeholderTypes: Option[] = [
|
||||
{value: 'funder', label: StakeholderEntities.FUNDER},
|
||||
{value: 'ri', label: StakeholderEntities.RI},
|
||||
{value: 'organization', label: StakeholderEntities.ORGANIZATION},
|
||||
{value: 'project', label: StakeholderEntities.PROJECT}
|
||||
];
|
||||
|
|
|
@ -5,6 +5,7 @@ import {Meta, Title} from "@angular/platform-browser";
|
|||
import {SEOService} from "../../sharedComponents/SEO/SEO.service";
|
||||
import {Breadcrumb} from "../../utils/breadcrumbs/breadcrumbs.component";
|
||||
import {Subscriber} from "rxjs";
|
||||
import {StakeholderEntities} from "../entities/stakeholder";
|
||||
|
||||
@Component({
|
||||
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!
|
||||
</p>
|
||||
<p>
|
||||
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route">funders</a>,
|
||||
<a [routerLink]="['../organization']" [relativeTo]="route">research institutions</a> and
|
||||
<a [routerLink]="['../ri']" [relativeTo]="route">research initiatives</a>)
|
||||
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route" class="uk-text-lowercase">{{entities.FUNDERS}}</a>,
|
||||
<a [routerLink]="['../organization']" [relativeTo]="route" class="uk-text-lowercase">{{entities.ORGANIZATIONS}}</a> and
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -67,6 +68,7 @@ import {Subscriber} from "rxjs";
|
|||
export class IndicatorThemesComponent implements OnInit, OnDestroy {
|
||||
private subscriptions: any[] = [];
|
||||
public properties = properties;
|
||||
public entities = StakeholderEntities;
|
||||
public breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'Resources - Themes'}];
|
||||
|
||||
constructor(private router: Router,
|
||||
|
|
|
@ -39,9 +39,9 @@ declare var ResizeObserver;
|
|||
</div>
|
||||
</div>
|
||||
<div *ngIf="divContents" uk-scrollspy="target: [uk-scrollspy-class]; cls: uk-animation-fade; delay: 250">
|
||||
<div id="graph_element" #graph_element class="uk-blur-background" uk-sticky="bottom: true"
|
||||
<div id="graph_element" #graph_element class="uk-blur-background uk-padding-xsmall" uk-sticky="end: true"
|
||||
[attr.offset]="graph_offset">
|
||||
<div class="uk-container uk-container-large uk-margin-small-top uk-margin-small-bottom">
|
||||
<div class="uk-container uk-container-large">
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@m">
|
||||
<img src="assets/common-assets/openaire-badge-1.png" alt="Powered by OpenAIRE graph" style="height: 17px;">
|
||||
</a>
|
||||
|
@ -50,7 +50,7 @@ declare var ResizeObserver;
|
|||
<div class="uk-section uk-container uk-container-large" uk-scrollspy-class>
|
||||
<div id="parentContainer" class="uk-grid uk-grid-large" uk-grid>
|
||||
<div class="uk-width-medium uk-margin-top">
|
||||
<div class="uk-sticky" uk-sticky="bottom: !#parentContainer; offset: 100;">
|
||||
<div class="uk-sticky" uk-sticky="end: !#parentContainer; offset: 100;">
|
||||
<slider-tabs type="scrollable" position="left">
|
||||
<slider-tab tabId="entities" tabTitle="1. Entities"></slider-tab>
|
||||
<slider-tab tabId="inherited-and-inferred-attributes" tabTitle="2. Inherited and Inferred Attributes"></slider-tab>
|
||||
|
@ -83,9 +83,9 @@ declare var ResizeObserver;
|
|||
</div>
|
||||
</div>
|
||||
<div *ngIf="divContents" uk-scrollspy="target: [uk-scrollspy-class]; cls: uk-animation-fade; delay: 250">
|
||||
<div id="graph_element" #graph_element class="uk-blur-background" uk-sticky="bottom: true"
|
||||
<div id="graph_element" #graph_element class="uk-blur-background uk-padding-xsmall" uk-sticky="end: true"
|
||||
[attr.offset]="graph_offset">
|
||||
<div class="uk-container uk-container-large uk-margin-small-top uk-margin-small-bottom uk-text-xsmall uk-text-right">
|
||||
<div class="uk-container uk-container-large uk-text-xsmall uk-text-right">
|
||||
<a href="https://graph.openaire.eu" target="_blank" class="uk-width-1-1 uk-width-auto@m">
|
||||
<img src="assets/common-assets/openaire-badge-1.png" alt="Powered by OpenAIRE graph" style="height: 17px;">
|
||||
</a>
|
||||
|
|
|
@ -27,12 +27,12 @@ export class NotificationConfiguration {
|
|||
@Component({
|
||||
selector: 'notification-sidebar',
|
||||
template: `
|
||||
<div *ngIf="!mobile" id="notifications-switcher" uk-toggle="" href="#notifications" class="uk-offcanvas-switcher uk-flex uk-flex-middle uk-flex-center">
|
||||
<a *ngIf="!mobile" id="notifications-switcher" uk-toggle href="#notifications" class="uk-link-reset uk-offcanvas-switcher uk-flex uk-flex-middle uk-flex-center">
|
||||
<icon name="mail" ratio="1.5" customClass="uk-text-background" flex="true" visuallyHidden="Notifications"></icon>
|
||||
<span [class.uk-hidden]="unreadCount === 0" class="uk-offcanvas-count uk-flex uk-flex-middle uk-flex-center">
|
||||
{{unreadCount}}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
<ng-template #main>
|
||||
<ng-container *ngIf="!notification">
|
||||
<div class="notification-list uk-position-relative">
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import {NgModule} from "@angular/core";
|
||||
import {NotifyFormComponent} from "./notify-form.component";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||
import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
import {NotificationUserModule} from "../notification-user/notification-user.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MatCheckboxModule, ReactiveFormsModule, InputModule, NotificationUserModule],
|
||||
imports: [CommonModule, ReactiveFormsModule, InputModule, NotificationUserModule],
|
||||
declarations: [NotifyFormComponent],
|
||||
exports: [NotifyFormComponent]
|
||||
})
|
||||
|
|
|
@ -302,7 +302,7 @@ export class MyOrcidLinksComponent {
|
|||
for(let work of works) {
|
||||
for(let pid of work['pids']) {
|
||||
let identifier: Identifier = Identifier.getIdentifierFromString(pid, false);
|
||||
this.orcidQuery += (this.orcidQuery ? " or " : "") + ('(pidclassid exact "'+identifier.class+'" and pid="'+StringUtils.URIEncode(identifier.id)+'")');
|
||||
this.orcidQuery += (this.orcidQuery ? " or " : "") + ('(pid="'+StringUtils.URIEncode(identifier.id)+'")');
|
||||
}
|
||||
}
|
||||
this.showLoading = false;
|
||||
|
|
|
@ -33,10 +33,7 @@
|
|||
[options]="fieldIdsOptions" (valueChange)="fieldIdsChanged(i,selectedField.id)" type="select"></div>
|
||||
<div input [(value)]="selectedField.includes" inputClass="border-bottom" [options]="getNotOperators(selectedField)" type="select"></div>
|
||||
</div>
|
||||
<!-- <mat-select [(ngModel)]="selectedField.id" name="selectField_{{i}}" [disableOptionCentering]="true" class="matSelection" panelClass="matSelectionPanel"
|
||||
(ngModelChange)="fieldIdsChanged(i,selectedField.id)"><!–(click)="fieldIdsChanged(i)" –>
|
||||
<mat-option *ngFor="let id of fieldIds" [value]="id">{{fieldIdsMap[id].name}} </mat-option>
|
||||
</mat-select>--></td>
|
||||
</td>
|
||||
<td *ngIf="selectedField.type == 'keyword' || selectedField.type == 'identifier'">
|
||||
<div class="uk-inline uk-width-expand">
|
||||
<a *ngIf="selectedField.value.length > 0" class="uk-form-icon uk-form-icon-flip"
|
||||
|
@ -105,12 +102,6 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- <div class="uk-margin-small-top">
|
||||
<button type="button" (click)="addField()" class="uk-button uk-button-link uk-flex uk-flex-middle">
|
||||
<icon name="add" [flex]="true"></icon>
|
||||
<span class="uk-margin-small-left">Add rule</span>
|
||||
</button>
|
||||
</div> -->
|
||||
<div class=" uk-text-center uk-margin-small-top">
|
||||
<div *ngIf="!validDateFrom && validDateTo" class="uk-text-danger">
|
||||
Please check your <u>from</u> date
|
||||
|
|
|
@ -10,7 +10,6 @@ import {DateFilterModule} from './dateFilter.module';
|
|||
import {SearchFormModule} from './searchForm.module';
|
||||
import {QuickSelectionsModule} from "./quick-selections.module";
|
||||
import {EntitiesSelectionModule} from "./entitiesSelection.module";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {IconsModule} from "../../utils/icons/icons.module";
|
||||
import {SearchInputModule} from "../../sharedComponents/search-input/search-input.module";
|
||||
import {AdvancedSearchInputModule} from "../../sharedComponents/advanced-search-input/advanced-search-input.module";
|
||||
|
@ -22,7 +21,7 @@ import {filters} from "../../utils/icons/icons";
|
|||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, RouterModule, EntitiesAutocompleteModule, StaticAutocompleteModule, DateFilterModule,
|
||||
SearchFormModule, QuickSelectionsModule, EntitiesSelectionModule, MatSelectModule, IconsModule, SearchInputModule, AdvancedSearchInputModule, InputModule
|
||||
SearchFormModule, QuickSelectionsModule, EntitiesSelectionModule, IconsModule, SearchInputModule, AdvancedSearchInputModule, InputModule
|
||||
],
|
||||
declarations: [
|
||||
AdvancedSearchFormComponent,
|
||||
|
|
|
@ -3,13 +3,12 @@ import {CommonModule} from '@angular/common';
|
|||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {EntitiesSelectionComponent} from "./entitiesSelection.component";
|
||||
import { MatSelectModule } from "@angular/material/select";
|
||||
import {InputModule} from "../../sharedComponents/input/input.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule,
|
||||
RouterModule, ReactiveFormsModule, MatSelectModule, InputModule
|
||||
RouterModule, ReactiveFormsModule, InputModule
|
||||
],
|
||||
declarations: [
|
||||
EntitiesSelectionComponent,
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
(stickyForm?'':' ') :
|
||||
(+ (stickyForm?'':' uk-section') +' uk-padding-remove-bottom uk-padding-remove-top ' +
|
||||
((usedBy == 'deposit' || usedBy == 'orcid') ? ' uk-padding-remove-top ' : ' '))"
|
||||
[attr.uk-sticky]="((stickyForm || (simpleView && mobile))?'{offset:100;top:90;cls-active:uk-active uk-sticky-below;cls-inactive:uk-sticky '+
|
||||
[attr.uk-sticky]="((stickyForm || (simpleView && mobile))?'{offset:100;start:90;cls-active:uk-active uk-sticky-below;cls-inactive:uk-sticky '+
|
||||
(usedBy != 'deposit' && usedBy != 'orcid' && (!customFilter || customFilter.queryFieldName != 'communityId') ?
|
||||
' uk-position-relative ' :(' uk-section ' ))+'}':null)">
|
||||
<div class="uk-background-norepeat uk-background-bottom-center" [ngClass]="searchForm.class">
|
||||
|
|
|
@ -982,7 +982,7 @@ export class NewSearchPageComponent implements OnInit, OnDestroy, OnChanges {
|
|||
doisParams += (doisParams.length > 0 ? " or " : "") + '(authorid="' + StringUtils.URIEncode(identifier.id) + '")';
|
||||
// doisParams += (doisParams.length > 0 ? " or " : "") +'(authorid="' + StringUtils.URIEncode(identifier.id) + '" and (authoridtype exact "orcid"))';
|
||||
} else {
|
||||
doisParams += (doisParams.length > 0 ? " or " : "") + '(pidclassid exact "' + identifier.class + '" and pid="' + StringUtils.URIEncode(identifier.id) + '")';
|
||||
doisParams += (doisParams.length > 0 ? " or " : "") + '(pid="' + StringUtils.URIEncode(identifier.id) + '")';
|
||||
// doisParams += (doisParams.length > 0 ? " or " : "") +'(pidclassid exact "' + identifier.class + '" and pid="' + identifier.id + '")';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import {ConfigurationService} from "../../utils/configuration/configuration.serv
|
|||
import {Subscription} from "rxjs";
|
||||
import {ActivatedRoute, Router} from "@angular/router";
|
||||
import {properties} from "../../../../environments/environment";
|
||||
import {OpenaireEntities} from "../../utils/properties/searchFields";
|
||||
|
||||
@Component({
|
||||
selector: 'quick-selections',
|
||||
|
|
|
@ -5,25 +5,21 @@ import { FormsModule } from '@angular/forms';
|
|||
import {SearchFilterComponent} from './searchFilter.component';
|
||||
import {SearchFilterModalComponent} from './searchFilterModal.component';
|
||||
import {ModalModule} from '../../utils/modal/modal.module';
|
||||
import { MatSelectModule } from "@angular/material/select";
|
||||
import {RouterModule} from "@angular/router";
|
||||
import {InputModule} from '../../sharedComponents/input/input.module';
|
||||
import {IconsModule} from "../../utils/icons/icons.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, ModalModule, MatSelectModule, RouterModule,
|
||||
CommonModule, FormsModule, ModalModule, RouterModule,
|
||||
InputModule, IconsModule
|
||||
],
|
||||
declarations: [
|
||||
SearchFilterComponent, SearchFilterModalComponent
|
||||
],
|
||||
|
||||
providers:[
|
||||
],
|
||||
exports: [
|
||||
SearchFilterComponent, SearchFilterModalComponent
|
||||
|
||||
]
|
||||
})
|
||||
export class SearchFilterModule { }
|
||||
export class SearchFilterModule {
|
||||
}
|
||||
|
|
|
@ -4,13 +4,11 @@ import { FormsModule } from '@angular/forms';
|
|||
import {RouterModule} from '@angular/router';
|
||||
|
||||
import {SearchSortingComponent} from './searchSorting.component';
|
||||
import { MatSelectModule } from "@angular/material/select";
|
||||
import {InputModule} from '../../sharedComponents/input/input.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule, FormsModule, RouterModule, MatSelectModule,
|
||||
InputModule
|
||||
CommonModule, FormsModule, RouterModule, InputModule
|
||||
],
|
||||
declarations: [
|
||||
SearchSortingComponent
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {COOKIE} from '../../login/utils/helper.class';
|
||||
import {HttpHeaders} from "@angular/common/http";
|
||||
|
||||
export type MediaType = 'application/json' | 'text/plain'
|
||||
|
@ -16,17 +15,12 @@ export class CustomOptions {
|
|||
return {
|
||||
headers: new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
'X-XSRF-TOKEN': COOKIE.getCookie(COOKIE.cookieName_id)?COOKIE.getCookie(COOKIE.cookieName_id):''
|
||||
}), withCredentials: true
|
||||
};
|
||||
}
|
||||
|
||||
public static getAuthOptions():{} {
|
||||
return {
|
||||
headers: new HttpHeaders({
|
||||
'X-XSRF-TOKEN': (COOKIE.getCookie(COOKIE.cookieName_id)) ? COOKIE.getCookie(COOKIE.cookieName_id) : ''
|
||||
}), withCredentials: true
|
||||
};
|
||||
return this.getAuthOptionsWithBody();
|
||||
}
|
||||
|
||||
}
|
|
@ -112,7 +112,7 @@
|
|||
<li><a target="_blank" href="https://www.openaire.eu/contact-noads">NOADs</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/guides">Guides</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/faqs">FAQs</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/frontpage/webinars">Webinars</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/support/webinars">Webinars</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/helpdesk">Ask a question</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<ul class="uk-nav uk-nav-default" uk-nav>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/news/">News</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/events">Events</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/blogs/magazine">Blogs</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/blogs">Blogs</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/newsletters">Newsletter</a></li>
|
||||
<li><a target="_blank" href="https://www.openaire.eu/public-documents">Documents</a></li>
|
||||
</ul>
|
||||
|
@ -180,8 +180,8 @@
|
|||
<div class="uk-grid uk-flex-center uk-text-xsmall uk-text-emphasis" uk-grid>
|
||||
<div class=" uk-flex uk-flex-top">
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/" rel="license" class="uk-link-reset">
|
||||
<icon name="cc" [preserveColor]="true" visuallyHidden="Creative-Commons"></icon>
|
||||
<icon name="by" [preserveColor]="true" visuallyHidden="Licence" class="uk-margin-xsmall-left"></icon>
|
||||
<icon name="cc" visuallyHidden="Creative-Commons"></icon>
|
||||
<icon name="by" visuallyHidden="Licence" class="uk-margin-xsmall-left"></icon>
|
||||
</a>
|
||||
<span class="uk-margin-small-left uk-width-expand">Unless otherwise indicated, all materials created by OpenAIRE are licenced under</span>
|
||||
<a class="uk-link-text uk-margin-medium-left" href="http://creativecommons.org/licenses/by/4.0/" rel="license"><u>CC ATTRIBUTION 4.0 INTERNATIONALLICENSE</u></a>
|
||||
|
@ -262,8 +262,8 @@
|
|||
<div class="uk-grid uk-flex-center uk-text-xsmall uk-text-emphasis" uk-grid>
|
||||
<div class=" uk-flex uk-flex-top">
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/" rel="license" class="uk-link-reset">
|
||||
<icon name="cc" [preserveColor]="true" visuallyHidden="Creative-Commons"></icon>
|
||||
<icon name="by" [preserveColor]="true" visuallyHidden="Licence" class="uk-margin-xsmall-left"></icon>
|
||||
<icon name="cc" visuallyHidden="Creative-Commons"></icon>
|
||||
<icon name="by" visuallyHidden="Licence" class="uk-margin-xsmall-left"></icon>
|
||||
</a>
|
||||
<span class="uk-margin-small-left uk-width-expand">Unless otherwise indicated, all materials created by OpenAIRE are licenced under</span>
|
||||
</div>
|
||||
|
|
|
@ -196,11 +196,14 @@ declare var UIkit;
|
|||
</div>
|
||||
</div>
|
||||
<div *ngIf="!mobile && type === 'date' && opened" class="uk-dropdown" #calendarBox
|
||||
uk-dropdown="pos: bottom-left; mode: none; boundary-align: true;" [attr.boundary]="'#' + id" (click)="$event.stopPropagation()">
|
||||
uk-dropdown="pos: bottom-left; mode: none; flip: false ; shift: false" [attr.target]="'#' + id" [attr.boundary]="'#' + id" (click)="$event.stopPropagation()">
|
||||
<mat-calendar [selected]="selectedDate" [startAt]="selectedDate" (selectedChange)="dateChanged($event)"></mat-calendar>
|
||||
</div>
|
||||
<mobile-dropdown *ngIf="mobile && type === 'date' && opened" (onClose)="focus(false)" #mobileDropdown>
|
||||
<mat-calendar [selected]="selectedDate" [startAt]="selectedDate" (selectedChange)="dateChanged($event)"></mat-calendar>
|
||||
</mobile-dropdown>
|
||||
<div *ngIf="!mobile && filteredOptions && filteredOptions.length > 0 && opened" class="options uk-dropdown" #optionBox
|
||||
uk-dropdown="pos: bottom-justify; mode: none; boundary-align: true;" [attr.boundary]="'#' + id">
|
||||
uk-dropdown="mode: none; stretch: true; flip: false; shift: false" [attr.boundary]="'#' + id">
|
||||
<ul class="uk-nav uk-dropdown-nav">
|
||||
<li *ngFor="let option of filteredOptions; let i=index" [class.uk-hidden]="option.hidden"
|
||||
[class.uk-active]="(formControl.value === option.value) || selectedIndex === i">
|
||||
|
@ -228,7 +231,7 @@ declare var UIkit;
|
|||
</div>
|
||||
</mobile-dropdown>
|
||||
</div>
|
||||
<span *ngIf="formControl?.invalid && formControl?.touched" class="uk-text-danger">
|
||||
<span *ngIf="formControl?.invalid && formControl?.touched" class="uk-text-small uk-text-danger">
|
||||
<span *ngIf="errors?.error">{{errors?.error}}</span>
|
||||
<span *ngIf="type === 'URL' || type === 'logoURL'">Please provide a valid URL (e.g. https://example.com)</span>
|
||||
</span>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div *ngIf="showMenu && activeHeader">
|
||||
<div id="main-menu-small" class="uk-hidden@m">
|
||||
<div id="main-menu-small" class="uk-hidden@m" [attr.uk-sticky]="hasStickyHeaderOnMobile?'':null">
|
||||
<nav class="uk-navbar-container uk-navbar" uk-navbar="delay-hide: 400">
|
||||
<div *ngIf="!onlyTop || userMenu" class="uk-navbar-left" [class.uk-light]='activeHeader.darkBg'>
|
||||
<a class="uk-navbar-toggle" href="#tm-mobile" uk-toggle>
|
||||
|
@ -31,7 +31,7 @@
|
|||
<ng-container *ngTemplateOutlet="header_template; context: {mobile: true}"></ng-container>
|
||||
</div>
|
||||
</nav>
|
||||
<ul class="uk-nav uk-nav-primary uk-list uk-list-large uk-margin-large-top uk-nav-parent-icon" uk-nav>
|
||||
<ul class="uk-nav uk-nav-primary uk-list uk-list-large uk-margin-large-top" uk-nav>
|
||||
<ng-container *ngIf="!onlyTop">
|
||||
<li *ngIf="showHomeMenuItem && currentRoute.route !== '/'">
|
||||
<a routerLink="/" (click)="closeCanvas(canvas)">Home</a>
|
||||
|
@ -40,17 +40,15 @@
|
|||
<li [class.uk-active]="isTheActiveMenu(menu)" [class.uk-parent]="menu.items.length > 0" [ngClass]="menu.customClass"
|
||||
*ngIf="isAtleastOneEnabled(menu.entitiesRequired,showEntity) && isAtleastOneEnabled(menu.routeRequired, showPage)">
|
||||
<!--a routerLink="{{menu.rootItem.route}}" [queryParams]=menu.rootItem.params class="uk-offcanvas-close custom-offcanvas-close">{{menu.rootItem.title}}</a-->
|
||||
<a *ngIf="menu.route && (isEnabled([menu.route], showPage) || !menu.routeRequired)"
|
||||
[routerLink]="menu.items.length === 0?menu.route:null" (click)="menu.items.length === 0?closeCanvas(canvas):null"
|
||||
<a *ngIf="!menu.url" [routerLink]="menu.route && (isEnabled([menu.route], showPage) || !menu.routeRequired) && menu.items.length === 0?menu.route:null"
|
||||
(click)="menu.items.length === 0?closeCanvas(canvas):null"
|
||||
[queryParams]="menu.params"
|
||||
[fragment]="menu.fragment">{{menu.title}}</a>
|
||||
<a *ngIf="!menu.route && menu.url"
|
||||
[href]="menu.items.length === 0?menu.url:null" (click)="menu.items.length === 0?closeCanvas(canvas):null"
|
||||
[class.custom-external]="menu.target != '_self'" [target]="menu.target">{{menu.title}}</a>
|
||||
<a *ngIf="(!menu.route && !menu.url) ||
|
||||
(menu.route && menu.routeRequired && !isEnabled([menu.route], showPage)
|
||||
&& isAtleastOneEnabled(menu.routeRequired, showPage))"
|
||||
(click)="menu.items.length === 0?closeCanvas(canvas):null">{{menu.title}}</a>
|
||||
[fragment]="menu.fragment">{{menu.title}}<span *ngIf="menu.items.length > 0" class="uk-nav-parent-icon"></span></a>
|
||||
<a *ngIf="menu.url"
|
||||
(click)="menu.items.length === 0?closeCanvas(canvas):null"
|
||||
[href]="menu.items.length === 0?menu.url:null"
|
||||
[class.custom-external]="menu.url && menu.target != '_self'" [target]="menu.url?menu.target:null">
|
||||
{{menu.title}}<span *ngIf="menu.items.length > 0" class="uk-nav-parent-icon"></span></a>
|
||||
<ul *ngIf="menu.items.length > 0" class="uk-nav-sub">
|
||||
<ng-container *ngFor="let submenu of menu.items">
|
||||
<li [class.uk-active]="isTheActiveMenu(submenu)" [ngClass]="submenu.customClass"
|
||||
|
@ -270,10 +268,9 @@
|
|||
<a *ngIf="menu.type == 'noAction'">
|
||||
{{menu.title}}
|
||||
</a>
|
||||
<div *ngIf="menu.items.length > 0" class="uk-navbar-dropdown uk-navbar-dropdown-bottom-left"
|
||||
<div *ngIf="menu.items.length > 0" class="uk-navbar-dropdown uk-navbar-dropdown-bottom-left uk-height-max-medium uk-overflow-auto"
|
||||
style="top: 80px; left: 0px;" id="{{menu._id}}" uk-toggle>
|
||||
<div class="uk-navbar-dropdown-grid uk-child-width-1-1 uk-grid uk-grid-stack" uk-grid="">
|
||||
<div class="uk-first-column uk-height-max-medium uk-overflow-auto">
|
||||
<div>
|
||||
<ul class="uk-nav uk-navbar-dropdown-nav">
|
||||
<ng-container *ngFor="let submenu of menu.items">
|
||||
<li [class.uk-active]="isTheActiveMenu(submenu)" [ngClass]="submenu.customClass">
|
||||
|
@ -293,7 +290,6 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
|
|
@ -63,6 +63,7 @@ export class NavigationBarComponent implements OnInit, OnDestroy, OnChanges {
|
|||
@Input() showLogo: boolean = true;
|
||||
@Input() notificationConfiguration: NotificationConfiguration;
|
||||
replaceHeader: boolean = false;
|
||||
hasStickyHeaderOnMobile: boolean = false;
|
||||
public activeHeader: Header;
|
||||
keyword: string = '';
|
||||
public isAuthorized: boolean = false;
|
||||
|
@ -86,6 +87,7 @@ export class NavigationBarComponent implements OnInit, OnDestroy, OnChanges {
|
|||
value: "Open Access"
|
||||
};
|
||||
@ViewChild('search_input') search_input: SearchInputComponent;
|
||||
@ViewChild('canvas') canvas: ElementRef;
|
||||
public routerHelper: RouterHelper = new RouterHelper();
|
||||
|
||||
constructor(private router: Router,
|
||||
|
@ -100,6 +102,9 @@ export class NavigationBarComponent implements OnInit, OnDestroy, OnChanges {
|
|||
this.searchMode = false;
|
||||
this.keyword = "";
|
||||
}));
|
||||
this.subs.push(this.layoutService.hasStickyHeaderOnMobile.subscribe(hasStickyHeaderOnMobile => {
|
||||
this.hasStickyHeaderOnMobile = hasStickyHeaderOnMobile;
|
||||
}))
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
|
@ -115,7 +120,7 @@ export class NavigationBarComponent implements OnInit, OnDestroy, OnChanges {
|
|||
}
|
||||
}
|
||||
|
||||
closeCanvas(element) {
|
||||
closeCanvas(element = this.canvas.nativeElement) {
|
||||
UIkit.offcanvas(element).hide();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<p>pick-icon works!</p>
|
|
@ -0,0 +1,21 @@
|
|||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Icon} from "../../utils/icons/icons";
|
||||
import {IconsService} from "../../utils/icons/icons.service";
|
||||
|
||||
@Component({
|
||||
selector: 'pick-icon',
|
||||
templateUrl: './pick-icon.component.html'
|
||||
})
|
||||
export class PickIconComponent implements OnInit, OnDestroy{
|
||||
customIcons: Icon[] = [];
|
||||
|
||||
constructor(private iconService: IconsService) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.customIcons = this.iconService.getAll();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { PickIconComponent } from './pick-icon.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
PickIconComponent
|
||||
],
|
||||
exports: [
|
||||
PickIconComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule
|
||||
]
|
||||
})
|
||||
export class PickIconModule { }
|
|
@ -1,13 +1,12 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {SharedModule} from '../../../openaireLibrary/shared/shared.module';
|
||||
import {SearchInputComponent} from './search-input.component';
|
||||
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||
import {IconsModule} from '../../utils/icons/icons.module';
|
||||
import {InputModule} from "../input/input.module";
|
||||
import {ClickModule} from "../../utils/click/click.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, MatAutocompleteModule, IconsModule, InputModule, ClickModule],
|
||||
imports: [SharedModule, IconsModule, InputModule, ClickModule],
|
||||
declarations: [SearchInputComponent],
|
||||
exports: [SearchInputComponent]
|
||||
})
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import {AfterContentInit, Component, ContentChildren, Input, OnDestroy, QueryList} from '@angular/core';
|
||||
import {SliderItemComponent} from "./slider-item.component";
|
||||
import {SliderNavItemComponent} from "./slider-nav-item.component";
|
||||
import {SliderContainerComponent} from "./slider-container.component";
|
||||
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
|
||||
import {Subscriber} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'slider-column',
|
||||
template: `
|
||||
<div class="uk-position-relative">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class SliderColumnComponent implements AfterContentInit, OnDestroy {
|
||||
@Input()
|
||||
type: 'slider' | 'nav' = null;
|
||||
@Input()
|
||||
animation = null;
|
||||
@ContentChildren(SliderItemComponent) items: QueryList<SliderItemComponent>;
|
||||
@ContentChildren(SliderNavItemComponent) navItems: QueryList<SliderNavItemComponent>;
|
||||
public isMobile: boolean;
|
||||
private subscriptions: any[] = [];
|
||||
|
||||
constructor(private layoutService: LayoutService) {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscriptions.forEach(value => {
|
||||
if (value instanceof Subscriber) {
|
||||
value.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.animation) {
|
||||
this.slides.forEach(slide => {
|
||||
slide.init(this.animation);
|
||||
});
|
||||
this.navItems.forEach(slide => {
|
||||
slide.init(this.animation);
|
||||
});
|
||||
}
|
||||
this.subscriptions.push(this.layoutService.isMobile.subscribe(isMobile => {
|
||||
this.isMobile = isMobile;
|
||||
}));
|
||||
}
|
||||
|
||||
change(time: number) {
|
||||
if (this.type === 'slider') {
|
||||
let slides = this.slides;
|
||||
for (let i = 0; i < slides.length; i++) {
|
||||
slides[i].setActive(slides[i].start <= time && (!slides[i + 1] || slides[i + 1].start > time));
|
||||
}
|
||||
}
|
||||
if (this.type === 'nav') {
|
||||
let slides = this.navItems;
|
||||
for (let i = 0; i < slides.length; i++) {
|
||||
slides.get(i).setActive(!this.isMobile || (slides.get(i).start <= time && (!slides.get(i + 1) || slides.get(i + 1).start > time)));
|
||||
slides.get(i).active = slides.get(i).start <= time && (!slides.get(i + 1) || slides.get(i + 1).start > time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setContainer(container: SliderContainerComponent) {
|
||||
if (this.type === 'nav') {
|
||||
this.navItems.forEach(item => {
|
||||
item.container = container;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get slides(): SliderItemComponent[] {
|
||||
return this.items.filter(item => item.type === 'slide');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
import {
|
||||
AfterContentInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChildren,
|
||||
ElementRef,
|
||||
Input, OnDestroy,
|
||||
OnInit,
|
||||
QueryList
|
||||
} from "@angular/core";
|
||||
import {SliderColumnComponent} from "./slider-column.component";
|
||||
|
||||
export class Stage {
|
||||
value: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'slider-container',
|
||||
template: `
|
||||
<div class="uk-grid uk-grid-large uk-flex uk-flex-middle uk-flex-between uk-child-width-1-1" [ngClass]="'uk-child-width-1-' + sliders?.length + '@m'" uk-grid>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div *ngIf="navigation === 'progress' && stages.length > 0" class="uk-flex uk-flex-center uk-margin-large-top">
|
||||
<div class="uk-width-1-3@m uk-width-1-2@s uk-child-width-1-1 uk-grid uk-grid-small uk-flex-middle" [ngClass]="'uk-child-width-1-' + stages.length" uk-grid>
|
||||
<div *ngFor="let stage of stages; let i=index">
|
||||
<progress (click)="start(i)" class="uk-progress" [value]="stage.value" [max]="stage.max"></progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class SliderContainerComponent implements OnInit, OnDestroy, AfterContentInit {
|
||||
private static INTERVAL = 10;
|
||||
private static ANIMATION_DURATION = 600; // UIKit progress animation duration
|
||||
|
||||
@ContentChildren(SliderColumnComponent) sliders: QueryList<SliderColumnComponent>;
|
||||
@Input()
|
||||
navigation: 'progress' | null = null;
|
||||
@Input()
|
||||
total: number = 0;
|
||||
@Input()
|
||||
period: number = 3000; // in ms (>= 1000ms)
|
||||
@Input()
|
||||
infinite: boolean = false;
|
||||
@Input()
|
||||
parent: HTMLDivElement;
|
||||
stages: Stage[] = [];
|
||||
time: number = 0;
|
||||
interval: any;
|
||||
observer: IntersectionObserver;
|
||||
initialized: boolean = false;
|
||||
stopped: boolean = true;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef, private element: ElementRef) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.period = this.period - SliderContainerComponent.ANIMATION_DURATION;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if(this.observer && typeof IntersectionObserver !== 'undefined') {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.setObserver();
|
||||
this.sliders.forEach(slider => {
|
||||
slider.setContainer(this);
|
||||
slider.navItems.forEach(item => {
|
||||
if(this.parent) {
|
||||
item.background = getComputedStyle(this.parent).backgroundColor;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setObserver() {
|
||||
if(typeof IntersectionObserver !== 'undefined') {
|
||||
let options = {
|
||||
root: null,
|
||||
rootMargin: '0px',
|
||||
threshold: 0.1
|
||||
};
|
||||
this.observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting && !this.initialized) {
|
||||
this.stopped = false;
|
||||
this.start(0);
|
||||
this.initialized = true;
|
||||
} else {
|
||||
this.initialized = false;
|
||||
this.stopped = true;
|
||||
}
|
||||
});
|
||||
}, options);
|
||||
this.observer.observe(this.element.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
start(time: number) {
|
||||
this.stages = [];
|
||||
for(let i = 0; i < this.total; i++) {
|
||||
this.stages.push({value: (time > i?this.period:(time - i)), max: this.period});
|
||||
}
|
||||
if(this.interval) {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
this.cdr.detectChanges();
|
||||
this.interval = setInterval(() => {
|
||||
let current = Math.floor(time);
|
||||
this.stages[current].value += SliderContainerComponent.INTERVAL;
|
||||
this.time = current + this.stages[current].value/this.stages[current].max;
|
||||
this.sliders.forEach(slider => {
|
||||
slider.change(this.time);
|
||||
});
|
||||
if(this.stages[current].value >= this.stages[current].max) {
|
||||
clearInterval(this.interval);
|
||||
let next = (current + 1 > this.total - 1)?0:current + 1;
|
||||
if(!this.stopped && (this.infinite || next > current)) {
|
||||
setTimeout(() => {
|
||||
this.start(next);
|
||||
}, SliderContainerComponent.ANIMATION_DURATION);
|
||||
}
|
||||
}
|
||||
}, SliderContainerComponent.INTERVAL);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import {Component, ElementRef, Input} from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: 'slider-item',
|
||||
template: `
|
||||
<ng-content></ng-content>
|
||||
`
|
||||
})
|
||||
export class SliderItemComponent {
|
||||
@Input()
|
||||
type: 'slide' | 'static' = 'slide';
|
||||
@Input()
|
||||
start: number;
|
||||
|
||||
constructor(private element: ElementRef) {
|
||||
}
|
||||
|
||||
init(animation: string) {
|
||||
this.element.nativeElement.classList.add(animation);
|
||||
}
|
||||
|
||||
setActive(active: boolean) {
|
||||
if(active) {
|
||||
this.element.nativeElement.classList.remove('uk-hidden');
|
||||
} else {
|
||||
this.element.nativeElement.classList.add('uk-hidden');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import {AfterViewInit, Component, ElementRef, Input, ViewChild} from "@angular/core";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
import {SliderContainerComponent} from "./slider-container.component";
|
||||
import {SliderItemComponent} from "./slider-item.component";
|
||||
|
||||
export interface Link {
|
||||
routerLink?: {
|
||||
commands: string[] | string,
|
||||
queryParams?: Object,
|
||||
fragment?: string,
|
||||
relativeTo?: ActivatedRoute
|
||||
}
|
||||
href?: string
|
||||
external?: boolean
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'slider-nav-item',
|
||||
template: `
|
||||
<div *ngIf="container" (click)="container.start(start)" class="uk-flex uk-flex-middle" [class.uk-active]="active">
|
||||
<div class="uk-width-expand">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<div *ngIf="link" class="action">
|
||||
<a *ngIf="link?.routerLink" #linkElement class="uk-text-decoration-none" [routerLink]="link.routerLink.commands" [queryParams]="link.routerLink.queryParams"
|
||||
[fragment]="link.routerLink.fragment" [relativeTo]="link.routerLink.relativeTo" [target]="link.external?'_blank':'_self'">
|
||||
<icon name="chevron_right" ratio="1.5" [flex]="true"></icon>
|
||||
</a>
|
||||
<a *ngIf="link?.href" #linkElement [href]="link.href" class="uk-text-decoration-none" [target]="link.external?'_blank':'_self'">
|
||||
<icon name="chevron_right" ratio="1.5" [flex]="true"></icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class SliderNavItemComponent extends SliderItemComponent implements AfterViewInit {
|
||||
@Input()
|
||||
link: Link = null;
|
||||
@Input()
|
||||
start: number;
|
||||
active: boolean = false;
|
||||
container: SliderContainerComponent;
|
||||
background: string;
|
||||
@ViewChild('linkElement') linkElement: ElementRef;
|
||||
|
||||
ngAfterViewInit() {
|
||||
if(this.linkElement) {
|
||||
this.linkElement.nativeElement.style.background = this.background;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue