Skip to content

Commit a828fef

Browse files
Merge pull request #1298 from topcoder-platform/develop
Self-service prod release
2 parents 28f66ac + 0ff512f commit a828fef

File tree

23 files changed

+595
-150
lines changed

23 files changed

+595
-150
lines changed

package-lock.json

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions/challenges.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ import {
4646
DELETE_CHALLENGE_PENDING,
4747
DELETE_CHALLENGE_SUCCESS,
4848
DELETE_CHALLENGE_FAILURE,
49-
LOAD_CHALLENGE_RESOURCES
49+
LOAD_CHALLENGE_RESOURCES,
50+
CHALLENGE_STATUS
5051
} from '../config/constants'
5152
import { loadProject } from './projects'
5253
import { removeChallengeFromPhaseProduct, saveChallengeAsPhaseProduct } from '../services/projects'
@@ -58,7 +59,7 @@ import { removeChallengeFromPhaseProduct, saveChallengeAsPhaseProduct } from '..
5859
/**
5960
* Loads active challenges of project by page
6061
*/
61-
export function loadChallengesByPage (page, projectId, status, filterChallengeName = null) {
62+
export function loadChallengesByPage (page, projectId, status, filterChallengeName = null, selfService = false, userHandle = null) {
6263
return (dispatch, getState) => {
6364
dispatch({
6465
type: LOAD_CHALLENGES_PENDING,
@@ -85,6 +86,12 @@ export function loadChallengesByPage (page, projectId, status, filterChallengeNa
8586
} else if (!(_.isInteger(projectId) && projectId > 0)) {
8687
filters['status'] = 'Active'
8788
}
89+
if (selfService) {
90+
filters.selfService = true
91+
if (userHandle && filters.status.toUpperCase() !== CHALLENGE_STATUS.DRAFT) {
92+
filters.selfServiceCopilot = userHandle
93+
}
94+
}
8895

8996
return fetchChallenges(filters, {
9097
page,

src/actions/sidebar.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ export function loadProjects (filterProjectName = '', myProjects = true) {
5252
}
5353
}
5454

55+
/**
56+
* Unloads projects of the authenticated user
57+
*/
58+
export function unloadProjects () {
59+
return (dispatch) => {
60+
dispatch({
61+
type: LOAD_PROJECTS_SUCCESS,
62+
projects: []
63+
})
64+
}
65+
}
66+
5567
/**
5668
* Reset active params. e.g activeProjectId
5769
*/

src/components/ChallengeEditor/ChallengeSchedule-Field/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,12 @@ class ChallengeScheduleField extends Component {
100100
phase.scheduledStartDate = startDate
101101
phase.scheduledEndDate = moment(startDate).add(phase.duration || 0, 'hours').toDate()
102102
phase.actualStartDate = phase.scheduledStartDate
103-
phase.actualEndDate = phase.scheduledEndDate
104103
} else {
105104
const preIndex = _.findIndex(phases, (p) => p.id === phase.predecessor)
106105
// `Invalid phase predecessor: ${phase.predecessor}`
107106
phase.scheduledStartDate = phases[preIndex].scheduledEndDate
108107
phase.scheduledEndDate = moment(phase.scheduledStartDate).add(phase.duration || 0, 'hours').toDate()
109108
phase.actualStartDate = phase.scheduledStartDate
110-
phase.actualEndDate = phase.scheduledEndDate
111109
}
112110
}
113111

src/components/ChallengeEditor/ChallengeView/index.js

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ const ChallengeView = ({
3737
enableEdit,
3838
onLaunchChallenge,
3939
onCloseTask,
40-
projectPhases
40+
projectPhases,
41+
assignYourselfCopilot,
42+
showRejectChallengeModal,
43+
loggedInUser
4144
}) => {
4245
const selectedType = _.find(metadata.challengeTypes, { id: challenge.typeId })
4346
const challengeTrack = _.find(metadata.challengeTracks, { id: challenge.trackId })
@@ -74,7 +77,7 @@ const ChallengeView = ({
7477
const reviewerFromResources = reviewerResource ? reviewerResource.memberHandle : ''
7578
let copilot, reviewer
7679
if (challenge) {
77-
copilot = challenge.copilot
80+
copilot = challenge.copilot || (challenge.legacy && challenge.legacy.selfServiceCopilot)
7881
reviewer = challenge.reviewer
7982
}
8083
copilot = copilot || copilotFromResources
@@ -103,18 +106,18 @@ const ChallengeView = ({
103106
</span>
104107
</div>
105108
{selectedMilestone &&
106-
<div className={styles.col}>
107-
<span><span className={styles.fieldTitle}>Milestone:</span> {selectedMilestone ? (
108-
<a href={`${CONNECT_APP_URL}/projects/${projectDetail.id}`} target='_blank'
109-
rel='noopener noreferrer'>
110-
{selectedMilestone.name}
111-
</a>
112-
) : ''}</span>
113-
</div>
109+
<div className={styles.col}>
110+
<span><span className={styles.fieldTitle}>Milestone:</span> {selectedMilestone ? (
111+
<a href={`${CONNECT_APP_URL}/projects/${projectDetail.id}`} target='_blank'
112+
rel='noopener noreferrer'>
113+
{selectedMilestone.name}
114+
</a>
115+
) : ''}</span>
116+
</div>
114117
}
115118
<div className={styles.col}>
116119
<span className={styles.fieldTitle}>Track:</span>
117-
<Track disabled type={challengeTrack} isActive key={challenge.trackId} onUpdateOthers={() => {}} />
120+
<Track disabled type={challengeTrack} isActive key={challenge.trackId} onUpdateOthers={() => { }} />
118121
</div>
119122
<div className={styles.col}>
120123
<span><span className={styles.fieldTitle}>Type:</span> {selectedType ? selectedType.name : ''}</span>
@@ -130,10 +133,11 @@ const ChallengeView = ({
130133
</div>
131134
</div>
132135
{isTask &&
133-
<AssignedMemberField challenge={challenge} assignedMemberDetails={assignedMemberDetails} readOnly />}
136+
<AssignedMemberField challenge={challenge} assignedMemberDetails={assignedMemberDetails} readOnly />}
134137
<CopilotField challenge={{
135-
copilot
136-
}} copilots={metadata.members} readOnly />
138+
copilot,
139+
selfService: challenge.legacy.selfService
140+
}} copilots={challengeResources} assignYourselfCopilot={assignYourselfCopilot} showRejectChallengeModal={showRejectChallengeModal} readOnly loggedInUser={loggedInUser} />
137141
<div className={cn(styles.row, styles.topRow)}>
138142
<div className={styles.col}>
139143
<span><span
@@ -259,7 +263,10 @@ ChallengeView.propTypes = {
259263
enableEdit: PropTypes.bool,
260264
onLaunchChallenge: PropTypes.func,
261265
onCloseTask: PropTypes.func,
262-
projectPhases: PropTypes.arrayOf(PropTypes.object)
266+
projectPhases: PropTypes.arrayOf(PropTypes.object),
267+
assignYourselfCopilot: PropTypes.func.isRequired,
268+
showRejectChallengeModal: PropTypes.func.isRequired,
269+
loggedInUser: PropTypes.object
263270
}
264271

265272
export default withRouter(ChallengeView)

src/components/ChallengeEditor/ChallengeViewTabs/index.js

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import LegacyLinks from '../../LegacyLinks'
1313
import ForumLink from '../../ForumLink'
1414
import Registrants from '../Registrants'
1515
import Submissions from '../Submissions'
16-
import { getResourceRoleByName } from '../../../util/tc'
17-
import { MESSAGE } from '../../../config/constants'
16+
import { checkAdmin, getResourceRoleByName } from '../../../util/tc'
17+
import { CHALLENGE_STATUS, MESSAGE } from '../../../config/constants'
1818
import Tooltip from '../../Tooltip'
1919
import CancelDropDown from '../Cancel-Dropdown'
2020
import 'react-tabs/style/react-tabs.css'
@@ -43,7 +43,11 @@ const ChallengeViewTabs = ({
4343
onLaunchChallenge,
4444
cancelChallenge,
4545
onCloseTask,
46-
projectPhases
46+
projectPhases,
47+
assignYourselfCopilot,
48+
showRejectChallengeModal,
49+
loggedInUser,
50+
onApproveChallenge
4751
}) => {
4852
const [selectedTab, setSelectedTab] = useState(0)
4953

@@ -80,6 +84,22 @@ const ChallengeViewTabs = ({
8084

8185
const isTask = _.get(challenge, 'task.isTask', false)
8286

87+
const isSelfService = challenge.legacy.selfService
88+
const isDraft = challenge.status.toUpperCase() === CHALLENGE_STATUS.DRAFT
89+
const isSelfServiceCopilot = challenge.legacy.selfServiceCopilot === loggedInUser.handle
90+
const isAdmin = checkAdmin(token)
91+
const canApprove = isSelfServiceCopilot && isDraft && isSelfService
92+
const hasBillingAccount = _.get(projectDetail, 'billingAccountId') !== null
93+
// only challenges that have a billing account can be launched AND
94+
// if this isn't self-service, permit launching if the challenge is draft
95+
// OR if this isn't a non-self-service draft, permit launching if:
96+
// a) the current user is either the self-service copilot or is an admin AND
97+
// b) the challenge is approved
98+
const canLaunch = hasBillingAccount &&
99+
((!isSelfService && isDraft) ||
100+
((isSelfServiceCopilot || isAdmin) &&
101+
challenge.status.toUpperCase() === CHALLENGE_STATUS.APPROVED))
102+
83103
return (
84104
<div className={styles.list}>
85105
<Helmet title='View Details' />
@@ -94,7 +114,7 @@ const ChallengeViewTabs = ({
94114
styles.actionButtonsLeft
95115
)}
96116
>
97-
{ isTask ? (<ForumLink challenge={challenge} />)
117+
{isTask ? (<ForumLink challenge={challenge} />)
98118
: (<LegacyLinks challenge={challenge} challengeView />)
99119
}
100120
</div>
@@ -107,13 +127,14 @@ const ChallengeViewTabs = ({
107127
styles.actionButtonsRight
108128
)}
109129
>
110-
{(challenge.status === 'Draft' || challenge.status === 'New') && <div className={styles['cancel-button']}><CancelDropDown challenge={challenge} onSelectMenu={cancelChallenge} /></div>}
111-
{challenge.status === 'Draft' && (
130+
{(isDraft || challenge.status === 'New') && !isSelfService &&
131+
(<div className={styles['cancel-button']}><CancelDropDown challenge={challenge} onSelectMenu={cancelChallenge} /></div>)}
132+
{canLaunch && (
112133
<div className={styles.button}>
113134
{challenge.legacyId || isTask ? (
114135
<PrimaryButton
115-
text={'Launch'}
116-
type={'info'}
136+
text='Launch'
137+
type='info'
117138
onClick={onLaunchChallenge}
118139
/>
119140
) : (
@@ -124,6 +145,15 @@ const ChallengeViewTabs = ({
124145
)}
125146
</div>
126147
)}
148+
{canApprove && (
149+
<div className={styles.button}>
150+
<PrimaryButton
151+
text='Approve'
152+
type='info'
153+
onClick={onApproveChallenge}
154+
/>
155+
</div>
156+
)}
127157
{isTask && challenge.status === 'Active' && (
128158
<div className={styles.button}>
129159
{assignedMemberDetails ? (
@@ -138,9 +168,18 @@ const ChallengeViewTabs = ({
138168
)}
139169
</div>
140170
)}
141-
{enableEdit && (
171+
{enableEdit && !isSelfService && (
142172
<PrimaryButton text={'Edit'} type={'info'} submit link={`./edit`} />
143173
)}
174+
{isSelfService && isDraft && (isAdmin || isSelfServiceCopilot) && (
175+
<div className={styles.button}>
176+
<PrimaryButton
177+
text='Reject challenge'
178+
type='danger'
179+
onClick={showRejectChallengeModal}
180+
/>
181+
</div>
182+
)}
144183
<PrimaryButton text={'Back'} type={'info'} submit link={`..`} />
145184
</div>
146185
</div>
@@ -208,6 +247,10 @@ const ChallengeViewTabs = ({
208247
onLaunchChallenge={onLaunchChallenge}
209248
onCloseTask={onCloseTask}
210249
projectPhases={projectPhases}
250+
assignYourselfCopilot={assignYourselfCopilot}
251+
showRejectChallengeModal={showRejectChallengeModal}
252+
onApproveChallenge={onApproveChallenge}
253+
loggedInUser={loggedInUser}
211254
/>
212255
)}
213256
{selectedTab === 1 && (
@@ -244,7 +287,11 @@ ChallengeViewTabs.propTypes = {
244287
onLaunchChallenge: PropTypes.func,
245288
cancelChallenge: PropTypes.func.isRequired,
246289
onCloseTask: PropTypes.func,
247-
projectPhases: PropTypes.arrayOf(PropTypes.object)
290+
projectPhases: PropTypes.arrayOf(PropTypes.object),
291+
assignYourselfCopilot: PropTypes.func.isRequired,
292+
showRejectChallengeModal: PropTypes.func.isRequired,
293+
loggedInUser: PropTypes.object.isRequired,
294+
onApproveChallenge: PropTypes.func
248295
}
249296

250297
export default ChallengeViewTabs

src/components/ChallengeEditor/Copilot-Field/Copilot-Field.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
display: flex;
4545
flex-direction: row;
4646
flex-wrap: wrap;
47+
width: 100%;
4748

4849
div {
4950
margin-right: 13px;

0 commit comments

Comments
 (0)