diff --git a/libs/openchallenges/about/src/lib/about-seo-data.ts b/libs/openchallenges/about/src/lib/about-seo-data.ts new file mode 100644 index 0000000000..f6e373e72a --- /dev/null +++ b/libs/openchallenges/about/src/lib/about-seo-data.ts @@ -0,0 +1,9 @@ +import { getDefaultSeoData } from '@sagebionetworks/openchallenges/util'; +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getSeoData = (): SeoData => { + // shallow merge + return Object.assign(getDefaultSeoData(), { + title: 'About OpenChallenges', + } as SeoData); +}; diff --git a/libs/openchallenges/about/src/lib/about.component.ts b/libs/openchallenges/about/src/lib/about.component.ts index 6c89f5a6ce..3fca76606e 100644 --- a/libs/openchallenges/about/src/lib/about.component.ts +++ b/libs/openchallenges/about/src/lib/about.component.ts @@ -1,7 +1,9 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, Renderer2 } from '@angular/core'; import { ConfigService } from '@sagebionetworks/openchallenges/config'; import { FooterComponent } from '@sagebionetworks/openchallenges/ui'; +import { getSeoData } from './about-seo-data'; +import { SeoService } from '@sagebionetworks/shared/util'; @Component({ selector: 'openchallenges-about', @@ -16,10 +18,15 @@ export class AboutComponent { public privacyPolicyUrl: string; public termsOfUseUrl: string; - constructor(private readonly configService: ConfigService) { + constructor( + private readonly configService: ConfigService, + private seoService: SeoService, + private renderer2: Renderer2 + ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; + this.seoService.setData(getSeoData(), this.renderer2); } } diff --git a/libs/openchallenges/challenge-search/src/lib/challenge-search-seo-data.ts b/libs/openchallenges/challenge-search/src/lib/challenge-search-seo-data.ts new file mode 100644 index 0000000000..fca724f3d8 --- /dev/null +++ b/libs/openchallenges/challenge-search/src/lib/challenge-search-seo-data.ts @@ -0,0 +1,9 @@ +import { getDefaultSeoData } from '@sagebionetworks/openchallenges/util'; +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getSeoData = (): SeoData => { + // shallow merge + return Object.assign(getDefaultSeoData(), { + title: 'Explore Challenges | OpenChallenges', + } as SeoData); +}; diff --git a/libs/openchallenges/challenge-search/src/lib/challenge-search.component.ts b/libs/openchallenges/challenge-search/src/lib/challenge-search.component.ts index 1ed2fc66d2..4d0202a428 100644 --- a/libs/openchallenges/challenge-search/src/lib/challenge-search.component.ts +++ b/libs/openchallenges/challenge-search/src/lib/challenge-search.component.ts @@ -3,6 +3,7 @@ import { Component, OnDestroy, OnInit, + Renderer2, ViewChild, } from '@angular/core'; import { @@ -84,6 +85,8 @@ import { DropdownModule } from 'primeng/dropdown'; import { InputTextModule } from 'primeng/inputtext'; import { PanelModule } from 'primeng/panel'; import { RadioButtonModule } from 'primeng/radiobutton'; +import { SeoService } from '@sagebionetworks/shared/util'; +import { getSeoData } from './challenge-search-seo-data'; @Component({ selector: 'openchallenges-challenge-search', @@ -194,12 +197,15 @@ export class ChallengeSearchComponent private organizationService: OrganizationService, private imageService: ImageService, private readonly configService: ConfigService, - private _snackBar: MatSnackBar + private _snackBar: MatSnackBar, + private seoService: SeoService, + private renderer2: Renderer2 ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; + this.seoService.setData(getSeoData(), this.renderer2); } ngOnInit() { diff --git a/libs/openchallenges/challenge/src/lib/challenge-seo-data.ts b/libs/openchallenges/challenge/src/lib/challenge-seo-data.ts new file mode 100644 index 0000000000..43da2400cc --- /dev/null +++ b/libs/openchallenges/challenge/src/lib/challenge-seo-data.ts @@ -0,0 +1,12 @@ +import { Challenge } from '@sagebionetworks/openchallenges/api-client-angular'; +import { SeoData } from '@sagebionetworks/shared/util'; +import { getDefaultSeoData } from '@sagebionetworks/openchallenges/util'; + +export const getSeoData = (challenge: Challenge): SeoData => { + const defaultSeoData = getDefaultSeoData(); + // shallow merge + return Object.assign(defaultSeoData, { + title: `${challenge.name} | OpenChallenges`, + description: challenge.description, + } as SeoData); +}; diff --git a/libs/openchallenges/challenge/src/lib/challenge.component.ts b/libs/openchallenges/challenge/src/lib/challenge.component.ts index 1351509862..c82ae0b6ca 100644 --- a/libs/openchallenges/challenge/src/lib/challenge.component.ts +++ b/libs/openchallenges/challenge/src/lib/challenge.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, Renderer2 } from '@angular/core'; import { ActivatedRoute, ParamMap, @@ -38,6 +38,8 @@ import { ChallengeOverviewComponent } from './challenge-overview/challenge-overv import { ChallengeStargazersComponent } from './challenge-stargazers/challenge-stargazers.component'; import { ChallengeStatsComponent } from './challenge-stats/challenge-stats.component'; import { CommonModule } from '@angular/common'; +import { SeoService } from '@sagebionetworks/shared/util'; +import { getSeoData } from './challenge-seo-data'; @Component({ selector: 'openchallenges-challenge', @@ -78,7 +80,9 @@ export class ChallengeComponent implements OnInit { private activatedRoute: ActivatedRoute, private router: Router, private challengeService: ChallengeService, - private readonly configService: ConfigService + private readonly configService: ConfigService, + private seoService: SeoService, + private renderer2: Renderer2 ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; @@ -111,6 +115,8 @@ export class ChallengeComponent implements OnInit { size: 250, }; + this.seoService.setData(getSeoData(challenge), this.renderer2); + // this.progressValue = // challenge.startDate && challenge.endDate // ? this.calcProgress( diff --git a/libs/openchallenges/home/src/lib/home.component.ts b/libs/openchallenges/home/src/lib/home.component.ts index 5b331bd648..51512c41f8 100644 --- a/libs/openchallenges/home/src/lib/home.component.ts +++ b/libs/openchallenges/home/src/lib/home.component.ts @@ -50,7 +50,6 @@ export class HomeComponent { this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; - this.seoService.setData(getSeoData(), this.renderer2); } } diff --git a/libs/openchallenges/org-profile/src/lib/org-profile-seo-data.ts b/libs/openchallenges/org-profile/src/lib/org-profile-seo-data.ts index e30a210724..28055f651c 100644 --- a/libs/openchallenges/org-profile/src/lib/org-profile-seo-data.ts +++ b/libs/openchallenges/org-profile/src/lib/org-profile-seo-data.ts @@ -10,18 +10,15 @@ export const getSeoData = ( imageUrl: string | undefined ): SeoData => { const defaultSeoData = getDefaultSeoData(); - - return new SeoData({ + // shallow merge + return Object.assign(defaultSeoData, { title: `${org.name} | OpenChallenges`, description: org.description, - url: '', imageUrl: imageUrl !== undefined ? optimizeImageUrlForSeo(imageUrl) : defaultSeoData.imageUrl, imageAlt: imageUrl !== undefined ? `${org.name} logo` : defaultSeoData.imageAlt, - publishDate: '2023-09-20T00:00:00Z', // TODO use org updatedAt? - jsonLds: [], - }); + } as SeoData); }; diff --git a/libs/openchallenges/org-search/src/lib/org-search-seo-data.ts b/libs/openchallenges/org-search/src/lib/org-search-seo-data.ts new file mode 100644 index 0000000000..8d5a1deec6 --- /dev/null +++ b/libs/openchallenges/org-search/src/lib/org-search-seo-data.ts @@ -0,0 +1,9 @@ +import { getDefaultSeoData } from '@sagebionetworks/openchallenges/util'; +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getSeoData = (): SeoData => { + // shallow merge + return Object.assign(getDefaultSeoData(), { + title: 'Discover Organizations | OpenChallenges', + } as SeoData); +}; diff --git a/libs/openchallenges/org-search/src/lib/org-search.component.ts b/libs/openchallenges/org-search/src/lib/org-search.component.ts index cb62f261b2..b95f093138 100644 --- a/libs/openchallenges/org-search/src/lib/org-search.component.ts +++ b/libs/openchallenges/org-search/src/lib/org-search.component.ts @@ -1,4 +1,10 @@ -import { AfterContentInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { + AfterContentInit, + Component, + OnDestroy, + OnInit, + Renderer2, +} from '@angular/core'; import { OrganizationService, OrganizationSearchQuery, @@ -56,6 +62,8 @@ import { DropdownModule } from 'primeng/dropdown'; import { InputTextModule } from 'primeng/inputtext'; import { PanelModule } from 'primeng/panel'; import { RadioButtonModule } from 'primeng/radiobutton'; +import { SeoService } from '@sagebionetworks/shared/util'; +import { getSeoData } from './org-search-seo-data'; @Component({ selector: 'openchallenges-org-search', @@ -127,12 +135,15 @@ export class OrgSearchComponent implements OnInit, AfterContentInit, OnDestroy { private organizationService: OrganizationService, private imageService: ImageService, private readonly configService: ConfigService, - private _snackBar: MatSnackBar + private _snackBar: MatSnackBar, + private seoService: SeoService, + private renderer2: Renderer2 ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; + this.seoService.setData(getSeoData(), this.renderer2); } ngOnInit() { diff --git a/libs/openchallenges/team/src/lib/team-seo-data.ts b/libs/openchallenges/team/src/lib/team-seo-data.ts new file mode 100644 index 0000000000..3bc02c5447 --- /dev/null +++ b/libs/openchallenges/team/src/lib/team-seo-data.ts @@ -0,0 +1,11 @@ +import { getDefaultSeoData } from '@sagebionetworks/openchallenges/util'; +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getSeoData = (): SeoData => { + // shallow merge + return Object.assign(getDefaultSeoData(), { + title: 'The OpenChallenges Team', + description: + "We're smart, we're hard-working, and we love a good challenge.", + } as SeoData); +}; diff --git a/libs/openchallenges/team/src/lib/team.component.ts b/libs/openchallenges/team/src/lib/team.component.ts index 1ae896fb6a..97518ed185 100644 --- a/libs/openchallenges/team/src/lib/team.component.ts +++ b/libs/openchallenges/team/src/lib/team.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, Renderer2 } from '@angular/core'; import { RouterModule } from '@angular/router'; import { Image, @@ -7,7 +7,9 @@ import { } from '@sagebionetworks/openchallenges/api-client-angular'; import { ConfigService } from '@sagebionetworks/openchallenges/config'; import { FooterComponent } from '@sagebionetworks/openchallenges/ui'; +import { SeoService } from '@sagebionetworks/shared/util'; import { Observable } from 'rxjs'; +import { getSeoData } from './team-seo-data'; @Component({ selector: 'openchallenges-team', @@ -27,16 +29,18 @@ export class TeamComponent implements OnInit { public verena$: Observable | undefined; public maria$: Observable | undefined; public jake$: Observable | undefined; - public amy$: Observable | undefined; constructor( private readonly configService: ConfigService, - private imageService: ImageService + private imageService: ImageService, + private seoService: SeoService, + private renderer2: Renderer2 ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; + this.seoService.setData(getSeoData(), this.renderer2); } ngOnInit() { @@ -58,8 +62,5 @@ export class TeamComponent implements OnInit { this.jake$ = this.imageService.getImage({ objectKey: 'team/jake.png', }); - this.amy$ = this.imageService.getImage({ - objectKey: 'team/amy.png', - }); } }