Skip to content
Open
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
53a4912
Initial work-manager port work
jmgasper Feb 11, 2026
3c3451d
UI and bug fixes
jmgasper Feb 12, 2026
6cfb37c
Additional UI and usbility tweaks for work manager port to platform-ui
jmgasper Feb 13, 2026
a6cc139
Merge branch 'dev' of github.com:topcoder-platform/platform-ui into w…
jmgasper Feb 18, 2026
caa121c
Merge with latest from dev
jmgasper Feb 18, 2026
468bc7e
Bunch of usability fixes, bug fixes, and new functionality added to m…
jmgasper Feb 19, 2026
57cf4c5
UI fixes for work and copilots apps for projects-api-v6
jmgasper Feb 20, 2026
078871a
Little fixes for issues found during validation testing.
jmgasper Feb 20, 2026
720400d
Fix issue with launch non design challenges
jmgasper Feb 20, 2026
7bc3fc4
UI updates for bugs noted in work app
jmgasper Feb 20, 2026
65a144d
UI updates for project filters
jmgasper Feb 20, 2026
5472ae2
QA bug fixes - PM3936-41
jmgasper Feb 20, 2026
dc87772
Additional QA fixes
jmgasper Feb 20, 2026
eb60a13
QA updates https://topcoder.atlassian.net/browse/PM-3933
jmgasper Feb 21, 2026
fe2435c
Remove redundant artifact buttons
jmgasper Feb 21, 2026
c9b1f97
Dev vs. prod path normalization
jmgasper Feb 22, 2026
94d681f
NDA and reviewer updates
jmgasper Feb 22, 2026
75ea1d2
PM-2645: fix tools add flow action selection
jmgasper Feb 22, 2026
5e9962c
PM-2346: Restrict admin payment type to valid values
jmgasper Feb 22, 2026
3c1dc2a
PM-1839: add fun challenge checkbox in work app
jmgasper Feb 22, 2026
938edc2
PM-1352: Restrict Work Manager to explicit roles
jmgasper Feb 22, 2026
7ae2767
PM-1643: add Wipro Allowed checkbox and payload mapping
jmgasper Feb 22, 2026
c9999b3
PM-2637: preserve topgear phase start times on save
jmgasper Feb 22, 2026
bb9429b
PM-2572: fix screening pass/fail selection for multi-screener challenges
jmgasper Feb 22, 2026
7d0de9d
PM-2515: fix approval tab and winners gating for failed approvals
jmgasper Feb 22, 2026
ea9be07
PM-2504: show completed approvals for non-submitter review roles
jmgasper Feb 22, 2026
2abcde2
PM-2459: Exclude screening-failed submissions from review progress
jmgasper Feb 22, 2026
606ebd8
PM-3000: Fix Review UI challenge link in system admin
jmgasper Feb 22, 2026
714fa80
PM-2691: handle legacy contest submission type formats
jmgasper Feb 22, 2026
a3ecbab
PM-3167: fix registration reopen eligibility during submission
jmgasper Feb 22, 2026
ea67ae1
PM-3206: improve wallet and wallet-admin mobile responsiveness
jmgasper Feb 22, 2026
1e756b6
PM-1803: Update talent search expert CTA to Let's Talk domain
jmgasper Feb 22, 2026
9f541c4
PS-359: Guard talent search against single-skill requests
jmgasper Feb 22, 2026
b4e6dba
PM-3172: show action column immediately for screener assignments
jmgasper Feb 22, 2026
d8f42fd
PM-2446: update appeal-response scores immediately
jmgasper Feb 22, 2026
c237b12
Tweaks for challenge editing / fun challenge checkbox
jmgasper Feb 23, 2026
eabc27b
Validate reviewers array earlier
jmgasper Feb 23, 2026
6f6165c
Aggregation of reviews fix
jmgasper Feb 23, 2026
fd72c19
Simplify challenge management links
jmgasper Feb 23, 2026
2704143
Fix for PM-3937 and terms API handling
jmgasper Feb 23, 2026
1abec5b
PM-3998: preserve 2R selection when creating design challenges
jmgasper Feb 23, 2026
b86e7bc
Better reviewer handling for multi-reviewer phases
jmgasper Feb 23, 2026
81914b0
2 round fixes, and show the Fun Challenge flag when editing challenges
jmgasper Feb 24, 2026
a706a68
Project invite handling, new system-admin emails popup
jmgasper Feb 24, 2026
a07ac73
Fix up invite acceptance
jmgasper Feb 24, 2026
67a063b
Fix invitation issue
jmgasper Feb 24, 2026
ee9b1ce
Better round handling
jmgasper Feb 24, 2026
b30f055
Updates to user emails dialog
jmgasper Feb 24, 2026
0adefe0
PM-4013: preserve empty terms when saving challenge updates
jmgasper Feb 24, 2026
16de4f3
PM-4012: preserve cleared terms in challenge editor
jmgasper Feb 24, 2026
d7258bb
PM-3998: preserve selected two-round option during challenge creation
jmgasper Feb 24, 2026
4fa864e
Hide volatility in stats for F2F / Bug Hunt
jmgasper Feb 24, 2026
754a597
Merge branch 'dev' of github.com:topcoder-platform/platform-ui into w…
jmgasper Feb 25, 2026
1fcb271
Fixes for challenge status sorting, and better route security to limi…
jmgasper Feb 25, 2026
5b64344
Fix up 0 wins display and address some minor issues on the role error…
jmgasper Feb 25, 2026
f24f8ca
When phase type changes, update the available scorecards
jmgasper Feb 25, 2026
c91c5e5
Merge branch 'dev' of github.com:topcoder-platform/platform-ui into w…
jmgasper Feb 27, 2026
0af1e4c
Better handling of copilot applications and notifications
jmgasper Feb 27, 2026
f8ae64a
Merge branch 'dev' of github.com:topcoder-platform/platform-ui into w…
jmgasper Mar 3, 2026
f63c9ad
Merge branch 'work-manager' of github.com:topcoder-platform/platform-…
jmgasper Mar 3, 2026
ad99bbf
QA fixes
jmgasper Mar 4, 2026
5e8486d
QA updates for showing project status and BA validation
jmgasper Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,8 @@ workflows:
only:
- dev
- mm-final-2025-reveal
- engagements
- HOTFIX-PM-3269

