Skip to content

Commit d3ae844

Browse files
committed
Final Fixes for Challenge Milstone Listing
2 parents 5d979d9 + 9ba9f6c commit d3ae844

File tree

21 files changed

+427
-83
lines changed

21 files changed

+427
-83
lines changed

config/constants/dev.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module.exports = {
5757
DASHBOARD_FAQ_CONTENT_ID : process.env.DASHBOARD_FAQ_CONTENT_ID,
5858
CONTENTFUL_DELIVERY_KEY : process.env.CONTENTFUL_DELIVERY_KEY,
5959
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID,
60+
CHALLENGE_ID_MAPPING: process.env.CHALLENGE_ID_MAPPING,
6061

6162
TAAS_APP_URL: 'https://platform.topcoder-dev.com/taas',
6263
DEFAULT_NDA_UUID: 'e5811a7b-43d1-407a-a064-69e5015b4900'

config/constants/master.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module.exports = {
5757
DASHBOARD_FAQ_CONTENT_ID : process.env.DASHBOARD_FAQ_CONTENT_ID,
5858
CONTENTFUL_DELIVERY_KEY : process.env.CONTENTFUL_DELIVERY_KEY,
5959
CONTENTFUL_SPACE_ID : process.env.CONTENTFUL_SPACE_ID,
60+
CHALLENGE_ID_MAPPING: process.env.CHALLENGE_ID_MAPPING,
6061

6162
TAAS_APP_URL: 'https://platform.topcoder.com/taas',
6263
DEFAULT_NDA_UUID: 'c41e90e5-4d0e-4811-bd09-38ff72674490'
Lines changed: 1 addition & 0 deletions
Loading

src/assets/icons/icon-copilot.svg

Lines changed: 1 addition & 6 deletions
Loading

src/assets/icons/icon-trash2.svg

Lines changed: 1 addition & 6 deletions
Loading

src/config/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ export const PROJECT_ATTACHMENTS_FOLDER = process.env.PROJECT_ATTACHMENTS_FOLDER
758758
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']
759759

760760
export const SEGMENT_KEY = process.env.CONNECT_SEGMENT_KEY
761+
762+
export const CHALLENGE_ID_MAPPING = process.env.CHALLENGE_ID_MAPPING || 'challengeId'
761763
/*
762764
* URLs
763765
*/

src/projects/detail/components/SimplePlan/ManageMilestones/ManageMilestones.jsx

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
import React from 'react'
55
import PT from 'prop-types'
6+
import moment from 'moment'
67
import FormsyForm from 'appirio-tech-react-components/components/Formsy'
78
import MilestoneRow from '../components/MilestoneRow'
89
import MilestoneChallengeHeader from '../components/MilestoneChallengeHeader'
@@ -11,10 +12,12 @@ import MilestoneChallengeFooter from '../components/MilestoneChallengeFooter'
1112
import MilestoneHeaderRow from '../components/MilestoneHeaderRow'
1213
import MilestoneDeleteButton from '../components/MilestoneDeleteButton'
1314
import MilestoneCopilots from '../components/MilestoneCopilots'
15+
import MilestoneMoveDateButton from '../components/MilestoneMoveDateButton'
16+
1417
import * as milestoneHelper from '../components/helpers/milestone'
1518
import IconUnselect from '../../../../../assets/icons/icon-disselect.svg'
1619
import IconCopilot from '../../../../../assets/icons/icon-copilot.svg'
17-
import IconCalendar from '../../../../../assets/icons/calendar.svg'
20+
import { CHALLENGE_ID_MAPPING } from '../../../../../config/constants'
1821
// import IconGridView from '../../../../../assets/icons/ui-16px-2_grid-45-gray.svg'
1922
// import IconGnattView from '../../../../../assets/icons/icon-gnatt-gray.svg'
2023

@@ -38,8 +41,26 @@ class ManageMilestones extends React.Component {
3841
this.onUnselectAll = this.onUnselectAll.bind(this)
3942
this.onDeleteAll = this.onDeleteAll.bind(this)
4043
this.onAddCopilotAll = this.onAddCopilotAll.bind(this)
44+
this.onMoveMilestoneDates = this.onMoveMilestoneDates.bind(this)
45+
this.onLoadChallengesByPage = this.onLoadChallengesByPage.bind(this)
4146
}
47+
onMoveMilestoneDates(days) {
48+
const {
49+
milestones,
50+
onSaveMilestone
51+
} = this.props
4252

53+
54+
if (days > 0) {
55+
const seletedMilestones = _.filter(milestones, m => m.selected)
56+
_.forEach(seletedMilestones, m => {
57+
m.startDate = moment(m.startDate).add(days, 'days')
58+
m.endDate = moment(m.endDate).add(days, 'days')
59+
this.onChange(m)
60+
onSaveMilestone(m.id)
61+
})
62+
}
63+
}
4364
onAddCopilotAll(member, isAdd) {
4465
const { milestones, onSaveMilestone } = this.props
4566
const seletedMilestones = _.filter(milestones, m => m.selected)
@@ -81,11 +102,21 @@ class ManageMilestones extends React.Component {
81102
}))
82103
onChangeMilestones(milestonesUnselected)
83104
}
105+
106+
onLoadChallengesByPage(index, milestone) {
107+
const { onGetChallenges } = this.props
108+
let challengeIds = _.map(milestone.products, `details.${CHALLENGE_ID_MAPPING}`).slice(6 * index, 7 * (index+1))
109+
challengeIds = _.filter(challengeIds)
110+
if (!challengeIds.length) {
111+
return
112+
}
113+
onGetChallenges(milestone.id, challengeIds)
114+
}
84115
onExpandChallenges(isExpand, milestone) {
85116
let expandList = this.state.expandList
86117
const { onGetChallenges } = this.props
87118

88-
let challengeIds = _.map(milestone.products, 'details.challengeId').slice(0, 6)
119+
let challengeIds = _.map(milestone.products, `details.${CHALLENGE_ID_MAPPING}`).slice(0, 6)
89120
challengeIds = _.filter(challengeIds)
90121
if (!challengeIds.length) {
91122
return
@@ -141,24 +172,46 @@ class ManageMilestones extends React.Component {
141172

142173
isExpandChallengeList(milestone) {
143174
const isExpand = _.find(this.state.expandList, (i) => i === milestone.id)
144-
if (isExpand && milestone.challenges) {
175+
if (isExpand) {
145176
return true
146177
} else {
147178
return false
148179
}
149180
}
150181

151182
renderChallengeTable(milestone) {
183+
const {
184+
isUpdatable
185+
} = this.props
152186
if (!this.isExpandChallengeList(milestone)) {
153187
return <tr />
154188
}
189+
190+
let challengeIds = _.map(milestone.products, `details.${CHALLENGE_ID_MAPPING}`).slice(0, 6)
191+
challengeIds = _.filter(challengeIds)
192+
// no challenges
193+
if (!challengeIds.length) {
194+
return [
195+
<MilestoneChallengeHeader isUpdatable={isUpdatable}/>,
196+
<MilestoneChallengeRow isEmpty isUpdatable={isUpdatable}/>
197+
]
198+
}
199+
200+
// loading challenges
201+
if (milestone.isLoadingChallenges) {
202+
return [
203+
<MilestoneChallengeHeader isUpdatable={isUpdatable}/>,
204+
<MilestoneChallengeRow isLoading isUpdatable={isUpdatable}/>
205+
]
206+
}
207+
155208
const rows = _.map(milestone.challenges, (c) => {
156-
return <MilestoneChallengeRow challenge={c}/>
209+
return <MilestoneChallengeRow challenge={c} isUpdatable={isUpdatable}/>
157210
})
158211
return [
159-
<MilestoneChallengeHeader />,
212+
<MilestoneChallengeHeader isUpdatable={isUpdatable}/>,
160213
...rows,
161-
<MilestoneChallengeFooter milestone={milestone}/>
214+
<MilestoneChallengeFooter milestone={milestone} onLoadChallengesByPage={this.onLoadChallengesByPage} isUpdatable={isUpdatable}/>
162215
]
163216
}
164217
getSelectCount() {
@@ -170,19 +223,23 @@ class ManageMilestones extends React.Component {
170223
renderAddCopilot() {
171224
const {
172225
projectMembers,
226+
milestones,
173227
} = this.props
174228

229+
const seletedMilestones = _.filter(milestones, m => m.selected)
230+
const copilots = _.intersectionBy(..._.map(seletedMilestones, 'members'), 'userId')
175231
return (
176232
<MilestoneCopilots
177233
edit
178-
customButton={<IconCopilot />}
179-
copilots={[]}
234+
customButton={<IconCopilot styleName="copilot-icon"/>}
235+
copilots={copilots}
180236
projectMembers={projectMembers}
181237
onAdd={(member) => this.onAddCopilotAll(member, true)}
182238
onRemove={(member) => this.onAddCopilotAll(member, false)}
183239
/>
184240
)
185241
}
242+
186243
render() {
187244
const {
188245
milestones,
@@ -191,6 +248,7 @@ class ManageMilestones extends React.Component {
191248
isUpdatable,
192249
} = this.props
193250

251+
const canEdit = isUpdatable && this.getSelectCount() > 0
194252
return (
195253
<div>
196254
<div styleName="toolbar">
@@ -201,18 +259,16 @@ class ManageMilestones extends React.Component {
201259
<IconGnattView />
202260
</button>
203261
<div styleName="separator" /> */}
204-
<div styleName="unselect-bottom" onClick={this.onUnselectAll}>
262+
{canEdit ? <div styleName="unselect-bottom" onClick={this.onUnselectAll}>
205263
<IconUnselect /> {this.getSelectCount()} PROJECTS SELECTED
206-
</div>
207-
<div styleName="delete-button">
264+
</div>: null }
265+
{ canEdit ? <div styleName="delete-button">
208266
<MilestoneDeleteButton onDelete={this.onDeleteAll}/>
209-
</div>
210-
<div styleName="icon">
267+
</div>: null }
268+
{ canEdit ? <div styleName="icon">
211269
{this.renderAddCopilot()}
212-
</div>
213-
<div styleName="icon">
214-
<IconCalendar />
215-
</div>
270+
</div>: null }
271+
{ canEdit ? <MilestoneMoveDateButton onMove={this.onMoveMilestoneDates}/>: null}
216272
{isUpdatable && (
217273
<button className="tc-btn tc-btn-primary tc-btn-sm" styleName="primary-button" onClick={this.onAdd}>
218274
ADD
@@ -229,6 +285,7 @@ class ManageMilestones extends React.Component {
229285
<Formsy.Form>
230286
<table styleName="milestones-table">
231287
<colgroup>
288+
<col style={{ width: '20px' }} />
232289
<col style={{ width: '20px' }} />{/* CHECKBOX */}
233290
<col style={{ width: '8%' }} />{/* MILESTONE */}
234291
<col />{/* DESCRIPTION */}

src/projects/detail/components/SimplePlan/ManageMilestones/ManageMilestones.scss

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@
2727
}
2828
.delete-button {
2929
cursor: pointer;
30-
30+
margin: 0 10px;
31+
32+
> span > button {
33+
height: 20px;
34+
width: 20px;
35+
svg {
36+
height: 20px;
37+
width: 20px;
38+
}
39+
}
3140
}
3241
.icon {
42+
margin: 0 10px;
3343
cursor: pointer;
3444
svg {
3545
margin: 0 8px;
@@ -53,6 +63,10 @@
5363
}
5464
}
5565

66+
.copilot-icon {
67+
width: 20px;
68+
}
69+
5670
.view-mode {
5771
&.active {
5872
svg path {
@@ -76,54 +90,56 @@
7690
text-align: left;
7791
vertical-align: middle;
7892

79-
// checkbox
93+
// expand arrow
8094
&:nth-child(1) {}
95+
// checkbox
96+
&:nth-child(2) {}
8197
// milestone
82-
&:nth-child(2) { padding-left: 5px; }
98+
&:nth-child(3) { padding-left: 5px; }
8399
// description
84-
&:nth-child(3) { padding-left: 15px; }
100+
&:nth-child(4) { padding-left: 15px; }
85101
// start date
86-
&:nth-child(4) { padding-left: 25px; }
102+
&:nth-child(5) { padding-left: 25px; }
87103
// end date
88-
&:nth-child(5) { padding-left: 15px; }
104+
&:nth-child(6) { padding-left: 15px; }
89105
// status
90-
&:nth-child(6) { padding-left: 20px; }
106+
&:nth-child(7) { padding-left: 20px; }
91107
// copilot
92-
&:nth-child(7) { padding-left: 25px; }
108+
&:nth-child(8) { padding-left: 25px; }
93109
}
94110

95111
:global(.edit-milestone-row) {
96112
th,
97113
td {
98114
// description
99-
&:nth-child(3) { padding-left: 10px; }
115+
&:nth-child(4) { padding-left: 10px; }
100116
// end date
101-
&:nth-child(5) { padding-left: 10px; }
117+
&:nth-child(6) { padding-left: 10px; }
102118
}
103119
}
104120

105121
@media screen and (max-width: 1024px - 1px) {
106122
th,
107123
td {
108124
// description
109-
&:nth-child(3) { padding-left: 5px; }
125+
&:nth-child(4) { padding-left: 5px; }
110126
// start date
111-
&:nth-child(4) { padding-left: 10px; }
127+
&:nth-child(5) { padding-left: 10px; }
112128
// end date
113-
&:nth-child(5) { padding-left: 5px; }
129+
&:nth-child(6) { padding-left: 5px; }
114130
// status
115-
&:nth-child(6) { padding-left: 10px; }
131+
&:nth-child(7) { padding-left: 10px; }
116132
// copilot
117-
&:nth-child(7) { padding-left: 15px; }
133+
&:nth-child(8) { padding-left: 15px; }
118134
}
119135

120136
:global(.edit-milestone-row) {
121137
th,
122138
td {
123139
// description
124-
&:nth-child(3) { padding-left: 5px; }
140+
&:nth-child(4) { padding-left: 5px; }
125141
// end date
126-
&:nth-child(5) { padding-left: 5px; }
142+
&:nth-child(6) { padding-left: 5px; }
127143
}
128144
}
129145
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
import PT from 'prop-types'
3+
import IconHelp from '../../../../../../assets/icons/help-me.svg'
4+
import './ConfirmMoveMilestoneDate.scss'
5+
6+
7+
class ConfirmMoveMilestoneDate extends React.Component {
8+
constructor(props) {
9+
super(props)
10+
11+
this.state = {
12+
value: 0
13+
}
14+
this.onInputChange = this.onInputChange.bind(this)
15+
}
16+
onInputChange(e) {
17+
this.setState({
18+
value: e.target.value
19+
})
20+
}
21+
render() {
22+
const {
23+
onClose
24+
} = this.props
25+
return (
26+
<div styleName="confirm-delete-milestone">
27+
<h3 styleName="title">
28+
<IconHelp styleName="icon" />
29+
Move Milestone Dates
30+
</h3>
31+
<p styleName="text">
32+
Move selected milestone dates
33+
</p>
34+
<div styleName="input-row">
35+
<input type="number" min={0} value={this.state.value} onChange={this.onInputChange}/> days
36+
</div>
37+
<div styleName="footer">
38+
<button type="button" className="tc-btn tc-btn-primary tc-btn-sm" onClick={() => onClose(true, this.state.value)}>MOVE</button>
39+
<button type="button" className="tc-btn tc-btn-warning tc-btn-sm" onClick={() => onClose()}>CANCEL</button>
40+
</div>
41+
</div>
42+
)
43+
}
44+
}
45+
46+
ConfirmMoveMilestoneDate.propTypes = {
47+
onClose: PT.func,
48+
}
49+
50+
export default ConfirmMoveMilestoneDate

0 commit comments

Comments
 (0)