Skip to content

Moving latest to prod #803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f0befe3
feat: git#733-Enable Scheduling of Challenges in the Future
Aug 18, 2020
60b7f43
feat: field to assign a member to a task
maxceem Aug 18, 2020
0954750
Merge branch 'develop' into feature/assign-task
maxceem Aug 18, 2020
eb066f9
fix: show assigned member handle
maxceem Aug 18, 2020
1119aeb
Merge pull request #756 from maxceem/feature/assign-task
Aug 19, 2020
b18bd4e
feat: git#757-Quick links for Direct and OR in Challenge View : Added…
Aug 19, 2020
ccd408f
Resolved conflicts
Aug 19, 2020
353c496
feat: allow only one prize for task
maxceem Aug 19, 2020
37aaa70
fix: creating new challenges
maxceem Aug 19, 2020
e493417
fix: view task when challenge is loading
maxceem Aug 19, 2020
99a208a
fix: remove warning
maxceem Aug 19, 2020
38e9120
fix: null pointer when creating a challenge
Aug 20, 2020
1b43c1b
Merge branch 'develop' into feature/assign-task
maxceem Aug 20, 2020
e450c48
feat: closing task
maxceem Aug 20, 2020
47ce087
chore: remove debugging code
maxceem Aug 20, 2020
72fd511
Merge pull request #758 from maxceem/feature/assign-task
Aug 24, 2020
eec1faf
Merge pull request #759 from maxceem/feature/one-prize-for-task
Aug 24, 2020
634fc17
Merge pull request #762 from maxceem/feature/close-task
Aug 24, 2020
37e7897
feat: git#674-Enable Chameleon in Production
Aug 26, 2020
212be3d
feat: git3763 - Hide Launch as Active button until challenge is saved…
Aug 27, 2020
513f51a
fix: fixed Back button height to be in sync with other buttons
Aug 27, 2020
41fcb8c
fix: git#764 - Remove terms API call as it is throwing 403 on page load
Aug 27, 2020
9fcc0bb
fix: challenge is not updated after "Save"
maxceem Aug 31, 2020
f333c00
Merge pull request #768 from maxceem/issue-767
Sep 1, 2020
117e86a
fix: update challenge in the list after editing
maxceem Sep 3, 2020
19abdb8
fix: 'Last Saved' Date and autosaving to redux
maxceem Sep 3, 2020
d8840c0
fix: display prize error next to the field
maxceem Sep 3, 2020
abc829c
fix: basic validation for new challenge
maxceem Sep 3, 2020
8b0f290
Merge pull request #790 from maxceem/issue-774
Sep 4, 2020
cabfd8e
Merge pull request #792 from maxceem/issue-777
Sep 4, 2020
24df4c3
Merge pull request #793 from topcoder-platform/issue-779
Sep 4, 2020
d04d7c4
Merge pull request #791 from maxceem/issue-785
Sep 4, 2020
d9f96b9
fix: git#747-Update "Launch Confirmation" Copy
Sep 8, 2020
78f7016
fix: creating multiple challenges at a time
maxceem Sep 8, 2020
2b8e9a8
Merge branch 'develop' into issue-779
maxceem Sep 8, 2020
a77181c
fix: don't show prize validation immediately
maxceem Sep 8, 2020
9ec9472
Merge pull request #796 from maxceem/issue-680
Sep 9, 2020
b6149b9
Merge pull request #797 from maxceem/issue-779
Sep 9, 2020
6141397
fix: show created challenge on the list
maxceem Sep 10, 2020
c9d7467
Merge pull request #799 from maxceem/issue-798
Sep 11, 2020
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
95 changes: 93 additions & 2 deletions src/actions/challenges.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
fetchResources,
fetchResourceRoles,
fetchChallengeTimelines,
fetchChallengeTracks
fetchChallengeTracks,
updateChallenge,
patchChallenge,
createChallenge as createChallengeAPI
} from '../services/challenges'
import {
LOAD_CHALLENGE_DETAILS_PENDING,
Expand All @@ -30,7 +33,13 @@ import {
LOAD_CHALLENGE_RESOURCES_SUCCESS,
LOAD_CHALLENGE_RESOURCES_FAILURE,
REMOVE_ATTACHMENT,
PAGE_SIZE
PAGE_SIZE,
UPDATE_CHALLENGE_DETAILS_PENDING,
UPDATE_CHALLENGE_DETAILS_SUCCESS,
UPDATE_CHALLENGE_DETAILS_FAILURE,
CREATE_CHALLENGE_PENDING,
CREATE_CHALLENGE_SUCCESS,
CREATE_CHALLENGE_FAILURE
} from '../config/constants'
import { fetchProjectById } from '../services/projects'
import { loadProject } from './projects'
Expand Down Expand Up @@ -197,6 +206,88 @@ export function loadChallengeDetails (projectId, challengeId) {
}
}