- work-manager
- deployQa:
context: org-global
requires:
Expand Down
5 changes: 5 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
REACT_APP_GROUPS_API_URL=https://api.topcoder-dev.com/v6/groups
REACT_APP_TERMS_API_URL=https://api.topcoder-dev.com/v5/terms
REACT_APP_RESOURCES_API_URL=https://api.topcoder-dev.com/v6/resources
REACT_APP_MEMBER_API_URL=https://api.topcoder-dev.com/v6/members
REACT_APP_RESOURCE_ROLES_API_URL=https://api.topcoder-dev.com/v6/resource-roles
5 changes: 5 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
REACT_APP_GROUPS_API_URL=https://api.topcoder.com/v6/groups
REACT_APP_TERMS_API_URL=https://api.topcoder.com/v5/terms

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The API version for REACT_APP_TERMS_API_URL is v5, while others are v6. Ensure this is intentional and that the correct API version is being used.

REACT_APP_RESOURCES_API_URL=https://api.topcoder.com/v6/resources
REACT_APP_MEMBER_API_URL=https://api.topcoder.com/v6/members
REACT_APP_RESOURCE_ROLES_API_URL=https://api.topcoder.com/v6/resource-roles
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"express": "^4.22.1",
"express-fileupload": "^1.5.2",
"express-interceptor": "^1.2.0",
"fflate": "^0.8.2",
"filestack-js": "^3.44.2",
"highcharts": "^10.3.3",
"highcharts-react-official": "^3.2.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
WearableIcon,
} from '~/apps/accounts/src/lib'

import { shouldUseUpdateTraitAction } from '../trait-action.utils'

import styles from './Devices.module.scss'

interface DevicesProps {
Expand Down Expand Up @@ -315,7 +317,9 @@ const Devices: FC<DevicesProps> = (props: DevicesProps) => {
},
}]

const action = props.devicesTrait ? updateMemberTraitsAsync : createMemberTraitsAsync
const action = shouldUseUpdateTraitAction(props.devicesTrait, deviceTypesData)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The shouldUseUpdateTraitAction function is used to determine the action, but its implementation is not visible here. Ensure that this function correctly handles all edge cases, especially when props.devicesTrait or deviceTypesData are undefined or null, to avoid potential runtime errors.

