Skip to content

Commit

Permalink
feat: Set page titles in OC app (#2182)
Browse files Browse the repository at this point in the history
* Add SEO data to the team page

* Add SEO metadata to the About page

* Add SEO metadata to the challenge profile page

* Set SEO metadata to the challenge profile page

* Add SEO metadata to the challenge search page

* Add SEO metadata to the org search page
  • Loading branch information
tschaffter authored Sep 30, 2023
1 parent 8395cb1 commit 31b4941
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 20 deletions.
9 changes: 9 additions & 0 deletions libs/openchallenges/about/src/lib/about-seo-data.ts
Original file line number Diff line number Diff line change
@@ -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);
};
11 changes: 9 additions & 2 deletions libs/openchallenges/about/src/lib/about.component.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Component,
OnDestroy,
OnInit,
Renderer2,
ViewChild,
} from '@angular/core';
import {
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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() {
Expand Down
12 changes: 12 additions & 0 deletions libs/openchallenges/challenge/src/lib/challenge-seo-data.ts
Original file line number Diff line number Diff line change
@@ -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);
};
10 changes: 8 additions & 2 deletions libs/openchallenges/challenge/src/lib/challenge.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Renderer2 } from '@angular/core';
import {
ActivatedRoute,
ParamMap,
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
1 change: 0 additions & 1 deletion libs/openchallenges/home/src/lib/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
9 changes: 9 additions & 0 deletions libs/openchallenges/org-search/src/lib/org-search-seo-data.ts
Original file line number Diff line number Diff line change
@@ -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);
};
15 changes: 13 additions & 2 deletions libs/openchallenges/org-search/src/lib/org-search.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { AfterContentInit, Component, OnDestroy, OnInit } from '@angular/core';
import {
AfterContentInit,
Component,
OnDestroy,
OnInit,
Renderer2,
} from '@angular/core';
import {
OrganizationService,
OrganizationSearchQuery,
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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() {
Expand Down
11 changes: 11 additions & 0 deletions libs/openchallenges/team/src/lib/team-seo-data.ts
Original file line number Diff line number Diff line change
@@ -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);
};
13 changes: 7 additions & 6 deletions libs/openchallenges/team/src/lib/team.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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,
ImageService,
} 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',
Expand All @@ -27,16 +29,18 @@ export class TeamComponent implements OnInit {
public verena$: Observable<Image> | undefined;
public maria$: Observable<Image> | undefined;
public jake$: Observable<Image> | undefined;
public amy$: Observable<Image> | 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() {
Expand All @@ -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',
});
}
}

0 comments on commit 31b4941

Please sign in to comment.