diff --git a/.gitignore b/.gitignore index ac1768f4..52cdafd6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -*.env \ No newline at end of file +*.env + +*.vscode diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 2a543acf..bff811c6 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -20,6 +20,7 @@ import Loader from '../../Loader' import PhaseInput from '../../PhaseInput' import LegacyLinks from '../../LegacyLinks' import AssignedMemberField from '../AssignedMember-Field' +import { getResourceRoleByName } from '../../../util/tc' const ChallengeView = ({ projectDetail, @@ -36,13 +37,9 @@ const ChallengeView = ({ const [openAdvanceSettings, setOpenAdvanceSettings] = useState(false) - const getResourceRoleByName = (name) => { - const { resourceRoles } = metadata - return resourceRoles ? resourceRoles.find(role => role.name === name) : null - } - const getResourceFromProps = (name) => { - const role = getResourceRoleByName(name) + const { resourceRoles } = metadata + const role = getResourceRoleByName(resourceRoles, name) return challengeResources && role && challengeResources.find(resource => resource.roleId === role.id) } diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 3170656d..907e616e 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -45,6 +45,7 @@ import PhaseInput from '../PhaseInput' import LegacyLinks from '../LegacyLinks' import AssignedMemberField from './AssignedMember-Field' import Tooltip from '../Tooltip' +import { getResourceRoleByName } from '../../util/tc' const theme = { container: styles.modalContainer @@ -115,6 +116,7 @@ class ChallengeEditor extends Component { this.getTemplatePhases = this.getTemplatePhases.bind(this) this.getAvailableTimelineTemplates = this.getAvailableTimelineTemplates.bind(this) this.autoUpdateChallengeThrottled = _.throttle(this.autoUpdateChallenge.bind(this), 3000) // 3s + this.updateResource = this.updateResource.bind(this) } componentDidMount () { @@ -195,22 +197,25 @@ class ChallengeEditor extends Component { * Close task when user confirm it */ onCloseTask () { - const { challenge: oldChallenge, assignedMemberDetails } = this.state - - // set assigned user as the only one winner - const newChallenge = { - ...oldChallenge, - winners: [{ - userId: assignedMemberDetails.userId, - handle: assignedMemberDetails.handle, - placement: 1 - }] - } + // before marking challenge as complete, save all the changes user might have made + this.updateAllChallengeInfo(this.state.challenge.status, () => { + const { challenge: oldChallenge, assignedMemberDetails } = this.state + + // set assigned user as the only one winner + const newChallenge = { + ...oldChallenge, + winners: [{ + userId: assignedMemberDetails.userId, + handle: assignedMemberDetails.handle, + placement: 1 + }] + } - this.setState({ - challenge: newChallenge - }, () => { - this.updateAllChallengeInfo('Completed') + this.setState({ + challenge: newChallenge + }, () => { + this.updateAllChallengeInfo('Completed') + }) }) } @@ -959,13 +964,9 @@ class ChallengeEditor extends Component { this.updateAllChallengeInfo(this.state.challenge.status) } - getResourceRoleByName (name) { - const { resourceRoles } = this.props.metadata - return resourceRoles ? resourceRoles.find(role => role.name === name) : null - } - async updateResource (challengeId, name, value, prevValue) { - const resourceRole = this.getResourceRoleByName(name) + const { resourceRoles } = this.props.metadata + const resourceRole = getResourceRoleByName(resourceRoles, name) const roleId = resourceRole.id await this.props.replaceResourceInRole(challengeId, roleId, value, prevValue) } @@ -986,8 +987,8 @@ class ChallengeEditor extends Component { } getResourceFromProps (name) { - const { challengeResources } = this.props - const role = this.getResourceRoleByName(name) + const { challengeResources, metadata: { resourceRoles } } = this.props + const role = getResourceRoleByName(resourceRoles, name) return challengeResources && role && challengeResources.find(resource => resource.roleId === role.id) } diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index 109e42ad..cd826637 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -27,9 +27,6 @@ import { createChallenge, replaceResourceInRole } from '../../actions/challenges' -import { - loadMemberDetails -} from '../../actions/members' import { connect } from 'react-redux' import { SUBMITTER_ROLE_UUID } from '../../config/constants' @@ -80,36 +77,13 @@ class ChallengeEditor extends Component { } componentWillReceiveProps (nextProps) { - const { match, challengeDetails } = this.props - const { match: newMatch, loadChallengeDetails, loadResources, challengeDetails: nextChallengeDetails } = nextProps + const { match } = this.props + const { match: newMatch, loadChallengeDetails, loadResources } = nextProps const projectId = _.get(newMatch.params, 'projectId', null) const challengeId = _.get(newMatch.params, 'challengeId', null) if (_.get(match.params, 'projectId', null) !== projectId || _.get(match.params, 'challengeId', null) !== challengeId) { this.fetchChallengeDetails(newMatch, loadChallengeDetails, loadResources) } - - // this section is called only one time as soon challenge details are loaded - if ( - _.get(challengeDetails, 'id') !== _.get(nextChallengeDetails, 'id') && - challengeId === _.get(nextChallengeDetails, 'id') - ) { - this.loadAssignedMemberDetails(nextProps) - } - } - - /** - * Load assign member details if challenge has a member assigned - * @param {Object} nextProps the latest props - */ - loadAssignedMemberDetails (nextProps) { - // cannot use `loadMemberDetails` form the `nextProps` because linter complains about unused prop - const { loadMemberDetails } = this.props - const { challengeDetails } = nextProps - const assignedMemberId = _.get(challengeDetails, 'task.memberId') - - if (assignedMemberId) { - loadMemberDetails(assignedMemberId) - } } async fetchChallengeDetails (newMatch, loadChallengeDetails, loadResources) { @@ -280,7 +254,6 @@ ChallengeEditor.propTypes = { loggedInUser: PropTypes.object, removeAttachment: PropTypes.func, failedToLoad: PropTypes.bool, - loadMemberDetails: PropTypes.func, updateChallengeDetails: PropTypes.func.isRequired, partiallyUpdateChallengeDetails: PropTypes.func.isRequired, createChallenge: PropTypes.func.isRequired, @@ -317,7 +290,6 @@ const mapDispatchToProps = { // loadChallengeTerms, loadResources, loadResourceRoles, - loadMemberDetails, updateChallengeDetails, partiallyUpdateChallengeDetails, createChallenge, diff --git a/src/util/tc.js b/src/util/tc.js index d22f7da8..9dc63376 100644 --- a/src/util/tc.js +++ b/src/util/tc.js @@ -1,7 +1,7 @@ /** * Topcoder related utilities */ -import { MARATHON_MATCH_SUBTRACKS, CHALLENGE_TRACKS, ALLOWED_USER_ROLES, ADMIN_ROLES } from '../config/constants' +import { MARATHON_MATCH_SUBTRACKS, CHALLENGE_TRACKS, ALLOWED_USER_ROLES, ADMIN_ROLES, SUBMITTER_ROLE_UUID } from '../config/constants' import _ from 'lodash' import { decodeToken } from 'tc-auth-lib' @@ -59,3 +59,21 @@ export const checkAdmin = (token) => { const roles = _.get(decodeToken(token), 'roles') return roles.some(val => ADMIN_ROLES.indexOf(val.toLowerCase()) > -1) } + +/** + * Get resource role by name + * + * @param {Object[]} resourceRoles list of resource roles + * @param {String} name resource role name + * + * @returns {Object} resource role or `null` + */ +export const getResourceRoleByName = (resourceRoles, name) => { + // there are multiple junk resource roles with 'Submitter' name, + // so we use `id` from config to find the correct one + if (name === 'Submitter') { + return _.find(resourceRoles, { id: SUBMITTER_ROLE_UUID }) || null + } else { + return _.find(resourceRoles, { name }) || null + } +}