Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9c12180
fix: allow MMs to have multiple places
hentrymartin Jul 4, 2025
e950796
Merge pull request #1657 from topcoder-platform/pm-1336
kkartunov Jul 9, 2025
e551d12
feat: modifications on copilot addition to project
hentrymartin Jul 25, 2025
537a94b
fix: action string
hentrymartin Jul 28, 2025
59cb7d7
fix: action string
hentrymartin Jul 28, 2025
9e8f76f
Merge pull request #1658 from topcoder-platform/pm-1506
kkartunov Jul 29, 2025
27fc0b8
fix: show error when invite comes from an closed opportunity
hentrymartin Jul 31, 2025
1f93074
Merge pull request #1659 from topcoder-platform/pm-1398
kkartunov Aug 1, 2025
fb89618
PM-1270 Exclude cancelled and completed projects from copilot request…
himaniraghav3 Aug 1, 2025
887e1ff
fix: added projectId query param to request form url
hentrymartin Aug 1, 2025
25c01d6
fix: added projectId query param to request form url
hentrymartin Aug 1, 2025
07b407b
Merge pull request #1661 from topcoder-platform/pm-1365
hentrymartin Aug 1, 2025
8ede5aa
Merge pull request #1660 from topcoder-platform/PM-1270
himaniraghav3 Aug 5, 2025
15f3784
PM-1608 - update project search: use exact match
vas3a Aug 11, 2025
7a1ed52
PM-1608 - project search: update search input
vas3a Aug 11, 2025
7c76389
PM-1609 - add navigation for user management
vas3a Aug 11, 2025
47f89e8
Limit challenge name to only letters and numbers to avoid Informix is…
jmgasper Aug 11, 2025
fb2d638
Limit challenge name to only letters and numbers to avoid Informix is…
jmgasper Aug 11, 2025
8990be2
Revert "Hacks for v6 challenge API"
jmgasper Aug 11, 2025
b70bdcc
Add .pem to gitignore
jmgasper Aug 11, 2025
b7707a5
Remove pem files
jmgasper Aug 11, 2025
16fea50
Merge pull request #1663 from topcoder-platform/PM-1608_project-searc…
vas3a Aug 12, 2025
8e91f82
Merge pull request #1664 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 12, 2025
9cd8413
Members api fix
vas3a Aug 12, 2025
de10459
Merge pull request #1666 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 12, 2025
0110fb3
PM-1609 - update button colors
vas3a Aug 13, 2025
c807031
Merge pull request #1667 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 13, 2025
94cdb18
show users button for all users
vas3a Aug 13, 2025
48346d2
Merge pull request #1668 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 13, 2025
5fff264
Fix button width & color
vas3a Aug 13, 2025
506c195
Merge pull request #1669 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 13, 2025
184e715
make users button available to everyone
vas3a Aug 13, 2025
754a730
Merge pull request #1670 from topcoder-platform/PM-1609_navigation-fo…
vas3a Aug 13, 2025
e6edb31
Merge branch 'develop' of github.com:topcoder-platform/work-manager i…
vas3a Sep 17, 2025
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ yarn-error.log*
*.pem
*.vscode
*.pem
*.vscode

