diff --git a/src/api/phaseMembers.js b/src/api/phaseMembers.js
new file mode 100644
index 000000000..60782e834
--- /dev/null
+++ b/src/api/phaseMembers.js
@@ -0,0 +1,9 @@
+import { axiosInstance as axios } from './requestInterceptor'
+import { PROJECTS_API_URL } from '../config/constants'
+
+export function updatePhaseMembers(projectId, phaseId, userIds) {
+ const url = `${PROJECTS_API_URL}/v5/projects/${projectId}/phases/${phaseId}/members`
+ const data = { userIds }
+ return axios.post(url, data)
+ .then(res => res.data)
+}
diff --git a/src/api/projects.js b/src/api/projects.js
index 41b3366d2..5198f572b 100644
--- a/src/api/projects.js
+++ b/src/api/projects.js
@@ -34,6 +34,19 @@ export function getProjectSuggestions() {
// TODO
}
+/**
+ * Get a challenge detail based on it's id
+ * @param {integer} challenId challenge id
+ * @return {object} challenge detail returned by api
+ */
+export function getChallengeById(challengeId) {
+ return axios.get(`${PROJECTS_API_URL}/v5/challenges/${challengeId}/`)
+ .then(resp => {
+ const res = resp.data
+ return res
+ })
+}
+
/**
* Get a project based on it's id
diff --git a/src/assets/icons/calendar.svg b/src/assets/icons/calendar.svg
new file mode 100644
index 000000000..e0823911f
--- /dev/null
+++ b/src/assets/icons/calendar.svg
@@ -0,0 +1,36 @@
+
+
diff --git a/src/assets/icons/icon-calendar.svg b/src/assets/icons/icon-calendar.svg
new file mode 100644
index 000000000..aadc80a0e
--- /dev/null
+++ b/src/assets/icons/icon-calendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/icon-calendar2.svg b/src/assets/icons/icon-calendar2.svg
new file mode 100644
index 000000000..b2ebdd5ba
--- /dev/null
+++ b/src/assets/icons/icon-calendar2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/icon-copilot.svg b/src/assets/icons/icon-copilot.svg
new file mode 100644
index 000000000..3ed5897d0
--- /dev/null
+++ b/src/assets/icons/icon-copilot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/icon-delete.svg b/src/assets/icons/icon-delete.svg
new file mode 100644
index 000000000..870e9145e
--- /dev/null
+++ b/src/assets/icons/icon-delete.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/icon-disselect.svg b/src/assets/icons/icon-disselect.svg
new file mode 100644
index 000000000..ba6201051
--- /dev/null
+++ b/src/assets/icons/icon-disselect.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/src/assets/icons/icon-save2.svg b/src/assets/icons/icon-save2.svg
new file mode 100644
index 000000000..264fda2ec
--- /dev/null
+++ b/src/assets/icons/icon-save2.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/icons/icon-trash2.svg b/src/assets/icons/icon-trash2.svg
new file mode 100644
index 000000000..b6088e145
--- /dev/null
+++ b/src/assets/icons/icon-trash2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/config/constants.js b/src/config/constants.js
index dcd197114..1203c00ab 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -12,6 +12,11 @@ export const LOAD_USER_CREDENTIAL_PENDING = 'LOAD_USER_CREDENTIAL_PENDING'
export const LOAD_USER_CREDENTIAL_SUCCESS = 'LOAD_USER_CREDENTIAL_SUCCESS'
export const LOAD_USER_CREDENTIAL_FAILURE = 'LOAD_USER_CREDENTIAL_FAILURE'
+// Load_CHALLEGNES
+export const LOAD_CHALLEGNES = 'LOAD_CHALLEGNES'
+export const LOAD_CHALLEGNES_PENDING = 'LOAD_CHALLEGNES_PENDING'
+export const LOAD_CHALLEGNES_SUCCESS = 'LOAD_CHALLEGNES_SUCCESS'
+export const LOAD_CHALLEGNES_FAILURE = 'Load_CHALLEGNES_FAILURE'
// Load organization configs
export const LOAD_ORG_CONFIG_SUCCESS = 'LOAD_ORG_CONFIG_SUCCESS'
@@ -753,6 +758,8 @@ export const PROJECT_ATTACHMENTS_FOLDER = process.env.PROJECT_ATTACHMENTS_FOLDER
export const FILE_PICKER_ACCEPT = process.env.FILE_PICKER_ACCEPT || ['.bmp', '.gif', '.jpg', '.tex', '.xls', '.xlsx', '.doc', '.docx', '.zip', '.txt', '.pdf', '.png', '.ppt', '.pptx', '.rtf', '.csv']
export const SEGMENT_KEY = process.env.CONNECT_SEGMENT_KEY
+
+export const CHALLENGE_ID_MAPPING = 'challengeGuid'
/*
* URLs
*/
@@ -766,6 +773,7 @@ export const ACCOUNTS_APP_REGISTER_URL = process.env.ACCOUNTS_APP_REGISTER_URL |
export const TC_API_URL = `https://api.${DOMAIN}`
export const DIRECT_PROJECT_URL = `https://www.${DOMAIN}/direct/projectOverview?formData.projectId=`
export const WORK_MANAGER_APP = `https://challenges.${DOMAIN}/projects`
+export const CHALLENGE_DETAIL_APP = `https://www.${DOMAIN}/challenges`
export const SALESFORCE_PROJECT_LEAD_LINK = process.env.SALESFORCE_PROJECT_LEAD_LINK
export const SALESFORCE_BILLING_ACCOUNT_LINK = process.env.SALESFORCE_BILLING_ACCOUNT_LINK
export const TC_NOTIFICATION_URL = process.env.TC_NOTIFICATION_URL || `${TC_API_URL}/v5/notifications`
diff --git a/src/projects/actions/phaseMember.js b/src/projects/actions/phaseMember.js
new file mode 100644
index 000000000..8cbb5ec42
--- /dev/null
+++ b/src/projects/actions/phaseMember.js
@@ -0,0 +1,10 @@
+import { updatePhaseMembers as updatePhaseMembersAPI } from '../../api/phaseMembers'
+
+export function updatePhaseMembers(projectId, phaseId, userIds) {
+ return (dispatch) => {
+ return dispatch({
+ type: 'UPDATE_PROJECT_PHASE_MEMBERS',
+ payload: updatePhaseMembersAPI(projectId, phaseId, userIds)
+ })
+ }
+}
diff --git a/src/projects/actions/project.js b/src/projects/actions/project.js
index 51278d20c..23cd461ee 100644
--- a/src/projects/actions/project.js
+++ b/src/projects/actions/project.js
@@ -2,6 +2,7 @@ import _ from 'lodash'
import moment from 'moment'
import { flatten, unflatten } from 'flat'
import { getProjectById,
+ getChallengeById,
createProject as createProjectAPI,
updateProject as updateProjectAPI,
deleteProject as deleteProjectAPI,
@@ -36,6 +37,7 @@ import {
DELETE_PROJECT,
PROJECT_DIRTY,
PROJECT_DIRTY_UNDO,
+ LOAD_CHALLEGNES,
LOAD_PROJECT_PHASES,
UPDATE_PRODUCT,
PROJECT_STATUS_DRAFT,
@@ -161,6 +163,7 @@ export function loadProjectInvite(projectId) {
}*/
}
+
/**
* Get project phases together with products
*
@@ -187,12 +190,40 @@ function getProjectPhasesWithProducts(projectId) {
'spentBudget',
'startDate',
'status',
+ 'members',
'updatedAt',
'updatedBy',
].join(',')
})
}
+/**
+ * Get challenges by challenge ids
+ *
+ * @param {Number} milestoneId milestone id
+ * @param {Array} challengeIds challenge ids
+ *
+ * @returns {Promise<[]>} resolves to the list of challenge
+ */
+export function getChallengesByIds(milestoneId, challengeIds) {
+
+ const requests = _.map(challengeIds, id => getChallengeById(id))
+ const challengesAPIs = Promise.all(requests)
+
+ return (dispatch) => {
+ return dispatch({
+ type: LOAD_CHALLEGNES,
+ payload: challengesAPIs,
+ meta: {
+ milestoneId
+ }
+ })
+ }
+
+}
+
+
+
/**
* Load project phases with populated products
*
@@ -286,14 +317,12 @@ function createProductsTimelineAndMilestone(project) {
*
* @return {Promise} project
*/
-export function createProjectPhaseAndProduct(project, productTemplate, status = PHASE_STATUS_DRAFT, startDate, endDate, createTimeline = true, budget, details) {
+export function createProjectPhaseAndProduct(project, productTemplate, status = PHASE_STATUS_DRAFT, startDate, endDate, createTimeline = true) {
const param = {
status,
name: productTemplate.name,
description: productTemplate.description,
productTemplateId: productTemplate.id,
- budget,
- details,
}
if (startDate) {
param['startDate'] = startDate.format('YYYY-MM-DD')
@@ -358,12 +387,12 @@ function createPhaseAndMilestonesRequest(project, productTemplate, status = PHAS
* @param {*} startDate
* @param {*} endDate
*/
-export function createPhaseWithoutTimeline(project, productTemplate, status, startDate, endDate, budget, details) {
+export function createPhaseWithoutTimeline(project, productTemplate, status, startDate, endDate) {
return (dispatch) => {
console.log(CREATE_PROJECT_PHASE)
return dispatch({
type: CREATE_PROJECT_PHASE,
- payload: createProjectPhaseAndProduct(project, productTemplate, status, startDate, endDate, false, budget, details)
+ payload: createProjectPhaseAndProduct(project, productTemplate, status, startDate, endDate, false)
})
}
}
diff --git a/src/projects/detail/components/SimplePlan/CreateSimplePlan/CreateSimplePlan.jsx b/src/projects/detail/components/SimplePlan/CreateSimplePlan/CreateSimplePlan.jsx
index b059b4503..e329aa0dc 100644
--- a/src/projects/detail/components/SimplePlan/CreateSimplePlan/CreateSimplePlan.jsx
+++ b/src/projects/detail/components/SimplePlan/CreateSimplePlan/CreateSimplePlan.jsx
@@ -3,7 +3,6 @@
*/
import React from 'react'
import PT from 'prop-types'
-import _ from 'lodash'
import GenericMenu from '../../../../../components/GenericMenu'
// import ProjectDetailsWidget from '../ProjectDetailsWidget'
import ManageMilestones from '../ManageMilestones'
@@ -21,19 +20,6 @@ const createTabs = ({ onClick } ) => ([
class CreateSimplePlan extends React.Component {
componentDidMount() {
- const { project, milestones, loadMembers } = this.props
-
- let copilotIds = []
- milestones.forEach((milestone) => {
- copilotIds = copilotIds.concat(_.get(milestone, 'details.copilots', []))
- })
-
- const projectMemberIds = project.members.map(member => member.userId)
- const missingMemberIds = _.difference(copilotIds, projectMemberIds)
- if (missingMemberIds.length) {
- loadMembers(missingMemberIds)
- }
-
const contentInnerElement = document.querySelector('.twoColsLayout-contentInner')
contentInnerElement.classList.add(styles['twoColsLayout-contentInner'])
}
@@ -51,8 +37,8 @@ class CreateSimplePlan extends React.Component {
onChangeMilestones,
onSaveMilestone,
onRemoveMilestone,
+ onGetChallenges,
isProjectLive,
- members,
isCustomer,
} = this.props
const onClickMilestonesTab = () => {}
@@ -83,12 +69,12 @@ class CreateSimplePlan extends React.Component {