From ef0a030e97eaca5c737e0d3730364f29a4a82bfd Mon Sep 17 00:00:00 2001 From: Rongrong Chai <73901500+rrchai@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:14:15 -0400 Subject: [PATCH] fix(openchallenges): create pipe to map enum values of challenge properties to labels (#2612) --- .../src/lib/challenge-search-filters.ts | 128 ++---------------- .../challenge-overview.component.html | 6 +- .../challenge-overview.component.ts | 25 ++-- .../challenge-card.component.html | 12 +- .../challenge-card.component.scss | 2 +- .../challenge-card.component.ts | 21 +-- libs/openchallenges/util/src/index.ts | 2 + .../lib/pipe/challenge-property-label.pipe.ts | 76 +++++++++++ .../lib/pipe/challenge-property-options.ts | 120 ++++++++++++++++ 9 files changed, 247 insertions(+), 145 deletions(-) create mode 100644 libs/openchallenges/util/src/lib/pipe/challenge-property-label.pipe.ts create mode 100644 libs/openchallenges/util/src/lib/pipe/challenge-property-options.ts diff --git a/libs/openchallenges/challenge-search/src/lib/challenge-search-filters.ts b/libs/openchallenges/challenge-search/src/lib/challenge-search-filters.ts index 39ca1cebea..f5284094c1 100644 --- a/libs/openchallenges/challenge-search/src/lib/challenge-search-filters.ts +++ b/libs/openchallenges/challenge-search/src/lib/challenge-search-filters.ts @@ -1,11 +1,11 @@ -import { - ChallengeCategory, - ChallengeIncentive, - ChallengeSort, - ChallengeStatus, - ChallengeSubmissionType, -} from '@sagebionetworks/openchallenges/api-client-angular'; import { Filter } from '@sagebionetworks/openchallenges/ui'; +import { + ChallengeCategoriesOptions, + ChallengeIncentivesOptions, + ChallengeStatusOptions, + ChallengeSubmissionTypesOptions, + ChallengeSortOptions, +} from '@sagebionetworks/openchallenges/util'; const thisYear = new Date().getFullYear(); @@ -55,113 +55,13 @@ export const challengeStartYearRangeFilter: Filter[] = [ }, ]; -export const challengeStatusFilter: Filter[] = [ - { - value: ChallengeStatus.Active, - label: 'Active', - }, - { - value: ChallengeStatus.Upcoming, - label: 'Upcoming', - }, - { - value: ChallengeStatus.Completed, - label: 'Completed', - }, -]; - -export const challengeSubmissionTypesFilter: Filter[] = [ - { - value: ChallengeSubmissionType.ContainerImage, - label: 'Container Image', - }, - { - value: ChallengeSubmissionType.Mlcube, - label: 'MLCube', - }, - { - value: ChallengeSubmissionType.Notebook, - label: 'Notebook', - }, - { - value: ChallengeSubmissionType.PredictionFile, - label: 'Prediction File', - }, - { - value: ChallengeSubmissionType.Other, - label: 'Other', - }, -]; - -export const challengeIncentivesFilter: Filter[] = [ - { - value: ChallengeIncentive.Monetary, - label: 'Monetary', - }, - { - value: ChallengeIncentive.Publication, - label: 'Publication', - }, - { - value: ChallengeIncentive.SpeakingEngagement, - label: 'Speaking Engagement', - }, - { - value: ChallengeIncentive.Other, - label: 'Other', - }, -]; - -export const challengePlatformsFilter: Filter[] = []; - +export const challengeCategoriesFilter: Filter[] = ChallengeCategoriesOptions; +export const challengeIncentivesFilter: Filter[] = ChallengeIncentivesOptions; export const challengeInputDataTypesFilter: Filter[] = []; - -export const challengeCategoriesFilter: Filter[] = [ - { - value: ChallengeCategory.Featured, - label: 'Featured', - }, - { - value: ChallengeCategory.Benchmark, - label: 'Benchmark', - }, - { - value: ChallengeCategory.Hackathon, - label: 'Hackathon', - }, - { - value: ChallengeCategory.StartingSoon, - label: 'Starting Soon', - }, - { - value: ChallengeCategory.EndingSoon, - label: 'Closing Soon', - }, - { - value: ChallengeCategory.RecentlyStarted, - label: 'Recently Launched', - }, - { - value: ChallengeCategory.RecentlyEnded, - label: 'Recently Completed', - }, -]; - export const challengeOrganizationsFilter: Filter[] = []; - export const challengeOrganizersFilter: Filter[] = []; - -export const challengeSortFilter: Filter[] = [ - { - value: ChallengeSort.Relevance, - label: 'Relevance', - }, - { - value: ChallengeSort.StartDate, - label: 'Start Date', - }, - { - value: ChallengeSort.Starred, - label: 'Most Starred', - }, -]; +export const challengePlatformsFilter: Filter[] = []; +export const challengeSortFilter: Filter[] = ChallengeSortOptions; +export const challengeStatusFilter: Filter[] = ChallengeStatusOptions; +export const challengeSubmissionTypesFilter: Filter[] = + ChallengeSubmissionTypesOptions; diff --git a/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.html b/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.html index f166275579..a0bbd06385 100644 --- a/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.html +++ b/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.html @@ -26,7 +26,7 @@

Challenge Details

Status - {{ prettify(challenge.status) }} + {{ challenge.status | challengeStatusLabel }} Platform @@ -58,7 +58,7 @@

Challenge Details

nowrap *ngFor="let submissionType of challenge.submissionTypes; let isLast = last" > - {{ prettify(submissionType) }}{{ isLast ? '' : ', ' }} @@ -69,7 +69,7 @@

Challenge Details

Incentive(s) - {{ prettify(incentive) }}{{ isLast ? '' : ', ' }} diff --git a/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.ts b/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.ts index c0089433f8..611fc4ce7d 100644 --- a/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.ts +++ b/libs/openchallenges/challenge/src/lib/challenge-overview/challenge-overview.component.ts @@ -5,28 +5,31 @@ import { MOCK_ORGANIZATION_CARDS, OrganizationCard, } from '@sagebionetworks/openchallenges/ui'; -import { MatIconModule } from '@angular/material/icon'; +import { MatIconModule } from '@angular/material/icon'; +import { + ChallengeIncentiveLabelPipe, + ChallengeStatusLabelPipe, + ChallengeSubmissionTypeLabelPipe, +} from '@sagebionetworks/openchallenges/util'; @Component({ selector: 'openchallenges-challenge-overview', standalone: true, - imports: [CommonModule, MatIconModule], + imports: [ + ChallengeIncentiveLabelPipe, + ChallengeStatusLabelPipe, + ChallengeSubmissionTypeLabelPipe, + CommonModule, + MatIconModule, + ], templateUrl: './challenge-overview.component.html', styleUrls: ['./challenge-overview.component.scss'], }) export class ChallengeOverviewComponent { @Input({ required: true }) challenge!: Challenge; organizationCards: OrganizationCard[] = MOCK_ORGANIZATION_CARDS; - // mockTopics = ['breast', 'cancer']; useNaIfFalsey(str: string | null | undefined) { - return str || 'Not available'; - } - - prettify(camel: string | undefined) { - return camel - ? camel.charAt(0).toUpperCase() + - camel.slice(1).replace(/_/g, ' ').toLowerCase() - : undefined; + return str ?? 'Not available'; } } diff --git a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.html b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.html index 5cdc3832af..b5f8532d3e 100644 --- a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.html +++ b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.html @@ -28,9 +28,17 @@ diff --git a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.scss b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.scss index 6a43e1a748..2c9f740e0d 100644 --- a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.scss +++ b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.scss @@ -32,7 +32,7 @@ } // FOOTER -.difficulty-tag { +.incentive-tag { display: flex; align-items: center; flex: 1; diff --git a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.ts b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.ts index b5396eacd8..6aa29f79f0 100644 --- a/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.ts +++ b/libs/openchallenges/ui/src/lib/challenge-card/challenge-card.component.ts @@ -7,13 +7,18 @@ import { Image, ImageService, } from '@sagebionetworks/openchallenges/api-client-angular'; -// import { startCase } from 'lodash'; +import { ChallengeIncentiveLabelPipe } from '@sagebionetworks/openchallenges/util'; import { Observable } from 'rxjs'; @Component({ selector: 'openchallenges-challenge-card', standalone: true, - imports: [CommonModule, MatIconModule, RouterModule], + imports: [ + ChallengeIncentiveLabelPipe, + CommonModule, + MatIconModule, + RouterModule, + ], templateUrl: './challenge-card.component.html', styleUrls: ['./challenge-card.component.scss'], }) @@ -22,7 +27,6 @@ export class ChallengeCardComponent implements OnInit { banner$: Observable | undefined; status!: string | undefined; desc!: string; - incentives!: string; statusClass!: string; time_info!: string | number; @@ -35,17 +39,6 @@ export class ChallengeCardComponent implements OnInit { this.desc = this.challenge.headline ? this.challenge.headline : this.challenge.description; - this.incentives = - this.challenge.incentives.length === 0 - ? 'No incentives listed' - : this.challenge.incentives - .map(function (s) { - return ( - s.charAt(0).toUpperCase() + - s.slice(1).replace(/_/g, ' ').toLowerCase() - ); - }) - .join(', '); this.banner$ = this.challenge.avatarUrl ? this.imageService.getImage({ objectKey: this.challenge.avatarUrl, diff --git a/libs/openchallenges/util/src/index.ts b/libs/openchallenges/util/src/index.ts index 054d809a3e..b92bfcda35 100644 --- a/libs/openchallenges/util/src/index.ts +++ b/libs/openchallenges/util/src/index.ts @@ -4,3 +4,5 @@ export * from './lib/handle-http-error'; export * from './lib/http-status-redirect'; export * from './lib/is-api-client-error'; export * from './lib/page-title.service'; +export * from './lib/pipe/challenge-property-label.pipe'; +export * from './lib/pipe/challenge-property-options'; diff --git a/libs/openchallenges/util/src/lib/pipe/challenge-property-label.pipe.ts b/libs/openchallenges/util/src/lib/pipe/challenge-property-label.pipe.ts new file mode 100644 index 0000000000..1861f8e2e4 --- /dev/null +++ b/libs/openchallenges/util/src/lib/pipe/challenge-property-label.pipe.ts @@ -0,0 +1,76 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { + ChallengeCategory, + ChallengeIncentive, + ChallengeSort, + ChallengeStatus, + ChallengeSubmissionType, +} from '@sagebionetworks/openchallenges/api-client-angular'; +import { + ChallengeCategoriesOptions, + ChallengeIncentivesOptions, + ChallengeSortOptions, + ChallengeStatusOptions, + ChallengeSubmissionTypesOptions, +} from './challenge-property-options'; + +@Pipe({ + name: 'challengeCategoryLabel', + standalone: true, +}) +export class ChallengeCategoryLabelPipe implements PipeTransform { + transform(category: ChallengeCategory): string | undefined { + const option = ChallengeCategoriesOptions.find( + (item) => item.value === category + ); + return option ? option.label : undefined; + } +} + +@Pipe({ + name: 'challengeIncentiveLabel', + standalone: true, +}) +export class ChallengeIncentiveLabelPipe implements PipeTransform { + transform(incentive: ChallengeIncentive): string | undefined { + const option = ChallengeIncentivesOptions.find( + (item) => item.value === incentive + ); + return option ? option.label : undefined; + } +} + +@Pipe({ + name: 'challengeSortLabel', + standalone: true, +}) +export class ChallengeSortLabelPipe implements PipeTransform { + transform(sort: ChallengeSort): string | undefined { + const option = ChallengeSortOptions.find((item) => item.value === sort); + return option ? option.label : undefined; + } +} + +@Pipe({ + name: 'challengeStatusLabel', + standalone: true, +}) +export class ChallengeStatusLabelPipe implements PipeTransform { + transform(status: ChallengeStatus): string | undefined { + const option = ChallengeStatusOptions.find((item) => item.value === status); + return option ? option.label : undefined; + } +} + +@Pipe({ + name: 'challengeSubmissionTypeLabel', + standalone: true, +}) +export class ChallengeSubmissionTypeLabelPipe implements PipeTransform { + transform(submissionType: ChallengeSubmissionType): string | undefined { + const option = ChallengeSubmissionTypesOptions.find( + (item) => item.value === submissionType + ); + return option ? option.label : undefined; + } +} diff --git a/libs/openchallenges/util/src/lib/pipe/challenge-property-options.ts b/libs/openchallenges/util/src/lib/pipe/challenge-property-options.ts new file mode 100644 index 0000000000..e70e51963e --- /dev/null +++ b/libs/openchallenges/util/src/lib/pipe/challenge-property-options.ts @@ -0,0 +1,120 @@ +import { + ChallengeCategory, + ChallengeIncentive, + ChallengeSort, + ChallengeStatus, + ChallengeSubmissionType, +} from '@sagebionetworks/openchallenges/api-client-angular'; + +type ChallengePropertyOption = { + value: + | ChallengeCategory + | ChallengeIncentive + | ChallengeSort + | ChallengeStatus + | ChallengeSubmissionType; + label: string; +}; + +export const ChallengeCategoriesOptions: ChallengePropertyOption[] = [ + { + value: ChallengeCategory.Featured, + label: 'Featured', + }, + { + value: ChallengeCategory.Benchmark, + label: 'Benchmark', + }, + { + value: ChallengeCategory.Hackathon, + label: 'Hackathon', + }, + { + value: ChallengeCategory.StartingSoon, + label: 'Starting Soon', + }, + { + value: ChallengeCategory.EndingSoon, + label: 'Closing Soon', + }, + { + value: ChallengeCategory.RecentlyStarted, + label: 'Recently Launched', + }, + { + value: ChallengeCategory.RecentlyEnded, + label: 'Recently Completed', + }, +]; + +export const ChallengeIncentivesOptions: ChallengePropertyOption[] = [ + { + value: ChallengeIncentive.Monetary, + label: 'Monetary', + }, + { + value: ChallengeIncentive.Publication, + label: 'Publication', + }, + { + value: ChallengeIncentive.SpeakingEngagement, + label: 'Speaking Engagement', + }, + { + value: ChallengeIncentive.Other, + label: 'Other', + }, +]; + +export const ChallengeSortOptions: ChallengePropertyOption[] = [ + { + value: ChallengeSort.Relevance, + label: 'Relevance', + }, + { + value: ChallengeSort.StartDate, + label: 'Start Date', + }, + { + value: ChallengeSort.Starred, + label: 'Most Starred', + }, +]; + +export const ChallengeStatusOptions: ChallengePropertyOption[] = [ + { + value: ChallengeStatus.Active, + label: 'Active', + }, + { + value: ChallengeStatus.Upcoming, + label: 'Upcoming', + }, + { + value: ChallengeStatus.Completed, + label: 'Completed', + }, +]; + +export const ChallengeSubmissionTypesOptions: ChallengePropertyOption[] = [ + { + value: ChallengeSubmissionType.ContainerImage, + label: 'Container Image', + }, + { + value: ChallengeSubmissionType.Mlcube, + label: 'MLCube', + }, + { + value: ChallengeSubmissionType.Notebook, + label: 'Notebook', + }, + { + value: ChallengeSubmissionType.PredictionFile, + label: 'Prediction File', + }, + { + value: ChallengeSubmissionType.Other, + label: 'Other', + }, +];