Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 31 additions & 38 deletions src/actions/challenges.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ import {
deleteResource as deleteResourceAPI
} from '../services/challenges'
import {
LOAD_CHALLENGE_DETAILS_PENDING,
LOAD_CHALLENGE_DETAILS_SUCCESS,
LOAD_CHALLENGE_DETAILS_FAILURE,
LOAD_CHALLENGE_MEMBERS_SUCCESS,
LOAD_CHALLENGE_DETAILS,
LOAD_CHALLENGE_METADATA_SUCCESS,
LOAD_CHALLENGES_FAILURE,
LOAD_CHALLENGES_PENDING,
Expand All @@ -45,7 +42,6 @@ import {
CREATE_CHALLENGE_SUCCESS,
CREATE_CHALLENGE_FAILURE
} from '../config/constants'
import { fetchProjectById } from '../services/projects'
import { loadProject } from './projects'

/**
Expand Down Expand Up @@ -171,41 +167,19 @@ export function loadChallenges (projectId, status, filterChallengeName = null) {
* Loads Challenge details
*/
export function loadChallengeDetails (projectId, challengeId) {
return async (dispatch, getState) => {
dispatch({
type: LOAD_CHALLENGE_DETAILS_PENDING,
challengeDetails: {}
})

return (dispatch, getState) => {
if (challengeId) {
fetchChallenge(challengeId).then((challenge) => {
dispatch({
type: LOAD_CHALLENGE_DETAILS_SUCCESS,
challengeDetails: challenge
})
loadProject(challenge.projectId)(dispatch, getState)
}).catch(() => {
dispatch({
type: LOAD_CHALLENGE_DETAILS_FAILURE
return dispatch({
type: LOAD_CHALLENGE_DETAILS,
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) {
dispatch(loadProject(projectId))
}
return challenge
})
})
} else {
dispatch({
type: LOAD_CHALLENGE_DETAILS_SUCCESS,
challengeDetails: null
})

if (projectId) {
fetchProjectById(projectId).then((selectedProject) => {
if (!selectedProject) return
const members = selectedProject.members
.filter(m => m.role === 'manager' || m.role === 'copilot')
dispatch({
type: LOAD_CHALLENGE_MEMBERS_SUCCESS,
members
})
})
}
}
}
}
Expand Down Expand Up @@ -427,7 +401,7 @@ export function loadResources (challengeId) {
})

if (challengeId) {
fetchResources(challengeId).then((resources) => {
return fetchResources(challengeId).then((resources) => {
dispatch({
type: LOAD_CHALLENGE_RESOURCES_SUCCESS,
challengeResources: resources
Expand Down Expand Up @@ -457,6 +431,12 @@ export function loadResourceRoles () {
}
}

/**
* Deletes a resource for the given challenge in given role
* @param {UUID} challengeId id of the challenge for which resource is to be deleted
* @param {UUID} roleId id of the role, the resource is in
* @param {String} memberHandle handle of the resource
*/
export function deleteResource (challengeId, roleId, memberHandle) {
const resource = {
challengeId,
Expand All @@ -471,6 +451,12 @@ export function deleteResource (challengeId, roleId, memberHandle) {
}
}

/**
* Creates a resource for the given challenge in given role
* @param {UUID} challengeId id of the challenge for which resource is to be created
* @param {UUID} roleId id of the role, the resource should be in
* @param {String} memberHandle handle of the resource
*/
export function createResource (challengeId, roleId, memberHandle) {
const resource = {
challengeId,
Expand All @@ -485,6 +471,13 @@ export function createResource (challengeId, roleId, memberHandle) {
}
}

/**
* Replaces the given resource in given role with new resource for the provided challenge
* @param {UUID} challengeId id of the challenge for which resource is to be replaced
* @param {UUID} roleId id of the role, the resource is in
* @param {String} newMember handle of the new resource
* @param {String} oldMember handle of the existing resource
*/
export function replaceResourceInRole (challengeId, roleId, newMember, oldMember) {
return async (dispatch) => {
if (newMember === oldMember) {
Expand Down
34 changes: 8 additions & 26 deletions src/actions/projects.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,26 @@
import {
LOAD_PROJECT_DETAILS_SUCCESS,
LOAD_PROJECT_DETAILS_PENDING,
LOAD_PROJECT_DETAILS_FAILURE,
LOAD_CHALLENGE_MEMBERS_SUCCESS
LOAD_CHALLENGE_MEMBERS_SUCCESS,
LOAD_PROJECT_DETAILS
} from '../config/constants'
import { fetchProjectById } from '../services/projects'

/**
* Loads project details
*/
export function loadProject (projectId) {
return async (dispatch, getState) => {
dispatch({
type: LOAD_PROJECT_DETAILS_PENDING,
projectDetail: {}
})
if (projectId) {
fetchProjectById(projectId).then((project) => {
dispatch({
type: LOAD_PROJECT_DETAILS_SUCCESS,
projectDetail: project
})

return (dispatch, getState) => {
return dispatch({
type: LOAD_PROJECT_DETAILS,
payload: fetchProjectById(projectId).then((project) => {
if (project && project.members) {
const members = project.members.filter(m => m.role === 'manager' || m.role === 'copilot')
dispatch({
type: LOAD_CHALLENGE_MEMBERS_SUCCESS,
members
})
}
}).catch(() => {
dispatch({
type: LOAD_PROJECT_DETAILS_FAILURE
})
return project
})
} else {
dispatch({
type: LOAD_PROJECT_DETAILS_SUCCESS,
projectDetail: null
})
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class FinalDeliverablesField extends Component {
const { challenge, readOnly, removeFileType } = this.props
const fileTypesMetadata = _.find(challenge.metadata, { name: 'fileTypes' })
const fileTypes = (fileTypesMetadata && JSON.parse(fileTypesMetadata.value)) || []
const isDuplicateValue = _.includes(fileTypes, this.state.newFileType.trim())
const isDuplicateValue = _.includes(fileTypes.map((fileType) => fileType.toLowerCase()), this.state.newFileType.toLowerCase().trim())

return (
<React.Fragment>
Expand Down
26 changes: 7 additions & 19 deletions src/components/ChallengeEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,30 +113,18 @@ 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.resetChallengeData((newState, finish) => {
this.state = {
...this.state,
...newState
}
if (finish) {
finish()
}
})
}

componentDidUpdate () {
componentDidMount () {
this.resetChallengeData(this.setState.bind(this))
}

componentWillReceiveProps (nextProps) {
// if member details weren't initially loaded and now they got loaded, then set them to the state
if (!this.state.assignedMemberDetails && nextProps.assignedMemberDetails) {
this.setState({ assignedMemberDetails: nextProps.assignedMemberDetails })
}
componentDidUpdate () {
this.resetChallengeData(this.setState.bind(this))
}

async resetChallengeData (setState = () => {}) {
const { isNew, challengeDetails, metadata, attachments, challengeId } = this.props
const { isNew, challengeDetails, metadata, attachments, challengeId, assignedMemberDetails } = this.props
if (
challengeDetails &&
challengeDetails.id &&
Expand All @@ -160,10 +148,11 @@ class ChallengeEditor extends Component {
}
challengeData.copilot = copilot || copilotFromResources
challengeData.reviewer = reviewer || reviewerFromResources
const challengeDetail = { ...dropdowns['newChallenge'], ...challengeData }
const challengeDetail = { ...challengeData }
const isOpenAdvanceSettings = challengeDetail.groups.length > 0
setState({
challenge: challengeDetail,
assignedMemberDetails,
draftChallenge: { data: {
..._.cloneDeep(challengeDetails),
copilot: challengeData.copilot,
Expand Down Expand Up @@ -303,7 +292,7 @@ class ChallengeEditor extends Component {
*/
onUpdateAssignedMember (option) {
const { challenge: oldChallenge } = this.state
const newChallenge = { ...oldChallenge, task: { isAssigned: false, memberId: null, isTask: true } }
const newChallenge = { ...oldChallenge }
let assignedMemberDetails

if (option && option.value) {
Expand Down Expand Up @@ -716,7 +705,6 @@ class ChallengeEditor extends Component {
'startDate',
'terms',
'prizeSets',
'task',
'winners'
], this.state.challenge)
challenge.legacy = _.assign(this.state.challenge.legacy, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
flex: 6;
flex-wrap: nowrap;
display: flex;
text-decoration: none;

.name {
flex:1;
Expand All @@ -102,6 +103,7 @@
flex-direction: column;
justify-content: center;
align-items: center;
text-decoration: none;
}

.col3 {
Expand Down Expand Up @@ -180,6 +182,26 @@
}
}
}

.createdAt {
color: $gray;
font-size: 10px;

&:hover {
text-decoration: none;
}
}

.lastUpdated {
text-decoration: none;
.lastUpdatedAt {
color: $tc-black;
}
.lastUpdatedBy {
color: $gray;
font-size: 10px;
}
}
}

.activateButton {
Expand Down
15 changes: 13 additions & 2 deletions src/components/ChallengesComponent/ChallengeCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { faFile, faUser } from '@fortawesome/free-solid-svg-icons'
import ChallengeStatus from '../ChallengeStatus'
import ChallengeTag from '../ChallengeTag'
import styles from './ChallengeCard.module.scss'
import { getFormattedDuration } from '../../../util/date'
import { getFormattedDuration, formatDate } from '../../../util/date'
import { CHALLENGE_STATUS, COMMUNITY_APP_URL, DIRECT_PROJECT_URL, ONLINE_REVIEW_URL } from '../../../config/constants'
import { patchChallenge } from '../../../services/challenges'
import ConfirmationModal from '../../Modal/ConfirmationModal'
Expand Down Expand Up @@ -172,6 +172,15 @@ const renderStatus = (status) => {
}
}

const renderLastUpdated = (challenge) => {
return (
<Link className={cn(styles.col2, styles.lastUpdated)} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
<div className={styles.lastUpdatedAt}>{formatDate(challenge.updated)}</div>
<div className={styles.lastUpdatedBy}>{challenge.updatedBy}</div>
</Link>
)
}

class ChallengeCard extends React.Component {
constructor (props) {
super(props)
Expand Down Expand Up @@ -206,7 +215,7 @@ class ChallengeCard extends React.Component {
try {
this.setState({ isSaving: true })
const response = await patchChallenge(challenge.id, { status: 'Active' })
this.setState({ isLaunch: true, isConfirm: response.data.id, isSaving: false })
this.setState({ isLaunch: true, isConfirm: response.id, isSaving: false })
} catch (e) {
const error = _.get(e, 'response.data.message', 'Unable to activate the challenge')
this.setState({ isSaving: false, error })
Expand Down Expand Up @@ -248,8 +257,10 @@ class ChallengeCard extends React.Component {
<div className={styles.name}>
<span className={styles.block}>{challenge.name}</span>
<ChallengeTag track={challenge.trackId} challengeType={challenge.type} />
<span className={styles.createdAt}>{`Created by ${challenge.createdBy} at ${formatDate(challenge.created)}`}</span>
</div>
</Link>
{renderLastUpdated(challenge)}
<Link className={styles.col2} to={`/projects/${challenge.projectId}/challenges/${challenge.id}/view`}>
{renderStatus(challenge.status.toUpperCase())}
</Link>
Expand Down
1 change: 1 addition & 0 deletions src/components/ChallengesComponent/ChallengeList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class ChallengeList extends Component {
challenges.length > 0 && (
<div className={styles.header}>
<div className={styles.col1}>Challenges Name</div>
<div className={styles.col2}>Last Updated</div>
<div className={styles.col2}>Status</div>
{(selectedTab === 0) && (<div className={styles.col3}>Current phase</div>)}
<div className={styles.col4}>&nbsp;</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Track/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import styles from './Track.module.scss'
const assets = require.context('../../assets/images/tracks', false, /svg/)

const Track = ({ type, isActive, onUpdateOthers, disabled }) => {
const icon = `./${type.abbreviation.toLowerCase()}.svg`
const icon = type ? `./${type.abbreviation.toLowerCase()}.svg` : ''

return (
<div className={cn(styles.container, { [styles.active]: isActive, [styles.disabled]: disabled })} onClick={() => onUpdateOthers({ field: 'trackId', value: type.id })}>
<div className={styles.icon}>
{ assets && assets.keys().includes(icon) ? <ReactSVG path={assets(`${icon}`)} /> : '' }
</div>
<span className={styles.name}>{type.name}</span>
<span className={styles.name}>{type ? type.name : 'Unkown'}</span>
</div>
)
}
Expand Down
4 changes: 3 additions & 1 deletion src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const LOAD_CHALLENGES_SUCCESS = 'LOAD_CHALLENGES_SUCCESS'
export const LOAD_CHALLENGES_PENDING = 'LOAD_CHALLENGES_PENDING'
export const LOAD_CHALLENGES_FAILURE = 'LOAD_CHALLENGES_FAILURE'

export const LOAD_CHALLENGE_DETAILS = 'LOAD_CHALLENGE_DETAILS'
export const LOAD_CHALLENGE_DETAILS_SUCCESS = 'LOAD_CHALLENGE_DETAILS_SUCCESS'
export const LOAD_CHALLENGE_DETAILS_PENDING = 'LOAD_CHALLENGE_DETAILS_PENDING'
export const LOAD_CHALLENGE_DETAILS_FAILURE = 'LOAD_CHALLENGE_DETAILS_FAILURE'
Expand All @@ -46,6 +47,7 @@ export const CREATE_CHALLENGE_SUCCESS = 'CREATE_CHALLENGE_SUCCESS'
export const CREATE_CHALLENGE_PENDING = 'CREATE_CHALLENGE_PENDING'
export const CREATE_CHALLENGE_FAILURE = 'CREATE_CHALLENGE_FAILURE'

export const LOAD_PROJECT_DETAILS = 'LOAD_PROJECT_DETAILS'
export const LOAD_PROJECT_DETAILS_SUCCESS = 'LOAD_PROJECT_DETAILS_SUCCESS'
export const LOAD_PROJECT_DETAILS_PENDING = 'LOAD_PROJECT_DETAILS_PENDING'
export const LOAD_PROJECT_DETAILS_FAILURE = 'LOAD_PROJECT_DETAILS_FAILURE'
Expand All @@ -54,7 +56,7 @@ export const LOAD_CHALLENGE_SUBMISSIONS_SUCCESS = 'LOAD_CHALLENGE_SUBMISSIONS_SU
export const LOAD_CHALLENGE_SUBMISSIONS_PENDING = 'LOAD_CHALLENGE_SUBMISSIONS_PENDING'
export const LOAD_CHALLENGE_SUBMISSIONS_FAILURE = 'LOAD_CHALLENGE_SUBMISSIONS_FAILURE'

export const LOAD_CHALLENGE_MEMBERS_SUCCESS = 'LOAD_CHALLENGE_MEMBERS'
export const LOAD_CHALLENGE_MEMBERS_SUCCESS = 'LOAD_CHALLENGE_MEMBERS_SUCCESS'
export const LOAD_CHALLENGE_METADATA_SUCCESS = 'LOAD_CHALLENGE_METADATA_SUCCESS'

export const SAVE_AUTH_TOKEN = 'SAVE_AUTH_TOKEN'
Expand Down
Loading