From fc2c8cb6b780a64dec2ea7cd4b54edcd0a4c5e95 Mon Sep 17 00:00:00 2001 From: maxceem Date: Wed, 30 Sep 2020 11:19:42 +0300 Subject: [PATCH 01/11] feat: create forums during challenge creation ref issue #845 --- config/constants/development.js | 3 ++- config/constants/production.js | 3 ++- src/components/ChallengeEditor/index.js | 19 ++++++++++++++++++- src/config/constants.js | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/config/constants/development.js b/config/constants/development.js index 89dba60d..4619d647 100644 --- a/config/constants/development.js +++ b/config/constants/development.js @@ -31,5 +31,6 @@ module.exports = { DES_TRACK_ID: '5fa04185-041f-49a6-bfd1-fe82533cd6c8', DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d', QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115', - SEGMENT_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n' + SEGMENT_API_KEY: 'QBtLgV8vCiuRX1lDikbMjcoe9aCHkF6n', + CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c'] } diff --git a/config/constants/production.js b/config/constants/production.js index f7f7e18b..835c1a23 100644 --- a/config/constants/production.js +++ b/config/constants/production.js @@ -31,5 +31,6 @@ module.exports = { DES_TRACK_ID: '5fa04185-041f-49a6-bfd1-fe82533cd6c8', DS_TRACK_ID: 'c0f5d461-8219-4c14-878a-c3a3f356466d', QA_TRACK_ID: '36e6a8d0-7e1e-4608-a673-64279d99c115', - SEGMENT_API_KEY: 'QSQAW5BWmZfLoKFNRgNKaqHvLDLJoGqF' + SEGMENT_API_KEY: 'QSQAW5BWmZfLoKFNRgNKaqHvLDLJoGqF', + CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c'] } diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 08118594..3d04e88d 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -15,7 +15,8 @@ import { PRIZE_SETS_TYPE, DEFAULT_TERM_UUID, DEFAULT_NDA_UUID, - SUBMITTER_ROLE_UUID + SUBMITTER_ROLE_UUID, + CREATE_FORUM_TYPE_IDS } from '../../config/constants' import { PrimaryButton, OutlineButton } from '../Buttons' import TrackField from './Track-Field' @@ -767,6 +768,10 @@ class ChallengeEditor extends Component { phases: this.getTemplatePhases(defaultTemplate) // prizeSets: this.getDefaultPrizeSets() } + const discussions = this.getDiscussionsConfig(newChallenge) + if (discussions) { + newChallenge.discussions = discussions + } try { const action = await createChallenge(newChallenge) const draftChallenge = { @@ -779,6 +784,18 @@ class ChallengeEditor extends Component { } } + getDiscussionsConfig (challenge) { + if (_.includes(CREATE_FORUM_TYPE_IDS, challenge.typeId)) { + return ([ + { + name: `${challenge.name} Discussion`, + type: 'challenge', + provider: 'vanilla' + } + ]) + } + } + getTemplatePhases (template) { const timelinePhaseIds = template.phases.map(timelinePhase => timelinePhase.phaseId || timelinePhase) const validPhases = _.cloneDeep(this.props.metadata.challengePhases).filter(challengePhase => { diff --git a/src/config/constants.js b/src/config/constants.js index e8b1072b..b17c3403 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -16,6 +16,7 @@ export const { QA_TRACK_ID, SEGMENT_API_KEY } = process.env +export const CREATE_FORUM_TYPE_IDS = typeof process.env.CREATE_FORUM_TYPE_IDS === 'string' ? process.env.CREATE_FORUM_TYPE_IDS.split(',') : process.env.CREATE_FORUM_TYPE_IDS // Actions export const LOAD_PROJECTS_SUCCESS = 'LOAD_PROJECTS_SUCCESS' From d743acd7e693a5a738da2adc5e9c373d840a962e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 30 Sep 2020 17:24:37 +0530 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20git#746-Add=20"Provide=20Applicat?= =?UTF-8?q?ion=20Feedback"=20Capability=20to=20Work=20Manager=20App=20?= =?UTF-8?q?=E2=80=94=20Linked=20to=20google=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sidebar/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Sidebar/index.js b/src/components/Sidebar/index.js index a9be7f96..a637147c 100644 --- a/src/components/Sidebar/index.js +++ b/src/components/Sidebar/index.js @@ -20,7 +20,7 @@ const Sidebar = ({ Active challenges - +
Give Application Feedback
From 1741fbfddabedac938b6059e35905e90a6fef7a5 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 30 Sep 2020 17:42:23 +0530 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20git#746-Add=20"Provide=20Applicat?= =?UTF-8?q?ion=20Feedback"=20Capability=20to=20Work=20Manager=20App=20?= =?UTF-8?q?=E2=80=94=20Linked=20to=20google=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Sidebar/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Sidebar/index.js b/src/components/Sidebar/index.js index a637147c..9e28cfdd 100644 --- a/src/components/Sidebar/index.js +++ b/src/components/Sidebar/index.js @@ -20,11 +20,11 @@ const Sidebar = ({ Active challenges - +
Give Application Feedback
- +

