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
@@ -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: