Skip to content

Commit 7378d26

Browse files
committed
F2F fixes for submissions showing and winners redirect
1 parent 6cf5fa7 commit 7378d26

File tree

6 files changed

+649
-358
lines changed

6 files changed

+649
-358
lines changed

src/apps/review/src/lib/components/TableIterativeReview/TableIterativeReview.tsx

Lines changed: 172 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import { ChallengeDetailContext, ReviewAppContext } from '../../contexts'
2929
import { getHandleUrl, isReviewPhase } from '../../utils'
3030
import { TableWrapper } from '../TableWrapper'
3131
import { ProgressBar } from '../ProgressBar'
32-
import { useSubmissionDownloadAccess } from '../../hooks'
32+
import { useRole, useSubmissionDownloadAccess } from '../../hooks'
33+
import type { useRoleProps } from '../../hooks'
3334
import type { UseSubmissionDownloadAccessResult } from '../../hooks/useSubmissionDownloadAccess'
35+
import { SUBMITTER } from '../../../config/index.config'
3436
// no-op
3537

3638
import styles from './TableIterativeReview.module.scss'
@@ -113,6 +115,115 @@ const hasActiveReview = (review: ReviewInfo | undefined): boolean => (
113115
Boolean(review?.id)
114116
)
115117

118+
const DOWNLOAD_OWN_SUBMISSION_TOOLTIP = 'You can only download your own submissions.'
119+
const VIEW_OWN_SCORECARD_TOOLTIP = 'You can only view scorecards for your own submissions.'
120+
121+
interface CompletedReviewRenderParams {
122+
canAccessReview: boolean
123+
outcomeLabel?: string
124+
reviewId?: string
125+
reviewPath?: string
126+
reviewScore?: number
127+
}
128+
129+
const renderCompletedReviewCell = ({
130+
canAccessReview,
131+
outcomeLabel,
132+
reviewId,
133+
reviewPath,
134+
reviewScore,
135+
}: CompletedReviewRenderParams): JSX.Element | undefined => {
136+
if (reviewScore !== undefined) {
137+
const normalisedOutcome = outcomeLabel?.toLowerCase()
138+
const isPassOutcome = normalisedOutcome === 'pass'
139+
const isFailOutcome = normalisedOutcome === 'fail'
140+
const outcomeIndicator = isPassOutcome ? (
141+
<span
142+
className={classNames(styles.statusIcon, styles.passIcon)}
143+
aria-label='Pass'
144+
>
145+
<IconOutline.CheckCircleIcon aria-hidden />
146+
</span>
147+
) : isFailOutcome ? (
148+
<span
149+
className={classNames(styles.statusIcon, styles.failIcon)}
150+
aria-label='Fail'
151+
>
152+
<IconOutline.XCircleIcon aria-hidden />
153+
</span>
154+
) : undefined
155+
const scoreDisplay = formatScore(reviewScore)
156+
const scoreElement = canAccessReview && reviewPath ? (
157+
<Link
158+
to={reviewPath}
159+
className={styles.scoreLink}
160+
>
161+
{scoreDisplay}
162+
</Link>
163+
) : (
164+
<span className={styles.scoreLink}>
165+
{scoreDisplay}
166+
</span>
167+
)
168+
const renderedScoreElement = !canAccessReview && reviewPath ? (
169+
<Tooltip
170+
content={VIEW_OWN_SCORECARD_TOOLTIP}
171+
triggerOn='click-hover'
172+
>
173+
<span className={styles.tooltipTrigger}>
174+
{scoreElement}
175+
</span>
176+
</Tooltip>
177+
) : (
178+
scoreElement
179+
)
180+
181+
return (
182+
<div className={styles.reviewCell}>
183+
<div className={styles.scoreRow}>
184+
{outcomeIndicator}
185+
{renderedScoreElement}
186+
</div>
187+
{!outcomeIndicator && outcomeLabel ? (
188+
<span className={styles.outcome}>
189+
{outcomeLabel}
190+
</span>
191+
) : undefined}
192+
</div>
193+
)
194+
}
195+
196+
if (!reviewId || !reviewPath) {
197+
return undefined
198+
}
199+
200+
const viewScorecardContent = canAccessReview ? (
201+
<Link
202+
to={reviewPath}
203+
className={styles.scoreLink}
204+
>
205+
View Scorecard
206+
</Link>
207+
) : (
208+
<span className={styles.scoreLink}>
209+
View Scorecard
210+
</span>
211+
)
212+
213+
return !canAccessReview ? (
214+
<Tooltip
215+
content={VIEW_OWN_SCORECARD_TOOLTIP}
216+
triggerOn='click-hover'
217+
>
218+
<span className={styles.tooltipTrigger}>
219+
{viewScorecardContent}
220+
</span>
221+
</Tooltip>
222+
) : (
223+
viewScorecardContent
224+
)
225+
}
226+
116227
export const TableIterativeReview: FC<Props> = (props: Props) => {
117228
const className = props.className
118229
const datas = props.datas
@@ -132,6 +243,16 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
132243
const { width: screenWidth }: WindowSize = useWindowSize()
133244
const isTablet = useMemo(() => screenWidth <= 744, [screenWidth])
134245
const { loginUserInfo }: ReviewAppContextModel = useContext(ReviewAppContext)
246+
const { actionChallengeRole, myChallengeResources }: useRoleProps = useRole()
247+
const isSubmitterView = actionChallengeRole === SUBMITTER
248+
const ownedMemberIds: Set<string> = useMemo(
249+
(): Set<string> => new Set(
250+
myChallengeResources
251+
.map(resource => resource.memberId)
252+
.filter((memberId): memberId is string => Boolean(memberId)),
253+
),
254+
[myChallengeResources],
255+
)
135256

136257
const isAdmin = useMemo(
137258
() => loginUserInfo?.roles?.some(
@@ -181,13 +302,16 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
181302
label: 'Submission ID',
182303
propertyName: 'id',
183304
renderer: (data: SubmissionInfo) => {
305+
const isOwnedSubmission = data.memberId
306+
? ownedMemberIds.has(data.memberId)
307+
: false
308+
const isOwnershipRestricted = isSubmitterView && !isOwnedSubmission
184309
const isRestrictedForMember = isSubmissionDownloadRestrictedForMember(
185310
data.memberId,
186311
)
187-
const tooltipMessage = getRestrictionMessageForMember(
312+
const memberRestrictionMessage = getRestrictionMessageForMember(
188313
data.memberId,
189-
) ?? restrictionMessage
190-
314+
)
191315
const failedScan = (data as SubmissionInfo).virusScan === false
192316
const isButtonDisabled = Boolean(
193317
isDownloading[data.id]
@@ -198,7 +322,11 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
198322
const downloadButton = (
199323
<button
200324
onClick={function onClick() {
201-
if (isRestrictedForMember || failedScan) {
325+
if (
326+
isRestrictedForMember
327+
|| failedScan
328+
|| isOwnershipRestricted
329+
) {
202330
return
203331
}
204332

@@ -228,21 +356,36 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
228356
})
229357
}
230358

231-
const shouldShowTooltip = isRestrictedForMember
232-
|| isSubmissionDownloadRestricted
233-
|| failedScan
359+
let tooltipContent: string | undefined
360+
if (failedScan) {
361+
tooltipContent = 'Submission failed virus scan'
362+
} else if (isRestrictedForMember) {
363+
tooltipContent = memberRestrictionMessage ?? restrictionMessage
364+
} else if (isOwnershipRestricted) {
365+
tooltipContent = DOWNLOAD_OWN_SUBMISSION_TOOLTIP
366+
} else if (isSubmissionDownloadRestricted && restrictionMessage) {
367+
tooltipContent = restrictionMessage
368+
}
369+
370+
const downloadControl = isOwnershipRestricted ? (
371+
<span className={styles.textBlue}>
372+
{data.id}
373+
</span>
374+
) : (
375+
downloadButton
376+
)
234377

235-
const renderedDownloadButton = shouldShowTooltip ? (
378+
const renderedDownloadButton = tooltipContent ? (
236379
<Tooltip
237-
content={failedScan ? 'Submission failed virus scan' : tooltipMessage}
380+
content={tooltipContent}
238381
triggerOn='click-hover'
239382
>
240383
<span className={styles.tooltipTrigger}>
241-
{downloadButton}
384+
{downloadControl}
242385
</span>
243386
</Tooltip>
244387
) : (
245-
downloadButton
388+
downloadControl
246389
)
247390

248391
return (
@@ -270,6 +413,8 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
270413
downloadSubmission,
271414
isDownloading,
272415
restrictionMessage,
416+
isSubmitterView,
417+
ownedMemberIds,
273418
],
274419
)
275420

@@ -310,6 +455,10 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
310455
label: columnLabel,
311456
renderer: (data: SubmissionInfo) => {
312457
const review = data.review
458+
const isOwnedSubmission = data.memberId
459+
? ownedMemberIds.has(data.memberId)
460+
: false
461+
const canAccessReview = !isSubmitterView || isOwnedSubmission
313462

314463
if (!hasActiveReview(review)) {
315464
return (
@@ -325,64 +474,17 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
325474
(review?.metadata as ScoreMetadata | undefined)?.outcome,
326475
)
327476
const reviewId = review?.id
477+
const reviewPath = reviewId ? `./../review/${reviewId}` : undefined
478+
const isCompleted = ['COMPLETED', 'SUBMITTED'].includes(status)
328479

329-
if (['COMPLETED', 'SUBMITTED'].includes(status) && reviewScore !== undefined) {
330-
if (!reviewId) {
331-
return undefined
332-
}
333-
334-
const normalisedOutcome = outcomeLabel?.toLowerCase()
335-
const isPassOutcome = normalisedOutcome === 'pass'
336-
const isFailOutcome = normalisedOutcome === 'fail'
337-
const outcomeIndicator = isPassOutcome ? (
338-
<span
339-
className={classNames(styles.statusIcon, styles.passIcon)}
340-
aria-label='Pass'
341-
>
342-
<IconOutline.CheckCircleIcon aria-hidden />
343-
</span>
344-
) : isFailOutcome ? (
345-
<span
346-
className={classNames(styles.statusIcon, styles.failIcon)}
347-
aria-label='Fail'
348-
>
349-
<IconOutline.XCircleIcon aria-hidden />
350-
</span>
351-
) : undefined
352-
353-
return (
354-
<div className={styles.reviewCell}>
355-
<div className={styles.scoreRow}>
356-
{outcomeIndicator}
357-
<Link
358-
to={`./../review/${reviewId}`}
359-
className={styles.scoreLink}
360-
>
361-
{formatScore(reviewScore)}
362-
</Link>
363-
</div>
364-
{!outcomeIndicator && outcomeLabel ? (
365-
<span className={styles.outcome}>
366-
{outcomeLabel}
367-
</span>
368-
) : undefined}
369-
</div>
370-
)
371-
}
372-
373-
if (['COMPLETED', 'SUBMITTED'].includes(status)) {
374-
if (!reviewId) {
375-
return undefined
376-
}
377-
378-
return (
379-
<Link
380-
to={`./../review/${reviewId}`}
381-
className={styles.scoreLink}
382-
>
383-
View Scorecard
384-
</Link>
385-
)
480+
if (isCompleted) {
481+
return renderCompletedReviewCell({
482+
canAccessReview,
483+
outcomeLabel,
484+
reviewId,
485+
reviewPath,
486+
reviewScore,
487+
})
386488
}
387489

388490
if (review?.reviewProgress) {
@@ -412,7 +514,7 @@ export const TableIterativeReview: FC<Props> = (props: Props) => {
412514
},
413515
type: 'element',
414516
}),
415-
[columnLabel],
517+
[columnLabel, isSubmitterView, ownedMemberIds],
416518
)
417519

418520
const reviewDateColumn: TableColumn<SubmissionInfo> = useMemo(

0 commit comments

Comments
 (0)