Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3baab7a
PM-5858: Require assignment before ticket closure
jmgasper Aug 12, 2026
0f9a4e7
PM-5861: Add totals to dashboard report tooltips
jmgasper Aug 12, 2026
c54f074
PM-5755: Simplify design screener assignment
jmgasper Aug 12, 2026
86ba781
PM-5787: Allow deferred checkpoint screener assignment
jmgasper Aug 12, 2026
c03b5dc
PM-5839: Render SFDC payment dates consistently
jmgasper Aug 12, 2026
4ea6c82
PM-5758: finish design submission limit handling
jmgasper Aug 12, 2026
9eafb1f
PM-5857: Label support team replies
jmgasper Aug 12, 2026
e6ad419
PM-5853: Rename active challenge label
jmgasper Aug 12, 2026
d2ffa7a
PM-5852: Open support description links in new tabs
jmgasper Aug 12, 2026
484b3f8
Merge pull request #2132 from topcoder-platform/PM-5861
jmgasper Aug 13, 2026
a4938ed
Merge pull request #2131 from topcoder-platform/PM-5858
jmgasper Aug 13, 2026
4ea0ffd
Merge pull request #2145 from topcoder-platform/PM-5852
jmgasper Aug 13, 2026
d27e284
Merge pull request #2144 from topcoder-platform/PM-5853
jmgasper Aug 13, 2026
12a043e
PM-5381 Add Member not found page for non existing handles
himaniraghav3 Aug 13, 2026
84915e9
Merge pull request #2148 from topcoder-platform/PM-5381
himaniraghav3 Aug 13, 2026
ebb2b98
PM-5879 Fix rating charts label
himaniraghav3 Aug 13, 2026
78816b0
Merge pull request #2149 from topcoder-platform/PM-5879
himaniraghav3 Aug 13, 2026
d0078fd
PM-5819 Fix engagement details styling
himaniraghav3 Aug 13, 2026
13572d3
Merge pull request #2150 from topcoder-platform/PM-5819
himaniraghav3 Aug 13, 2026
45a6496
PM-5820 Show N/A daysLeftInEngagement for terminated assignments
himaniraghav3 Aug 13, 2026
1d238d7
Merge pull request #2151 from topcoder-platform/PM-5820
himaniraghav3 Aug 13, 2026
258fa37
Merge pull request #2134 from topcoder-platform/PM-5755
jmgasper Aug 14, 2026
599e1cb
Merge branch 'dev' into PM-5787-2
jmgasper Aug 14, 2026
78d9986
Merge pull request #2135 from topcoder-platform/PM-5787-2
jmgasper Aug 14, 2026
d04dbf5
Merge pull request #2138 from topcoder-platform/PM-5839
jmgasper Aug 14, 2026
656dbd8
Merge pull request #2140 from topcoder-platform/PM-5758-1
jmgasper Aug 14, 2026
b847b62
Merge dev into PM-5857
jmgasper Aug 14, 2026
03b20d6
Merge pull request #2141 from topcoder-platform/PM-5857
jmgasper Aug 14, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import "@libs/ui/styles/includes";

.container {
min-height: 60vh;
display: flex;
align-items: center;
justify-content: center;
}

.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: $sp-6;
padding: $sp-15 $sp-8;
color: $black-100;

svg {
color: $black-60;
}
}

.title {
@include font-barlow;
font-weight: 600;
font-size: 28px;
line-height: 34px;
margin: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FC, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'

import { Button, ContentLayout, IconOutline, IconSolid, PageTitle } from '~/libs/ui'

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

interface MemberNotFoundProps {
memberHandle?: string
}

const MemberNotFound: FC<MemberNotFoundProps> = (props: MemberNotFoundProps) => {
const navigate = useNavigate()

const handleBack = useCallback(() => {
navigate(-1)
}, [navigate])

return (
<ContentLayout outerClass={styles.container}>
<PageTitle>Profile Not Found | Topcoder</PageTitle>

<div className={styles.content} role='alert'>
<IconOutline.ExclamationCircleIcon className='icon-xxxl' />
<h2 className={styles.title}>We were unable to locate that profile</h2>
{props.memberHandle && (
<p className='body-main'>
No member was found with the handle
{' '}
<strong>{props.memberHandle}</strong>
.
</p>
)}
<Button
link
size='lg'
iconToLeft
icon={IconSolid.ArrowLeftIcon}
onClick={handleBack}
>
Go back
</Button>
</div>
</ContentLayout>
)
}

export default MemberNotFound
1 change: 1 addition & 0 deletions src/apps/profiles/src/components/MemberNotFound/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MemberNotFound } from './MemberNotFound'
1 change: 1 addition & 0 deletions src/apps/profiles/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './tc-achievements/SRMView/ChallengesGrid'
export * from './EditMemberPropertyBtn'
export * from './AddButton'
export * from './EmptySection'
export * from './MemberNotFound'
23 changes: 19 additions & 4 deletions src/apps/profiles/src/member-badges/MemberBadgesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'
import { Params, useNavigate, useParams } from 'react-router-dom'
import { AxiosError } from 'axios'
import { bind } from 'lodash'

import { profileGetPublicAsync, useMemberBadges, UserBadge, UserBadgesResponse, UserProfile } from '~/libs/core'
import { Button, ContentLayout, IconSolid, LoadingSpinner } from '~/libs/ui'

import { MemberBadgeModal } from '../components'
import { MemberBadgeModal, MemberNotFound } from '../components'

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

Expand All @@ -19,6 +20,7 @@ const MemberBadgesPage: FC<{}> = () => {
] = useState()

const [profileReady, setProfileReady]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
const [notFound, setNotFound]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)