? updateMemberTraitsAsync
: createMemberTraitsAsync

action(
props.profile.handle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
TelevisionServiceProviderIcon,
} from '~/apps/accounts/src/lib'

import { shouldUseUpdateTraitAction } from '../trait-action.utils'

import { serviceProviderTypes } from './service-provider-types.config'
import styles from './ServiceProvider.module.scss'

Expand Down Expand Up @@ -216,7 +218,9 @@ const ServiceProvider: FC<ServiceProviderProps> = (props: ServiceProviderProps)
},
}]

const action = props.serviceProviderTrait ? updateMemberTraitsAsync : createMemberTraitsAsync
const action = shouldUseUpdateTraitAction(props.serviceProviderTrait, serviceProviderTypesData)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The shouldUseUpdateTraitAction function is used to determine the action to perform, but its implementation is not visible in this diff. Ensure that this function correctly handles all edge cases, especially when props.serviceProviderTrait or serviceProviderTypesData are undefined or null, to prevent potential runtime errors.

? updateMemberTraitsAsync
: createMemberTraitsAsync

action(
props.profile.handle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { createMemberTraitsAsync, updateMemberTraitsAsync, UserProfile, UserTrai
import { Button, Collapsible, ConfirmModal, IconOutline, InputSelect, InputText } from '~/libs/ui'
import { SettingSection, SoftwareIcon } from '~/apps/accounts/src/lib'

import { shouldUseUpdateTraitAction } from '../trait-action.utils'

import { softwareTypes } from './software-types.config'
import styles from './Software.module.scss'

Expand Down Expand Up @@ -170,7 +172,9 @@ const Software: FC<SoftwareProps> = (props: SoftwareProps) => {
},
}]

const action = props.softwareTrait ? updateMemberTraitsAsync : createMemberTraitsAsync
const action = shouldUseUpdateTraitAction(props.softwareTrait, softwareTypesData)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The function shouldUseUpdateTraitAction is used to determine whether to call updateMemberTraitsAsync or createMemberTraitsAsync. Ensure that this function is thoroughly tested to handle all edge cases, as incorrect logic here could lead to unintended API calls.

? updateMemberTraitsAsync
: createMemberTraitsAsync

