From 8e91b3a5c60269ed860df7d866e170cfeec3b5e2 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 28 Sep 2020 12:36:17 +0530 Subject: [PATCH 01/12] feat: git#829 - Support direct link for a challenge without having project id known --- src/routes.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/routes.js b/src/routes.js index 6e83c648..f0c20d00 100644 --- a/src/routes.js +++ b/src/routes.js @@ -13,11 +13,50 @@ import ChallengeList from './containers/Challenges' import ChallengeEditor from './containers/ChallengeEditor' import { getFreshToken } from 'tc-accounts' import { saveToken } from './actions/auth' +import { loadChallengeDetails } from './actions/challenges' import { connect } from 'react-redux' import { checkAllowedRoles } from './util/tc' const { ACCOUNTS_APP_LOGIN_URL } = process.env +class RedirectToChallenge extends React.Component { + componentWillMount () { + const { match, loadChallengeDetails } = this.props + const challengeId = match.params.challengeId + loadChallengeDetails(null, challengeId) + } + + componentWillReceiveProps (nextProps) { + const projectId = _.get(nextProps.challengeDetails, 'projectId') + const challengeId = _.get(nextProps.challengeDetails, 'id') + if (projectId && challengeId) { + console.log('Redircting to full URL') + this.props.history.replace(`/projects/${projectId}/challenges/${challengeId}/view`) + } + } + + render () { + return
Redirecting...
+ } +} + +let mapStateToProps = ({ challenges: { challengeDetails } }) => ({ + challengeDetails +}) + +let mapDispatchToProps = { + loadChallengeDetails +} + +RedirectToChallenge.propTypes = { + loadChallengeDetails: PropTypes.func, + challengeDetails: PropTypes.object, + match: PropTypes.object, + history: PropTypes.object +} + +const ConnectRedirectToChallenge = connect(mapStateToProps, mapDispatchToProps)(RedirectToChallenge) + class Routes extends React.Component { componentWillMount () { this.checkAuth() @@ -72,6 +111,7 @@ class Routes extends React.Component { , )()} /> + renderApp( @@ -92,11 +132,11 @@ class Routes extends React.Component { } } -const mapStateToProps = ({ auth }) => ({ +mapStateToProps = ({ auth }) => ({ ...auth }) -const mapDispatchToProps = { +mapDispatchToProps = { saveToken } From 314007b2be8a8b445eda82965bb99fb512adf92b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 28 Sep 2020 19:58:23 +0530 Subject: [PATCH 02/12] fix: Error in activating from challenge listing page, even though API succeeds --- src/components/ChallengesComponent/ChallengeCard/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengesComponent/ChallengeCard/index.js b/src/components/ChallengesComponent/ChallengeCard/index.js index e42371c2..d8db954a 100644 --- a/src/components/ChallengesComponent/ChallengeCard/index.js +++ b/src/components/ChallengesComponent/ChallengeCard/index.js @@ -206,7 +206,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 }) From b952a0f6baed9cbf503b68077d7fae00a6dba42e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 10:41:13 +0530 Subject: [PATCH 03/12] =?UTF-8?q?fix:=20git#24-Task=20assigned=20member=20?= =?UTF-8?q?is=20not=20being=20synced=20to=20the=20legacy=20=E2=80=94=20bet?= =?UTF-8?q?ter=20way=20of=20handling=20the=20task.*=20fields?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ChallengeEditor/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 682c4274..c23effa2 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -303,7 +303,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) { @@ -716,7 +716,6 @@ class ChallengeEditor extends Component { 'startDate', 'terms', 'prizeSets', - 'task', 'winners' ], this.state.challenge) challenge.legacy = _.assign(this.state.challenge.legacy, { From 56cd3d2347d805f0834288bdd4fe08ab7333f0bb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 11:33:37 +0530 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20git#839,=20Loading=20edit=20URL=20?= =?UTF-8?q?for=20a=20challenge=20directly=20does=20not=20load=20copilot=20?= =?UTF-8?q?information=20=E2=80=94=20waiting=20for=20resources=20to=20load?= =?UTF-8?q?=20before=20fetching=20challenge=20details=20to=20make=20sure?= =?UTF-8?q?=20we=20always=20have=20resources=20ready=20for=20the=20challen?= =?UTF-8?q?ge.=20Better=20is=20to=20refactor=20the=20logic=20to=20actions?= =?UTF-8?q?=20and=20make=20a=20multi=20promise=20call=20to=20make=20sure?= =?UTF-8?q?=20all=20data=20is=20available=20when=20challenge=20details=20a?= =?UTF-8?q?re=20loaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/challenges.js | 2 +- src/containers/ChallengeEditor/index.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 6661b4c5..f58e7d15 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -427,7 +427,7 @@ export function loadResources (challengeId) { }) if (challengeId) { - fetchResources(challengeId).then((resources) => { + return fetchResources(challengeId).then((resources) => { dispatch({ type: LOAD_CHALLENGE_RESOURCES_SUCCESS, challengeResources: resources diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index ef853d94..af13f55d 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -105,10 +105,10 @@ class ChallengeEditor extends Component { } } - fetchChallengeDetails (newMatch, loadChallengeDetails, loadResources) { + async fetchChallengeDetails (newMatch, loadChallengeDetails, loadResources) { const projectId = _.get(newMatch.params, 'projectId', null) const challengeId = _.get(newMatch.params, 'challengeId', null) - loadResources(challengeId) + await loadResources(challengeId) loadChallengeDetails(projectId, challengeId) } From 859aaee6b720b2ec555606af5c29f8e5415f6e5a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 11:34:23 +0530 Subject: [PATCH 05/12] fix: Null pointer check for challenge track --- src/components/Track/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Track/index.js b/src/components/Track/index.js index a1d5c262..999106e2 100644 --- a/src/components/Track/index.js +++ b/src/components/Track/index.js @@ -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 (
onUpdateOthers({ field: 'trackId', value: type.id })}>
{ assets && assets.keys().includes(icon) ? : '' }
- {type.name} + {type ? type.name : 'Unkown'}
) } From 8d0f3fa67fa44a4809db5125ae4e679645850b2a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 11:36:58 +0530 Subject: [PATCH 06/12] fix: better error handling --- src/actions/challenges.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index f58e7d15..6e85cabd 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -184,9 +184,10 @@ export function loadChallengeDetails (projectId, challengeId) { challengeDetails: challenge }) loadProject(challenge.projectId)(dispatch, getState) - }).catch(() => { + }).catch((error) => { dispatch({ - type: LOAD_CHALLENGE_DETAILS_FAILURE + type: LOAD_CHALLENGE_DETAILS_FAILURE, + error }) }) } else { From 5a0c8d110551fccb9dce2a83192e05f56a4079b6 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 11:38:23 +0530 Subject: [PATCH 07/12] fix: clean up, avoid using componentWillReceiveProps and triggering state changes in constructor. --- src/components/ChallengeEditor/index.js | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index c23effa2..08118594 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -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 && @@ -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, From 5f01d70c4e60a79ca80280978373c501313a8197 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 12:07:48 +0530 Subject: [PATCH 08/12] feat: refactoring the way redux is being used. Better use of included technology --- src/actions/challenges.js | 72 ++++++++++++++++---------------------- src/actions/projects.js | 34 +++++------------- src/config/constants.js | 4 ++- src/reducers/challenges.js | 10 +++--- src/reducers/projects.js | 2 +- 5 files changed, 49 insertions(+), 73 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 6e85cabd..ab596b3d 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -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, @@ -45,7 +42,6 @@ import { CREATE_CHALLENGE_SUCCESS, CREATE_CHALLENGE_FAILURE } from '../config/constants' -import { fetchProjectById } from '../services/projects' import { loadProject } from './projects' /** @@ -171,43 +167,18 @@ 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: {} - }) - - if (challengeId) { - fetchChallenge(challengeId).then((challenge) => { - dispatch({ - type: LOAD_CHALLENGE_DETAILS_SUCCESS, - challengeDetails: challenge - }) - loadProject(challenge.projectId)(dispatch, getState) - }).catch((error) => { - dispatch({ - type: LOAD_CHALLENGE_DETAILS_FAILURE, - error - }) - }) - } else { - dispatch({ - type: LOAD_CHALLENGE_DETAILS_SUCCESS, - challengeDetails: null + return (dispatch, getState) => { + 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 }) - - 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 - }) - }) - } - } + }) } } @@ -458,6 +429,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, @@ -472,6 +449,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, @@ -486,6 +469,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) { diff --git a/src/actions/projects.js b/src/actions/projects.js index 8c86a4f2..cd93004e 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -1,8 +1,6 @@ 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' @@ -10,18 +8,10 @@ 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({ @@ -29,16 +19,8 @@ export function loadProject (projectId) { members }) } - }).catch(() => { - dispatch({ - type: LOAD_PROJECT_DETAILS_FAILURE - }) + return project }) - } else { - dispatch({ - type: LOAD_PROJECT_DETAILS_SUCCESS, - projectDetail: null - }) - } + }) } } diff --git a/src/config/constants.js b/src/config/constants.js index 5f4ad9a0..e8b1072b 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -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' @@ -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' @@ -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' diff --git a/src/reducers/challenges.js b/src/reducers/challenges.js index f9237977..45d6a44c 100644 --- a/src/reducers/challenges.js +++ b/src/reducers/challenges.js @@ -90,14 +90,15 @@ export default function (state = initialState, action) { case UPDATE_CHALLENGE_DETAILS_FAILURE: case CREATE_CHALLENGE_FAILURE: return { ...state, isLoading: false, attachments: [], challenge: null, failedToLoad: true } - case LOAD_CHALLENGE_DETAILS_SUCCESS: + case LOAD_CHALLENGE_DETAILS_SUCCESS: { return { ...state, - challengeDetails: action.challengeDetails, + challengeDetails: action.payload, isLoading: false, - attachments: _.has(action.challengeDetails, 'attachments') ? action.challengeDetails.attachments : [], + attachments: _.has(action.payload, 'attachments') ? action.payload.attachments : [], failedToLoad: false } + } case UPDATE_CHALLENGE_DETAILS_SUCCESS: { // During editing the challenge we might change its status, so when we came back to the challenge list // updated challenge might have to be removed from the list, or added to the list, or just updated @@ -213,8 +214,9 @@ export default function (state = initialState, action) { [action.metadataKey]: action.metadataValue } } - case LOAD_CHALLENGE_MEMBERS_SUCCESS: + case LOAD_CHALLENGE_MEMBERS_SUCCESS: { return { ...state, metadata: { ...state.metadata, members: action.members } } + } case UPLOAD_ATTACHMENT_PENDING: return { ...state, isUploading: true, isSuccess: false, uploadingId: action.challengeId } case UPLOAD_ATTACHMENT_SUCCESS: diff --git a/src/reducers/projects.js b/src/reducers/projects.js index 9d17ba7f..0d28f3a2 100644 --- a/src/reducers/projects.js +++ b/src/reducers/projects.js @@ -21,7 +21,7 @@ export default function (state = initialState, action) { case LOAD_PROJECT_DETAILS_SUCCESS: return { ...state, - projectDetail: action.projectDetail, + projectDetail: action.payload, isLoading: false } default: From cc9eca084093f950f90f68199de4f0fe208982ef Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 12:56:51 +0530 Subject: [PATCH 09/12] fix: fixed the side effect, of last refactoring, on Launch New action --- src/actions/challenges.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index ab596b3d..13e8b58f 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -168,17 +168,19 @@ export function loadChallenges (projectId, status, filterChallengeName = null) { */ export function loadChallengeDetails (projectId, challengeId) { return (dispatch, getState) => { - 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 + if (challengeId) { + 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 + }) }) - }) + } } } From 378ede69d661fcfef391b469dd207c3cd1340273 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 15:11:36 +0530 Subject: [PATCH 10/12] fix: git#840-Clicking Back button from Edit Challenge screen shows empty list of challenges --- src/containers/ChallengeEditor/index.js | 3 ++- src/containers/Challenges/index.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index af13f55d..7f6e35a0 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -106,7 +106,8 @@ class ChallengeEditor extends Component { } async fetchChallengeDetails (newMatch, loadChallengeDetails, loadResources) { - const projectId = _.get(newMatch.params, 'projectId', null) + let projectId = _.get(newMatch.params, 'projectId', null) + projectId = projectId ? parseInt(projectId) : null const challengeId = _.get(newMatch.params, 'challengeId', null) await loadResources(challengeId) loadChallengeDetails(projectId, challengeId) diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index f2ec3292..c24257bc 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -35,9 +35,9 @@ class Challenges extends Component { if (menu === 'NULL' && activeProjectId !== -1) { resetSidebarActiveParams() } else { - // this.props.loadChallengesByPage(1, projectId ? parseInt(projectId) : -1, CHALLENGE_STATUS.ACTIVE, '') if (projectId) { this.props.loadProject(projectId) + this.reloadChallenges(this.props) } } } From 6471eaaf1ea5c0bd752112c18ec20ac5d957fe50 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 29 Sep 2020 15:12:06 +0530 Subject: [PATCH 11/12] feat: adding audit fields for quick info in list view --- .../ChallengeCard/ChallengeCard.module.scss | 22 +++++++++++++++++++ .../ChallengeCard/index.js | 13 ++++++++++- .../ChallengeList/index.js | 1 + src/util/date.js | 9 ++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/components/ChallengesComponent/ChallengeCard/ChallengeCard.module.scss b/src/components/ChallengesComponent/ChallengeCard/ChallengeCard.module.scss index bf8e9417..5613eaa9 100644 --- a/src/components/ChallengesComponent/ChallengeCard/ChallengeCard.module.scss +++ b/src/components/ChallengesComponent/ChallengeCard/ChallengeCard.module.scss @@ -85,6 +85,7 @@ flex: 6; flex-wrap: nowrap; display: flex; + text-decoration: none; .name { flex:1; @@ -102,6 +103,7 @@ flex-direction: column; justify-content: center; align-items: center; + text-decoration: none; } .col3 { @@ -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 { diff --git a/src/components/ChallengesComponent/ChallengeCard/index.js b/src/components/ChallengesComponent/ChallengeCard/index.js index d8db954a..c832e5af 100644 --- a/src/components/ChallengesComponent/ChallengeCard/index.js +++ b/src/components/ChallengesComponent/ChallengeCard/index.js @@ -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' @@ -172,6 +172,15 @@ const renderStatus = (status) => { } } +const renderLastUpdated = (challenge) => { + return ( + +
{formatDate(challenge.updated)}
+
{challenge.updatedBy}
+ + ) +} + class ChallengeCard extends React.Component { constructor (props) { super(props) @@ -248,8 +257,10 @@ class ChallengeCard extends React.Component {
{challenge.name} + {`Created by ${challenge.createdBy} at ${formatDate(challenge.created)}`}
+ {renderLastUpdated(challenge)} {renderStatus(challenge.status.toUpperCase())} diff --git a/src/components/ChallengesComponent/ChallengeList/index.js b/src/components/ChallengesComponent/ChallengeList/index.js index b1c2e30f..552050c0 100644 --- a/src/components/ChallengesComponent/ChallengeList/index.js +++ b/src/components/ChallengesComponent/ChallengeList/index.js @@ -192,6 +192,7 @@ class ChallengeList extends Component { challenges.length > 0 && (
Challenges Name
+
Last Updated
Status
{(selectedTab === 0) && (
Current phase
)}
 
diff --git a/src/util/date.js b/src/util/date.js index ac13c371..b5017f29 100644 --- a/src/util/date.js +++ b/src/util/date.js @@ -17,6 +17,15 @@ export const getLastDate = (dates) => { return Math.max(...dates) } +/** + * Get formatted date + * @param {Date} date date to be formatted + * @returns formatted Date + */ +export const formatDate = (date) => { + return moment(date).format('DD/MM/YYYY') +} + /** * Formats duration * @param duration Duration From 6d9b21eeeeffa48417a5993478e89b060e1e4fa8 Mon Sep 17 00:00:00 2001 From: maxceem Date: Tue, 29 Sep 2020 12:50:13 +0300 Subject: [PATCH 12/12] fix: avoid duplicate final deliverable ref issue #781 #782 --- src/components/ChallengeEditor/FinalDeliverables-Field/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/FinalDeliverables-Field/index.js b/src/components/ChallengeEditor/FinalDeliverables-Field/index.js index 57ead390..fbec690e 100644 --- a/src/components/ChallengeEditor/FinalDeliverables-Field/index.js +++ b/src/components/ChallengeEditor/FinalDeliverables-Field/index.js @@ -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 (