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) |
0; else na_prize">
- {{ 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',
+ },
+];