- {sortedSubmissions.map(s => {
- const rating = s.registrant && !_.isNil(s.registrant.rating)
- ? s.registrant.rating
- : '-'
- const memberHandle = _.get(s.registrant, 'memberHandle', '')
- const email = _.get(s.registrant, 'email', '')
- const submissionDate = moment(s.created).format('MMM DD, YYYY HH:mm')
- return (
-
+
+
+
+
{!isF2F && !isBugHunt && (
-
-
- {rating}
-
-
+
+
+ |
)}
-
-
-
- {email}
-
-
-
-
- {submissionDate}
-
-
-
-
-
- {s.id}
-
-
-
-
- {s.legacySubmissionId}
-
-
- {canDownloadSubmission ? (
+
Username
+
+
+
+
+
+
+ |
+
+
- ) : null}
-
- )
- })}
+ |
+
+
+ |
+
+ Submission ID (UUID)
+ |
+
+ Legacy submission ID
+ |
+ {canDownloadSubmission ? (
+ Actions
+ | ) : null}
+
+
+
+ {sortedSubmissions.map(s => {
+ const rating = s.registrant && !_.isNil(s.registrant.rating)
+ ? s.registrant.rating
+ : '-'
+ const memberHandle = _.get(s.registrant, 'memberHandle', '')
+ const email = _.get(s.registrant, 'email', '')
+ const submissionDate = moment(s.created).format('MMM DD, YYYY HH:mm')
+ return (
+
+ {!isF2F && !isBugHunt && (
+
+
+ {rating}
+
+ |
+ )}
+
+
+ {memberHandle}
+
+ |
+
+
+ {email}
+
+ |
+
+
+ {submissionDate}
+
+ |
+
+
+ {!_.isEmpty(s.review) && s.review[0].score
+ ? parseFloat(s.review[0].score).toFixed(2)
+ : 'N/A'}
+ /
+ {s.reviewSummation && s.reviewSummation[0].aggregateScore
+ ? parseFloat(s.reviewSummation[0].aggregateScore).toFixed(2)
+ : 'N/A'}
+
+ |
+
+
+ {s.id}
+
+ |
+
+
+ {s.legacySubmissionId}
+
+ |
+ {canDownloadSubmission ? (
+
+ | ) : null}
+
+ )
+ })}
+
+
{canDownloadSubmission ? (
diff --git a/src/components/ChallengesComponent/ChallengeCard/index.js b/src/components/ChallengesComponent/ChallengeCard/index.js
index 1afedc34..4347bc98 100644
--- a/src/components/ChallengesComponent/ChallengeCard/index.js
+++ b/src/components/ChallengesComponent/ChallengeCard/index.js
@@ -15,6 +15,7 @@ import { formatDate } from '../../../util/date'
import { CHALLENGE_STATUS, COMMUNITY_APP_URL, DIRECT_PROJECT_URL, MESSAGE, ONLINE_REVIEW_URL, PROJECT_ROLES } from '../../../config/constants'
import ConfirmationModal from '../../Modal/ConfirmationModal'
import { checkChallengeEditPermission, checkReadOnlyRoles } from '../../../util/tc'
+import { getCurrentPhase } from '../../../util/phase'
import AlertModal from '../../Modal/AlertModal'
import Tooltip from '../../Tooltip'
@@ -121,7 +122,7 @@ class ChallengeCard extends React.Component {
isCheckChalengePermission: false,
hasEditChallengePermission: false,
loginUserRoleInProject: '',
- currentPhase: this.getCurrentPhase(props.challenge),
+ currentPhase: getCurrentPhase(props.challenge),
forumLink: this.getForumLink(props.challenge)
}
this.onUpdateConfirm = this.onUpdateConfirm.bind(this)
@@ -132,10 +133,6 @@ class ChallengeCard extends React.Component {
this.onLaunchChallenge = this.onLaunchChallenge.bind(this)
}
- getCurrentPhase (challenge) {
- return challenge.phases.filter((p) => p.isOpen).map((p) => p.name).join(' / ') || '-'
- }
-
getForumLink (challenge) {
const discussionsHaveUrls = (challenge.discussions || []).filter((p) => !!p.url)
return discussionsHaveUrls.length ? discussionsHaveUrls[0].url : ''
@@ -146,7 +143,7 @@ class ChallengeCard extends React.Component {
if (!_.isEqual(challenge.phases, prevProps.challenge.phases)) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
- currentPhase: this.getCurrentPhase(challenge)
+ currentPhase: getCurrentPhase(challenge)
})
}
if (!_.isEqual(challenge.discussions, prevProps.challenge.discussions)) {
diff --git a/src/components/UserCard/index.js b/src/components/UserCard/index.js
index 318ce474..87387714 100644
--- a/src/components/UserCard/index.js
+++ b/src/components/UserCard/index.js
@@ -6,7 +6,6 @@ import { PROJECT_ROLES } from '../../config/constants'
import PrimaryButton from '../Buttons/PrimaryButton'
import AlertModal from '../Modal/AlertModal'
import { updateProjectMemberRole } from '../../services/projects'
-import { wait } from '../../util/helper'
import _ from 'lodash'
const theme = {
@@ -42,12 +41,11 @@ class UserCard extends Component {
isUpdatingPermission: true
})
- const { user, reloadProjectMembers } = this.props
+ const { user, updateProjectNember } = this.props
try {
- await updateProjectMemberRole(user.projectId, user.id, newRole)
- await wait(1000)
- reloadProjectMembers(user.projectId)
+ const newUserInfoRole = await updateProjectMemberRole(user.projectId, user.id, newRole)
+ updateProjectNember(newUserInfoRole)
this.setState({ showSuccessModal: true })
} catch (e) {
const error = _.get(
@@ -176,7 +174,7 @@ class UserCard extends Component {
UserCard.propTypes = {
user: PropTypes.object,
- reloadProjectMembers: PropTypes.func.isRequired,
+ updateProjectNember: PropTypes.func.isRequired,
onRemoveClick: PropTypes.func.isRequired,
isEditable: PropTypes.bool
}
diff --git a/src/components/Users/index.js b/src/components/Users/index.js
index b0a95933..1f9e7846 100644
--- a/src/components/Users/index.js
+++ b/src/components/Users/index.js
@@ -11,7 +11,6 @@ import SelectUserAutocomplete from '../SelectUserAutocomplete'
import { PROJECT_ROLES } from '../../config/constants'
import { checkAdmin } from '../../util/tc'
import { addUserToProject, removeUserFromProject } from '../../services/projects'
-import { wait } from '../../util/helper'
import ConfirmationModal from '../Modal/ConfirmationModal'
const theme = {
@@ -90,9 +89,7 @@ class Users extends Component {
}
async onAddUserConfirmClick () {
- console.log('in onAddUserConfirmClick')
- console.log('in onAddUserConfirmClick this.state.userToAdd', this.state.userToAdd)
- const { reloadProjectMembers } = this.props
+ const { addNewProjectMember } = this.props
if (this.state.isAdding) { return }
this.setState({
@@ -101,7 +98,6 @@ class Users extends Component {
})
if (!this.state.userToAdd) {
- console.log('in if')
this.setState({
showSelectUserError: true
})
@@ -113,10 +109,10 @@ class Users extends Component {
})
try {
- await addUserToProject(this.state.projectOption.value, this.state.userToAdd.userId, this.state.userPermissionToAdd)
+ const newUserInfo = await addUserToProject(this.state.projectOption.value, this.state.userToAdd.userId, this.state.userPermissionToAdd)
+ newUserInfo.handle = this.state.userToAdd.handle
// wait for a second so that project's members are updated
- await wait(1000)
- if (this.state.projectOption.value) { reloadProjectMembers(this.state.projectOption.value) }
+ addNewProjectMember(newUserInfo)
this.resetAddUserState()
} catch (e) {
const error = _.get(
@@ -167,13 +163,12 @@ class Users extends Component {
async onRemoveConfirmClick () {
if (this.state.isRemoving) { return }
- const { reloadProjectMembers } = this.props
+ const { removeProjectNember } = this.props
const userToRemove = this.state.userToRemove
try {
this.setState({ isRemoving: true })
await removeUserFromProject(userToRemove.projectId, userToRemove.id)
- await wait(1000)
- if (this.state.projectOption.value) { reloadProjectMembers(this.state.projectOption.value) }
+ removeProjectNember(userToRemove)
this.resetRemoveUserState()
} catch (e) {
@@ -196,7 +191,7 @@ class Users extends Component {
}
render () {
- const { projects, projectMembers, reloadProjectMembers, isEditable } = this.props
+ const { projects, projectMembers, updateProjectNember, isEditable } = this.props
const projectOptions = projects.map(p => {
return {
label: p.name,
@@ -402,7 +397,11 @@ class Users extends Component {
_.map(projectMembers, (member) => {
return (
-
+
)
})
@@ -419,7 +418,9 @@ class Users extends Component {
Users.propTypes = {
loadProject: PropTypes.func.isRequired,
- reloadProjectMembers: PropTypes.func.isRequired,
+ updateProjectNember: PropTypes.func.isRequired,
+ removeProjectNember: PropTypes.func.isRequired,
+ addNewProjectMember: PropTypes.func.isRequired,
auth: PropTypes.object,
isEditable: PropTypes.bool,
projects: PropTypes.arrayOf(PropTypes.object),
diff --git a/src/config/constants.js b/src/config/constants.js
index c422e346..d1c2c10e 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -205,6 +205,12 @@ export const ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES = [
PROJECT_ROLES.WRITE
]
+export const ALLOWED_EDIT_RESOURCE_ROLES = [
+ 'administrator',
+ PROJECT_ROLES.MANAGER,
+ PROJECT_ROLES.COPILOT
+]
+
export const CHALLENGE_STATUS = {
ACTIVE: 'ACTIVE',
NEW: 'NEW',
diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js
index 9c6e9396..f2d3c17c 100644
--- a/src/containers/ChallengeEditor/index.js
+++ b/src/containers/ChallengeEditor/index.js
@@ -28,7 +28,9 @@ import {
partiallyUpdateChallengeDetails,
deleteChallenge,
createChallenge,
- replaceResourceInRole
+ replaceResourceInRole,
+ createResource,
+ deleteResource
} from '../../actions/challenges'
import { loadSubmissions } from '../../actions/challengeSubmissions'
@@ -420,7 +422,9 @@ class ChallengeEditor extends Component {
loggedInUser,
projectPhases,
isProjectPhasesLoading,
- showRejectChallengeModal
+ showRejectChallengeModal,
+ createResource,
+ deleteResource
// members
} = this.props
const {
@@ -616,6 +620,8 @@ class ChallengeEditor extends Component {
showRejectChallengeModal={this.showRejectChallengeModal}
loggedInUser={loggedInUser}
onApproveChallenge={this.onApproveChallenge}
+ createResource={createResource}
+ deleteResource={deleteResource}
/>
)}
/>
@@ -667,6 +673,8 @@ ChallengeEditor.propTypes = {
partiallyUpdateChallengeDetails: PropTypes.func.isRequired,
createChallenge: PropTypes.func.isRequired,
deleteChallenge: PropTypes.func.isRequired,
+ createResource: PropTypes.func.isRequired,
+ deleteResource: PropTypes.func.isRequired,
replaceResourceInRole: PropTypes.func,
loadProject: PropTypes.func,
projectPhases: PropTypes.arrayOf(PropTypes.object),
@@ -731,7 +739,9 @@ const mapDispatchToProps = {
deleteChallenge,
createChallenge,
replaceResourceInRole,
- loadProject
+ loadProject,
+ createResource,
+ deleteResource
}
export default withRouter(
diff --git a/src/containers/Users/index.js b/src/containers/Users/index.js
index e38b3a32..e1b8a3ff 100644
--- a/src/containers/Users/index.js
+++ b/src/containers/Users/index.js
@@ -3,16 +3,21 @@ import { connect } from 'react-redux'
import _ from 'lodash'
import PT from 'prop-types'
import UsersComponent from '../../components/Users'
-import { loadProject, reloadProjectMembers } from '../../actions/projects'
import { PROJECT_ROLES } from '../../config/constants'
+import { fetchProjectById } from '../../services/projects'
class Users extends Component {
constructor (props) {
super(props)
this.state = {
- loginUserRoleInProject: ''
+ loginUserRoleInProject: '',
+ projectMembers: null
}
+ this.loadProject = this.loadProject.bind(this)
+ this.updateProjectNember = this.updateProjectNember.bind(this)
+ this.removeProjectNember = this.removeProjectNember.bind(this)
+ this.addNewProjectMember = this.addNewProjectMember.bind(this)
}
componentDidMount () {
@@ -27,9 +32,13 @@ class Users extends Component {
}
componentWillReceiveProps (nextProps) {
- const { projectDetail, loggedInUser } = nextProps
- if (projectDetail && loggedInUser) {
- const projectMembers = projectDetail.members
+ const { loggedInUser } = nextProps
+ const { projectMembers } = this.state
+ this.updateLoginUserRoleInProject(projectMembers, loggedInUser)
+ }
+
+ updateLoginUserRoleInProject (projectMembers, loggedInUser) {
+ if (projectMembers && loggedInUser) {
const loginUserProjectInfo = _.find(projectMembers, { userId: loggedInUser.userId })
if (loginUserProjectInfo && this.state.loginUserRoleInProject !== loginUserProjectInfo.role) {
this.setState({
@@ -39,52 +48,88 @@ class Users extends Component {
}
}
+ loadProject (projectId) {
+ fetchProjectById(projectId).then((project) => {
+ const projectMembers = _.get(project, 'members')
+ this.setState({
+ projectMembers
+ })
+ const { loggedInUser } = this.props
+ this.updateLoginUserRoleInProject(projectMembers, loggedInUser)
+ })
+ }
+
+ updateProjectNember (newMemberInfo) {
+ const { projectMembers } = this.state
+ const newProjectMembers = projectMembers.map(pm => pm.id === newMemberInfo.id ? ({
+ ...pm,
+ ...newMemberInfo
+ }) : pm)
+ const { loggedInUser } = this.props
+ this.setState({
+ projectMembers: newProjectMembers
+ })
+ this.updateLoginUserRoleInProject(newProjectMembers, loggedInUser)
+ }
+
+ removeProjectNember (projectMember) {
+ const { projectMembers } = this.state
+ const newProjectMembers = _.filter(projectMembers, pm => pm.id !== projectMember.id)
+ const { loggedInUser } = this.props
+ this.setState({
+ projectMembers: newProjectMembers
+ })
+ this.updateLoginUserRoleInProject(newProjectMembers, loggedInUser)
+ }
+
+ addNewProjectMember (projectMember) {
+ const { projectMembers } = this.state
+ const newProjectMembers = [
+ ...projectMembers,
+ projectMember
+ ]
+ const { loggedInUser } = this.props
+ this.setState({
+ projectMembers: newProjectMembers
+ })
+ this.updateLoginUserRoleInProject(newProjectMembers, loggedInUser)
+ }
+
render () {
const {
projects,
- loadProject,
- projectMembers,
- auth,
- reloadProjectMembers,
- projectDetail
+ auth
} = this.props
+ const {
+ projectMembers
+ } = this.state
return (
)
}
}
-const mapStateToProps = ({ sidebar, challenges, auth, projects }) => {
+const mapStateToProps = ({ sidebar, auth }) => {
return {
projects: sidebar.projects,
- projectMembers: _.get(challenges, 'metadata.members'),
- projectDetail: projects.projectDetail,
auth,
loggedInUser: auth.user
}
}
-const mapDispatchToProps = {
- loadProject,
- reloadProjectMembers
-}
-
Users.propTypes = {
- loadProject: PT.func.isRequired,
- reloadProjectMembers: PT.func.isRequired,
projects: PT.arrayOf(PT.object),
- projectMembers: PT.arrayOf(PT.object),
auth: PT.object,
- projectDetail: PT.object,
loggedInUser: PT.object
}
-export default connect(mapStateToProps, mapDispatchToProps)(Users)
+export default connect(mapStateToProps)(Users)
diff --git a/src/reducers/challenges.js b/src/reducers/challenges.js
index 56b816dd..5e455c56 100644
--- a/src/reducers/challenges.js
+++ b/src/reducers/challenges.js
@@ -240,8 +240,6 @@ export default function (state = initialState, action) {
}
}
case CREATE_CHALLENGE_RESOURCE_FAILURE: {
- const resource = action.payload
- console.log(resource)
return { ...state, isLoading: false, failedToCreate: true }
}
case DELETE_CHALLENGE_RESOURCE_SUCCESS: {
diff --git a/src/services/challenges.js b/src/services/challenges.js
index 51380f84..31e00f92 100644
--- a/src/services/challenges.js
+++ b/src/services/challenges.js
@@ -227,8 +227,16 @@ export async function createResource (resource) {
* @returns {Promise<*>}
*/
export async function fetchResources (challengeId) {
- const response = await axiosInstance.get(`${RESOURCES_API_URL}?challengeId=${challengeId}`)
- return _.get(response, 'data', [])
+ let page = 0
+ let totalPage = 1
+ let resouces = []
+ while (page < totalPage) {
+ page += 1
+ const response = await axiosInstance.get(`${RESOURCES_API_URL}?challengeId=${challengeId}&page=${page}&perPage=5000`)
+ resouces = [...resouces, ..._.get(response, 'data', [])]
+ totalPage = parseInt(response.headers['x-total-pages'])
+ }
+ return resouces
}
/**
diff --git a/src/services/user.js b/src/services/user.js
index f483b0d9..b52c72a1 100644
--- a/src/services/user.js
+++ b/src/services/user.js
@@ -32,13 +32,22 @@ export async function searchProfiles (fields, queryObject = {}, limit) {
* @returns {Promise<*>}
*/
export async function searchProfilesByUserIds (userIds, fields = 'userId,handle,firstName,lastName,email', limit) {
- return searchProfiles(
- fields,
- {
- userIds
- },
- limit
- )
+ const chunkSize = 50
+ let userInfos = []
+ const uniqueUserIds = _.uniq(userIds)
+ for (let i = 0; i < uniqueUserIds.length; i += chunkSize) {
+ const chunkUserIds = uniqueUserIds.slice(i, i + chunkSize)
+ const userInfosTmp = await searchProfiles(
+ fields,
+ {
+ userIds: chunkUserIds
+ },
+ limit
+ )
+ userInfos = [...userInfos, ...userInfosTmp]
+ }
+
+ return userInfos
}
/**
diff --git a/src/util/phase.js b/src/util/phase.js
index 8ff9068b..dc94b216 100644
--- a/src/util/phase.js
+++ b/src/util/phase.js
@@ -6,3 +6,7 @@ export const canChangeDuration = phase => {
}
return moment(phase.scheduledEndDate).isAfter()
}
+
+export const getCurrentPhase = (challenge) => {
+ return (challenge ? challenge.phases : []).filter((p) => p.isOpen).map((p) => p.name).join(' / ') || '-'
+}
diff --git a/src/util/tc.js b/src/util/tc.js
index fbdf589b..2f85ed5d 100644
--- a/src/util/tc.js
+++ b/src/util/tc.js
@@ -8,7 +8,8 @@ import {
ADMIN_ROLES,
SUBMITTER_ROLE_UUID,
READ_ONLY_ROLES,
- ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES
+ ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES,
+ ALLOWED_EDIT_RESOURCE_ROLES
} from '../config/constants'
import _ from 'lodash'
import { decodeToken } from 'tc-auth-lib'
@@ -180,6 +181,14 @@ export const checkDownloadSubmissionRoles = resourceRoles => {
return resourceRoles.some(val => ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES.indexOf(val.toLowerCase()) > -1)
}
+/**
+ * Checks if this role can edit resources
+ * @param resourceRoles
+ */
+export const checkEditResourceRoles = resourceRoles => {
+ return resourceRoles.some(val => _.filter(ALLOWED_EDIT_RESOURCE_ROLES, (r) => val.toLowerCase().indexOf(r) > -1).length > 0)
+}
+
/**
* Checks if token has any of the admin roles
* @param token