Skip to content

Commit da5d4f6

Browse files
committed
QA fixes
1 parent 37b1181 commit da5d4f6

File tree

6 files changed

+383
-39
lines changed

6 files changed

+383
-39
lines changed

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

Lines changed: 187 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import classNames from 'classnames'
1414
import { TableMobile } from '~/apps/admin/src/lib/components/common/TableMobile'
1515
import { IsRemovingType } from '~/apps/admin/src/lib/models'
1616
import { MobileTableColumn } from '~/apps/admin/src/lib/models/MobileTableColumn.model'
17+
import { getRatingColor } from '~/libs/core'
1718
import { handleError, useWindowSize, WindowSize } from '~/libs/shared'
1819
import { IconOutline, Table, TableColumn } from '~/libs/ui'
1920

@@ -34,6 +35,7 @@ import {
3435
aggregateSubmissionReviews,
3536
challengeHasSubmissionLimit,
3637
isReviewPhase,
38+
isReviewPhaseCurrentlyOpen,
3739
refreshChallengeReviewData,
3840
REOPEN_MESSAGE_OTHER,
3941
REOPEN_MESSAGE_SELF,
@@ -223,6 +225,24 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
223225
[datas, latestSubmissions, restrictToLatest],
224226
)
225227

228+
if (process.env.NODE_ENV !== 'production') {
229+
try {
230+
console.debug('[TableReview] submissionsForAggregation', submissionsForAggregation.map(submission => ({
231+
id: submission.id,
232+
reviewResourceId: submission.review?.resourceId,
233+
reviewHandle: submission.review?.reviewerHandle,
234+
reviewId: submission.review?.id,
235+
reviews: submission.reviews?.map(review => ({
236+
resourceId: review.resourceId,
237+
reviewerHandle: review.reviewerHandle,
238+
score: review.score,
239+
})),
240+
})))
241+
} catch {
242+
// ignore logging issues
243+
}
244+
}
245+
226246
const aggregatedSubmissionRows = useMemo<AggregatedSubmissionReviews[]>(() => (
227247
aggregateSubmissionReviews({
228248
mappingReviewAppeal,
@@ -274,6 +294,70 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
274294
),
275295
[aggregatedSubmissionRows],
276296
)
297+
interface ReviewerColumnMetadata {
298+
label: string
299+
renderLabel?: () => JSX.Element
300+
}
301+
const reviewerColumnMetadata = useMemo<ReviewerColumnMetadata[]>(() => (
302+
Array.from({ length: maxReviewCount }, (unused, index) => {
303+
const reviewerDetails = aggregatedSubmissionRows
304+
.map(aggregated => aggregated.reviews?.[index])
305+
.filter((detail): detail is AggregatedReviewDetail => Boolean(detail))
306+
307+
const resolvedHandle = reviewerDetails
308+
.map(detail => detail.reviewerHandle?.trim()
309+
|| detail.reviewInfo?.reviewerHandle?.trim()
310+
|| undefined)
311+
.find((handle): handle is string => Boolean(handle))
312+
313+
if (!resolvedHandle) {
314+
return {
315+
label: `Reviewer ${index + 1}`,
316+
}
317+
}
318+
319+
const resolvedRating = reviewerDetails
320+
.map(detail => detail.reviewerMaxRating
321+
?? detail.reviewInfo?.reviewerMaxRating
322+
?? undefined)
323+
.find((rating): rating is number => (
324+
typeof rating === 'number'
325+
&& Number.isFinite(rating)
326+
))
327+
328+
const resolvedColor = reviewerDetails
329+
.map(detail => detail.reviewerHandleColor
330+
?? detail.reviewInfo?.reviewerHandleColor
331+
?? undefined)
332+
.find((color): color is string => Boolean(color))
333+
334+
const ratingDisplay = resolvedRating !== undefined
335+
? Math.round(resolvedRating)
336+
: undefined
337+
const baseLabel = ratingDisplay !== undefined
338+
? `${resolvedHandle} (${ratingDisplay})`
339+
: resolvedHandle
340+
341+
const renderLabel = (): JSX.Element => {
342+
const color = resolvedColor
343+
?? (resolvedRating !== undefined
344+
? getRatingColor(resolvedRating)
345+
: undefined)
346+
?? '#2a2a2a'
347+
348+
return (
349+
<span style={{ color }}>
350+
{baseLabel}
351+
</span>
352+
)
353+
}
354+
355+
return {
356+
label: baseLabel,
357+
renderLabel,
358+
}
359+
})
360+
), [aggregatedSubmissionRows, maxReviewCount])
277361

278362
const [isReopening, setIsReopening] = useState(false)
279363
const [pendingReopen, setPendingReopen] = useState<PendingReopenState | undefined>(undefined)
@@ -388,14 +472,14 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
388472
}
389473

390474
const buildPrimaryAction = (): JSX.Element | undefined => {
391-
if (!hasReviewRole || !myReviewDetail) {
475+
if (!myReviewDetail) {
392476
return undefined
393477
}
394478

395479
const reviewInfo = myReviewDetail.reviewInfo
396480
const status = (reviewInfo?.status ?? '').toUpperCase()
397481

398-
if (status === 'COMPLETED' || status === 'SUBMITTED') {
482+
if (hasReviewRole && (status === 'COMPLETED' || status === 'SUBMITTED')) {
399483
return (
400484
<div className={styles.completedAction} key='completed-indicator'>
401485
<span className={styles.completedIcon}>
@@ -408,6 +492,10 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
408492
)
409493
}
410494

495+
if (!hasReviewRole) {
496+
return undefined
497+
}
498+
411499
const reviewId = reviewInfo?.id ?? myReviewDetail.reviewId
412500
if (reviewId) {
413501
return (
@@ -425,6 +513,50 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
425513
return undefined
426514
}
427515

516+
const buildReopenAction = (): JSX.Element | undefined => {
517+
if (!myReviewDetail?.reviewInfo?.id) {
518+
return undefined
519+
}
520+
521+
const reviewDetail = myReviewDetail
522+
const reviewInfo = reviewDetail.reviewInfo!
523+
const status = (reviewInfo.status ?? '').toUpperCase()
524+
if (status !== 'COMPLETED') {
525+
return undefined
526+
}
527+
528+
if (!isReviewPhaseCurrentlyOpen(challengeInfo, reviewInfo.phaseId)) {
529+
return undefined
530+
}
531+
532+
const resourceId = reviewInfo.resourceId ?? reviewDetail.resourceId
533+
const isOwnReview = resourceId ? myReviewerResourceIds.has(resourceId) : false
534+
535+
if (!canManageCompletedReviews && !isOwnReview) {
536+
return undefined
537+
}
538+
539+
const reviewId = reviewInfo.id
540+
const isPendingReopen = pendingReopen?.review?.reviewInfo?.id === reviewId
541+
542+
function handleReopenClick(): void {
543+
openReopenDialog(submission, reviewDetail)
544+
}
545+
546+
return (
547+
<button
548+
key='reopen-review'
549+
type='button'
550+
className={classNames(styles.submit, styles.textBlue)}
551+
onClick={handleReopenClick}
552+
disabled={isReopening && isPendingReopen}
553+
>
554+
<i className='icon-reopen' />
555+
Reopen Review
556+
</button>
557+
)
558+
}
559+
428560
const historyKeyForRow = getSubmissionHistoryKey(submission.memberId, submission.id)
429561
const rowHistory = historyByMember.get(historyKeyForRow) ?? []
430562

@@ -453,6 +585,7 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
453585

454586
appendAction(buildPrimaryAction(), 'primary')
455587
appendAction(buildHistoryAction(), 'history')
588+
appendAction(buildReopenAction(), 'reopen')
456589

457590
if (!actionEntries.length) {
458591
return (
@@ -481,11 +614,16 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
481614
</span>
482615
)
483616
}, [
617+
canManageCompletedReviews,
484618
canViewHistory,
619+
challengeInfo,
485620
handleHistoryButtonClick,
486621
hasReviewRole,
487622
historyByMember,
623+
isReopening,
488624
myReviewerResourceIds,
625+
openReopenDialog,
626+
pendingReopen,
489627
shouldShowHistoryActions,
490628
])
491629

@@ -557,10 +695,13 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
557695
)
558696

559697
for (let index = 0; index < maxReviewCount; index += 1) {
698+
const metadata = reviewerColumnMetadata[index] ?? {
699+
label: `Reviewer ${index + 1}`,
700+
}
560701
baseColumns.push(
561702
{
562703
columnId: `reviewer-${index}`,
563-
label: `Reviewer ${index + 1}`,
704+
label: metadata.renderLabel ?? metadata.label,
564705
renderer: (submission: SubmissionRow) => renderReviewerCell(submission, index),
565706
type: 'element',
566707
},
@@ -606,11 +747,48 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
606747
openReopenDialog,
607748
challengeInfo,
608749
pendingReopen,
750+
reviewerColumnMetadata,
609751
])
610752

611753
const columnsMobile = useMemo<MobileTableColumn<SubmissionRow>[][]>(
612754
() => columns.map(column => {
613-
if (column.label === 'Action' || column.label === 'Actions') {
755+
const resolveLabelString = (): string => {
756+
if (typeof column.label === 'string') {
757+
return column.label
758+
}
759+
760+
if (typeof column.label === 'function') {
761+
const labelResult = column.label()
762+
if (typeof labelResult === 'string') {
763+
return labelResult
764+
}
765+
766+
const columnId = column.columnId ?? ''
767+
if (columnId.startsWith('reviewer-')) {
768+
const reviewerIndexRaw = columnId.split('-')[1]
769+
const reviewerIndex = reviewerIndexRaw
770+
? Number.parseInt(reviewerIndexRaw, 10)
771+
: NaN
772+
if (!Number.isNaN(reviewerIndex)) {
773+
return reviewerColumnMetadata[reviewerIndex]?.label
774+
?? `Reviewer ${reviewerIndex + 1}`
775+
}
776+
777+
return 'Reviewer'
778+
}
779+
780+
return ''
781+
}
782+
783+
return column.label ?? ''
784+
}
785+
786+
const resolvedLabel = resolveLabelString()
787+
const labelForAction = typeof column.label === 'string'
788+
? column.label
789+
: resolvedLabel
790+
791+
if (labelForAction === 'Action' || labelForAction === 'Actions') {
614792
return [
615793
{
616794
...column,
@@ -620,15 +798,17 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
620798
]
621799
}
622800

801+
const labelText = resolvedLabel || ''
802+
623803
return [
624804
{
625805
...column,
626806
className: '',
627-
label: `${column.label as string} label`,
807+
label: labelText ? `${labelText} label` : 'label',
628808
mobileType: 'label',
629809
renderer: () => (
630810
<div>
631-
{column.label as string}
811+
{labelText}
632812
:
633813
</div>
634814
),
@@ -640,7 +820,7 @@ export const TableReview: FC<TableReviewProps> = (props: TableReviewProps) => {
640820
},
641821
] as MobileTableColumn<SubmissionRow>[]
642822
}),
643-
[columns],
823+
[columns, reviewerColumnMetadata],
644824
)
645825

646826
return (

src/apps/review/src/lib/components/common/TableColumnRenderers.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,29 @@ export function renderReviewScoreCell(
229229
)
230230
}
231231

232+
const aggregatedReviews = submission.aggregated?.reviews ?? []
233+
const hasIncompleteReview = aggregatedReviews.some(review => {
234+
const statusRaw = review.reviewInfo?.status ?? review.status
235+
const normalizedStatus = typeof statusRaw === 'string'
236+
? statusRaw
237+
.trim()
238+
.toUpperCase()
239+
: ''
240+
if (!normalizedStatus) {
241+
return true
242+
}
243+
244+
return normalizedStatus !== 'COMPLETED'
245+
})
246+
247+
if (!aggregatedReviews.length || hasIncompleteReview) {
248+
return (
249+
<span className={styles.notReviewed}>
250+
--
251+
</span>
252+
)
253+
}
254+
232255
const rawScoreDisplay = submission.aggregated?.averageFinalScoreDisplay
233256
if (!rawScoreDisplay) {
234257
return (

0 commit comments

Comments
 (0)