# e2e test case
test-automation/temp
test-automation/test-results
10 changes: 5 additions & 5 deletions config/constants/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ module.exports = {
MEMBER_API_URL: `${DEV_API_HOSTNAME}/v6/members`,
CHALLENGE_API_URL: `${DEV_API_HOSTNAME}/v6/challenges`,
CHALLENGE_API_VERSION: '1.1.0',
CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v6/timeline-templates`,
CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v6/challenge-types`,
CHALLENGE_TRACKS_URL: `${DEV_API_HOSTNAME}/v6/challenge-tracks`,
CHALLENGE_PHASES_URL: `${DEV_API_HOSTNAME}/v6/challenge-phases`,
CHALLENGE_TIMELINES_URL: `${DEV_API_HOSTNAME}/v6/challenge-timelines`,
CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v5/timeline-templates`,
CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v5/challenge-types`,
CHALLENGE_TRACKS_URL: `${DEV_API_HOSTNAME}/v5/challenge-tracks`,
CHALLENGE_PHASES_URL: `${DEV_API_HOSTNAME}/v5/challenge-phases`,
CHALLENGE_TIMELINES_URL: `${DEV_API_HOSTNAME}/v5/challenge-timelines`,
PROJECT_API_URL: `${DEV_API_HOSTNAME}/v5/projects`,
GROUPS_API_URL: `${DEV_API_HOSTNAME}/v6/groups`,
TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`,
Expand Down
200 changes: 99 additions & 101 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@
"build": "node scripts/build.js",
"lint": "eslint ./src",
"lint:fix": "eslint --fix ./src",
"test": "node scripts/test.js",
"restart": "node scripts/build.js && node server.js"
"test": "node scripts/test.js"
},
"eslintConfig": {
"extends": [
Expand Down
4 changes: 2 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ function check () {
}
app.use(healthCheck.middleware([check]))
app.use((req, res, next) => {
//res.header('Referrer-Policy', 'strict-origin-when-cross-origin')
res.header('Referrer-Policy', 'strict-origin-when-cross-origin')
res.header('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
res.header('X-Content-Type-Options', 'nosniff')
// res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload')
res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload')
res.header('Cache-control', 'public, max-age=0')
res.header('Pragma', 'no-cache')
res.setHeader('X-Frame-Options', 'DENY')
Expand Down
2 changes: 1 addition & 1 deletion src/actions/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) {
if (!isNaN(projectNameOrIdFilter)) { // if it is number
filters['id'] = parseInt(projectNameOrIdFilter, 10)
} else { // text search
filters['keyword'] = decodeURIComponent(projectNameOrIdFilter)
filters['keyword'] = `"${decodeURIComponent(projectNameOrIdFilter)}"`
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/actions/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function searchUserProjects (isAdmin = true, keyword) {
sort: 'updatedAt desc',
perPage: 20,
page: 1,
keyword
keyword: `"${keyword}"`
}
if (!isAdmin) {
filters['memberOnly'] = true
Expand Down
4 changes: 2 additions & 2 deletions src/components/Buttons/OutlineButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const OutlineButton = ({ type, text, link, onClick, url, className, submit, disa

if (!_.isEmpty(link)) {
return (
<Link className={cn(styles.container, styles[type], className)} to={`${link}`}>
<Link className={cn(styles.container, styles[type], className)} to={link}>
<span>{text}</span>
</Link>
)
Expand All @@ -38,7 +38,7 @@ const OutlineButton = ({ type, text, link, onClick, url, className, submit, disa
OutlineButton.propTypes = {
type: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
link: PropTypes.string,
link: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
url: PropTypes.string,
className: PropTypes.string,
onClick: PropTypes.func,
Expand Down
39 changes: 32 additions & 7 deletions src/components/ChallengeEditor/ChallengeName-Field/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,47 @@ import styles from './ChallengeName-Field.module.scss'
import cn from 'classnames'

const ChallengeNameField = ({ challenge, onUpdateInput }) => {
const handleChange = (e) => {
// Remove any characters that are NOT letters, numbers, or spaces
const sanitizedValue = e.target.value.replace(/[^a-zA-Z0-9 ]/g, '')
onUpdateInput({
target: {
name: e.target.name,
value: sanitizedValue
}
})
}

return (
<>
<div className={styles.row}>
<div className={cn(styles.field, styles.col1)}>
<label htmlFor='challengeName'>Work Name <span>*</span> :</label>
<label htmlFor='challengeName'>
Work Name <span>*</span> :
</label>
</div>
<div className={cn(styles.field, styles.col2)}>
<input className={styles.challengeName} id='name' name='name' type='text' placeholder='Work Name' value={challenge.name} maxLength='200' required onChange={onUpdateInput} />
<input
className={styles.challengeName}
id='name'
name='name'
type='text'
placeholder='Work Name'
value={challenge.name}
maxLength='200'
required
onChange={handleChange}
/>
</div>
</div>
{ challenge.submitTriggered && !challenge.name && <div className={styles.row}>
<div className={cn(styles.field, styles.col1)} />
<div className={cn(styles.field, styles.col2, styles.error)}>
Work Name is required field
{challenge.submitTriggered && !challenge.name && (
<div className={styles.row}>
<div className={cn(styles.field, styles.col1)} />
<div className={cn(styles.field, styles.col2, styles.error)}>
Work Name is required field
</div>
</div>
</div> }
)}
</>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChallengeEditor/ChallengeViewTabs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ const ChallengeViewTabs = ({
(<div className={styles['cancel-button']}><CancelDropDown challenge={challenge} onSelectMenu={cancelChallenge} /></div>)}
{canLaunch && (
<div className={styles.button}>
{challenge ? (
{challenge.legacyId || isTask ? (
<PrimaryButton
text='Launch'
type='info'
Expand Down
3 changes: 1 addition & 2 deletions src/components/ChallengeEditor/Copilot-Field/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import { PrimaryButton } from '../../Buttons'
import CopilotCard from '../../CopilotCard'

import styles from './Copilot-Field.module.scss'
import { PRIZE_SETS_TYPE } from '../../../config/constants'

const CopilotField = ({ copilots, challenge, onUpdateOthers, readOnly, assignYourselfCopilot, loggedInUser }) => {
let errMessage = 'Please set a copilot'
const handleProperty = copilots.handle ? 'handle' : 'memberHandle'
const selectedCopilot = _.find(copilots, { [handleProperty]: challenge.copilot })
const selectedCopilotHandle = selectedCopilot ? selectedCopilot[handleProperty] : undefined
const copilotFee = _.find(challenge.prizeSets, p => p.type === PRIZE_SETS_TYPE.COPILOT_PAYMENT, [])
const copilotFee = _.find(challenge.prizeSets, p => p.type === 'copilot', [])
const selfService = challenge.selfService
const copilotIsSelf = loggedInUser && selectedCopilotHandle === loggedInUser.handle
const assignButtonText = `${selectedCopilot && copilotIsSelf ? 'Una' : 'A'}ssign Yourself`
Expand Down
16 changes: 8 additions & 8 deletions src/components/ChallengeEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ class ChallengeEditor extends Component {
return { ...p, prizes }
})
challenge.status = status
if (status === CHALLENGE_STATUS.ACTIVE && isTask) {
if (status === 'Active' && isTask) {
challenge.startDate = moment().format()
}

Expand Down Expand Up @@ -1028,7 +1028,7 @@ class ChallengeEditor extends Component {
}

const newChallenge = {
status: CHALLENGE_STATUS.NEW,
status: 'New',
projectId: this.props.projectId,
name,
typeId,
Expand Down Expand Up @@ -1100,7 +1100,7 @@ class ChallengeEditor extends Component {
return ([
{
name: `${challenge.name} Discussion`,
type: 'CHALLENGE',
type: 'challenge',
provider: 'vanilla'
}
])
Expand Down Expand Up @@ -1295,11 +1295,11 @@ class ChallengeEditor extends Component {
}

async onActiveChallenge () {
this.updateAllChallengeInfo(CHALLENGE_STATUS.ACTIVE)
this.updateAllChallengeInfo('Active')
}

async saveDraft () {
this.updateAllChallengeInfo(CHALLENGE_STATUS.DRAFT)
this.updateAllChallengeInfo('Draft')
}

async onlySave () {
Expand Down Expand Up @@ -1492,7 +1492,7 @@ class ChallengeEditor extends Component {
Closing Task Confirmation Modal and Error Modal
*/
if (isCloseTask && !isConfirm) {
const taskPrize = _.get(_.find(challenge.prizeSets, { type: PRIZE_SETS_TYPE.CHALLENGE_PRIZES }), 'prizes[0].value')
const taskPrize = _.get(_.find(challenge.prizeSets, { type: 'placement' }), 'prizes[0].value')
const assignedMemberId = _.get(assignedMemberDetails, 'userId')
const assignedMember = _.get(assignedMemberDetails, 'handle', `User Id: ${assignedMemberId}`)

Expand Down Expand Up @@ -1540,7 +1540,7 @@ class ChallengeEditor extends Component {
<AlertModal
title='Success'
message={
challenge.status === CHALLENGE_STATUS.DRAFT
challenge.status === 'Draft'
? 'Your challenge is saved as draft'
: 'We have scheduled your challenge and processed the payment'
}
Expand Down Expand Up @@ -1595,7 +1595,7 @@ class ChallengeEditor extends Component {
!preventCopilotFromActivatingTask
) && (
<div className={styles.button}>
{!this.state.hasValidationErrors ? (
{(challenge.legacyId || isTask) && !this.state.hasValidationErrors ? (
<PrimaryButton text={'Launch as Active'} type={'info'} onClick={this.toggleLaunch} />
) : (
<Tooltip content={MESSAGE.NO_LEGACY_CHALLENGE}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChallengesComponent/ChallengeCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class ChallengeCard extends React.Component {
this.setState({ isSaving: true })
const isTask = _.get(challenge, 'task.isTask', false)
const payload = {
status: CHALLENGE_STATUS.ACTIVE
status: 'Active'
}
if (isTask) {
payload.startDate = moment().format()
Expand Down
22 changes: 19 additions & 3 deletions src/components/ChallengesComponent/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'
import ProjectStatus from './ProjectStatus'
import { PROJECT_ROLES, PROJECT_STATUS, COPILOTS_URL } from '../../config/constants'
import { PROJECT_ROLES, PROJECT_STATUS, COPILOTS_URL, CHALLENGE_STATUS } from '../../config/constants'
import { PrimaryButton, OutlineButton } from '../Buttons'
import ChallengeList from './ChallengeList'
import styles from './ChallengesComponent.module.scss'
Expand Down Expand Up @@ -53,6 +53,12 @@ const ChallengesComponent = ({
const isReadOnly = checkReadOnlyRoles(auth.token) || loginUserRoleInProject === PROJECT_ROLES.READ
const isAdminOrCopilot = checkAdminOrCopilot(auth.token, activeProject)

const projectStatus = activeProject && activeProject.status
? activeProject.status.toUpperCase()
: ''
const isCompletedOrCancelled =
projectStatus === CHALLENGE_STATUS.CANCELLED || projectStatus === CHALLENGE_STATUS.COMPLETED

useEffect(() => {
const loggedInUser = auth.user
const projectMembers = activeProject.members
Expand Down Expand Up @@ -85,6 +91,16 @@ const ChallengesComponent = ({
</div>
{activeProject && activeProject.id && !isReadOnly ? (
<div className={styles.projectActionButtonWrapper}>
<OutlineButton
text={'Users'}
type='info'
submit
link={{
pathname: '/users',
state: { projectId: activeProjectId, projectName: activeProject.name }
}}
className={styles.btnOutline}
/>
{isAdminOrCopilot && (
<OutlineButton
text={'Assets Library'}
Expand All @@ -94,11 +110,11 @@ const ChallengesComponent = ({
className={styles.btnOutline}
/>
)}
{(checkAdmin(auth.token) || checkManager(auth.token)) && (
{(checkAdmin(auth.token) || checkManager(auth.token)) && !isCompletedOrCancelled && (
<OutlineButton
text='Request Copilot'
type={'info'}
url={`${COPILOTS_URL}/requests/new`}
url={`${COPILOTS_URL}/requests/new?projectId=${activeProject.id}`}
target={'_blank'}
/>
)}
Expand Down
3 changes: 3 additions & 0 deletions src/components/Select/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export default {
paddingRight: '6px',
paddingLeft: '10px',
border: 'none',
width: '100%',
display: 'grid',
gridTemplateColumns: '1fr',
input: {
width: '100% !important',
height: 'auto !important',
Expand Down
15 changes: 7 additions & 8 deletions src/components/Users/Users.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,18 @@
}
}

.textContent {
font-size: 18px;
margin-top: 25px;
}


.row {
display: flex;
margin-bottom: 16px;
align-items: center;

input {
max-width: 280px;

@include upto-sm {
display: block;
padding-bottom: 10px;
Expand Down Expand Up @@ -400,22 +403,18 @@
justify-content: center;
}

.addButtonContainer {
.addButtonContainer, .buttonWrapper {
display: flex;
justify-content: flex-start;
height: 30px;
margin-top: 20px;
margin-bottom: 20px;
gap: 8px;
> * {
width: 125px;
width: max-content;
}
}

.addUserContentContainer {

}

.tcRadioButton {
.tc-radioButton-label {
@include roboto-light();
Expand Down
Loading
Loading