const memberBadges: UserBadgesResponse | undefined = useMemberBadges(profile?.userId as number, { limit: 100 })

Expand All @@ -39,12 +41,21 @@ const MemberBadgesPage: FC<{}> = () => {

useEffect(() => {
if (routeParams.memberHandle) {
setProfileReady(false)
setNotFound(false)
setProfile(undefined)

profileGetPublicAsync(routeParams.memberHandle)
.then(userProfile => {
setProfile(userProfile)
setProfileReady(true)
})
// TODO: NOT FOUND PAGE redirect/dispaly via catch
.catch((e: AxiosError) => {
if (e.code === AxiosError.ERR_BAD_REQUEST && e.response?.status === 404) {
setNotFound(true)
setProfileReady(true)
}
})
}
}, [routeParams.memberHandle])

Expand All @@ -54,9 +65,13 @@ const MemberBadgesPage: FC<{}> = () => {

return (
<>
<LoadingSpinner hide={profileReady && !!memberBadges} />
<LoadingSpinner hide={profileReady && (notFound || !!memberBadges)} />

{profileReady && notFound && (
<MemberNotFound memberHandle={routeParams.memberHandle} />
)}

{profileReady && profile && !!memberBadges && (
{profileReady && profile && !notFound && !!memberBadges && (
<ContentLayout
outerClass={styles.container}
>
Expand Down
16 changes: 13 additions & 3 deletions src/apps/profiles/src/member-profile/MemberProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AxiosError } from 'axios'
import { profileContext, ProfileContextData, profileGetPublicAsync, UserProfile } from '~/libs/core'
import { LoadingSpinner } from '~/libs/ui'

import { rootRoute } from '../profiles.routes'
import { MemberNotFound } from '../components'
import { notifyUniNavi } from '../lib'

import { ProfilePageLayout } from './page-layout'
Expand All @@ -21,6 +21,7 @@ const MemberProfilePage: FC<{}> = () => {
] = useState()

const [profileReady, setProfileReady]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
const [notFound, setNotFound]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
const { isTalentSearch }: MemberProfileContextValue = useMemberProfileContext()

const { profile: authProfile }: ProfileContextData = useContext(profileContext)
Expand All @@ -31,14 +32,19 @@ const MemberProfilePage: FC<{}> = () => {

useEffect(() => {
if (routeParams.memberHandle) {
setProfileReady(false)
setNotFound(false)
setProfile(undefined)

profileGetPublicAsync(routeParams.memberHandle)
.then(userProfile => {
setProfile({ ...userProfile } as UserProfile)
setProfileReady(true)
})
.catch((e: AxiosError) => {
if (e.code === AxiosError.ERR_BAD_REQUEST && e.response?.status === 404) {
navigate(rootRoute)
setNotFound(true)
setProfileReady(true)
}
})
}
Expand All @@ -58,7 +64,11 @@ const MemberProfilePage: FC<{}> = () => {
<>
<LoadingSpinner hide={profileReady} />

{profileReady && profile && (
{profileReady && notFound && (
<MemberNotFound memberHandle={routeParams.memberHandle} />
)}

{profileReady && profile && !notFound && (
<ProfilePageLayout
handleBackBtn={handleBackBtn}
isTalentSearch={isTalentSearch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,23 @@ describe('MemberRatingInfoModal', () => {
expect(parseFloat(marker.style.left))
.toBeCloseTo((3.5 / 6) * 100)
})

it('hides the 2200+ axis label when the distribution has no elite buckets', () => {
render(
<MemberRatingInfoModal
audienceLabel='developers'
onClose={jest.fn()}
percentile={15}
profile={baseProfile}
rating={1646}
ratingDistribution={ratingDistribution}
/>,
)

expect(screen.getByText('1500'))
.toBeInTheDocument()
expect(screen.queryByText('2200+'))
.not
.toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,29 @@ const getAxisLabelPosition = (
return 100
}

/**
* Returns axis labels that fall within the visible distribution ranges.
*
* Hides labels such as `2200+` when the trimmed histogram has no elite buckets,
* which otherwise stack on top of the final label (e.g. `1500`).
*
* @param {RatingDistributionRange[]} ranges - Visible rating distribution ranges.
* @returns {Array<{ label: string, value: number }>} Axis labels to render.
*/
const getVisibleAxisLabels = (
ranges: RatingDistributionRange[],
): Array<{ label: string, value: number }> => {
if (ranges.length === 0) {
return chartAxisLabels
}

const chartEnd = ranges[ranges.length - 1].end

return chartAxisLabels.filter((axisLabel: { label: string, value: number }) => (
axisLabel.value <= chartEnd
))
}

/**
* Calculates a bar height for a histogram count.
*
Expand Down Expand Up @@ -359,6 +382,9 @@ const MemberRatingInfoModal: FC<MemberRatingInfoModalProps> = (props: MemberRati
const markerPosition: number = props.rating !== undefined
? getMarkerPosition(props.rating, distributionRanges)
: 0
const visibleAxisLabels: Array<{ label: string, value: number }> = useMemo(() => (
getVisibleAxisLabels(distributionRanges)
), [distributionRanges])
const shouldStackMarkerRating: boolean = props.rating !== undefined && (
markerPosition >= stackedMarkerPositionThreshold
|| props.rating >= stackedMarkerRatingThreshold
Expand Down Expand Up @@ -476,7 +502,7 @@ const MemberRatingInfoModal: FC<MemberRatingInfoModalProps> = (props: MemberRati
)}

<div className={styles.axisLabels}>
{chartAxisLabels.map((axisLabel: { label: string, value: number }) => (
{visibleAxisLabels.map((axisLabel: { label: string, value: number }) => (
<span
key={axisLabel.label}
style={{
Expand Down
Loading
Loading