diff --git a/explore/src/app/app-routing.module.ts b/explore/src/app/app-routing.module.ts index dfd6c7fb..a24b2e53 100644 --- a/explore/src/app/app-routing.module.ts +++ b/explore/src/app/app-routing.module.ts @@ -10,6 +10,7 @@ const routes: Routes = [ {path: 'mail-preferences', loadChildren: () => import('./userEmailPreferences/mailPrefs.module').then(m => m.LibMailPrefsModule)}, {path: 'sdgs', loadChildren: () => import('./sdg/sdg.module').then(m => m.LibSdgModule)}, {path: 'fields-of-science', loadChildren: () => import('./fos/fos.module').then(m => m.LibFosModule), data: {extraOffset: 100}}, + // {path: 'funders', loadChildren: () => import('./funders/funders.module').then(m => m.FundersModule)}, {path: 'contact-us', loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule), data: {hasQuickContact: false}}, // ORCID Pages {path: 'orcid', loadChildren: () => import('./orcid/orcid.module').then(m => m.LibOrcidModule)}, diff --git a/explore/src/app/app.component.ts b/explore/src/app/app.component.ts index 97c796c7..5292ea5c 100644 --- a/explore/src/app/app.component.ts +++ b/explore/src/app/app.component.ts @@ -191,6 +191,7 @@ export class AppComponent { new MenuItem("", "Registries", "", "/search/entity-registries", false, ["datasource"], ["/search/entity-registries"], {}), new MenuItem("", "Browse all", "", "/search/find/dataproviders", false, ["datasource"], ["/search/find/dataproviders"], {})] ), + // new MenuItem("funders", "Funders", "", "/funders", false, [], ["/funders"], {}), ]; if (Session.isPortalAdministrator(this.user)) { this.userMenuItems.push(new MenuItem("", "Manage all links", "", "/claims", false, [], ["/claims"], {})); diff --git a/explore/src/app/funders/funders-routing.module.ts b/explore/src/app/funders/funders-routing.module.ts new file mode 100644 index 00000000..5359a285 --- /dev/null +++ b/explore/src/app/funders/funders-routing.module.ts @@ -0,0 +1,19 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; + +import {FundersComponent} from './funders.component'; +import {IsRouteEnabled} from "../openaireLibrary/error/isRouteEnabled.guard"; +import {PreviousRouteRecorder} from "../openaireLibrary/utils/piwik/previousRouteRecorder.guard"; + +@NgModule({ + imports: [ + RouterModule.forChild([ + { + path: '', component: FundersComponent, + canActivate: [IsRouteEnabled], + canDeactivate: [PreviousRouteRecorder] + } + ]) + ] +}) +export class FundersRoutingModule { } diff --git a/explore/src/app/funders/funders.component.html b/explore/src/app/funders/funders.component.html new file mode 100644 index 00000000..2844f376 --- /dev/null +++ b/explore/src/app/funders/funders.component.html @@ -0,0 +1,150 @@ + +
+
+
+ +
+
+
+
+
+ + + Funders + +
+

+ Be an integral part of the open R&I ecosystem +

+
+ Lorem ipsum dolor sit amet, consetetur sadipscing + {{formatNumber(projectsNumber)}} grants from + {{formatNumber(fundersNumber)}} funders, linked to + {{formatNumber(researchProductsNumber)}} funded research outputs. +
+ +
+
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+ + + +
{{(funder.openAccessResearchProducts / funder.researchProducts) * 100 | number : '1.0-0'}}%
+
+ Open Access +
+
+ +
+ + + OpenAIRE placeholder logo + +
+ +
+ + {{funder.alias}} + +
+ +
+ + {{funder.name}} + +
+ +
+
+
+
+ + +
+
+
+
\ No newline at end of file diff --git a/explore/src/app/funders/funders.component.less b/explore/src/app/funders/funders.component.less new file mode 100644 index 00000000..32d0b5d5 --- /dev/null +++ b/explore/src/app/funders/funders.component.less @@ -0,0 +1,36 @@ +@import (reference) "~src/assets/openaire-theme/less/color.less"; + + +.custom-coins-dot:after { + content: ""; + background-image: url("~src/assets/explore-assets/funders/coins.svg"); + display: inline-block; + background-size: 100% 100%; + height: 35px; + width: 35px; + margin-right: 10px; +} + +.custom-handshake-dot:after { + content: ""; + background-image: url("~src/assets/explore-assets/funders/handshake.svg"); + display: inline-block; + background-size: 100% 100%; + height: 30px; + width: 30px; + margin-left: 10px; +} + +.setType(@color) { + border-bottom: 4px solid fade(@color, 30%); + + & .type { + color: @color; + } +} + +.uk-card { + &.funder { + .setType(@funder-color); + } +} \ No newline at end of file diff --git a/explore/src/app/funders/funders.component.ts b/explore/src/app/funders/funders.component.ts new file mode 100644 index 00000000..b3b44da3 --- /dev/null +++ b/explore/src/app/funders/funders.component.ts @@ -0,0 +1,260 @@ +import {Component, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {Subscriber, Subscription, zip} from "rxjs"; +import {Meta, Title} from "@angular/platform-browser"; + +import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties"; +import {PiwikService} from "../openaireLibrary/utils/piwik/piwik.service"; +import {SEOService} from "../openaireLibrary/sharedComponents/SEO/SEO.service"; +import {Breadcrumb} from "../openaireLibrary/utils/breadcrumbs/breadcrumbs.component"; +import {properties} from "../../environments/environment"; +import {RefineFieldResultsService} from '../openaireLibrary/services/refineFieldResults.service'; +import {StakeholderService} from '../openaireLibrary/monitor/services/stakeholder.service'; +import {Option} from '../openaireLibrary/sharedComponents/input/input.component'; +import {StringUtils} from '../openaireLibrary/utils/string-utils.class'; +import {HelperFunctions} from '../openaireLibrary/utils/HelperFunctions.class'; +import {NumberUtils} from '../openaireLibrary/utils/number-utils.class'; + +@Component({ + selector: 'funders', + templateUrl: './funders.component.html', + styleUrls: ['funders.component.less'] +}) + +export class FundersComponent implements OnInit { + private subscriptions: Subscription[] = []; + url: string = null; + pageTitle: string = "OpenAIRE - Explore | Funders"; + pageDescription: string = "Funders | Be an integral part of the open R&I ecosystem"; + properties: EnvProperties = properties; + breadcrumbs: Breadcrumb[] = [{name: 'home', route: '/'}, {name: 'funders'}]; + showLoading: boolean = true; + index: number = 0; + funders: any[] = []; + groupedFunders: any[] = []; + options: Option[]; + sortBy: string = 'all'; + currentPage: number = 1; + pageSize: number = 6; + fundersNumber: number = 0; + researchProductsNumber: number = 0; + projectsNumber: number = 0; + fundersMap = new Map(); + + constructor(private router: Router, + private meta: Meta, + private title: Title, + private seoService: SEOService, + private piwikService: PiwikService, + private refineFieldResultsService: RefineFieldResultsService, + private stakeholderService: StakeholderService) { + } + + ngOnInit() { + this.title.setTitle('OpenAIRE - Explore | Funders'); + this.properties = properties; + if (this.properties.enablePiwikTrack && (typeof document !== 'undefined')) { + this.subscriptions.push( this.piwikService.trackView(this.properties, this.pageTitle, this.properties.piwikSiteId).subscribe()); + } + this.url = this.properties.domain + this.router.url; + this.seoService.createLinkForCanonicalURL(this.url); + this.updateUrl(this.url); + this.updateTitle(this.pageTitle); + this.updateDescription(this.pageDescription); + this.options = [ + {value: 'all', label: 'All funders'}, + {value: 'dashboard', label: 'Funders with dashboard'} + ]; + this.getFunders(); + } + + ngOnDestroy() { + this.subscriptions.forEach(subscription => { + if (subscription instanceof Subscriber) { + subscription.unsubscribe(); + } + }); + } + + private updateDescription(description: string) { + this.meta.updateTag({content: description}, "name='description'"); + this.meta.updateTag({content: description}, "property='og:description'"); + } + + private updateTitle(title: string) { + var title = ((title.length > 50) ? title.substring(0, 50) : title); + this.title.setTitle(title); + this.meta.updateTag({content: title}, "property='og:title'"); + } + + private updateUrl(url: string) { + this.meta.updateTag({content: url}, "property='og:url'"); + } + + private getFunders() { + let refineParams = '&fq=resultbestaccessright%20exact%20%22Open%20Access%22'; + this.subscriptions.push( + zip( + this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['relfunder'], 'result', this.properties), + this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['relfunder'], 'result', this.properties, refineParams), + this.refineFieldResultsService.getRefineFieldsResultsByEntityName(['funder'], 'project', this.properties), + this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, 'funder') + ).subscribe((data: any[]) => { + // storing all needed data to a map + // 1st call + let queriedFunders1 = data[0][1][0].values; + queriedFunders1.forEach(queriedFunder => { + this.fundersMap.set(queriedFunder.id, { + "id": queriedFunder.id, + "name": queriedFunder.name, + "alias": '', + "researchProducts": +queriedFunder.number, + "openAccessResearchProducts": 0, + "projects": 0, + "monitorDashboard": '', + "monitorDashboardStatus": '', + "logoUrl": '' + }); + }); + + // 2nd call + let queriedFunders2 = data[1][1][0].values; + queriedFunders2.forEach(queriedFunder => { + if(this.fundersMap.has(queriedFunder.id)) { + this.fundersMap.get(queriedFunder.id).openAccessResearchProducts = +queriedFunder.number; + } + }); + + // 3rd call + let queriedFunders3 = data[2][1][0].values; + queriedFunders3.forEach(queriedFunder => { + if(this.fundersMap.has(queriedFunder.id)) { + this.fundersMap.get(queriedFunder.id).projects = +queriedFunder.number; + } else { + this.fundersMap.set(queriedFunder.id, { + "id": queriedFunder.id, + "name": queriedFunder.name, + "alias": '', + "researchProducts": 0, + "openAccessResearchProducts": 0, + "projects": +queriedFunder.number, + "monitorDashboard": '', + "monitorDashboardStatus": '', + "logoUrl": '' + }); + } + }); + + // 4th call + let queriedFunders4 = data[3]; + queriedFunders4.forEach(queriedFunder => { + let id = queriedFunder.index_id + '||' + queriedFunder.index_name + '||' + queriedFunder.index_shortName; + if(this.fundersMap.has(id)) { + this.fundersMap.get(id).alias = queriedFunder.alias; + this.fundersMap.get(id).monitorDashboard = queriedFunder.alias; + this.fundersMap.get(id).monitorDashboardStatus = queriedFunder.visibility; + this.fundersMap.get(id).logoUrl = queriedFunder.logoUrl; + } else { + this.fundersMap.set(id, { + "id": queriedFunder.id, + "name": queriedFunder.name, + "alias": queriedFunder.alias, + "researchProducts": 0, + "openAccessResearchProducts": 0, + "projects": 0, + "monitorDashboard": queriedFunder.alias, + "monitorDashboardStatus": queriedFunder.visibility, + "logoUrl": queriedFunder.logoUrl + }); + } + }); + // convert funders map into an array + this.funders = Array.from(this.fundersMap.values()); + // group funders based on their initial letter + this.groupFunders(this.funders); + // calculate total numbers for intro content + this.calculateNumbers(); + + this.showLoading = false; + }) + ); + } + + private groupFunders(funders) { + if(funders.length === 0) { + return []; + } + funders.sort((a, b) => a['name'].localeCompare(b['name'])); + this.index = 0; + this.groupedFunders = Object.values( + funders.reduce((acc, funder) => { + let firstLetter = funder['name'][0].toLocaleUpperCase(); + if(!acc[firstLetter]) { + acc[firstLetter] = {group: firstLetter, data: [funder]}; + } else { + acc[firstLetter].data.push(funder); + } + return acc; + },{}) + ) + if(funders.length > 1) { + this.groupedFunders.unshift({group: 'All', data: funders}); + } + } + + private calculateSum(array, property) { + let sum = 0; + array.forEach(element => { + sum += element[property]; + }); + return sum; + } + + private calculateNumbers() { + this.fundersNumber = this.funders.length; + this.researchProductsNumber = this.calculateSum(this.funders, 'researchProducts'); + this.projectsNumber = this.calculateSum(this.funders, 'projects'); + } + + get showContentWithNumbers() { + return this.fundersNumber && this.researchProductsNumber && this.projectsNumber; + } + + formatNumber(num: number | string) { + let formatted = NumberUtils.roundNumber(+num); + return formatted.number + formatted.size; + } + + changeDisplayedFunders(i) { + this.currentPage = 1; + this.index = i; + } + + urlEncodeAndQuote(str: string): string { + return StringUtils.quote(StringUtils.URIEncode(str)); + } + + sortByChanged() { + let displayedFunders = this.funders; + if(this.sortBy == 'dashboard') { + displayedFunders = this.funders.filter(funder => funder.monitorDashboard && funder.monitorDashboard?.length > 0 && funder.monitorDashboardStatus != 'PRIVATE'); + } + this.currentPage = 1; + this.groupFunders(displayedFunders); + } + + public updateCurrentPage($event) { + this.currentPage = $event.value; + HelperFunctions.scrollToId('target'); + } +} diff --git a/explore/src/app/funders/funders.module.ts b/explore/src/app/funders/funders.module.ts new file mode 100644 index 00000000..dbaa3bbf --- /dev/null +++ b/explore/src/app/funders/funders.module.ts @@ -0,0 +1,46 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {RouterModule} from '@angular/router'; + +import {FundersComponent} from './funders.component'; +import {FundersRoutingModule} from "./funders-routing.module"; +import {AlertModalModule} from "../openaireLibrary/utils/modal/alertModal.module"; +import {PiwikService} from "../openaireLibrary/utils/piwik/piwik.service"; +import {IsRouteEnabled} from "../openaireLibrary/error/isRouteEnabled.guard"; +import {Schema2jsonldModule} from "../openaireLibrary/sharedComponents/schema2jsonld/schema2jsonld.module"; +import {SEOServiceModule} from "../openaireLibrary/sharedComponents/SEO/SEOService.module"; +import {BreadcrumbsModule} from "../openaireLibrary/utils/breadcrumbs/breadcrumbs.module"; +import {LoadingModule} from "../openaireLibrary/utils/loading/loading.module"; +import {RefineFieldResultsServiceModule} from '../openaireLibrary/services/refineFieldResultsService.module'; +import {LogoUrlPipeModule} from '../openaireLibrary/utils/pipes/logoUrlPipe.module'; +import {IconsModule} from '../openaireLibrary/utils/icons/icons.module'; +import {IconsService} from '../openaireLibrary/utils/icons/icons.service'; +import {open_access} from '../openaireLibrary/utils/icons/icons'; +import {closed_access} from '../openaireLibrary/utils/icons/icons'; +import {InputModule} from '../openaireLibrary/sharedComponents/input/input.module'; +import {PagingModule} from '../openaireLibrary/utils/paging.module'; + + +@NgModule({ + imports: [ + FundersRoutingModule, CommonModule, RouterModule, AlertModalModule, + Schema2jsonldModule, SEOServiceModule, BreadcrumbsModule, LoadingModule, + RefineFieldResultsServiceModule, LogoUrlPipeModule, IconsModule, InputModule, + PagingModule + ], + declarations: [ + FundersComponent + ], + providers: [ + PiwikService, IsRouteEnabled + ], + exports: [ + FundersComponent + ] +}) + +export class FundersModule { + constructor(private iconsService: IconsService) { + this.iconsService.registerIcons([open_access, closed_access]); + } +} diff --git a/explore/src/app/openaireLibrary b/explore/src/app/openaireLibrary index 1d31c7b2..7e5317e9 160000 --- a/explore/src/app/openaireLibrary +++ b/explore/src/app/openaireLibrary @@ -1 +1 @@ -Subproject commit 1d31c7b23444ab7377477b54ff11cc3662ccf0c2 +Subproject commit 7e5317e920340db31fff4eca37497b5ab37e50c7 diff --git a/explore/src/assets/common-assets b/explore/src/assets/common-assets index 8e96d241..0b4b42ee 160000 --- a/explore/src/assets/common-assets +++ b/explore/src/assets/common-assets @@ -1 +1 @@ -Subproject commit 8e96d2414f01589fa7abe8a2f9f33dc94fc3e2ca +Subproject commit 0b4b42ee9584da0237f307ebd22f68fbdeb978a4 diff --git a/explore/src/assets/explore-assets/funders/coins.svg b/explore/src/assets/explore-assets/funders/coins.svg new file mode 100644 index 00000000..6b3d73fb --- /dev/null +++ b/explore/src/assets/explore-assets/funders/coins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/explore/src/assets/explore-assets/funders/funders.png b/explore/src/assets/explore-assets/funders/funders.png new file mode 100644 index 00000000..0ff764f5 Binary files /dev/null and b/explore/src/assets/explore-assets/funders/funders.png differ diff --git a/explore/src/assets/explore-assets/funders/handshake.svg b/explore/src/assets/explore-assets/funders/handshake.svg new file mode 100644 index 00000000..03470325 --- /dev/null +++ b/explore/src/assets/explore-assets/funders/handshake.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/explore/src/assets/explore-custom.less b/explore/src/assets/explore-custom.less index f53269c3..3109db9f 100644 --- a/explore/src/assets/explore-custom.less +++ b/explore/src/assets/explore-custom.less @@ -33,6 +33,14 @@ /* Landing */ @landing-portal-color: @explore-color; -.deposit { +.deposit, .funders { @import (multiple) "~src/assets/openaire-theme/less/_import"; } + +.monitor-dashboard-link { + color: @monitor-color; +} + +.funders-title { + color: @primary-color; +} diff --git a/explore/src/assets/openaire-theme b/explore/src/assets/openaire-theme index a49dca9a..4794bc9f 160000 --- a/explore/src/assets/openaire-theme +++ b/explore/src/assets/openaire-theme @@ -1 +1 @@ -Subproject commit a49dca9a915f04fb85f86eb446d045056bbe229c +Subproject commit 4794bc9f623dcf90e85f45f569269b9c25911f44 diff --git a/explore/src/environments/environment.beta.ts b/explore/src/environments/environment.beta.ts index 90db5608..9f183019 100644 --- a/explore/src/environments/environment.beta.ts +++ b/explore/src/environments/environment.beta.ts @@ -61,6 +61,7 @@ export let properties: EnvProperties = { datasourcesAPI: "https://beta.services.openaire.eu/openaire/ds/api/", + monitorServiceAPIURL: "https://beta.services.openaire.eu/uoa-monitor-service", adminToolsAPIURL: "https://beta.services.openaire.eu/uoa-admin-tools/", adminToolsCommunity: "openaire", diff --git a/explore/src/environments/environment.prod.ts b/explore/src/environments/environment.prod.ts index 46e0355c..084c6848 100644 --- a/explore/src/environments/environment.prod.ts +++ b/explore/src/environments/environment.prod.ts @@ -65,6 +65,7 @@ export let properties: EnvProperties = { datasourcesAPI: "https://services.openaire.eu/openaire/ds/api/", + monitorServiceAPIURL: "https://services.openaire.eu/uoa-monitor-service", adminToolsAPIURL: "https://services.openaire.eu/uoa-admin-tools/", adminToolsCommunity: "openaire", diff --git a/explore/src/environments/environment.ts b/explore/src/environments/environment.ts index bfee0b7d..4ebf0dca 100644 --- a/explore/src/environments/environment.ts +++ b/explore/src/environments/environment.ts @@ -66,6 +66,7 @@ export let properties: EnvProperties = { cacheUrl: "http://dl170.madgik.di.uoa.gr:3000/get?url=", + monitorServiceAPIURL: "http://duffy.di.uoa.gr:19380/uoa-monitor-service", adminToolsAPIURL: "http://duffy.di.uoa.gr:19280/uoa-admin-tools/", adminToolsCommunity: "openaire",