Have an urgent issue?
E: support@topcoder.com From c59b44513a63a38855818e9804664509fdcde3b4 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 5 Oct 2020 11:30:57 +0530 Subject: [PATCH 04/11] fix: git#849-Error message displayed while activating the challenge but it won't reflect the actual situation of the error --- src/actions/challenges.js | 6 ++++-- src/reducers/challenges.js | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 13e8b58f..3e9bc6f2 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -203,10 +203,12 @@ export function updateChallengeDetails (challengeId, challengeDetails) { type: UPDATE_CHALLENGE_DETAILS_SUCCESS, challengeDetails: challenge }) - }).catch(() => { + }).catch((error) => { dispatch({ - type: UPDATE_CHALLENGE_DETAILS_FAILURE + type: UPDATE_CHALLENGE_DETAILS_FAILURE, + error }) + return Promise.reject(error) }) } } diff --git a/src/reducers/challenges.js b/src/reducers/challenges.js index 45d6a44c..bb60551e 100644 --- a/src/reducers/challenges.js +++ b/src/reducers/challenges.js @@ -87,7 +87,6 @@ export default function (state = initialState, action) { case LOAD_CHALLENGES_FAILURE: return { ...state, isLoading: false } case LOAD_CHALLENGE_DETAILS_FAILURE: - case UPDATE_CHALLENGE_DETAILS_FAILURE: case CREATE_CHALLENGE_FAILURE: return { ...state, isLoading: false, attachments: [], challenge: null, failedToLoad: true } case LOAD_CHALLENGE_DETAILS_SUCCESS: { @@ -143,6 +142,8 @@ export default function (state = initialState, action) { failedToLoad: false } } + case UPDATE_CHALLENGE_DETAILS_FAILURE: + return { ...state, isLoading: false, attachments: [], challenge: null, failedToLoad: false, failedToUpdate: true } case CREATE_CHALLENGE_SUCCESS: { // if we are showing the list of challenges with the same status as we just created, // then add the new challenge to the beginning of the current challenge list From 5bbf6c6bd984c95228dfe5d3ff0d0f7bcd97f47d Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 5 Oct 2020 12:17:38 +0530 Subject: [PATCH 05/11] fix: git#477-Popup displayed when we add a button inside the 'Public specification' --- src/components/ChallengeEditor/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 3d04e88d..59309b6f 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1199,7 +1199,7 @@ class ChallengeEditor extends Component { { isDraft &&

