Skip to content

Commit af2ee4f

Browse files
authored
Merge pull request #1452 from topcoder-platform/multiround
Multiround
2 parents 68a32ee + c29f228 commit af2ee4f

File tree

18 files changed

+524
-43
lines changed

18 files changed

+524
-43
lines changed

.circleci/config.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ parameters:
99

1010
defaults: &defaults
1111
docker:
12-
- image: circleci/python:2.7-stretch-browsers
12+
- image: cimg/python:3.11.0-browsers
1313

1414
test_defaults: &test_defaults
1515
docker:
@@ -19,8 +19,10 @@ install_dependency: &install_dependency
1919
name: Installation of build and deployment dependencies.
2020
command: |
2121
sudo apt install jq
22-
sudo pip install awscli --upgrade
23-
sudo pip install docker-compose
22+
sudo apt update
23+
sudo apt install python3-pip
24+
sudo pip3 install awscli --upgrade
25+
sudo pip3 install docker-compose
2426
2527
install_test_dependency: &install_test_dependency
2628
name: Installation of build and deployment dependencies.
@@ -150,7 +152,7 @@ workflows:
150152
context : org-global
151153
filters: &filters-dev
152154
branches:
153-
only: ['develop', 'Oct_2022_Release']
155+
only: ['develop', 'multiround']
154156

155157
# Production builds are exectuted only on tagged commits to the
156158
# master branch.

