diff --git a/__tests__/shared/actions/challenge-listing/index.js b/__tests__/shared/actions/challenge-listing/index.js index 7656142c2c..eac761a013 100644 --- a/__tests__/shared/actions/challenge-listing/index.js +++ b/__tests__/shared/actions/challenge-listing/index.js @@ -82,7 +82,7 @@ describe('challengeListing.getPastChallengesInit', () => { }); describe('challengeListing.getChallengeSubtracksDone', () => { - global.fetch = mockFetch(true, [{ description: 'dummy' }]); + global.fetch = mockFetch(true, { result: { status: 200, content: [{ description: 'dummy' }] } }); const a = actions.getChallengeSubtracksDone(); @@ -91,7 +91,7 @@ describe('challengeListing.getChallengeSubtracksDone', () => { }); test('payload is a promise which resolves to the expected object', () => - a.payload.then(res => expect(res).toEqual(['dummy', 'dummy']))); + a.payload.then(res => expect(res).toEqual([{ description: 'dummy' }]))); }); describe('challengeListing.getChallengeTagsDone', () => { diff --git a/__tests__/shared/components/SubmissionManagement/__snapshots__/Submission.jsx.snap b/__tests__/shared/components/SubmissionManagement/__snapshots__/Submission.jsx.snap index 7032d4a3b4..c597906f97 100644 --- a/__tests__/shared/components/SubmissionManagement/__snapshots__/Submission.jsx.snap +++ b/__tests__/shared/components/SubmissionManagement/__snapshots__/Submission.jsx.snap @@ -27,7 +27,7 @@ exports[`Snapshot match 1`] = ` className="src-shared-components-SubmissionManagement-Submission-___styles__action-col___2M1RY" > @@ -77,7 +77,7 @@ exports[`Snapshot match 2`] = ` className="src-shared-components-SubmissionManagement-Submission-___styles__action-col___2M1RY" > diff --git a/__tests__/shared/components/SubmissionManagement/__snapshots__/SubmissionManagement.jsx.snap b/__tests__/shared/components/SubmissionManagement/__snapshots__/SubmissionManagement.jsx.snap index 41fbddbe59..f4eb777deb 100644 --- a/__tests__/shared/components/SubmissionManagement/__snapshots__/SubmissionManagement.jsx.snap +++ b/__tests__/shared/components/SubmissionManagement/__snapshots__/SubmissionManagement.jsx.snap @@ -26,20 +26,22 @@ exports[`Matches shallow shapshot 1`] = `

-

- - 0 - H - 0 - M -

-

- left -

+
+

+ + 0 + H + 0 + M +

+

+ left +

+
- Filter - Filter + + 3 +
+ + +
+
}
- - - + { /* TODO: At the moment we just fetch downloads from the legacy Topcoder Studio API, and we don't need any JS code to this. diff --git a/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx b/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx index de38655fa3..04ba96e3a4 100644 --- a/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx +++ b/src/shared/components/SubmissionManagement/SubmissionManagement/index.jsx @@ -72,10 +72,18 @@ export default function SubmissionManagement(props) {

{currentPhase.phaseType}

-

- {days > 0 && (`${days}D`)} {hours}H {minutes}M -

-

left

+ { + challenge.status !== 'COMPLETED' ? ( +
+

+ {days > 0 && (`${days}D`)} {hours}H {minutes}M +

+

left

+
+ ) : ( +

The challenge has ended

+ ) + }
@@ -106,16 +114,18 @@ export default function SubmissionManagement(props) { /> }
-
- - {(!isDevelop || !submissions || submissions.length === 0) - ? 'Add Submission' : 'Update Submission'} - -
+ {now.isBefore(challenge.submissionEndDate) && +
+ + {(!isDevelop || !submissions || submissions.length === 0) + ? 'Add Submission' : 'Update Submission'} + +
+ } ); } diff --git a/src/shared/components/TopcoderHeader/Auth/index.jsx b/src/shared/components/TopcoderHeader/Auth/index.jsx index 7468532285..da6b592164 100644 --- a/src/shared/components/TopcoderHeader/Auth/index.jsx +++ b/src/shared/components/TopcoderHeader/Auth/index.jsx @@ -20,6 +20,9 @@ export default function Auth({ column }) {
{ event.stopPropagation(); }} + role="button" + tabIndex="0" > {

Empty Feedback', }} /> } diff --git a/src/shared/components/challenge-detail/Checkpoints/styles.scss b/src/shared/components/challenge-detail/Checkpoints/styles.scss index ecbd33115b..4adef10ae0 100644 --- a/src/shared/components/challenge-detail/Checkpoints/styles.scss +++ b/src/shared/components/challenge-detail/Checkpoints/styles.scss @@ -109,6 +109,10 @@ $tc-gray-border: #aaaab5; color: $tc-gray-90; line-height: 25px; padding: 15px 15px 25px; + + span { + color: $tc-gray-40; + } } } } diff --git a/src/shared/components/challenge-detail/Header/ChallengeTags.jsx b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx index adbabfa64b..47c1574254 100644 --- a/src/shared/components/challenge-detail/Header/ChallengeTags.jsx +++ b/src/shared/components/challenge-detail/Header/ChallengeTags.jsx @@ -8,6 +8,7 @@ import React from 'react'; import PT from 'prop-types'; +import _ from 'lodash'; import { Link } from 'utils/router'; import './style.scss'; @@ -20,8 +21,17 @@ export default function ChallengeTags(props) { subTrackStyle, eventStyle, setChallengeListingFilter, + challengeSubtracksMap, } = props; + const stylizedSubTrack = (t) => { + if (challengeSubtracksMap[t]) { + return challengeSubtracksMap[t].name; + } + return (t || '').replace(/_/g, ' ') + .replace(/\w\S*/g, txt => _.capitalize(txt)); + }; + return (

{ @@ -30,7 +40,7 @@ export default function ChallengeTags(props) { onClick={() => setChallengeListingFilter({ subtracks: [subTrack] })} to="/challenges" styleName={`tag-common ${subTrackStyle}`} - >{subTrack} + >{stylizedSubTrack(subTrack)} } { events.map(event => ( @@ -71,4 +81,5 @@ ChallengeTags.propTypes = { subTrackStyle: PT.string.isRequired, eventStyle: PT.string.isRequired, setChallengeListingFilter: PT.func.isRequired, + challengeSubtracksMap: PT.shape().isRequired, }; diff --git a/src/shared/components/challenge-detail/Header/Prizes.jsx b/src/shared/components/challenge-detail/Header/Prizes.jsx index b3abf3bb3b..cea928a045 100644 --- a/src/shared/components/challenge-detail/Header/Prizes.jsx +++ b/src/shared/components/challenge-detail/Header/Prizes.jsx @@ -19,6 +19,7 @@ export default function Prizes({ prizes }) { { (prizes && prizes.length) ? prizes.map((prize, index) => { + if (!prize) return null; const rank = index + 1; return (
@@ -29,7 +30,7 @@ export default function Prizes({ prizes }) {

$ - {prize} + {prize.toLocaleString()}

diff --git a/src/shared/components/challenge-detail/Header/TabSelector/index.jsx b/src/shared/components/challenge-detail/Header/TabSelector/index.jsx index 43a0bc17ea..096243fddf 100644 --- a/src/shared/components/challenge-detail/Header/TabSelector/index.jsx +++ b/src/shared/components/challenge-detail/Header/TabSelector/index.jsx @@ -21,6 +21,7 @@ export default function ChallengeViewSelector(props) { challenge, checkpointCount, numRegistrants, + numSubmissions, onSelectorClicked, selectedView, status, @@ -61,8 +62,7 @@ export default function ChallengeViewSelector(props) {
{ e.preventDefault(); onSelectorClicked('SUBMISSIONS'); }} styleName={getSelectorStyle(selectedView, 'SUBMISSIONS', trackLower)} - > - SUBMISSIONS + >SUBMISSIONS ({numSubmissions}) } { @@ -102,6 +102,7 @@ ChallengeViewSelector.propTypes = { }), checkpointCount: PT.number, numRegistrants: PT.number, + numSubmissions: PT.number, onSelectorClicked: PT.func.isRequired, selectedView: PT.string.isRequired, status: PT.string.isRequired, diff --git a/src/shared/components/challenge-detail/Header/index.jsx b/src/shared/components/challenge-detail/Header/index.jsx index 0276da2504..884ef7d121 100755 --- a/src/shared/components/challenge-detail/Header/index.jsx +++ b/src/shared/components/challenge-detail/Header/index.jsx @@ -34,6 +34,7 @@ export default function ChallengeHeader(props) { setChallengeListingFilter, unregisterFromChallenge, unregistering, + challengeSubtracksMap, } = props; const { @@ -66,8 +67,6 @@ export default function ChallengeHeader(props) { const theme = themeFactory(trackLower); - const stylizedSubTrack = (subTrack || '').replace('_', ' ') - .replace(/\w\S*/g, txt => _.capitalize(txt)); const subTrackStyle = `${trackLower}-accent-background`; const eventStyle = `${trackLower}-accent-color`; const eventNames = (events || []).map((event => (event.eventName || '').toUpperCase())); @@ -82,9 +81,10 @@ export default function ChallengeHeader(props) { const registrationEnded = new Date(registrationEndDate).getTime() < Date.now() || status.toLowerCase() !== 'active'; const submissionEnded = new Date(submissionEndDate).getTime() < Date.now(); const hasSubmissions = userDetails && userDetails.hasUserSubmittedForReview; - const nextDeadline = currentPhases && currentPhases.length > 0 && currentPhases[0].phaseType; + const nextPhaseIndex = hasRegistered ? 1 : 0; + const nextDeadline = currentPhases.length > 0 && currentPhases[nextPhaseIndex].phaseType; const deadlineEnd = currentPhases && currentPhases.length > 0 ? - new Date(currentPhases[0].scheduledEndTime).getTime() : Date.now(); + new Date(currentPhases[nextPhaseIndex].scheduledEndTime).getTime() : Date.now(); const currentTime = Date.now(); const timeDiff = deadlineEnd > currentTime ? deadlineEnd - currentTime : 0; const duration = moment.duration(timeDiff); @@ -104,6 +104,11 @@ export default function ChallengeHeader(props) { return false; }); + relevantPhases.push({ + phaseType: 'Registration', + scheduledEndTime: registrationEndDate, + }); + relevantPhases.sort((a, b) => { if (a.phaseType.toLowerCase().includes('registration')) { return -1; @@ -175,7 +180,8 @@ export default function ChallengeHeader(props) {

{name}

{props.showDeadlineDetail ? Hide Deadlines - : View All Deadlines + : Show Deadlines }
@@ -297,4 +303,5 @@ ChallengeHeader.propTypes = { showDeadlineDetail: PT.bool.isRequired, unregisterFromChallenge: PT.func.isRequired, unregistering: PT.bool.isRequired, + challengeSubtracksMap: PT.shape().isRequired, }; diff --git a/src/shared/components/challenge-detail/Header/style.scss b/src/shared/components/challenge-detail/Header/style.scss index e17de18e99..f784517963 100644 --- a/src/shared/components/challenge-detail/Header/style.scss +++ b/src/shared/components/challenge-detail/Header/style.scss @@ -253,6 +253,7 @@ color: $tc-black; line-height: 25px; margin: 25px 15px 0; + padding-bottom: 20px; .bonus-text { .bonus-highlight { diff --git a/src/shared/components/challenge-detail/Registrants/index.jsx b/src/shared/components/challenge-detail/Registrants/index.jsx index 3feff9d481..9071018ab0 100644 --- a/src/shared/components/challenge-detail/Registrants/index.jsx +++ b/src/shared/components/challenge-detail/Registrants/index.jsx @@ -90,7 +90,7 @@ export default function Registrants(props) {
}
-
Round 2 Submitted Date
+
{isDesign ? 'Round 2 ' : ''}Submitted Date
{formatDate(getDate(submissions, r.handle) || r.submissionDate)} diff --git a/src/shared/components/challenge-detail/Specification/styles.scss b/src/shared/components/challenge-detail/Specification/styles.scss index 8d51cb67cb..9671b814a9 100644 --- a/src/shared/components/challenge-detail/Specification/styles.scss +++ b/src/shared/components/challenge-detail/Specification/styles.scss @@ -57,6 +57,10 @@ $tc-link-visited: #0c4e98; padding-left: 17px; } } + + img { + max-width: 100%; + } } } @@ -146,6 +150,7 @@ $tc-link-visited: #0c4e98; line-height: 20px; margin: (2 * $base-unit) 0 (3 * $base-unit); padding: 3 * $base-unit; + display: block; } pre { diff --git a/src/shared/components/challenge-detail/Submissions/index.jsx b/src/shared/components/challenge-detail/Submissions/index.jsx index 767f6d48a2..d84c99412f 100644 --- a/src/shared/components/challenge-detail/Submissions/index.jsx +++ b/src/shared/components/challenge-detail/Submissions/index.jsx @@ -19,7 +19,7 @@ function renderSubmission(s) {
{`#${s.submissionId}`} - {s.submitter} + {s.submitter}
{`${moment(s.submissionTime).format('MMM DD,YYYY HH:mm')} EDT`}
diff --git a/src/shared/components/challenge-detail/Submissions/style.scss b/src/shared/components/challenge-detail/Submissions/style.scss index 496ddb6a93..2dc9b8f5cc 100644 --- a/src/shared/components/challenge-detail/Submissions/style.scss +++ b/src/shared/components/challenge-detail/Submissions/style.scss @@ -32,6 +32,7 @@ line-height: 30px; color: $tc-black; margin-bottom: 10px; + text-align: center; @include xxs-to-xs { font-size: 28px; @@ -76,6 +77,7 @@ font-size: 16px; line-height: 22px; font-weight: 500; + text-align: center; @include xxs-to-xs { text-align: center; @@ -85,15 +87,12 @@ .content { display: flex; flex-wrap: wrap; - - @include xxs-to-xs { - justify-content: center; - } + justify-content: center; } .submission { border: 1px solid $tc-gray-10; - margin: 0 17px 25px; + margin: 0 14px 25px; width: 227px; font-size: 12px; diff --git a/src/shared/components/challenge-detail/Winners/index.jsx b/src/shared/components/challenge-detail/Winners/index.jsx index fb61396256..34e2ea54bb 100644 --- a/src/shared/components/challenge-detail/Winners/index.jsx +++ b/src/shared/components/challenge-detail/Winners/index.jsx @@ -43,7 +43,7 @@ export default function Winners(props) { 'place-1': w.placement === 1, 'place-2': w.placement === 2, 'place-3': w.placement === 3 })} - key={w.handle} + key={submissionId} >
{w.placement}
@@ -76,11 +76,11 @@ export default function Winners(props) {
ID: #{getId(submissions, w)}
} { - w.submissionDownloadLink && + (w.submissionDownloadLink && viewable) && Download }
diff --git a/src/shared/components/challenge-listing/ChallengeCard/Prize/style.scss b/src/shared/components/challenge-listing/ChallengeCard/Prize/style.scss index 7fbf2fa4c7..0e15d76262 100644 --- a/src/shared/components/challenge-listing/ChallengeCard/Prize/style.scss +++ b/src/shared/components/challenge-listing/ChallengeCard/Prize/style.scss @@ -9,7 +9,7 @@ white-space: nowrap; @include xxs-to-xs { - margin-left: 10px; + display: inline-block; } } @@ -20,5 +20,4 @@ .symbol { display: inline-block; color: $tc-gray-50; - min-width: 2 * $base-unit + 1; } diff --git a/src/shared/components/challenge-listing/ChallengeCard/index.jsx b/src/shared/components/challenge-listing/ChallengeCard/index.jsx index bf8d0f2f34..40c2633cd7 100644 --- a/src/shared/components/challenge-listing/ChallengeCard/index.jsx +++ b/src/shared/components/challenge-listing/ChallengeCard/index.jsx @@ -25,7 +25,14 @@ const VISIBLE_TECHNOLOGIES = 3; const ID_LENGTH = 6; // Get the End date of a challenge -const getEndDate = date => moment(date).format('MMM DD'); +const getEndDate = (c) => { + let phases = c.allPhases; + if (c.subTrack === 'FIRST_2_FINISH' && c.status === 'COMPLETED') { + phases = c.allPhases.filter(p => p.phaseType === 'Iterative Review' && p.phaseStatus === 'Closed'); + } + const endPhaseDate = Math.max(...phases.map(d => new Date(d.scheduledEndTime))); + return moment(endPhaseDate).format('MMM DD'); +}; /* TODO: Note that this component uses a dirty trick to cheat linter and to be * able to modify an argument: it aliases challenge prop, then mutates it in @@ -133,7 +140,7 @@ function ChallengeCard({
{challenge.status === 'ACTIVE' ? 'Ends ' : 'Ended '} - {getEndDate(challenge.submissionEndDate)} + {getEndDate(challenge)} - Filter + { + filterRulesCount ? ( + {filterRulesCount} + ) : null + } false} onDatesChange={this.props.onDatesChange} diff --git a/src/shared/components/challenge-listing/Filters/EditTrackPanel/index.jsx b/src/shared/components/challenge-listing/Filters/EditTrackPanel/index.jsx index ef2d8cb88d..0e4f419374 100644 --- a/src/shared/components/challenge-listing/Filters/EditTrackPanel/index.jsx +++ b/src/shared/components/challenge-listing/Filters/EditTrackPanel/index.jsx @@ -47,7 +47,7 @@ const EditTrackPanel = props => ( />
- Data Science + Data Science ({ label: item, value: item }); - + const mapSubtracks = item => ({ label: item.name, value: item.subTrack }); return (
@@ -80,6 +80,8 @@ export default function FiltersPanel({