/**
* Update challenge details
*
* @param {String} challengeId challenge id
* @param {Object} challengeDetails challenge data
*
* @returns {Promise<{ type: string, challengeDetails: object }>} action object
*/
export function updateChallengeDetails (challengeId, challengeDetails) {
return async (dispatch) => {
dispatch({
type: UPDATE_CHALLENGE_DETAILS_PENDING
})

return updateChallenge(challengeId, challengeDetails).then((challenge) => {
return dispatch({
type: UPDATE_CHALLENGE_DETAILS_SUCCESS,
challengeDetails: challenge
})
}).catch(() => {
dispatch({
type: UPDATE_CHALLENGE_DETAILS_FAILURE
})
})
}
}

/**
* Create a new challenge
*
* @param {Object} challengeDetails challenge data
*
* @returns {Promise<{ type: string, challengeDetails: object }>} action object
*/
export function createChallenge (challengeDetails) {
return async (dispatch) => {
dispatch({
type: CREATE_CHALLENGE_PENDING
})

return createChallengeAPI(challengeDetails).then((challenge) => {
return dispatch({
type: CREATE_CHALLENGE_SUCCESS,
challengeDetails: challenge
})
}).catch(() => {
dispatch({
type: CREATE_CHALLENGE_FAILURE
})
})
}
}

/**
* Partially update challenge details
*
* The difference from `updateChallengeDetails` that this method internally uses `PATCH` API method instead of `PUT`.
*
* @param {String} challengeId challenge id
* @param {Object} partialChallengeDetails partial challenge data
*
* @returns {Promise<{ type: string, challengeDetails: object }>} action object
*/
export function partiallyUpdateChallengeDetails (challengeId, partialChallengeDetails) {
return async (dispatch) => {
dispatch({
type: UPDATE_CHALLENGE_DETAILS_PENDING
})

return patchChallenge(challengeId, partialChallengeDetails).then((challenge) => {
return dispatch({
type: UPDATE_CHALLENGE_DETAILS_SUCCESS,
challengeDetails: challenge
})
}).catch(() => {
dispatch({
type: UPDATE_CHALLENGE_DETAILS_FAILURE
})
})
}
}

export function loadTimelineTemplates () {
return async (dispatch) => {
const timelineTemplates = await fetchTimelineTemplates()
Expand Down
44 changes: 44 additions & 0 deletions src/actions/members.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { LOAD_MEMBER_PENDING, LOAD_MEMBER_FAILURE, LOAD_MEMBER_SUCCESS } from '../config/constants'
import { searchProfilesByUserIds } from '../services/user'
import _ from 'lodash'

/**
* Load user details into the store
* @param userId user id
* @param forceReload if `true` then member details would be reloaded even if already loaded
* @returns {Function}
*/
export function loadMemberDetails (userId, forceReload = false) {
return (dispatch, getState) => {
const members = getState().members.members
const existentMember = _.find(members, { userId })

// don't reload member details if already loaded, unless we force to
if (existentMember && !forceReload) {
return
}

dispatch({
type: LOAD_MEMBER_PENDING,
meta: {
userId
}
})
searchProfilesByUserIds([userId]).then(([foundUser]) => {
if (foundUser) {
dispatch({
type: LOAD_MEMBER_SUCCESS,
payload: foundUser
})
} else {
dispatch({
type: LOAD_MEMBER_FAILURE
})
}
}).catch(() => {
dispatch({
type: LOAD_MEMBER_FAILURE
})
})
}
}
5 changes: 3 additions & 2 deletions src/components/Buttons/OutlineButton/Outline.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@
}
}