config/constants/development.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = {
3333
DES_TRACK_ID: '5fa04185-041f-49a6-bfd1-fe82533cd6c8',
3434
DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d',
3535
QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115',
36+
CP_TRACK_ID: '9d6e0de8-df14-4c76-ba0a-a9a8cb03a4ea',
3637
CHALLENGE_TYPE_ID: '927abff4-7af9-4145-8ba1-577c16e64e2e',
3738
MARATHON_TYPE_ID: '929bc408-9cf2-4b3e-ba71-adfbf693046c',
3839
SEGMENT_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n',
@@ -44,5 +45,6 @@ module.exports = {
4445
// if idle for this many minutes, show user a prompt saying they'll be logged out
4546
IDLE_TIMEOUT_MINUTES: 10,
4647
// duration to show the prompt saying user will be logged out, before actually logging out the user
47-
IDLE_TIMEOUT_GRACE_MINUTES: 5
48+
IDLE_TIMEOUT_GRACE_MINUTES: 5,
49+
MULTI_ROUND_CHALLENGE_TEMPLATE_ID: 'd4201ca4-8437-4d63-9957-3f7708184b07'
4850
}

config/constants/production.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ module.exports = {
3333
DES_TRACK_ID: '5fa04185-041f-49a6-bfd1-fe82533cd6c8',
3434
DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d',
3535
QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115',
36+
CP_TRACK_ID: '9d6e0de8-df14-4c76-ba0a-a9a8cb03a4ea',
3637
CHALLENGE_TYPE_ID: '927abff4-7af9-4145-8ba1-577c16e64e2e',
3738
MARATHON_TYPE_ID: '929bc408-9cf2-4b3e-ba71-adfbf693046c',
3839
SEGMENT_API_KEY: 'QSQAW5BWmZfLoKFNRgNKaqHvLDLJoGqF',
@@ -42,5 +43,6 @@ module.exports = {
4243
FILE_PICKER_REGION: 'us-east-1',
4344
FILE_PICKER_CNAME: 'fs.topcoder.com',
4445
IDLE_TIMEOUT_MINUTES: 10,
45-
IDLE_TIMEOUT_GRACE_MINUTES: 5
46+
IDLE_TIMEOUT_GRACE_MINUTES: 5,
47+
MULTI_ROUND_CHALLENGE_TEMPLATE_ID: 'd4201ca4-8437-4d63-9957-3f7708184b07'
4648
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
@import "../../../styles/includes";
2+
3+
.row {
4+
box-sizing: border-box;
5+
display: flex;
6+
flex-direction: row;
7+
margin: 30px 30px 0 30px;
8+
align-content: space-between;
9+
justify-content: flex-start;
10+
11+
.field {
12+
@include upto-sm {
13+
display: block;
14+
padding-bottom: 10px;
15+
}
16+
17+
label {
18+
@include roboto-bold();
19+
20+
font-size: 16px;
21+
line-height: 19px;
22+
font-weight: 500;
23+
color: $tc-gray-80;
24+
}
25+
26+
&.col1 {
27+
max-width: 185px;
28+
min-width: 185px;
29+
margin-right: 14px;
30+
white-space: nowrap;
31+
display: flex;
32+
align-items: center;
33+
flex-grow: 1;
34+
35+
span {
36+
color: $tc-red;
37+
}
38+
}
39+
40+
&.col2.error {
41+
color: $tc-red;
42+
margin-top: -25px;
43+
}
44+
&.col2 {
45+
align-self: flex-end;
46+
width: 80%;
47+
margin-bottom: auto;
48+
margin-top: auto;
49+
display: flex;
50+
flex-direction: row;
51+
max-width: 600px;
52+
min-width: 600px;
53+
}
54+
}
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import _ from 'lodash'
2+
import React from 'react'
3+
import PropTypes from 'prop-types'
4+
import Select from '../../Select'
5+
import cn from 'classnames'
6+
import styles from './ChallengeType-Field.module.scss'
7+
8+
const ChallengeTypeField = ({ types, onUpdateSelect, challenge, disabled }) => {
9+
return (
10+
<>
11+
<div className={styles.row}>
12+
<div className={cn(styles.field, styles.col1)}>
13+
<label htmlFor='type'>Challenge Type <span>*</span> :</label>
14+
</div>
15+
<div className={cn(styles.field, styles.col2, { [styles.disabled]: disabled })}>
16+
<Select
17+
name='challenge_type'
18+
options={_.map(types, type => ({ label: type, value: type }))}
19+
placeholder='Challenge Type'
20+
isClearable={false}
21+
onChange={(e) => onUpdateSelect(e.value, false, 'challengeType')}
22+
isDisabled={disabled}
23+
/>
24+
</div>
25+
</div>
26+
{ challenge.submitTriggered && !challenge.challengeType && <div className={styles.row}>
27+
<div className={cn(styles.field, styles.col1)} />
28+
<div className={cn(styles.field, styles.col2, styles.error)}>
29+
Challenge Type is required field
30+
</div>
31+
</div> }
32+
</>
33+
)
34+
}
35+
36+
ChallengeTypeField.defaultProps = {
37+
types: [],
38+
disabled: false
39+
}
40+
41+
ChallengeTypeField.propTypes = {
42+
types: PropTypes.arrayOf(PropTypes.shape()).isRequired,
43+
onUpdateSelect: PropTypes.func.isRequired,
44+
challenge: PropTypes.shape().isRequired,
45+
disabled: PropTypes.bool
46+
}
47+
48+
export default ChallengeTypeField

src/components/ChallengeEditor/ChallengeView/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ import {
2424
REVIEW_TYPES,
2525
CONNECT_APP_URL,
2626
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
27+
MULTI_ROUND_CHALLENGE_TEMPLATE_ID,
2728
DS_TRACK_ID
2829
} from '../../../config/constants'
2930
import PhaseInput from '../../PhaseInput'
31+
import CheckpointPrizesField from '../CheckpointPrizes-Field'
3032

3133
const ChallengeView = ({
3234
projectDetail,
@@ -96,6 +98,7 @@ const ChallengeView = ({
9698
const showTimeline = false // disables the timeline for time being https://github.com/topcoder-platform/challenge-engine-ui/issues/706
9799
const isTask = _.get(challenge, 'task.isTask', false)
98100
const phases = _.get(challenge, 'phases', [])
101+
const showCheckpointPrizes = _.get(challenge, 'timelineTemplateId') === MULTI_ROUND_CHALLENGE_TEMPLATE_ID
99102
const isDataScience = challenge.trackId === DS_TRACK_ID
100103
const useDashboardData = _.find(challenge.metadata, { name: 'show_data_dashboard' })
101104
const useDashboard = useDashboardData ? useDashboardData.value : true
@@ -125,7 +128,7 @@ const ChallengeView = ({
125128
</div>
126129
}
127130
<div className={styles.col}>
128-
<span className={styles.fieldTitle}>Track:</span>
131+
<span className={styles.fieldTitle}>Domain:</span>
129132
<Track disabled type={challengeTrack} isActive key={challenge.trackId} onUpdateOthers={() => { }} />
130133
</div>
131134
<div className={styles.col}>
@@ -239,6 +242,11 @@ const ChallengeView = ({
239242
readOnly
240243
/>}
241244
<ChallengePrizesField challenge={challenge} readOnly />
245+
{
246+
showCheckpointPrizes && (
247+
<CheckpointPrizesField challenge={challenge} readOnly />
248+
)
249+
}
242250
<CopilotFeeField challenge={challenge} readOnly />
243251
<ChallengeTotalField challenge={challenge} />
244252
</div>

src/components/ChallengeEditor/CheckpointPrizes-Field/CheckpointPrizes-Field.module.scss

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
width: 271px;
5151
}
5252
input:last-of-type {
53-
width: 187px;
53+
width: 70px;
5454
margin-right: 10px;
5555
}
5656
}
@@ -61,3 +61,45 @@
6161
color: $tc-red;
6262
cursor: pointer;
6363
}
64+
65+
.dollarIcon {
66+
color: $tc-black;
67+
cursor: pointer;
68+
}
69+
70+
.checkpointPrizeContainer {
71+
margin: 0 0 0 60px;
72+
}
73+
74+
.checkpointLabel {
75+
font-weight: 500 !important;
76+
padding-left: 0 !important;
77+
}
78+
79+
.checkpointPrizeContainer > div {
80+
display: inline-block;
81+
margin-right: 10px;
82+
}
83+
84+
.checkpointPrizeInputContainer {
85+
box-sizing: border-box;
86+
display: flex;
87+
height: 40px;
88+
}
89+
90+
.checkpointPrizeAmountContainer {
91+
box-sizing: border-box;
92+
display: flex;
93+
justify-content: center;
94+
align-items: center;
95+
background-color: $tc-prize-bg;
96+
border: 1px solid $tc-gray-40;
97+
max-width: 50px;
98+
min-width: 50px;
99+
width: 50px;
100+
border-right-width: 0;
101+
}
102+
103+
.checkpointSelect {
104+
max-width: 85px;
105+
}

src/components/ChallengeEditor/CheckpointPrizes-Field/index.js

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,88 @@ import React from 'react'
22
import PropTypes from 'prop-types'
33
import styles from './CheckpointPrizes-Field.module.scss'
44
import cn from 'classnames'
5-
import { range } from 'lodash'
5+
import _ from 'lodash'
66
import { validateValue } from '../../../util/input-check'
7-
import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE, CHALLENGE_PRIZE_TYPE } from '../../../config/constants'
7+
import {
8+
VALIDATION_VALUE_TYPE,
9+
PRIZE_SETS_TYPE,
10+
CHALLENGE_PRIZE_TYPE,
11+
MAX_CHECKPOINT_PRIZE_COUNT,
12+
DEFAULT_CHECKPOINT_PRIZE,
13+
DEFAULT_CHECKPOINT_PRIZE_COUNT
14+
} from '../../../config/constants'
15+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
16+
import { faDollarSign } from '@fortawesome/free-solid-svg-icons'
17+
import Select from '../../Select'
818

9-
const CheckpointPrizesField = ({ challenge, onUpdateOthers }) => {
19+
const CheckpointPrizesField = ({ challenge, onUpdateOthers, readOnly }) => {
1020
const type = PRIZE_SETS_TYPE.CHECKPOINT_PRIZES
11-
const checkpointPrize = challenge.prizeSets.find(p => p.type === type) || { type: CHALLENGE_PRIZE_TYPE.USD, prizes: [] }
12-
const number = checkpointPrize.prizes.length
13-
const amount = checkpointPrize.prizes.length ? checkpointPrize.prizes[0].value : 0
21+
const prizeSets = _.get(challenge, 'prizeSets') || []
22+
const checkpointPrize = prizeSets.find(p => p.type === type) || { type: PRIZE_SETS_TYPE.CHECKPOINT_PRIZES, prizes: [], 'description': 'Checkpoint Prizes' }
23+
const number = _.get(checkpointPrize, 'prizes.length') || DEFAULT_CHECKPOINT_PRIZE_COUNT
24+
const amount = _.get(checkpointPrize, 'prizes.length') ? checkpointPrize.prizes[0].value : DEFAULT_CHECKPOINT_PRIZE
25+
26+
// update the check point prize with default values if it's not already defined
27+
if (_.get(checkpointPrize, 'prizes.length') === 0) {
28+
onChange(number, amount)
29+
}
1430

1531
function onChange (number, amount) {
16-
checkpointPrize.prizes = range(validateValue(number, VALIDATION_VALUE_TYPE.INTEGER))
17-
.map(i => ({ type: CHALLENGE_PRIZE_TYPE.USD, value: validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER, '$') }))
18-
onUpdateOthers({ field: 'prizeSets', value: [...challenge.prizeSets.filter(p => p.type !== type), +number && checkpointPrize].filter(p => p) })
32+
checkpointPrize.prizes = _.range(validateValue(number, VALIDATION_VALUE_TYPE.INTEGER))
33+
.map(i => ({ type: CHALLENGE_PRIZE_TYPE.USD, value: +validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER) }))
34+
onUpdateOthers({ field: 'prizeSets', value: [...prizeSets.filter(p => p.type !== type), +number && checkpointPrize].filter(p => p) })
1935
}
36+
2037
return (
21-
<div className={styles.row}>
22-
<div className={cn(styles.field, styles.col1)}>
23-
<label htmlFor='checkpointPrizes'>Checkpoint Prizes :</label>
38+
<>
39+
<div className={cn(styles.row)}>
40+
<div className={cn(styles.field, styles.col1)}>
41+
<label htmlFor={`checkpointPrizes`} className={styles.checkpointLabel}>Checkpoint Prizes :</label>
42+
</div>
43+
{
44+
readOnly ? (
45+
<div className={cn(styles.field, styles.col2)}>
46+
${amount} for each submission up to {number} submissions
47+
</div>
48+
) : (
49+
<div className={cn(styles.field, styles.col2)}>
50+
<div>
51+
<div className={styles.checkpointPrizeInputContainer}>
52+
<div className={styles.checkpointPrizeAmountContainer}>
53+
<FontAwesomeIcon className={styles.dollarIcon} icon={faDollarSign} />
54+
</div>
55+
<input id='checkpointPrize' name='checkpointPrize' type='text' placeholder='' value={amount} maxLength='7' required onChange={(e) => onChange(number, e.target.value)} />
56+
</div>
57+
</div>
58+
<div>
59+
for each submission up to&nbsp;&nbsp;
60+
</div>
61+
<div className={styles.checkpointSelect}>
62+
<Select
63+
name='submissions'
64+
options={_.range(1, MAX_CHECKPOINT_PRIZE_COUNT + 1).map((v) => ({ label: v, value: v }))}
65+
value={{ label: number, value: number }}
66+
isClearable={false}
67+
onChange={e => onChange(e.value, amount)}
68+
isDisabled={false}
69+
/>
70+
</div>
71+
</div>
72+
)
73+
}
2474
</div>
25-
<div className={cn(styles.field, styles.col2)}>
26-
<input id='checkNumber' name='checkNumber' type='text' placeholder='Number of checkpoint prizes' value={number} maxLength='200' onChange={e => onChange(e.target.value, amount)} />
27-
<input id='checkAmount' name='checkAmount' type='text' placeholder='Amount per prizes' value={amount} maxLength='200' onChange={e => onChange(number, e.target.value)} />
28-
</div>
29-
</div>
75+
</>
3076
)
3177
}
3278

79+
CheckpointPrizesField.defaultProps = {
80+
readOnly: false
81+
}
82+
3383
CheckpointPrizesField.propTypes = {
3484
challenge: PropTypes.shape().isRequired,
35-
onUpdateOthers: PropTypes.func.isRequired
85+
onUpdateOthers: PropTypes.func,
86+
readOnly: PropTypes.bool
3687
}
3788

3889
export default CheckpointPrizesField

src/components/ChallengeEditor/Description-Field/Description-Field.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
overflow: auto;
1414

1515
&.isPrivate {
16-
max-height: 205px;
16+
max-height: 400px;
1717
}
1818

1919
:global {

0 commit comments

Comments
 (0)