Skip to content

Commit b041baa

Browse files
authored
Merge pull request #4771 from cagdas001/integration-v5-challenge-api
fix(challenge-listing): sorting by dates & phase displaying
2 parents 9b07faa + f8c7b4c commit b041baa

File tree

5 files changed

+85
-21
lines changed

5 files changed

+85
-21
lines changed

src/shared/components/challenge-listing/ChallengeCard/Status/index.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import LeaderboardAvatar from 'components/challenge-listing/LeaderboardAvatar';
66
import { config, Link } from 'topcoder-react-utils';
77
import { TABS as DETAIL_TABS } from 'actions/page/challenge-details';
88
import 'moment-duration-format';
9+
import { phaseEndDate } from 'utils/challenge-listing/helper';
910
import {
1011
getTimeLeft,
1112
} from 'utils/challenge-detail/helper';
@@ -271,7 +272,7 @@ export default function ChallengeStatus(props) {
271272
<ChallengeProgressBar
272273
color="green"
273274
value={getPhaseProgress(statusPhase)}
274-
isLate={moment().isAfter(statusPhase.scheduledEndDate)}
275+
isLate={moment().isAfter(phaseEndDate(statusPhase))}
275276
/>
276277
<div styleName="time-left">
277278
{getTimeLeft(statusPhase, 'to go').text}

src/shared/components/challenge-listing/Tooltips/ProgressBarTooltip/index.jsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import _ from 'lodash';
1717
import React from 'react';
1818
import PT from 'prop-types';
1919
import Tooltip from 'components/Tooltip';
20+
import { phaseStartDate, phaseEndDate } from 'utils/challenge-listing/helper';
2021
import LoaderIcon from '../../../Loader/Loader';
2122
import './style.scss';
2223

@@ -95,31 +96,32 @@ function Tip(props) {
9596
if (!c || _.isEmpty(c)) return <div />;
9697

9798
const allPhases = c.phases || [];
98-
const endPhaseDate = Math.max(...allPhases.map(d => new Date(d.scheduledEndDate)));
99+
const endPhaseDate = Math.max(...allPhases.map(d => phaseEndDate(d)));
99100
const registrationPhase = allPhases.find(phase => phase.name === 'Registration');
100101
const submissionPhase = allPhases.find(phase => phase.name === 'Submission');
102+
const checkpointPhase = allPhases.find(phase => phase.name === 'Checkpoint Submission');
101103

102104
if (registrationPhase) {
103105
steps.push({
104-
date: new Date(registrationPhase.scheduledStartDate),
106+
date: phaseStartDate(registrationPhase),
105107
name: 'Start',
106108
});
107109
}
108-
if (c.checkpointSubmissionEndDate) {
110+
if (checkpointPhase) {
109111
steps.push({
110-
date: new Date(c.checkpointSubmissionEndDate),
112+
date: phaseEndDate(checkpointPhase),
111113
name: 'Checkpoint',
112114
});
113115
}
114116
const iterativeReviewPhase = allPhases.find(phase => phase.isOpen && phase.name === 'Iterative Review');
115117
if (iterativeReviewPhase) {
116118
steps.push({
117-
date: new Date(iterativeReviewPhase.scheduledEndDate),
119+
date: phaseEndDate(iterativeReviewPhase),
118120
name: 'Iterative Review',
119121
});
120122
} else if (submissionPhase) {
121123
steps.push({
122-
date: new Date(submissionPhase.scheduledEndDate),
124+
date: phaseEndDate(submissionPhase),
123125
name: 'Submission',
124126
});
125127
}

src/shared/utils/challenge-detail/helper.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { challenge as challengeUtils } from 'topcoder-react-lib';
99
import { config } from 'topcoder-react-utils';
1010
import Prize from 'components/challenge-listing/ChallengeCard/Prize';
1111
import { BUCKETS, getBuckets } from 'utils/challenge-listing/buckets';
12+
import { phaseEndDate } from 'utils/challenge-listing/helper';
1213

1314
const Filter = challengeUtils.filter;
1415

@@ -42,7 +43,7 @@ export function getEndDate(challenge) {
4243
if (type === 'First2Finish' && challenge.status === 'Completed') {
4344
phases = challenge.phases.filter(p => p.phaseType === 'Iterative Review' && p.phaseStatus === 'Closed');
4445
}
45-
const endPhaseDate = Math.max(...phases.map(d => new Date(d.scheduledEndDate)));
46+
const endPhaseDate = Math.max(...phases.map(d => phaseEndDate(d)));
4647
return moment(endPhaseDate).format('MMM DD');
4748
}
4849

@@ -65,7 +66,7 @@ export function getTimeLeft(
6566
return { late: false, text: FF_TIME_LEFT_MSG };
6667
}
6768

68-
let time = moment(phase.scheduledEndDate).diff();
69+
let time = moment(phaseEndDate(phase)).diff();
6970
const late = time < 0;
7071
if (late) time = -time;
7172

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import moment from 'moment';
2+
3+
/**
4+
* Returns phase's end date.
5+
* @param {Object} phase
6+
* @return {Date}
7+
*/
8+
export function phaseEndDate(phase) {
9+
// Case 1: phase is still open. take the `scheduledEndDate`
10+
// Case 2: phase is not open but `scheduledStartDate` is a future date.
11+
// This means phase is not yet started. So take the `scheduledEndDate`
12+
if (phase.isOpen || moment(phase.scheduledStartDate).isAfter()) {
13+
return new Date(phase.scheduledEndDate);
14+
}
15+
// for other cases, take the `actualEndDate` as phase is already closed
16+
return new Date(phase.actualEndDate);
17+
}
18+
19+
/**
20+
* Returns phase's start date.
21+
* @param {Object} phase
22+
* @return {Date}
23+
*/
24+
export function phaseStartDate(phase) {
25+
// Case 1: Phase is not yet started. take the `scheduledStartDate`
26+
if (phase.isOpen !== true && moment(phase.scheduledStartDate).isAfter()) {
27+
return new Date(phase.scheduledStartDate);
28+
}
29+
// For all other cases, take the `actualStartDate` as phase is already started
30+
return new Date(phase.actualStartDate);
31+
}

src/shared/utils/challenge-listing/sort.js

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import moment from 'moment';
66
import { find, sumBy } from 'lodash';
7+
import { phaseStartDate, phaseEndDate } from './helper';
78

89
export const SORTS = {
910
CURRENT_PHASE: 'current-phase',
@@ -52,13 +53,21 @@ export default {
5253
},
5354
[SORTS.MOST_RECENT]: {
5455
func: (a, b) => {
55-
const getRegistrationStartDate = (challenge) => {
56+
const getChallengeStartDate = (challenge) => {
57+
// extract the phases from `challenge.phases`,
58+
// as `challenge.registrationStartDate` returned from API is not reliable
5659
const registrationPhase = find(challenge.phases, p => p.name === 'Registration');
57-
return registrationPhase.actualStartDate || registrationPhase.scheduledStartDate;
60+
const submissionPhase = find(challenge.phases, p => p.name === 'Submission');
61+
// registration phase exists
62+
if (registrationPhase) {
63+
return moment(phaseStartDate(registrationPhase));
64+
}
65+
// registration phase doesnt exist, This is possibly a F2F or TSK. Take submission phase
66+
return moment(phaseStartDate(submissionPhase));
5867
};
59-
const aRegistrationStartDate = getRegistrationStartDate(a);
60-
const bRegistrationStartDate = getRegistrationStartDate(b);
61-
return moment(bRegistrationStartDate).diff(aRegistrationStartDate);
68+
const aChallengeStartDate = getChallengeStartDate(a);
69+
const bChallengeStartDate = getChallengeStartDate(b);
70+
return bChallengeStartDate.diff(aChallengeStartDate);
6271
},
6372
name: 'Most recent',
6473
},
@@ -77,12 +86,20 @@ export default {
7786
[SORTS.TIME_TO_REGISTER]: {
7887
func: (a, b) => {
7988
const getRegistrationEndDate = (challenge) => {
89+
// extract the registration phase from `challenge.phases`,
90+
// as `challenge.registrationEndDate` returned from API is not reliable
8091
const registrationPhase = find(challenge.phases, p => p.name === 'Registration');
81-
return registrationPhase.actualEndDate || registrationPhase.scheduledEndDate;
92+
const submissionPhase = find(challenge.phases, p => p.name === 'Submission');
93+
// case 1: registration phase exists
94+
if (registrationPhase) {
95+
return moment(phaseEndDate(registrationPhase));
96+
}
97+
// case 2: registration phase doesn't exist. Take submission phase instead.
98+
return moment(phaseEndDate(submissionPhase));
8299
};
83100

84-
const aDate = moment(getRegistrationEndDate(a) || a.submissionEndTimestamp);
85-
const bDate = moment(getRegistrationEndDate(b) || b.submissionEndTimestamp);
101+
const aDate = getRegistrationEndDate(a);
102+
const bDate = getRegistrationEndDate(b);
86103

87104
if (aDate.isBefore() && bDate.isAfter()) return 1;
88105
if (aDate.isAfter() && bDate.isBefore()) return -1;
@@ -94,11 +111,23 @@ export default {
94111
},
95112
[SORTS.TIME_TO_SUBMIT]: {
96113
func: (a, b) => {
97-
function nextSubEndDate(o) {
98-
if (o.checkpointSubmissionEndDate && moment(o.checkpointSubmissionEndDate).isAfter()) {
99-
return moment(o.checkpointSubmissionEndDate);
114+
function nextSubEndDate(challenge) {
115+
// extract the submission and checkpoint (if any) phases from `challenge.phases`,
116+
// as `challenge.submissionEndDate` returned from API is not reliable
117+
const checkpointPhase = find(challenge.phases, p => p.name === 'Checkpoint Submission');
118+
const submissionPhase = find(challenge.phases, p => p.name === 'Submission');
119+
// Case 1: challenge has checkpoint submission phase
120+
if (!!checkpointPhase === true) {
121+
// Case 1.1: checkpoint submission phase is still open.
122+
// then take the `scheduledEndDate` of this phase.
123+
// Case 1.2: checkpoint submission phase is closed
124+
// but its `scheduledStartDate` is a future date.
125+
// This means this phase is not yet started. Take the `scheduledEndDate` of this phase.
126+
if (checkpointPhase.isOpen || moment(checkpointPhase.scheduledStartDate).isAfter()) {
127+
return moment(checkpointPhase.scheduledEndDate);
128+
}
100129
}
101-
return moment(o.submissionEndTimestamp);
130+
return moment(phaseEndDate(submissionPhase));
102131
}
103132

104133
const aDate = nextSubEndDate(a);

0 commit comments

Comments
 (0)