&.disable {
&.disable,
&[disabled] {
@include roboto-light();
font-weight: 300;

border: 1px solid $tc-gray-40;
cursor: default;

span {
color: $tc-gray-40;
Expand Down
12 changes: 9 additions & 3 deletions src/components/Buttons/OutlineButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ import cn from 'classnames'
import styles from './Outline.module.scss'
import _ from 'lodash'

const OutlineButton = ({ type, text, link, onClick, url, className, submit }) => {
const OutlineButton = ({ type, text, link, onClick, url, className, submit, disabled }) => {
if (_.isEmpty(link) && _.isEmpty(url)) {
return (
<button type={submit ? 'submit' : 'button'} className={cn(styles.container, styles[type], className)} onClick={submit ? null : onClick}>
<button
type={submit ? 'submit' : 'button'}
className={cn(styles.container, styles[type], className)}
onClick={submit ? null : onClick}
disabled={disabled}
>
<span>{text}</span>
</button>
)
Expand Down Expand Up @@ -37,7 +42,8 @@ OutlineButton.propTypes = {
url: PropTypes.string,
className: PropTypes.string,
onClick: PropTypes.func,
submit: PropTypes.bool
submit: PropTypes.bool,
disabled: PropTypes.bool
}

export default OutlineButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@import "../../../styles/includes";

.row {
box-sizing: border-box;
display: flex;
flex-direction: row;
margin: 30px 30px 0 30px;
align-content: space-between;
justify-content: flex-start;

.field {
@include upto-sm {
display: block;
padding-bottom: 10px;
}

label {
@include roboto-bold();

font-size: 16px;
line-height: 19px;
font-weight: 500;
color: $tc-gray-80;
}

&.col1 {
max-width: 185px;
min-width: 185px;
margin-right: 14px;
white-space: nowrap;
display: flex;
align-items: center;
}

&.col2 {
align-self: flex-end;
margin-bottom: auto;
margin-top: auto;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
}

.readOnlyValue {
margin-bottom: 0.5rem; // the same like `label` to be aligned
}
}

48 changes: 48 additions & 0 deletions src/components/ChallengeEditor/AssignedMember-Field/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Field to choose assigned member using autocomplete.
*/
import React from 'react'
import PropTypes from 'prop-types'
import cn from 'classnames'
import styles from './AssignedMember-Field.module.scss'
import SelectUserAutocomplete from '../../SelectUserAutocomplete'

const AssignedMemberField = ({ challenge, onChange, assignedMemberDetails, readOnly }) => {
const value = challenge.task.memberId ? {
// if we know assigned member details, then show user `handle`, otherwise fallback to `userId`
label: assignedMemberDetails ? assignedMemberDetails.handle : `User id: ${challenge.task.memberId}`,
value: challenge.task.memberId
} : null

return (
<div className={styles.row}>
<div className={cn(styles.field, styles.col1)}>
<label htmlFor='assignedMember'>Assigned Member :</label>
</div>
<div className={cn(styles.field, styles.col2)}>
{readOnly ? (
value && <div className={styles.readOnlyValue}>{value.label}</div>
) : (
<SelectUserAutocomplete
value={value}
onChange={onChange}
/>
)}
</div>
</div>
)
}

AssignedMemberField.defaultProps = {
assignedMemberDetails: null,
readOnly: false
}

AssignedMemberField.propTypes = {
challenge: PropTypes.shape().isRequired,
onChange: PropTypes.func,
assignedMemberDetails: PropTypes.shape(),
readOnly: PropTypes.bool
}

export default AssignedMemberField
15 changes: 11 additions & 4 deletions src/components/ChallengeEditor/ChallengeEditor.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,21 @@
}

.actionButtons {
&.button {
position: absolute;
top: 30px;
a {
height: 40px;
position: absolute;
top: 30px;
right: 20px;
}
}

.actionButtonsLeft {
left: 20px;
}

.actionButtonsRight {
right: 20px;
}

.buttonContainer {
display: flex;
margin: 0px 30px;
Expand Down
26 changes: 15 additions & 11 deletions src/components/ChallengeEditor/ChallengePrizes-Field/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ class ChallengePrizesField extends Component {

renderPrizes () {
const { currentPrizeIndex } = this.state
const { readOnly } = this.props
const { readOnly, challenge } = this.props
const isTask = _.get(challenge, 'task.isTask', false)
return _.map(this.getChallengePrize().prizes, (prize, index, { length }) => (
<div key={`${index}-${prize.amount}-edit`}>
<div className={styles.row}>
<div className={cn(styles.field, styles.col1)}>
<label htmlFor={`${index}-prize`}>Prize {index + 1} {!readOnly && (<span>*</span>)}:</label>
<label htmlFor={`${index}-prize`}>Prize {!isTask ? index + 1 : ''} {!readOnly && (<span>*</span>)}:</label>
</div>
{readOnly ? (
<span>${prize.value}</span>
Expand All @@ -83,20 +84,23 @@ class ChallengePrizesField extends Component {
}
</div>)}
</div>
{!readOnly && (prize.value === '' || (length > 1 && +prize.value === 0)) && <div className={styles.row}>
<div className={cn(styles.field, styles.col1)} />
<div className={cn(styles.field, styles.col2, styles.error)}>
{prize.value === ''
? 'Prize amount is required field'
: 'Prize amount must be more than zero'}
{!readOnly && challenge.submitTriggered && (prize.value === '' || (+prize.value <= 0 || +prize.value > 1000000)) && (
<div className={styles.row}>
<div className={cn(styles.field, styles.col1)} />
<div className={cn(styles.field, styles.col2, styles.error)}>
{prize.value === ''
? 'Prize amount is required field'
: 'Prize amount must be more than 0 and no more than 1000000'}
</div>
</div>
</div>}
)}
</div>
))
}

render () {
const { readOnly } = this.props
const { readOnly, challenge } = this.props
const isTask = _.get(challenge, 'task.isTask', false)
return (
<div className={styles.container}>
<div className={styles.row}>
Expand All @@ -105,7 +109,7 @@ class ChallengePrizesField extends Component {
</div>
</div>
{ this.renderPrizes() }
{!readOnly && (<div className={styles.button} onClick={this.addNewPrize}>
{!readOnly && !isTask && (<div className={styles.button} onClick={this.addNewPrize}>
<PrimaryButton text={'Add New Prize'} type={'info'} />
</div>)}
</div>
Expand Down
Loading