Skip to content

Commit 329cd32

Browse files
authored
Merge branch 'cf-jan-2021' into issue-1007
2 parents a4a603c + f2408b2 commit 329cd32

File tree

20 files changed

+284
-32
lines changed

20 files changed

+284
-32
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ npm install
3939

4040
1. copy the environment file in docs/dev.env to /.env
4141

42-
1. add `127.0.0.1 local.topcoder-dev.com` to your /etc/hosts file
43-
4442
1. If you are using local instances of the API's, change the DEV_API_HOSTNAME in configs/constants/development.js to match your local api endpoint.
4543
- For example change it to 'http://localhost:3000/',
4644

@@ -50,7 +48,7 @@ npm install
5048
npm run dev
5149
```
5250

53-
You can access the app from [http://local.topcoder-dev.com:3001/](http://local.topcoder-dev.com:3001/)
51+
You can access the app from [http://localhost:3000/](http://localhost:3000/)
5452

5553
The page will reload if you make edits.
5654

config/constants/development.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ module.exports = {
77
COMMUNITY_APP_URL: `https://www.${DOMAIN}`,
88
MEMBER_API_URL: `${DEV_API_HOSTNAME}/v4/members`,
99
MEMBER_API_V3_URL: `${DEV_API_HOSTNAME}/v3/members`,
10-
DEV_APP_URL: `http://local.${DOMAIN}`,
1110
CHALLENGE_API_URL: `${DEV_API_HOSTNAME}/v5/challenges`,
1211
CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v5/timeline-templates`,
1312
CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v5/challenge-types`,

config/constants/production.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ module.exports = {
77
COMMUNITY_APP_URL: `https://www.${DOMAIN}`,
88
MEMBER_API_URL: `${PROD_API_HOSTNAME}/v4/members`,
99
MEMBER_API_V3_URL: `${PROD_API_HOSTNAME}/v3/members`,
10-
DEV_APP_URL: `https://submission-review.${DOMAIN}`,
1110
CHALLENGE_API_URL: `${PROD_API_HOSTNAME}/v5/challenges`,
1211
CHALLENGE_TIMELINE_TEMPLATES_URL: `${PROD_API_HOSTNAME}/v5/timeline-templates`,
1312
CHALLENGE_TYPES_URL: `${PROD_API_HOSTNAME}/v5/challenge-types`,

public/favicon.ico

15.4 KB
Binary file not shown.

scripts/start.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ checkBrowsers(paths.appPath, isInteractive)
9999
clearConsole()
100100
}
101101
console.log(chalk.cyan('Starting the development server...\n'))
102-
openBrowser(constants.DEV_APP_URL ? `${constants.DEV_APP_URL}:${process.env.PORT || 3000}` : urls.localUrlForBrowser)
102+
openBrowser(urls.localUrlForBrowser)
103103
})
104104

105105
const SIGNALS = ['SIGINT', 'SIGTERM']

src/actions/challenges.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
fetchResourceRoles,
1414
fetchChallengeTimelines,
1515
fetchChallengeTracks,
16+
fetchGroupDetail,
1617
updateChallenge,
1718
patchChallenge,
19+
deleteChallenge as deleteChallengeAPI,
1820
createChallenge as createChallengeAPI,
1921
createResource as createResourceAPI,
2022
deleteResource as deleteResourceAPI
@@ -38,6 +40,9 @@ import {
3840
CREATE_CHALLENGE_PENDING,
3941
CREATE_CHALLENGE_SUCCESS,
4042
CREATE_CHALLENGE_FAILURE,
43+
DELETE_CHALLENGE_PENDING,
44+
DELETE_CHALLENGE_SUCCESS,
45+
DELETE_CHALLENGE_FAILURE,
4146
LOAD_CHALLENGE_RESOURCES
4247
} from '../config/constants'
4348
import { loadProject } from './projects'
@@ -182,6 +187,14 @@ export function loadChallengeDetails (projectId, challengeId) {
182187
}
183188
}
184189

190+
/**
191+
* Loads group details
192+
*/
193+
export function loadGroupDetails (groupIds) {
194+
const promiseAll = groupIds.map(id => fetchGroupDetail(id))
195+
return Promise.all(promiseAll)
196+
}
197+
185198
/**
186199
* Update challenge details
187200
*
@@ -267,6 +280,26 @@ export function partiallyUpdateChallengeDetails (challengeId, partialChallengeDe
267280
}
268281
}
269282

283+
export function deleteChallenge (challengeId) {
284+
return async (dispatch) => {
285+
dispatch({
286+
type: DELETE_CHALLENGE_PENDING
287+
})
288+
289+
return deleteChallengeAPI(challengeId).then((challenge) => {
290+
return dispatch({
291+
type: DELETE_CHALLENGE_SUCCESS,
292+
challengeDetails: challenge
293+
})
294+
}).catch((error) => {
295+
dispatch({
296+
type: DELETE_CHALLENGE_FAILURE
297+
})
298+
throw error
299+
})
300+
}
301+
}
302+
270303
export function loadTimelineTemplates () {
271304
return async (dispatch) => {
272305
const timelineTemplates = await fetchTimelineTemplates()

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,10 @@
4545
.readOnlyValue {
4646
margin-bottom: 0.5rem; // the same like `label` to be aligned
4747
}
48+
49+
.assignSelfField {
50+
margin-left: 20px;
51+
padding-top: 6px;
52+
}
4853
}
4954

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import cn from 'classnames'
77
import styles from './AssignedMember-Field.module.scss'
88
import SelectUserAutocomplete from '../../SelectUserAutocomplete'
99

10-
const AssignedMemberField = ({ challenge, onChange, assignedMemberDetails, readOnly }) => {
10+
const AssignedMemberField = ({ challenge, onAssignSelf, onChange, assignedMemberDetails, readOnly }) => {
1111
const value = assignedMemberDetails ? {
1212
// if we know assigned member details, then show user `handle`, otherwise fallback to `userId`
1313
label: assignedMemberDetails.handle,
1414
value: assignedMemberDetails.userId + ''
1515
} : null
16+
1617
return (
1718
<div className={styles.row}>
1819
<div className={cn(styles.field, styles.col1)}>
@@ -28,6 +29,15 @@ const AssignedMemberField = ({ challenge, onChange, assignedMemberDetails, readO
2829
/>
2930
)}
3031
</div>
32+
{
33+
!readOnly &&
34+
<div className={styles.assignSelfField}>
35+
<a href='#' onClick={(e) => {
36+
e.preventDefault()
37+
onAssignSelf()
38+
}}>Assign to me</a>
39+
</div>
40+
}
3141
</div>
3242
)
3343
}
@@ -41,7 +51,8 @@ AssignedMemberField.propTypes = {
4151
challenge: PropTypes.shape().isRequired,
4252
onChange: PropTypes.func,
4353
assignedMemberDetails: PropTypes.shape(),
44-
readOnly: PropTypes.bool
54+
readOnly: PropTypes.bool,
55+
onAssignSelf: PropTypes.func
4556
}
4657

4758
export default AssignedMemberField

src/components/ChallengeEditor/ChallengeEditor.module.scss

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@
241241
.actionButtons {
242242
position: absolute;
243243
top: 30px;
244-
a {
244+
a,button {
245245
height: 40px;
246246
}
247247
}
@@ -251,7 +251,13 @@
251251
}
252252

253253
.actionButtonsRight {
254+
display: flex;
255+
align-items: center;
254256
right: 20px;
257+
258+
button {
259+
margin-right: 20px;
260+
}
255261
}
256262

257263
.buttonContainer {

src/components/ChallengeEditor/ChallengeView/index.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react'
1+
import React, { useState, useEffect } from 'react'
22
import _ from 'lodash'
33
import { Helmet } from 'react-helmet'
44
import PropTypes from 'prop-types'
@@ -21,6 +21,7 @@ import PhaseInput from '../../PhaseInput'
2121
import LegacyLinks from '../../LegacyLinks'
2222
import AssignedMemberField from '../AssignedMember-Field'
2323
import { getResourceRoleByName } from '../../../util/tc'
24+
import { loadGroupDetails } from '../../../actions/challenges'
2425
import Tooltip from '../../Tooltip'
2526
import { MESSAGE, REVIEW_TYPES } from '../../../config/constants'
2627

@@ -40,6 +41,18 @@ const ChallengeView = ({
4041
const challengeTrack = _.find(metadata.challengeTracks, { id: challenge.trackId })
4142

4243
const [openAdvanceSettings, setOpenAdvanceSettings] = useState(false)
44+
const [groups, setGroups] = useState('')
45+
46+
useEffect(() => {
47+
if (challenge.groups && challenge.groups.length > 0) {
48+
loadGroupDetails(challenge.groups).then(res => {
49+
const groups = _.map(res, 'name').join(', ')
50+
setGroups(groups)
51+
})
52+
} else {
53+
setGroups('')
54+
}
55+
}, [challenge.groups])
4356

4457
const getResourceFromProps = (name) => {
4558
const { resourceRoles } = metadata
@@ -167,7 +180,7 @@ const ChallengeView = ({
167180
</div>
168181
{openAdvanceSettings && (<div className={cn(styles.row, styles.topRow)}>
169182
<div className={styles.col}>
170-
<span><span className={styles.fieldTitle}>Groups:</span> {challenge.groups ? challenge.groups.join(', ') : ''}</span>
183+
<span><span className={styles.fieldTitle}>Groups:</span> {groups}</span>
171184
</div>
172185
</div>)}
173186
{

src/components/ChallengeEditor/index.js

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class ChallengeEditor extends Component {
6868
super(props)
6969
this.state = {
7070
isLaunch: false,
71+
isDeleteLaunch: false,
7172
isConfirm: false,
7273
isClose: false,
7374
isOpenAdvanceSettings: false,
@@ -90,6 +91,7 @@ class ChallengeEditor extends Component {
9091
this.onUpdateOthers = this.onUpdateOthers.bind(this)
9192
this.onUpdateCheckbox = this.onUpdateCheckbox.bind(this)
9293
this.onUpdateAssignedMember = this.onUpdateAssignedMember.bind(this)
94+
this.onAssignSelf = this.onAssignSelf.bind(this)
9395
this.addFileType = this.addFileType.bind(this)
9496
this.removeFileType = this.removeFileType.bind(this)
9597
this.updateFileTypesMetadata = this.updateFileTypesMetadata.bind(this)
@@ -121,6 +123,8 @@ class ChallengeEditor extends Component {
121123
this.getAvailableTimelineTemplates = this.getAvailableTimelineTemplates.bind(this)
122124
this.autoUpdateChallengeThrottled = _.throttle(this.validateAndAutoUpdateChallenge.bind(this), 3000) // 3s
123125
this.updateResource = this.updateResource.bind(this)
126+
this.onDeleteChallenge = this.onDeleteChallenge.bind(this)
127+
this.deleteModalLaunch = this.deleteModalLaunch.bind(this)
124128
}
125129

126130
componentDidMount () {
@@ -131,6 +135,27 @@ class ChallengeEditor extends Component {
131135
this.resetChallengeData(this.setState.bind(this))
132136
}
133137

138+
deleteModalLaunch () {
139+
if (!this.state.isDeleteLaunch) {
140+
this.setState({ isDeleteLaunch: true })
141+
}
142+
}
143+
144+
async onDeleteChallenge () {
145+
const { deleteChallenge, challengeDetails, history } = this.props
146+
try {
147+
this.setState({ isSaving: true })
148+
// Call action to delete the challenge
149+
await deleteChallenge(challengeDetails.id)
150+
this.setState({ isSaving: false })
151+
this.resetModal()
152+
history.push(`/projects/${challengeDetails.projectId}/challenges`)
153+
} catch (e) {
154+
const error = _.get(e, 'response.data.message', 'Unable to Delete the challenge')
155+
this.setState({ isSaving: false, error })
156+
}
157+
}
158+
134159
/**
135160
* Validates challenge and if its valid calling an autosave method
136161
*
@@ -206,7 +231,7 @@ class ChallengeEditor extends Component {
206231
}
207232

208233
resetModal () {
209-
this.setState({ isLoading: false, isConfirm: false, isLaunch: false, error: null, isCloseTask: false })
234+
this.setState({ isLoading: false, isConfirm: false, isLaunch: false, error: null, isCloseTask: false, isDeleteLaunch: false })
210235
}
211236

212237
/**
@@ -335,6 +360,22 @@ class ChallengeEditor extends Component {
335360
})
336361
}
337362

363+
/**
364+
* Update Assigned Member to Current User
365+
*/
366+
onAssignSelf () {
367+
const { loggedInUser } = this.props
368+
369+
const assignedMemberDetails = {
370+
handle: loggedInUser.handle,
371+
userId: loggedInUser.userId
372+
}
373+
374+
this.setState({
375+
assignedMemberDetails
376+
})
377+
}
378+
338379
/**
339380
* Update Single Select
340381
* @param option The select option
@@ -1336,6 +1377,7 @@ class ChallengeEditor extends Component {
13361377
challenge={challenge}
13371378
onChange={this.onUpdateAssignedMember}
13381379
assignedMemberDetails={assignedMemberDetails}
1380+
onAssignSelf={this.onAssignSelf}
13391381
/>
13401382
)}
13411383
<CopilotField challenge={challenge} copilots={metadata.members} onUpdateOthers={this.onUpdateOthers} />
@@ -1383,6 +1425,19 @@ class ChallengeEditor extends Component {
13831425
/>
13841426
</div>
13851427
)}
1428+
{
1429+
this.state.isDeleteLaunch && !this.state.isConfirm && (
1430+
<ConfirmationModal
1431+
title='Confirm Delete'
1432+
message={`Do you want to delete "${challenge.name}"?`}
1433+
theme={theme}
1434+
isProcessing={isSaving}
1435+
errorMessage={this.state.error}
1436+
onCancel={this.resetModal}
1437+
onConfirm={this.onDeleteChallenge}
1438+
/>
1439+
)
1440+
}
13861441
{ showTimeline && (
13871442
<ChallengeScheduleField
13881443
templates={this.getAvailableTimelineTemplates()}
@@ -1436,6 +1491,7 @@ class ChallengeEditor extends Component {
14361491
</div>
14371492
<div className={styles.title}>{getTitle(isNew)}</div>
14381493
<div className={cn(styles.actionButtons, styles.actionButtonsRight)}>
1494+
{this.props.challengeDetails.status === 'New' && <PrimaryButton text={'Delete'} type={'danger'} onClick={this.deleteModalLaunch} />}
14391495
<PrimaryButton text={'Back'} type={'info'} submit link={`/projects/${projectDetail.id}/challenges`} />
14401496
</div>
14411497
<div className={styles.textRequired}>* Required</div>
@@ -1479,7 +1535,9 @@ ChallengeEditor.propTypes = {
14791535
updateChallengeDetails: PropTypes.func.isRequired,
14801536
createChallenge: PropTypes.func,
14811537
replaceResourceInRole: PropTypes.func,
1482-
partiallyUpdateChallengeDetails: PropTypes.func.isRequired
1538+
partiallyUpdateChallengeDetails: PropTypes.func.isRequired,
1539+
deleteChallenge: PropTypes.func.isRequired,
1540+
loggedInUser: PropTypes.shape().isRequired
14831541
}
14841542

14851543
export default withRouter(ChallengeEditor)

src/components/ChallengesComponent/ChallengeCard/ChallengeCard.module.scss

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,31 @@
257257
}
258258
}
259259

260+
.deleteButton {
261+
height: 22px;
262+
width: 86px;
263+
border-radius: 11.5px;
264+
display: flex;
265+
justify-content: center;
266+
align-items: center;
267+
background-color: $tc-red;
268+
border-color: $tc-red;
269+
cursor: pointer;
270+
271+
span {
272+
@include roboto;
273+
274+
font-size: 14px;
275+
font-weight: 400;
276+
line-height: 17px;
277+
color: $white;
278+
text-transform: capitalize;
279+
display: flex;
280+
justify-content: center;
281+
align-items: center;
282+
}
283+
}
284+
260285
.icon {
261286
vertical-align: bottom;
262287
}

0 commit comments

Comments
 (0)