@@ -17,6 +17,9 @@ import { PrimaryButton } from 'topcoder-react-ui-kit';
1717import { Link } from 'topcoder-react-utils' ;
1818import { COMPETITION_TRACKS } from 'utils/tc' ;
1919import { phaseEndDate } from 'utils/challenge-listing/helper' ;
20+ import {
21+ getTimeLeft ,
22+ } from 'utils/challenge-detail/helper' ;
2023
2124import LeftArrow from 'assets/images/arrow-prev.svg' ;
2225
@@ -30,6 +33,10 @@ import TabSelector from './TabSelector';
3033
3134import style from './style.scss' ;
3235
36+ /* Holds day and hour range in ms. */
37+ const HOUR_MS = 60 * 60 * 1000 ;
38+ const DAY_MS = 24 * HOUR_MS ;
39+
3340export default function ChallengeHeader ( props ) {
3441 const {
3542 isLoggedIn,
@@ -108,6 +115,10 @@ export default function ChallengeHeader(props) {
108115 registrationEnded = ! regPhase . isOpen ;
109116 }
110117
118+ const currentPhases = challenge . phases
119+ . filter ( p => p . name !== 'Registration' && p . isOpen )
120+ . sort ( ( a , b ) => moment ( a . scheduledEndDate ) . diff ( b . scheduledEndDate ) ) [ 0 ] ;
121+
111122 const trackLower = track ? track . replace ( ' ' , '-' ) . toLowerCase ( ) : 'design' ;
112123
113124 const eventNames = ( events || [ ] ) . map ( ( event => ( event . eventName || '' ) . toUpperCase ( ) ) ) ;
@@ -128,6 +139,31 @@ export default function ChallengeHeader(props) {
128139 */
129140 const hasSubmissions = ! _ . isEmpty ( mySubmissions ) ;
130141
142+ const openPhases = sortedAllPhases . filter ( p => p . isOpen ) ;
143+ let nextPhase = openPhases [ 0 ] ;
144+ if ( hasRegistered && openPhases [ 0 ] && openPhases [ 0 ] . name === 'Registration' ) {
145+ nextPhase = openPhases [ 1 ] || { } ;
146+ }
147+
148+ const deadlineEnd = moment ( nextPhase && phaseEndDate ( nextPhase ) ) ;
149+ const currentTime = moment ( ) ;
150+
151+ const timeDiff = getTimeLeft ( currentPhases , 'to go' ) ;
152+
153+ if ( ! timeDiff . late ) {
154+ timeDiff . text = timeDiff . text . replace ( 'to go' , '' ) ;
155+ }
156+
157+ let timeLeft = deadlineEnd . isAfter ( currentTime )
158+ ? deadlineEnd . diff ( currentTime ) : 0 ;
159+
160+ let format ;
161+ if ( timeLeft > DAY_MS ) format = 'D[d] H[h]' ;
162+ else if ( timeLeft > HOUR_MS ) format = 'H[h] m[min]' ;
163+ else format = 'm[min] s[s]' ;
164+
165+ timeLeft = moment . duration ( timeLeft ) . format ( format ) ;
166+
131167 let relevantPhases = [ ] ;
132168
133169 if ( showDeadlineDetail ) {
@@ -177,31 +213,52 @@ export default function ChallengeHeader(props) {
177213 || phaseEndDate ( p ) . getTime ( ) < endPhaseDate ) ) ;
178214 relevantPhases . push ( {
179215 id : - 1 ,
180- name : 'Winners' ,
216+ name : 'Winners Announced ' ,
181217 isOpen : false ,
182218 actualEndDate : endPhaseDate ,
183219 scheduledEndDate : endPhaseDate ,
184220 } ) ;
185221 } else if ( relevantPhases . length > 1 ) {
186- const lastPhase = relevantPhases [ relevantPhases . length - 1 ] ;
187- const lastPhaseTime = phaseEndDate ( lastPhase ) . getTime ( ) ;
188-
222+ // const lastPhase = relevantPhases[relevantPhases.length - 1];
223+ // const lastPhaseTime = phaseEndDate(lastPhase).getTime();
189224 const appealsEndDate = phaseEndDate ( sortedAllPhases [ sortedAllPhases . length - 1 ] ) ;
190- const appealsEnd = appealsEndDate . getTime ( ) ;
191- if ( lastPhaseTime < appealsEnd ) {
192- relevantPhases . push ( {
193- id : - 1 ,
194- name : 'Winners' ,
195- isOpen : false ,
196- actualEndDate : appealsEndDate ,
197- scheduledEndDate : appealsEndDate ,
198- } ) ;
199- }
225+ // const appealsEnd = appealsEndDate.getTime();
226+ relevantPhases . push ( {
227+ id : - 1 ,
228+ name : 'Winners Announced' ,
229+ isOpen : false ,
230+ actualEndDate : appealsEndDate ,
231+ scheduledEndDate : appealsEndDate ,
232+ } ) ;
200233 }
201234 }
202235
203236 const checkpointCount = checkpoints && checkpoints . numberOfPassedScreeningSubmissions ;
204237
238+ let nextDeadlineMsg ;
239+ switch ( ( status || '' ) . toLowerCase ( ) ) {
240+ case 'completed' :
241+ nextDeadlineMsg = (
242+ < div styleName = "completed" >
243+ The challenge is finished.
244+ </ div >
245+ ) ;
246+ break ;
247+ case 'active' :
248+ break ;
249+ default :
250+ nextDeadlineMsg = (
251+ < div >
252+ Status:
253+ ‌
254+ < span styleName = "deadline-highlighted" >
255+ { _ . upperFirst ( _ . lowerCase ( status ) ) }
256+ </ span >
257+ </ div >
258+ ) ;
259+ break ;
260+ }
261+
205262 // Legacy MMs have a roundId field, but new MMs do not.
206263 // This is used to disable registration/submission for legacy MMs.
207264 const isLegacyMM = isMM ( challenge ) && Boolean ( challenge . roundId ) ;
@@ -382,7 +439,18 @@ export default function ChallengeHeader(props) {
382439 < div styleName = "deadlines-view" >
383440 < div styleName = { `deadlines-overview ${ showDeadlineDetail ? 'opened' : '' } ` } >
384441 < div styleName = "deadlines-overview-text" >
385- Competition Timeline
442+ { nextDeadlineMsg }
443+ {
444+ ( status || '' ) . toLowerCase ( ) === 'active'
445+ && (
446+ < div styleName = "current-phase" >
447+ { currentPhases && `${ currentPhases . name } Ends: ` }
448+ < span styleName = "deadline-highlighted" >
449+ { timeDiff . text }
450+ </ span >
451+ </ div >
452+ )
453+ }
386454 </ div >
387455 < a
388456 onClick = { onToggleDeadlines }
@@ -462,7 +530,6 @@ ChallengeHeader.propTypes = {
462530 timelineTemplateId : PT . string ,
463531 reliabilityBonus : PT . any ,
464532 userDetails : PT . any ,
465- currentPhases : PT . any ,
466533 numOfRegistrants : PT . any ,
467534 numOfCheckpointSubmissions : PT . any ,
468535 numOfSubmissions : PT . any ,
0 commit comments