- +
} } {!isLoading && isActive &&
@@ -1231,7 +1231,7 @@ class ChallengeEditor extends Component { { actionButtons } ) : ( -
+ e.preventDefault()}>
From 229350d69f86163681b96daf4605248430a52d65 Mon Sep 17 00:00:00 2001 From: maxceem Date: Mon, 5 Oct 2020 13:08:01 +0300 Subject: [PATCH 06/11] fix: prizes object format ref issue #847 --- .../ChallengeEditor/ChallengePrizes-Field/index.js | 4 ++-- .../ChallengeEditor/CheckpointPrizes-Field/index.js | 6 +++--- src/components/ChallengeEditor/CopilotFee-Field/index.js | 6 +++--- src/components/ChallengeEditor/ReviewCost-Field/index.js | 6 +++--- src/config/constants.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengePrizes-Field/index.js b/src/components/ChallengeEditor/ChallengePrizes-Field/index.js index b6b804c5..12a850d7 100644 --- a/src/components/ChallengeEditor/ChallengePrizes-Field/index.js +++ b/src/components/ChallengeEditor/ChallengePrizes-Field/index.js @@ -26,7 +26,7 @@ class ChallengePrizesField extends Component { addNewPrize () { const challengePrize = this.getChallengePrize() - challengePrize.prizes = [...challengePrize.prizes, { type: CHALLENGE_PRIZE_TYPE.MONEY, value: 1 }] + challengePrize.prizes = [...challengePrize.prizes, { type: CHALLENGE_PRIZE_TYPE.USD, value: 1 }] this.onUpdateValue(challengePrize) } @@ -55,7 +55,7 @@ class ChallengePrizesField extends Component { getChallengePrize () { const type = PRIZE_SETS_TYPE.CHALLENGE_PRIZES - return (this.props.challenge.prizeSets && this.props.challenge.prizeSets.length && this.props.challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type: CHALLENGE_PRIZE_TYPE.MONEY, value: 0 }] } + return (this.props.challenge.prizeSets && this.props.challenge.prizeSets.length && this.props.challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type: CHALLENGE_PRIZE_TYPE.USD, value: 0 }] } } renderPrizes () { diff --git a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js index 9afd7790..5f1fe512 100644 --- a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js +++ b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js @@ -4,17 +4,17 @@ import styles from './CheckpointPrizes-Field.module.scss' import cn from 'classnames' import { range } from 'lodash' import { validateValue } from '../../../util/input-check' -import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE, CHALLENGE_PRIZE_TYPE } from '../../../config/constants' const CheckpointPrizesField = ({ challenge, onUpdateOthers }) => { const type = PRIZE_SETS_TYPE.CHECKPOINT_PRIZES - const checkpointPrize = challenge.prizeSets.find(p => p.type === type) || { type, prizes: [] } + const checkpointPrize = challenge.prizeSets.find(p => p.type === type) || { type: CHALLENGE_PRIZE_TYPE.USD, prizes: [] } const number = checkpointPrize.prizes.length const amount = checkpointPrize.prizes.length ? checkpointPrize.prizes[0].value : 0 function onChange (number, amount) { checkpointPrize.prizes = range(validateValue(number, VALIDATION_VALUE_TYPE.INTEGER)) - .map(i => ({ type: 'Prize ' + i, value: validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER, '$') })) + .map(i => ({ type: CHALLENGE_PRIZE_TYPE.USD, value: validateValue(amount, VALIDATION_VALUE_TYPE.INTEGER, '$') })) onUpdateOthers({ field: 'prizeSets', value: [...challenge.prizeSets.filter(p => p.type !== type), +number && checkpointPrize].filter(p => p) }) } return ( diff --git a/src/components/ChallengeEditor/CopilotFee-Field/index.js b/src/components/ChallengeEditor/CopilotFee-Field/index.js index d7d357de..fbee9df3 100644 --- a/src/components/ChallengeEditor/CopilotFee-Field/index.js +++ b/src/components/ChallengeEditor/CopilotFee-Field/index.js @@ -5,11 +5,11 @@ import PropTypes from 'prop-types' import { validateValue } from '../../../util/input-check' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faDollarSign } from '@fortawesome/free-solid-svg-icons' -import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE, CHALLENGE_PRIZE_TYPE } from '../../../config/constants' const CopilotFeeField = ({ challenge, onUpdateOthers, readOnly }) => { const type = PRIZE_SETS_TYPE.COPILOT_PAYMENT - const copilotFee = (challenge.prizeSets && challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type, value: 0 }] } + const copilotFee = (challenge.prizeSets && challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type: CHALLENGE_PRIZE_TYPE.USD, value: 0 }] } const value = copilotFee.prizes[0].value function onChange (e) { @@ -17,7 +17,7 @@ const CopilotFeeField = ({ challenge, onUpdateOthers, readOnly }) => { if (parseInt(value) > 1000000) { value = '1000000' } - copilotFee.prizes = [{ type, value }] + copilotFee.prizes = [{ type: CHALLENGE_PRIZE_TYPE.USD, value }] onUpdateOthers({ field: 'prizeSets', value: [...challenge.prizeSets.filter(p => p.type !== type), copilotFee] }) } diff --git a/src/components/ChallengeEditor/ReviewCost-Field/index.js b/src/components/ChallengeEditor/ReviewCost-Field/index.js index 11f288fc..03099bf6 100644 --- a/src/components/ChallengeEditor/ReviewCost-Field/index.js +++ b/src/components/ChallengeEditor/ReviewCost-Field/index.js @@ -3,16 +3,16 @@ import PropTypes from 'prop-types' import styles from './ReviewCost-Field.module.scss' import cn from 'classnames' import { validateValue } from '../../../util/input-check' -import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE, CHALLENGE_PRIZE_TYPE } from '../../../config/constants' const ReviewCostField = ({ challenge, onUpdateOthers }) => { const type = PRIZE_SETS_TYPE.REVIEWER_PAYMENT - const reviewCost = challenge.prizeSets.find(p => p.type === type) || { type, prizes: [{ type, value: 0 }] } + const reviewCost = challenge.prizeSets.find(p => p.type === type) || { type, prizes: [{ type: CHALLENGE_PRIZE_TYPE.USD, value: 0 }] } const value = reviewCost.prizes[0].value function onChange (e) { const value = validateValue(e.target.value, VALIDATION_VALUE_TYPE.INTEGER, '$') - reviewCost.prizes = [{ type, value }] + reviewCost.prizes = [{ type: CHALLENGE_PRIZE_TYPE.USD, value }] onUpdateOthers({ field: 'prizeSets', value: [...challenge.prizeSets.filter(p => p.type !== type), reviewCost] }) } diff --git a/src/config/constants.js b/src/config/constants.js index b17c3403..833c02e5 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -135,7 +135,7 @@ export const VALIDATION_VALUE_TYPE = { } export const CHALLENGE_PRIZE_TYPE = { - MONEY: 'money' + USD: 'USD' } export const ALLOWED_USER_ROLES = [ From 7445b5780a870e7def12e6799fbe796b65fbc6c3 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 6 Oct 2020 14:29:11 +0530 Subject: [PATCH 07/11] fix: git#860-Error message not displayed when updating challenge --- src/components/ChallengeEditor/ChallengeEditor.module.scss | 6 ++++++ src/components/ChallengeEditor/index.js | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/ChallengeEditor.module.scss b/src/components/ChallengeEditor/ChallengeEditor.module.scss index 23c723f8..b66f644a 100644 --- a/src/components/ChallengeEditor/ChallengeEditor.module.scss +++ b/src/components/ChallengeEditor/ChallengeEditor.module.scss @@ -397,3 +397,9 @@ align-items: center; } +.errorContainer { + .errorMessage { + color: $red; + } +} + diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 59309b6f..aeccb226 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1025,7 +1025,7 @@ class ChallengeEditor extends Component { return
Error loading challenge
} const isTask = _.get(challenge, 'task.isTask', false) - const { assignedMemberDetails } = this.state + const { assignedMemberDetails, error } = this.state let isActive = false let isDraft = false let isCompleted = false @@ -1179,6 +1179,8 @@ class ChallengeEditor extends Component { ) } + const errorContainer =
{error}
+ const actionButtons = {!isLoading && this.state.hasValidationErrors &&
Please fix the errors before saving
} { @@ -1228,6 +1230,7 @@ class ChallengeEditor extends Component {
+ { errorContainer } { actionButtons } ) : ( @@ -1348,6 +1351,7 @@ class ChallengeEditor extends Component {
+ { errorContainer } { actionButtons } ) From 64c5b62ba314e192cb978d8a122123b0d7c7e486 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 6 Oct 2020 14:29:48 +0530 Subject: [PATCH 08/11] fix: handling mismatch of data types for projectId field on the challenge object, for some challenges it is string --- src/actions/challenges.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 3e9bc6f2..4c52f003 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -174,7 +174,7 @@ export function loadChallengeDetails (projectId, challengeId) { payload: fetchChallenge(challengeId).then((challenge) => { // TODO remove this unncessary check, or better utilize the the case when given project id // does not match with challenge's project id - if (challenge.projectId === projectId) { + if (challenge.projectId == projectId) { // eslint-disable-line dispatch(loadProject(projectId)) } return challenge From 8516ebd324e4e55907f1cfb7c19d01ffbe682ec1 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 6 Oct 2020 17:57:48 +0530 Subject: [PATCH 09/11] fix: git#853-No validation displayed if the user doesn't have proper permission to edit the challenge after activating it - Removed auto redirection to view page on success. We might need to handle the success via separate modal. --- src/components/ChallengeEditor/index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index aeccb226..d1a72bb9 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -936,11 +936,7 @@ class ChallengeEditor extends Component { } async onlySave () { - this.updateAllChallengeInfo(this.state.challenge.status, () => { - this.resetModal() - const { history } = this.props - history.push('./view') - }) + this.updateAllChallengeInfo(this.state.challenge.status) } getResourceRoleByName (name) { From e824c82fc42ceacada0fd1ea3e0bcd51da39e749 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 6 Oct 2020 17:58:54 +0530 Subject: [PATCH 10/11] feat: removing unused alert --- src/components/ChallengeEditor/index.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index d1a72bb9..8f6d4433 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1057,7 +1057,6 @@ class ChallengeEditor extends Component { let activateModal = null let closeTaskModal = null let draftModal = null - let savedModal = null let { type } = challenge if (!type) { @@ -1069,20 +1068,6 @@ class ChallengeEditor extends Component { } } } - if (!isNew && challenge.status === 'New' && isLaunch && isConfirm) { - savedModal = ( - - ) - } if (!isNew && isLaunch && !isConfirm) { activateModal = ( @@ -1366,7 +1351,6 @@ class ChallengeEditor extends Component {
{ activateModal } { draftModal } - { savedModal } { closeTaskModal }
{ challengeForm } From b4f3dbb0dd514c84ef54c21243033daf36330ccb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 9 Oct 2020 20:01:07 +0530 Subject: [PATCH 11/11] feat: git#558 - Unauthorize Copilot Can Access Challenges - Checking if user is part of the project and is having a role with write access (fullAccess:true) to edit the challenge --- src/actions/challenges.js | 30 ++++--------------- .../ChallengeEditor/ChallengeView/index.js | 16 ++++++++-- src/config/constants.js | 1 + .../ChallengeEditor.module.scss | 7 +++++ src/containers/ChallengeEditor/index.js | 29 ++++++++++++++++-- src/reducers/challenges.js | 2 +- src/reducers/projects.js | 8 +++-- 7 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 src/containers/ChallengeEditor/ChallengeEditor.module.scss diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 4c52f003..641b9542 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -28,9 +28,6 @@ import { UPLOAD_ATTACHMENT_FAILURE, UPLOAD_ATTACHMENT_PENDING, UPLOAD_ATTACHMENT_SUCCESS, - LOAD_CHALLENGE_RESOURCES_PENDING, - LOAD_CHALLENGE_RESOURCES_SUCCESS, - LOAD_CHALLENGE_RESOURCES_FAILURE, CREATE_CHALLENGE_RESOURCE, DELETE_CHALLENGE_RESOURCE, REMOVE_ATTACHMENT, @@ -40,7 +37,8 @@ import { UPDATE_CHALLENGE_DETAILS_FAILURE, CREATE_CHALLENGE_PENDING, CREATE_CHALLENGE_SUCCESS, - CREATE_CHALLENGE_FAILURE + CREATE_CHALLENGE_FAILURE, + LOAD_CHALLENGE_RESOURCES } from '../config/constants' import { loadProject } from './projects' @@ -396,27 +394,11 @@ export function loadChallengeTerms () { } export function loadResources (challengeId) { - return async (dispatch) => { - dispatch({ - type: LOAD_CHALLENGE_RESOURCES_PENDING, - challengeResources: {} - }) - + return (dispatch, getState) => { if (challengeId) { - return fetchResources(challengeId).then((resources) => { - dispatch({ - type: LOAD_CHALLENGE_RESOURCES_SUCCESS, - challengeResources: resources - }) - }).catch(() => { - dispatch({ - type: LOAD_CHALLENGE_RESOURCES_FAILURE - }) - }) - } else { - dispatch({ - type: LOAD_CHALLENGE_RESOURCES_SUCCESS, - challengeResources: null + return dispatch({ + type: LOAD_CHALLENGE_RESOURCES, + payload: fetchResources(challengeId) }) } } diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 31b74caf..164787f6 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -21,7 +21,16 @@ import PhaseInput from '../../PhaseInput' import LegacyLinks from '../../LegacyLinks' import AssignedMemberField from '../AssignedMember-Field' -const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources, token, isLoading, challengeId, assignedMemberDetails }) => { +const ChallengeView = ({ + projectDetail, + challenge, + metadata, + challengeResources, + token, + isLoading, + challengeId, + assignedMemberDetails, + enableEdit }) => { const selectedType = _.find(metadata.challengeTypes, { id: challenge.typeId }) const challengeTrack = _.find(metadata.challengeTracks, { id: challenge.trackId }) @@ -64,7 +73,7 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources,
View Details
- + { enableEdit && }
@@ -202,7 +211,8 @@ ChallengeView.propTypes = { isLoading: PropTypes.bool.isRequired, challengeId: PropTypes.string.isRequired, challengeResources: PropTypes.arrayOf(PropTypes.object), - assignedMemberDetails: PropTypes.shape() + assignedMemberDetails: PropTypes.shape(), + enableEdit: PropTypes.bool } export default withRouter(ChallengeView) diff --git a/src/config/constants.js b/src/config/constants.js index 833c02e5..5ae0d333 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -66,6 +66,7 @@ export const UPLOAD_ATTACHMENT_PENDING = 'UPLOAD_ATTACHMENT_PENDING' export const UPLOAD_ATTACHMENT_FAILURE = 'UPLOAD_ATTACHMENT_FAILURE' export const UPLOAD_ATTACHMENT_SUCCESS = 'UPLOAD_ATTACHMENT_SUCCESS' +export const LOAD_CHALLENGE_RESOURCES = 'LOAD_CHALLENGE_RESOURCES' export const LOAD_CHALLENGE_RESOURCES_SUCCESS = 'LOAD_CHALLENGE_RESOURCES_SUCCESS' export const LOAD_CHALLENGE_RESOURCES_PENDING = 'LOAD_CHALLENGE_RESOURCES_PENDING' export const LOAD_CHALLENGE_RESOURCES_FAILURE = 'LOAD_CHALLENGE_RESOURCES_FAILURE' diff --git a/src/containers/ChallengeEditor/ChallengeEditor.module.scss b/src/containers/ChallengeEditor/ChallengeEditor.module.scss new file mode 100644 index 00000000..ce43f8f3 --- /dev/null +++ b/src/containers/ChallengeEditor/ChallengeEditor.module.scss @@ -0,0 +1,7 @@ +@import "../../styles/includes"; + + +.errorContainer { + color: $red; + padding: 10px; +} \ No newline at end of file diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index 7f6e35a0..75aae0c2 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -5,6 +5,7 @@ import { withRouter, Route } from 'react-router-dom' import ChallengeEditorComponent from '../../components/ChallengeEditor' import ChallengeViewComponent from '../../components/ChallengeEditor/ChallengeView' import Loader from '../../components/Loader' +import styles from './ChallengeEditor.module.scss' import { loadTimelineTemplates, @@ -113,10 +114,21 @@ class ChallengeEditor extends Component { loadChallengeDetails(projectId, challengeId) } + isEditable () { + const { hasProjectAccess, metadata: { resourceRoles }, challengeResources, loggedInUser } = this.props + if (!hasProjectAccess) { + return false + } + const userRoles = _.filter(challengeResources, cr => cr.memberId === `${loggedInUser.userId}`) + const userResourceRoles = _.filter(resourceRoles, rr => _.some(userRoles, ur => ur.roleId === rr.id)) + return _.some(userResourceRoles, urr => urr.fullAccess && urr.isActive) + } + render () { const { match, isLoading, + isProjectLoading, challengeDetails, challengeResources, metadata, @@ -132,6 +144,7 @@ class ChallengeEditor extends Component { replaceResourceInRole // members } = this.props + if (isProjectLoading || isLoading) return const challengeId = _.get(match.params, 'challengeId', null) if (challengeId && (!challengeDetails || !challengeDetails.id)) { return () @@ -144,6 +157,7 @@ class ChallengeEditor extends Component { handle: submitters[0].memberHandle } } + const enableEdit = this.isEditable() return
)) } /> - You don't have access to edit the challenge
} + { enableEdit && (( @@ -196,6 +211,7 @@ class ChallengeEditor extends Component { /> )) } /> + } )) } /> @@ -237,6 +254,8 @@ ChallengeEditor.propTypes = { loadResourceRoles: PropTypes.func, challengeResources: PropTypes.arrayOf(PropTypes.object), challengeDetails: PropTypes.object, + isProjectLoading: PropTypes.bool, + hasProjectAccess: PropTypes.bool, projectDetail: PropTypes.object, // history: PropTypes.object, metadata: PropTypes.shape({ @@ -246,6 +265,7 @@ ChallengeEditor.propTypes = { createAttachment: PropTypes.func, attachments: PropTypes.arrayOf(PropTypes.shape()), token: PropTypes.string, + loggedInUser: PropTypes.object, removeAttachment: PropTypes.func, failedToLoad: PropTypes.bool, loadMemberDetails: PropTypes.func, @@ -256,14 +276,17 @@ ChallengeEditor.propTypes = { // members: PropTypes.arrayOf(PropTypes.shape()) } -const mapStateToProps = ({ projects: { projectDetail }, challenges: { challengeDetails, challengeResources, metadata, isLoading, attachments, failedToLoad }, auth: { token }, members: { members } }) => ({ +const mapStateToProps = ({ projects, challenges: { challengeDetails, challengeResources, metadata, isLoading, attachments, failedToLoad }, auth: { token, user }, members: { members } }) => ({ challengeDetails, - projectDetail, + hasProjectAccess: projects.hasProjectAccess, + projectDetail: projects.projectDetail, challengeResources, metadata, isLoading, + isProjectLoading: projects.isLoading, attachments, token, + loggedInUser: user, failedToLoad // members }) diff --git a/src/reducers/challenges.js b/src/reducers/challenges.js index bb60551e..df398975 100644 --- a/src/reducers/challenges.js +++ b/src/reducers/challenges.js @@ -166,7 +166,7 @@ export default function (state = initialState, action) { case LOAD_CHALLENGE_RESOURCES_SUCCESS: return { ...state, - challengeResources: action.challengeResources, + challengeResources: action.payload, isLoading: false, failedToLoad: false } diff --git a/src/reducers/projects.js b/src/reducers/projects.js index 0d28f3a2..f80e9a93 100644 --- a/src/reducers/projects.js +++ b/src/reducers/projects.js @@ -1,6 +1,7 @@ /** * Reducer to process actions related to project */ +import _ from 'lodash' import { LOAD_PROJECT_DETAILS_FAILURE, LOAD_PROJECT_DETAILS_PENDING, @@ -16,12 +17,15 @@ export default function (state = initialState, action) { switch (action.type) { case LOAD_PROJECT_DETAILS_PENDING: return { ...state, isLoading: true } - case LOAD_PROJECT_DETAILS_FAILURE: - return { ...state, isLoading: false } + case LOAD_PROJECT_DETAILS_FAILURE: { + const status = _.get(action, 'payload.response.status', 500) + return { ...state, isLoading: false, hasProjectAccess: status !== 403 } + } case LOAD_PROJECT_DETAILS_SUCCESS: return { ...state, projectDetail: action.payload, + hasProjectAccess: true, isLoading: false } default: