Skip to content

Commit d097e7b

Browse files
authored
Merge pull request #1556 from topcoder-platform/develop
Prod deploy - v20.11
2 parents 3741b5a + 355a653 commit d097e7b

File tree

25 files changed

+1484
-1247
lines changed

25 files changed

+1484
-1247
lines changed

src/actions/challenges.js

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ import {
3535
REMOVE_ATTACHMENT_FAILURE,
3636
REMOVE_ATTACHMENT_PENDING,
3737
REMOVE_ATTACHMENT_SUCCESS,
38-
CREATE_CHALLENGE_RESOURCE,
38+
CREATE_CHALLENGE_RESOURCE_PENDING,
39+
CREATE_CHALLENGE_RESOURCE_SUCCESS,
40+
CREATE_CHALLENGE_RESOURCE_FAILURE,
3941
DELETE_CHALLENGE_RESOURCE,
4042
PAGE_SIZE,
4143
UPDATE_CHALLENGE_DETAILS_PENDING,
@@ -661,17 +663,50 @@ export function deleteResource (challengeId, roleId, memberHandle) {
661663
* @param {UUID} challengeId id of the challenge for which resource is to be created
662664
* @param {UUID} roleId id of the role, the resource should be in
663665
* @param {String} memberHandle handle of the resource
666+
* @param {String} email email of member
667+
* @param {String} userId id of member
664668
*/
665-
export function createResource (challengeId, roleId, memberHandle) {
669+
export function createResource (challengeId, roleId, memberHandle, email, userId) {
666670
const resource = {
667671
challengeId,
668672
roleId,
669673
memberHandle
670674
}
671-
return (dispatch, getState) => {
672-
return dispatch({
673-
type: CREATE_CHALLENGE_RESOURCE,
674-
payload: createResourceAPI(resource)
675+
return async (dispatch, getState) => {
676+
dispatch({
677+
type: CREATE_CHALLENGE_RESOURCE_PENDING
678+
})
679+
680+
let newResource
681+
try {
682+
newResource = await createResourceAPI(resource)
683+
} catch (error) {
684+
const errorMessage = _.get(error, 'response.data.message', 'Create resource fail.')
685+
dispatch({
686+
type: CREATE_CHALLENGE_RESOURCE_FAILURE
687+
})
688+
return {
689+
success: false,
690+
errorMessage
691+
}
692+
}
693+
694+
let userEmail = email
695+
if (!userEmail && userId) {
696+
try {
697+
const memberInfos = await searchProfilesByUserIds([userId])
698+
if (memberInfos.length > 0) {
699+
userEmail = memberInfos[0].email
700+
}
701+
} catch (error) {
702+
}
703+
}
704+
dispatch({
705+
type: CREATE_CHALLENGE_RESOURCE_SUCCESS,
706+
payload: {
707+
...newResource,
708+
email: userEmail
709+
}
675710
})
676711
}
677712
}

src/assets/images/ico-trash.svg

Lines changed: 3 additions & 0 deletions
Loading

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
}
2525

2626
&.col1 {
27-
max-width: 185px;
28-
min-width: 185px;
27+
width: 100px;
2928
margin-right: 14px;
3029
white-space: nowrap;
3130
display: flex;
3231
align-items: center;
32+
33+
&.showAssignToMe {
34+
width: 185px;
35+
}
3336
}
3437

3538
&.col2 {

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

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

10-
const AssignedMemberField = ({ challenge, onAssignSelf, onChange, assignedMemberDetails, readOnly }) => {
10+
const AssignedMemberField = ({
11+
challenge,
12+
onAssignSelf,
13+
onChange,
14+
assignedMemberDetails,
15+
readOnly,
16+
showAssignToMe,
17+
label
18+
}) => {
1119
const value = assignedMemberDetails ? {
1220
// if we know assigned member details, then show user `handle`, otherwise fallback to `userId`
1321
label: assignedMemberDetails.handle,
@@ -16,8 +24,14 @@ const AssignedMemberField = ({ challenge, onAssignSelf, onChange, assignedMember
1624

1725
return (
1826
<div className={styles.row}>
19-
<div className={cn(styles.field, styles.col1)}>
20-
<label htmlFor='assignedMember'>Assigned Member :</label>
27+
<div className={cn(
28+
styles.field,
29+
styles.col1,
30+
{
31+
[styles.showAssignToMe]: showAssignToMe
32+
}
33+
)}>
34+
<label htmlFor='assignedMember'>{label} :</label>
2135
</div>
2236
<div className={cn(styles.field, styles.col2)}>
2337
{readOnly ? (
@@ -30,29 +44,33 @@ const AssignedMemberField = ({ challenge, onAssignSelf, onChange, assignedMember
3044
)}
3145
</div>
3246
{
33-
!readOnly &&
34-
<div className={styles.assignSelfField}>
47+
(!readOnly && showAssignToMe)
48+
? (<div className={styles.assignSelfField}>
3549
<a href='#' onClick={(e) => {
3650
e.preventDefault()
3751
onAssignSelf()
3852
}}>Assign to me</a>
39-
</div>
53+
</div>) : null
4054
}
4155
</div>
4256
)
4357
}
4458

4559
AssignedMemberField.defaultProps = {
4660
assignedMemberDetails: null,
47-
readOnly: false
61+
readOnly: false,
62+
showAssignToMe: true,
63+
label: 'Assigned Member'
4864
}
4965

5066
AssignedMemberField.propTypes = {
5167
challenge: PropTypes.shape().isRequired,
5268
onChange: PropTypes.func,
5369
assignedMemberDetails: PropTypes.shape(),
5470
readOnly: PropTypes.bool,
55-
onAssignSelf: PropTypes.func
71+
showAssignToMe: PropTypes.bool,
72+
onAssignSelf: PropTypes.func,
73+
label: PropTypes.string
5674
}
5775

5876
export default AssignedMemberField

src/components/ChallengeEditor/ChallengeViewTabs/index.js

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import ChallengeViewComponent from '../ChallengeView'
1111
import { PrimaryButton } from '../../Buttons'
1212
import LegacyLinks from '../../LegacyLinks'
1313
import ForumLink from '../../ForumLink'
14-
import Registrants from '../Registrants'
14+
import ResourcesTab from '../Resources'
1515
import Submissions from '../Submissions'
16-
import { checkAdmin, checkReadOnlyRoles, getResourceRoleByName } from '../../../util/tc'
16+
import { checkAdmin, checkEditResourceRoles, checkReadOnlyRoles } from '../../../util/tc'
1717
import { CHALLENGE_STATUS, MESSAGE } from '../../../config/constants'
1818
import Tooltip from '../../Tooltip'
1919
import CancelDropDown from '../Cancel-Dropdown'
2020
import 'react-tabs/style/react-tabs.css'
2121
import styles from './ChallengeViewTabs.module.scss'
22+
import ResourcesAdd from '../ResourcesAdd'
2223

2324
function getSelectorStyle (selectedView, currentView) {
2425
return cn(styles['challenge-selector-common'], {
@@ -47,20 +48,23 @@ const ChallengeViewTabs = ({
4748
assignYourselfCopilot,
4849
showRejectChallengeModal,
4950
loggedInUser,
50-
onApproveChallenge
51+
onApproveChallenge,
52+
createResource,
53+
deleteResource
5154
}) => {
5255
const [selectedTab, setSelectedTab] = useState(0)
56+
const [showAddResourceModal, setShowAddResourceModal] = useState(false)
57+
const { resourceRoles } = metadata
5358
const loggedInUserResource = useMemo(
5459
() => {
5560
if (!loggedInUser) {
5661
return null
5762
}
58-
const loggedInUserResourceTmps = _.filter(challengeResources, { memberId: `${loggedInUser.userId}` })
63+
const loggedInUserResourceTmps = _.cloneDeep(_.filter(challengeResources, { memberId: `${loggedInUser.userId}` }))
5964
let loggedInUserResourceTmp = null
6065
if (loggedInUserResourceTmps.length > 0) {
6166
loggedInUserResourceTmp = loggedInUserResourceTmps[0]
6267
loggedInUserResourceTmp.resources = loggedInUserResourceTmps
63-
const { resourceRoles } = metadata
6468
if (resourceRoles) {
6569
let roles = []
6670
_.forEach(loggedInUserResourceTmps, resource => {
@@ -74,37 +78,38 @@ const ChallengeViewTabs = ({
7478
},
7579
[loggedInUser, challengeResources, metadata]
7680
)
77-
78-
const registrants = useMemo(() => {
79-
const { resourceRoles } = metadata
80-
const role = getResourceRoleByName(resourceRoles, 'Submitter')
81-
if (role && challengeResources) {
82-
const registrantList = challengeResources.filter(
83-
resource => resource.roleId === role.id
81+
const canEditResource = useMemo(
82+
() => {
83+
return selectedTab === 1 &&
84+
(
85+
(
86+
loggedInUserResource &&
87+
checkEditResourceRoles(loggedInUserResource.roles)
88+
) ||
89+
checkAdmin(token)
8490
)
85-
// Add submission date to registrants
86-
registrantList.forEach((r, i) => {
87-
const submission = (challengeSubmissions || []).find(s => {
88-
return '' + s.memberId === '' + r.memberId
89-
})
90-
if (submission) {
91-
registrantList[i].submissionDate = submission.created
92-
}
93-
})
94-
return registrantList
95-
} else {
96-
return []
97-
}
98-
}, [metadata, challengeResources, challengeSubmissions])
91+
},
92+
[loggedInUserResource, token, selectedTab]
93+
)
94+
95+
const allResources = useMemo(() => {
96+
return challengeResources.map(rs => {
97+
if (!rs.role) {
98+
const roleInfo = _.find(resourceRoles, { id: rs.roleId })
99+
rs.role = roleInfo ? roleInfo.name : ''
100+
}
101+
return rs
102+
})
103+
}, [metadata, challengeResources])
99104

100105
const submissions = useMemo(() => {
101106
return _.map(challengeSubmissions, s => {
102-
s.registrant = _.find(registrants, r => {
107+
s.registrant = _.find(allResources, r => {
103108
return +r.memberId === s.memberId
104109
})
105110
return s
106111
})
107-
}, [challengeSubmissions, registrants])
112+
}, [challengeSubmissions, allResources])
108113

109114
const isTask = _.get(challenge, 'task.isTask', false)
110115

@@ -193,9 +198,14 @@ const ChallengeViewTabs = ({
193198
)}
194199
</div>
195200
)}
196-
{enableEdit && (
201+
{enableEdit && !canEditResource && (
197202
<PrimaryButton text={'Edit'} type={'info'} submit link={`./edit`} />
198203
)}
204+
{canEditResource && (
205+
<PrimaryButton text={'Add'} type={'info'} onClick={() => {
206+
setShowAddResourceModal(true)
207+
}} />
208+
)}
199209
{isSelfService && isDraft && (isAdmin || isSelfServiceCopilot || enableEdit) && (
200210
<div className={styles.button}>
201211
<PrimaryButton
@@ -205,7 +215,7 @@ const ChallengeViewTabs = ({
205215
/>
206216
</div>
207217
)}
208-
<PrimaryButton text={'Back'} type={'info'} submit link={`..`} />
218+
{!canEditResource ? (<PrimaryButton text={'Back'} type={'info'} submit link={`..`} />) : null}
209219
</div>
210220
</div>
211221
<div className={styles['challenge-view-selector']}>
@@ -223,22 +233,20 @@ const ChallengeViewTabs = ({
223233
>
224234
DETAILS
225235
</a>
226-
{registrants.length ? (
227-
<a
228-
tabIndex='1'
229-
role='tab'
230-
aria-selected={selectedTab === 1}
231-
onClick={e => {
232-
setSelectedTab(1)
233-
}}
234-
onKeyPress={e => {
235-
setSelectedTab(1)
236-
}}
237-
className={getSelectorStyle(selectedTab, 1)}
238-
>
239-
REGISTRANTS ({registrants.length})
240-
</a>
241-
) : null}
236+
<a
237+
tabIndex='1'
238+
role='tab'
239+
aria-selected={selectedTab === 1}
240+
onClick={e => {
241+
setSelectedTab(1)
242+
}}
243+
onKeyPress={e => {
244+
setSelectedTab(1)
245+
}}
246+
className={getSelectorStyle(selectedTab, 1)}
247+
>
248+
RESOURCES
249+
</a>
242250
{challengeSubmissions.length ? (
243251
<a
244252
tabIndex='2'
@@ -279,7 +287,14 @@ const ChallengeViewTabs = ({
279287
/>
280288
)}
281289
{selectedTab === 1 && (
282-
<Registrants challenge={challenge} registrants={registrants} />
290+
<ResourcesTab
291+
challenge={challenge}
292+
resources={allResources}
293+
canEditResource={canEditResource}
294+
deleteResource={deleteResource}
295+
submissions={submissions}
296+
loggedInUserResource={loggedInUserResource}
297+
/>
283298
)}
284299
{selectedTab === 2 && (
285300
<Submissions
@@ -289,6 +304,13 @@ const ChallengeViewTabs = ({
289304
loggedInUserResource={loggedInUserResource}
290305
/>
291306
)}
307+
{showAddResourceModal ? (<ResourcesAdd
308+
onClose={() => setShowAddResourceModal(false)}
309+
challenge={challenge}
310+
loggedInUser={loggedInUser}
311+
resourceRoles={resourceRoles}
312+
createResource={createResource}
313+
/>) : null}
292314
</div>
293315
)
294316
}
@@ -319,6 +341,8 @@ ChallengeViewTabs.propTypes = {
319341
onCloseTask: PropTypes.func,
320342
projectPhases: PropTypes.arrayOf(PropTypes.object),
321343
assignYourselfCopilot: PropTypes.func.isRequired,
344+
createResource: PropTypes.func.isRequired,
345+
deleteResource: PropTypes.func.isRequired,
322346
showRejectChallengeModal: PropTypes.func.isRequired,
323347
loggedInUser: PropTypes.object.isRequired,
324348
onApproveChallenge: PropTypes.func

0 commit comments

Comments
 (0)