action(
props.profile.handle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { createMemberTraitsAsync, updateMemberTraitsAsync, UserProfile, UserTrai
import { Button, Collapsible, ConfirmModal, IconOutline, InputText } from '~/libs/ui'
import { SettingSection, SubscriptionsIcon } from '~/apps/accounts/src/lib'

import { shouldUseUpdateTraitAction } from '../trait-action.utils'

import styles from './Subscriptions.module.scss'

interface SubscriptionsProps {
Expand Down Expand Up @@ -150,7 +152,9 @@ const Subscriptions: FC<SubscriptionsProps> = (props: SubscriptionsProps) => {
setIsSaving(false)
})
} else {
const action = props.subscriptionsTrait ? updateMemberTraitsAsync : createMemberTraitsAsync
const action = shouldUseUpdateTraitAction(props.subscriptionsTrait, subscriptionsTypesData)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
The function shouldUseUpdateTraitAction is used to determine the action to perform, but there is no error handling if this function fails or returns an unexpected value. Consider adding error handling to ensure that the correct action is always executed.

? updateMemberTraitsAsync
: createMemberTraitsAsync
action(
props.profile.handle,
[{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { shouldUseUpdateTraitAction } from './trait-action.utils'

describe('shouldUseUpdateTraitAction', () => {
it('returns true when the initial trait exists', () => {
expect(shouldUseUpdateTraitAction({ traitId: 'software' }, undefined))
.toBe(true)
})

it('returns true when local traits exist even without the initial trait', () => {
expect(shouldUseUpdateTraitAction(undefined, [{ name: 'Chrome' }]))
.toBe(true)
})

it('returns false when both initial and local traits are missing', () => {
expect(shouldUseUpdateTraitAction(undefined, undefined))
.toBe(false)
})
})
13 changes: 13 additions & 0 deletions src/apps/accounts/src/settings/tabs/tools/trait-action.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { UserTrait } from '~/libs/core'

/**
* Determine whether tool traits should use update or create action.
* The initial trait prop can be stale while the user stays on the tab,
* so local list state is also considered to avoid duplicate create calls.
*/
export function shouldUseUpdateTraitAction(
initialTrait: UserTrait | undefined,
localTraitsData: UserTrait[] | undefined,
): boolean {
return Boolean(initialTrait || localTraitsData?.length)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
The use of Boolean(initialTrait || localTraitsData?.length) could lead to unexpected behavior if initialTrait is null or an empty object, as it will still return true. Consider explicitly checking for the presence of initialTrait and the length of localTraitsData for clarity and correctness.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
canOpenReviewUi,
getReviewUiChallengeUrl,
} from './reviewUiLink'

describe('ChallengeList review UI helpers', () => {
describe('canOpenReviewUi', () => {
it('returns true when challenge has a uuid id', () => {
expect(canOpenReviewUi('challenge-uuid'))
.toBe(true)
})

it('returns false when challenge id is empty', () => {
expect(canOpenReviewUi(''))
.toBe(false)
})

it('returns false when challenge id is only whitespace', () => {
expect(canOpenReviewUi(' '))
.toBe(false)
})
})

describe('getReviewUiChallengeUrl', () => {
it('builds review ui url using challenge id path', () => {
expect(getReviewUiChallengeUrl('https://review.topcoder-dev.com', 'challenge-uuid'))
.toBe('https://review.topcoder-dev.com/challenge-uuid')
})
})
})
51 changes: 30 additions & 21 deletions src/apps/admin/src/lib/components/ChallengeList/ChallengeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import { Paging } from '../../models/challenge-management/Pagination'
import { checkIsMM } from '../../utils/challenge'

import { MobileListView } from './MobileListView'
import {
canOpenReviewUi,
getReviewUiChallengeUrl,
} from './reviewUiLink'
import styles from './ChallengeList.module.scss'

export interface ChallengeListProps {
Expand Down Expand Up @@ -180,11 +184,12 @@ const Actions: FC<{
},
)

const hasChallengeDetailsAccess = canOpenReviewUi(props.challenge.id)
const hasProjectId

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ performance]
The function canOpenReviewUi is called twice with the same argument props.challenge.id on lines 187 and 192. Consider storing the result in a variable to avoid redundant function calls and improve performance.

= 'projectId' in props.challenge
&& props.challenge.projectId !== undefined
const hasLegacyId
= 'legacyId' in props.challenge && props.challenge.legacyId !== undefined
= typeof props.challenge.projectId === 'number'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[💡 readability]
The check typeof props.challenge.projectId === 'number' is redundant if props.challenge.projectId > 0 is already being checked, as the greater-than comparison will implicitly ensure it's a number. Consider removing the typeof check for simplicity.

&& props.challenge.projectId > 0
const hasWorkManagerAccess = hasProjectId && hasChallengeDetailsAccess
const hasReviewUiAccess = canOpenReviewUi(props.challenge.id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[❗❗ correctness]
The canOpenReviewUi function is used to determine if the Review UI link should be enabled. Ensure that this function correctly handles all edge cases, such as invalid or missing challenge IDs, to avoid potential runtime errors.


return (
<div className={styles.rowActions}>
Expand Down Expand Up @@ -234,17 +239,20 @@ const Actions: FC<{
classNames={{ menu: 'challenge-list-actions-dropdown-menu' }}
>
<ul>
<li>
<a
href={`${EnvironmentConfig.ADMIN.CHALLENGE_URL}/${props.challenge.id}`}
target='_blank'
rel='noreferrer'
>
Challenge Details
</a>
<li className={cn({ disabled: !hasChallengeDetailsAccess })}>
{hasChallengeDetailsAccess && (
<a
href={`${EnvironmentConfig.ADMIN.CHALLENGE_URL}/${props.challenge.id}`}
target='_blank'
rel='noreferrer'
>
Challenge Details
</a>
)}
{!hasChallengeDetailsAccess && <span>Challenge Details</span>}
</li>
<li className={cn({ disabled: !hasProjectId })}>
{hasProjectId && (
<li className={cn({ disabled: !hasWorkManagerAccess })}>
{hasWorkManagerAccess && (
<a
href={
`${EnvironmentConfig.ADMIN.WORK_MANAGER_URL}/projects/${props.challenge.projectId}/challenges/${props.challenge.id}/view` /* eslint-disable-line max-len */
Expand All @@ -255,21 +263,22 @@ const Actions: FC<{
Work Manager
</a>
)}
{!hasProjectId && <span>Work Manager</span>}
{!hasWorkManagerAccess && <span>Work Manager</span>}
</li>
<li className={cn({ disabled: !hasLegacyId })}>
{hasLegacyId && (
<li className={cn({ disabled: !hasReviewUiAccess })}>
{hasReviewUiAccess && (
<a
href={
`${EnvironmentConfig.ADMIN.REVIEW_UI_URL}/=${props.challenge.id}` /* eslint-disable-line max-len */
}
href={getReviewUiChallengeUrl(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ security]
Consider validating the URL returned by getReviewUiChallengeUrl to ensure it is correctly formatted and safe to use. This can help prevent potential security vulnerabilities such as open redirects.

EnvironmentConfig.ADMIN.REVIEW_UI_URL,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Passing an empty string as a fallback for challengeLinkId in getReviewUiChallengeUrl could lead to unexpected behavior if challengeLinkId is null or undefined. Ensure that getChallengeLinkId always returns a valid string or handle the fallback case more explicitly.

props.challenge.id,
)}
target='_blank'
rel='noreferrer'
>
Review UI
</a>
)}
{!hasLegacyId && <span>Review UI</span>}
{!hasReviewUiAccess && <span>Review UI</span>}
</li>
</ul>
</DropdownMenu>
Expand Down
16 changes: 16 additions & 0 deletions src/apps/admin/src/lib/components/ChallengeList/reviewUiLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Returns whether the Review UI link can be opened for a challenge.
*/
export function canOpenReviewUi(challengeId?: string): boolean {
return Boolean(challengeId?.trim())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[💡 performance]
The use of challengeId?.trim() in Boolean(challengeId?.trim()) is redundant because Boolean() will already handle null or undefined values. Consider simplifying to Boolean(challengeId && challengeId.trim()) to ensure trim() is only called when challengeId is not null or undefined.

}

/**
* Builds the Review UI URL for a challenge id.
*/
export function getReviewUiChallengeUrl(
reviewUiBaseUrl: string,
challengeId: string,
): string {
return `${reviewUiBaseUrl}/${challengeId}`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Ensure that reviewUiBaseUrl does not end with a trailing slash to prevent double slashes in the URL. Consider normalizing the URL components to handle such cases.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@import '@libs/ui/styles/includes';

.modal {
width: 1240px !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Using !important can make it difficult to override styles in the future, potentially leading to maintainability issues. Consider if this is necessary or if the specificity of the selector can be increased instead.

max-width: calc(100vw - 32px) !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
The use of !important here may lead to maintainability challenges. Evaluate if increasing selector specificity could be a better approach.

}

.container {
display: flex;
flex-direction: column;
gap: 20px;

th:first-child {
padding-left: 16px !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Avoid using !important unless absolutely necessary, as it can complicate future style overrides and maintenance.

}
}

.actionButtons {
display: flex;
justify-content: flex-end;
gap: 6px;
}

.tableCellNoWrap {
white-space: nowrap;
text-align: left !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
Consider removing !important to improve maintainability unless it's critical for the style to be applied.

}

.statusCell {
text-align: center !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ design]
The fixed width of 90px for .statusCell could cause layout issues on smaller screens or if the content size changes. Consider using a more flexible layout approach.

width: 90px;
}

.emailStatus {
align-items: center;
display: inline-flex;
justify-content: center;
min-height: 24px;
min-width: 24px;

svg {
width: 20px;
height: 20px;
}
}

.emailStatusDelivered {
color: $green-120;
}

.emailStatusFailed {
color: $red-110;
}

.tableCell {
min-width: 220px;
white-space: break-spaces !important;
text-align: left !important;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ maintainability]
The use of !important should be minimized to ensure easier maintenance and flexibility in styling.

}

.loadingSpinnerContainer {
position: relative;
height: 100px;

.spinner {
background: none;
}
}

.noRecordFound {
padding: 16px 16px 32px;
text-align: center;
}

.desktopTable {
overflow-x: auto;
overflow-y: visible;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ design]
Using overflow-y: visible; might cause content to overflow the container if the content height exceeds the container height. Ensure this behavior is intentional and won't lead to layout issues.


thead th {
position: sticky;
top: 0;
z-index: 2;
background: $tc-white;
}

td {
vertical-align: middle;
}
}
Loading
Loading