);
}
-Card.defaultProps = {
- past: null,
-};
-
Card.propTypes = {
- past: PT.bool,
title: PT.string.isRequired,
- time: PT.string.isRequired,
+ start: PT.string.isRequired,
+ end: PT.string.isRequired,
+ showRange: PT.bool.isRequired,
};
diff --git a/src/shared/components/challenge-detail/Header/DeadlinesPanel/Card/style.scss b/src/shared/components/challenge-detail/Header/DeadlinesPanel/Card/style.scss
index d2ed40c6c5..575418ac4e 100644
--- a/src/shared/components/challenge-detail/Header/DeadlinesPanel/Card/style.scss
+++ b/src/shared/components/challenge-detail/Header/DeadlinesPanel/Card/style.scss
@@ -2,42 +2,76 @@
.open {
color: $tc-white;
+
+ svg {
+ path {
+ fill: $tc-white;
+ }
+ }
}
.past {
color: #aaa;
+
+ svg {
+ path {
+ fill: #aaa;
+ }
+ }
}
.open,
.past {
@include roboto-regular;
- margin-bottom: 10px;
- padding: 0 20px;
- border-left: 1px solid $tc-gray-40;
+ flex: 0 0 auto;
+ position: relative;
+ padding: 0 24px;
+ min-width: 200px;
+ margin-bottom: 24px;
+
+ &:not(:last-child)::after {
+ content: '';
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 2px;
+ height: 100%;
+ border-radius: 1px;
+ background-color: #767676;
+ }
@include xs-to-md {
- border-left: none;
- border-bottom: 1px solid $tc-gray-40;
- padding-top: 16px;
- padding-bottom: 16px;
+ padding: 16px 0;
+ margin-bottom: 0;
- &:last-child {
- border-bottom: none;
+ &:not(:last-child)::after {
+ height: 2px;
+ width: 100%;
+ top: unset;
+ bottom: 0;
}
}
- &:first-child {
- border-left: none;
+ svg {
+ height: 13.13px;
+ width: 13.13px;
}
}
.date {
- font-size: 20px;
- letter-spacing: -0.5px;
- line-height: 25px;
- font-weight: 500;
- margin-top: 7px;
+ @include roboto-medium;
+
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ line-height: 24px;
+ margin-bottom: 4px;
+
+ @include xs-to-md {
+ font-size: 14px;
+ line-height: 20px;
+ }
span {
margin-left: 8px;
@@ -45,38 +79,61 @@
}
.time {
- font-size: 20px;
- letter-spacing: -0.5px;
- line-height: 25px;
- font-weight: 500;
- margin-top: 4px;
+ @include roboto-medium;
+
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ line-height: 24px;
+
+ @include xs-to-md {
+ font-size: 14px;
+ line-height: 20px;
+ }
span {
margin-left: 8px;
}
}
-.open .date {
- color: $tc-white;
+.title {
+ @include roboto-bold;
- svg {
- path: {
- fill: $tc-white;
- }
+ font-size: 12px;
+ letter-spacing: 1px;
+ line-height: 16px;
+ margin-bottom: 16px;
+ text-transform: uppercase;
+
+ @include xs-to-md {
+ font-size: 10px;
+ line-height: 12px;
+ margin-bottom: 20px;
}
}
-.past .date { color: #aaa; }
-.title {
- font-size: 12px;
- font-weight: 700;
- letter-spacing: 0;
- color: #fff;
- line-height: 15px;
- margin-bottom: 5px;
- text-transform: uppercase;
+.sections {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+
+ @include xs-to-md {
+ flex-direction: row;
+ gap: 24px;
+ }
}
-.past .title {
- color: $tc-gray-40;
+.section {
+ @include xs-to-md {
+ flex: 1;
+ }
+}
+
+.section-title {
+ @include roboto-medium;
+
+ display: inline-block;
+ font-size: 11px;
+ line-height: 10px;
+ margin-bottom: 4px;
}
diff --git a/src/shared/components/challenge-detail/Header/DeadlinesPanel/index.jsx b/src/shared/components/challenge-detail/Header/DeadlinesPanel/index.jsx
index 710a5bf509..7eb853efd1 100644
--- a/src/shared/components/challenge-detail/Header/DeadlinesPanel/index.jsx
+++ b/src/shared/components/challenge-detail/Header/DeadlinesPanel/index.jsx
@@ -11,29 +11,62 @@ import Card from './Card';
import './style.scss';
export default function DeadlinesPanel({ deadlines }) {
- /* Calculates challenge start time. */
- let start = deadlines[0] || {};
- start = phaseStartDate(start);
- const started = moment(start).isBefore();
+ let hasSubmissionPhase = false;
+
+ const getCardProps = (deadline, index) => {
+ let { name } = deadline;
+ let showRange = true;
+ name = name.replace(/\bCheckpoint\b/, 'Checkpoint');
+ if (/.+submission/i.test(name)) {
+ hasSubmissionPhase = true;
+ name = name.replace(/submission/i, 'Submission');
+ } else {
+ switch (name) {
+ case 'Submission':
+ name = hasSubmissionPhase ? 'Final Submission' : 'Submission';
+ break;
+ case 'Review':
+ name = hasSubmissionPhase ? 'Final Review' : name;
+ break;
+ case 'Appeals':
+ name = hasSubmissionPhase ? 'Appeals Due' : name;
+ break;
+ default:
+ }
+ }
+ if (index === deadlines.length - 1) {
+ name = 'Winners Announced';
+ showRange = false;
+ }
+
+ const start = phaseStartDate(deadline);
+ const end = phaseEndDate(deadline);
+
+ return {
+ name, start, end, showRange,
+ };
+ };
return (
-
+
Timezone:
{moment.tz.guess()}
-
- { deadlines.map((d, index) => (
-
- ))}
+ { deadlines.map((d, index) => {
+ const {
+ name, start, end, showRange,
+ } = getCardProps(d, index);
+ return (
+
+ );
+ })}
);
}
diff --git a/src/shared/components/challenge-detail/Header/DeadlinesPanel/style.scss b/src/shared/components/challenge-detail/Header/DeadlinesPanel/style.scss
index 8f814362a1..982af5ffb6 100644
--- a/src/shared/components/challenge-detail/Header/DeadlinesPanel/style.scss
+++ b/src/shared/components/challenge-detail/Header/DeadlinesPanel/style.scss
@@ -5,14 +5,16 @@
display: flex;
flex-wrap: wrap;
justify-content: space-between;
- padding: 30px 0 15px 0;
+ padding: 24px 0 34px 0;
position: relative;
+ margin-bottom: -24px;
@include xs-to-md {
flex-direction: column;
padding-top: 0;
padding-right: 16px;
padding-left: 16px;
+ margin-bottom: 0;
}
&.left {
@@ -21,10 +23,16 @@
}
.timezone {
- @include tc-label-sm;
+ @include roboto-regular;
- color: $tc-gray-40;
+ color: #d4d4d4;
+ font-size: 12px;
+ line-height: 18px;
position: absolute;
- right: 12px;
- bottom: 6px;
+ right: 24px;
+ bottom: 8px;
+
+ @include xs-to-md {
+ right: 16px;
+ }
}
diff --git a/src/shared/components/challenge-detail/Header/index.jsx b/src/shared/components/challenge-detail/Header/index.jsx
index 08a055d3a4..9898b37059 100644
--- a/src/shared/components/challenge-detail/Header/index.jsx
+++ b/src/shared/components/challenge-detail/Header/index.jsx
@@ -12,6 +12,7 @@ import { isMM } from 'utils/challenge';
import PT from 'prop-types';
import React from 'react';
+import { useMediaQuery } from 'react-responsive';
import { PrimaryButton } from 'topcoder-react-ui-kit';
import { Link } from 'topcoder-react-utils';
import { COMPETITION_TRACKS } from 'utils/tc';
@@ -29,10 +30,6 @@ import TabSelector from './TabSelector';
import style from './style.scss';
-/* Holds day and hour range in ms. */
-const HOUR_MS = 60 * 60 * 1000;
-const DAY_MS = 24 * HOUR_MS;
-
export default function ChallengeHeader(props) {
const {
isLoggedIn,
@@ -50,7 +47,7 @@ export default function ChallengeHeader(props) {
unregistering,
challengeTypesMap,
selectedView,
- showDeadlineDetail,
+ showDeadlineDetail: showDeadlineDetailProp,
hasFirstPlacement,
hasThriveArticles,
hasRecommendedChallenges,
@@ -78,9 +75,12 @@ export default function ChallengeHeader(props) {
track,
} = challenge;
+ const desktop = useMediaQuery({ minWidth: 1024 });
+ const showDeadlineDetail = desktop || showDeadlineDetailProp;
+
const tags = challenge.tags || [];
- const allPhases = challenge.phases || [];
+ const allPhases = _.filter(challenge.phases || [], p => p.name !== 'Post-Mortem');
const sortedAllPhases = _.cloneDeep(allPhases)
.sort((a, b) => moment(phaseEndDate(a)).diff(phaseEndDate(b)));
@@ -128,26 +128,6 @@ export default function ChallengeHeader(props) {
*/
const hasSubmissions = !_.isEmpty(mySubmissions);
- const openPhases = sortedAllPhases.filter(p => p.isOpen);
- let nextPhase = openPhases[0];
- if (hasRegistered && openPhases[0] && openPhases[0].name === 'Registration') {
- nextPhase = openPhases[1] || {};
- }
- const nextDeadline = nextPhase && nextPhase.name;
-
- const deadlineEnd = moment(nextPhase && phaseEndDate(nextPhase));
- const currentTime = moment();
-
- let timeLeft = deadlineEnd.isAfter(currentTime)
- ? deadlineEnd.diff(currentTime) : 0;
-
- let format;
- if (timeLeft > DAY_MS) format = 'D[d] H[h]';
- else if (timeLeft > HOUR_MS) format = 'H[h] m[min]';
- else format = 'm[min] s[s]';
-
- timeLeft = moment.duration(timeLeft).format(format);
-
let relevantPhases = [];
if (showDeadlineDetail) {
@@ -156,15 +136,7 @@ export default function ChallengeHeader(props) {
const end = phaseEndDate(phase);
return moment(end).isAfter();
}
- const phaseLowerCase = phase.name.toLowerCase();
- if (phaseLowerCase.includes('screening') || phaseLowerCase.includes('specification')) {
- return false;
- }
- if (phaseLowerCase.includes('registration') || phaseLowerCase.includes('checkpoint')
- || phaseLowerCase.includes('submission') || phaseLowerCase.includes('review')) {
- return true;
- }
- return false;
+ return true;
});
relevantPhases.sort((a, b) => {
@@ -178,6 +150,26 @@ export default function ChallengeHeader(props) {
const bEndDate = phaseEndDate(b);
return moment(aEndDate).diff(bEndDate);
});
+
+ if (trackLower === 'design') {
+ // condition for 2 round challenge for now
+ let finalStartDate;
+ if (relevantPhases.length === 8 || challenge.timelineTemplateId === 'd4201ca4-8437-4d63-9957-3f7708184b07') {
+ relevantPhases = _.filter(relevantPhases, p => !(p.name.toLowerCase().includes('checkpoint screening') || p.name.toLowerCase().includes('screening')));
+ _.map(relevantPhases, (phase) => {
+ if (phase.name === 'Checkpoint Review') {
+ finalStartDate = phase.scheduledEndDate;
+ }
+ if (phase.name === 'Submission') {
+ // eslint-disable-next-line no-param-reassign
+ phase.scheduledStartDate = finalStartDate;
+ }
+ });
+ }
+ }
+ if (trackLower === 'quality-assurance') {
+ relevantPhases = _.filter(relevantPhases, p => !(p.name.toLowerCase().includes('specification submission') || p.name.toLowerCase().includes('specification review')));
+ }
if (type === 'First2Finish' && status === 'Completed') {
const phases2 = allPhases.filter(p => p.name === 'Iterative Review' && !p.isOpen);
const endPhaseDate = Math.max(...phases2.map(d => phaseEndDate(d)));
@@ -210,41 +202,6 @@ export default function ChallengeHeader(props) {
const checkpointCount = checkpoints && checkpoints.numberOfPassedScreeningSubmissions;
- let nextDeadlineMsg;
- switch ((status || '').toLowerCase()) {
- case 'active':
- nextDeadlineMsg = (
-
- Next Deadline:
- {' '}
- {
-
- {nextDeadline || '-'}
-
- }
-
- );
- break;
- case 'completed':
- nextDeadlineMsg = (
-
- The challenge is finished.
-
- );
- break;
- default:
- nextDeadlineMsg = (
-
- Status:
-
-
- {_.upperFirst(_.lowerCase(status))}
-
-
- );
- break;
- }
-
// Legacy MMs have a roundId field, but new MMs do not.
// This is used to disable registration/submission for legacy MMs.
const isLegacyMM = isMM(challenge) && Boolean(challenge.roundId);
@@ -425,18 +382,7 @@ export default function ChallengeHeader(props) {
- {nextDeadlineMsg}
- {
- (status || '').toLowerCase() === 'active'
- && (
-
- Current Deadline Ends:{' '}
-
- {timeLeft}
-
-
- )
- }
+ Competition Timeline