From 9b810047f94d1920fcccc8c50cecd009d554edde Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Thu, 27 Nov 2025 12:37:32 +0200 Subject: [PATCH] Add ai reviewers on all tables --- .../ChallengeDetailsContent.tsx | 4 ++ .../TabContentCheckpoint.tsx | 2 + .../TabContentIterativeReview.tsx | 2 + .../TabContentScreening.tsx | 2 + .../TableCheckpointSubmissions.module.scss | 20 ++++++ .../TableCheckpointSubmissions.tsx | 58 +++++++++++++++-- .../TableIterativeReview.module.scss | 20 ++++++ .../TableIterativeReview.tsx | 24 +++++++ .../TableSubmissionScreening.module.scss | 20 ++++++ .../TableSubmissionScreening.tsx | 65 +++++++++++++++++-- 10 files changed, 203 insertions(+), 14 deletions(-) diff --git a/src/apps/review/src/lib/components/ChallengeDetailsContent/ChallengeDetailsContent.tsx b/src/apps/review/src/lib/components/ChallengeDetailsContent/ChallengeDetailsContent.tsx index fb4787a5a..69033a337 100644 --- a/src/apps/review/src/lib/components/ChallengeDetailsContent/ChallengeDetailsContent.tsx +++ b/src/apps/review/src/lib/components/ChallengeDetailsContent/ChallengeDetailsContent.tsx @@ -178,6 +178,7 @@ const renderSubmissionTab = ({ downloadSubmission={downloadSubmission} isActiveChallenge={isActiveChallenge} showScreeningColumns={!isSubmissionTab && !isTopgearSubmissionTab} + aiReviewers={aiReviewers} /> ) } @@ -438,6 +439,7 @@ export const ChallengeDetailsContent: FC = (props: Props) => { isDownloading={isDownloadingSubmission} downloadSubmission={handleSubmissionDownload} mode={checkpointMode} + aiReviewers={aiReviewers} /> ) } @@ -479,6 +481,7 @@ export const ChallengeDetailsContent: FC = (props: Props) => { downloadSubmission={handleSubmissionDownload} isActiveChallenge={props.isActiveChallenge} columnLabel='Post-Mortem' + aiReviewers={aiReviewers} /> ) } @@ -494,6 +497,7 @@ export const ChallengeDetailsContent: FC = (props: Props) => { downloadSubmission={handleSubmissionDownload} isActiveChallenge={props.isActiveChallenge} phaseIdFilter={props.selectedPhaseId} + aiReviewers={aiReviewers} /> ) } diff --git a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentCheckpoint.tsx b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentCheckpoint.tsx index 78fa8e350..b29473055 100644 --- a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentCheckpoint.tsx +++ b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentCheckpoint.tsx @@ -24,6 +24,7 @@ interface Props { downloadSubmission: (submissionId: string) => void challengeStatus?: string mode?: 'submission' | 'screening' | 'review' + aiReviewers?: { aiWorkflowId: string }[] } export const TabContentCheckpoint: FC = (props: Props) => { @@ -148,6 +149,7 @@ export const TabContentCheckpoint: FC = (props: Props) => { isDownloading={props.isDownloading} downloadSubmission={props.downloadSubmission} mode={mode} + aiReviewers={props.aiReviewers} /> ) } diff --git a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentIterativeReview.tsx b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentIterativeReview.tsx index f452bcff0..6d9d40fae 100644 --- a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentIterativeReview.tsx +++ b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentIterativeReview.tsx @@ -28,6 +28,7 @@ interface Props { isActiveChallenge: boolean columnLabel?: string phaseIdFilter?: string + aiReviewers?: { aiWorkflowId: string }[] } const getSubmissionPriority = (submission: SubmissionInfo): number => { @@ -203,6 +204,7 @@ export const TabContentIterativeReview: FC = (props: Props) => { hideSubmissionColumn={shouldHideSubmissionColumn} isChallengeCompleted={isChallengeCompleted} hasPassedThreshold={hasPassedPostMortemThreshold} + aiReviewers={props.aiReviewers} /> ) } diff --git a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentScreening.tsx b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentScreening.tsx index 344bd0aa7..095447b83 100644 --- a/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentScreening.tsx +++ b/src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentScreening.tsx @@ -23,6 +23,7 @@ interface Props { isActiveChallenge: boolean showScreeningColumns?: boolean challengeStatus?: string + aiReviewers?: { aiWorkflowId: string }[] } export const TabContentScreening: FC = (props: Props) => { @@ -136,6 +137,7 @@ export const TabContentScreening: FC = (props: Props) => { downloadSubmission={props.downloadSubmission} hideHandleColumn={hideHandleColumn} showScreeningColumns={showScreeningColumns} + aiReviewers={props.aiReviewers} /> ) } diff --git a/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.module.scss b/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.module.scss index e97276bf3..b5d65a003 100644 --- a/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.module.scss +++ b/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.module.scss @@ -184,3 +184,23 @@ color: var(--GrayFontColor); font-size: 14px; } + +.aiReviews { + margin: $sp-2 0; + + :global(.trigger) { + width: fit-content; + margin-left: auto; + } + + :global(.reviews-table) { + margin-left: auto; + width: 75%; + margin-bottom: -9px; + + @include ltelg { + width: auto; + margin-left: -1*$sp-4; + } + } +} diff --git a/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.tsx b/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.tsx index 8a1495866..ffcd4dfed 100644 --- a/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.tsx +++ b/src/apps/review/src/lib/components/TableCheckpointSubmissions/TableCheckpointSubmissions.tsx @@ -16,6 +16,7 @@ import { UserRole } from '~/libs/core' import { handleError } from '~/apps/admin/src/lib/utils' import { + BackendSubmission, ChallengeDetailContextModel, ReviewAppContextModel, Screening, @@ -33,6 +34,7 @@ import { updateReview } from '../../services' import { ConfirmModal } from '../ConfirmModal' import { useSubmissionDownloadAccess } from '../../hooks' import type { UseSubmissionDownloadAccessResult } from '../../hooks/useSubmissionDownloadAccess' +import { CollapsibleAiReviewsRow } from '../CollapsibleAiReviewsRow' import styles from './TableCheckpointSubmissions.module.scss' @@ -42,6 +44,7 @@ interface Props { isDownloading: IsRemovingType downloadSubmission: (submissionId: string) => void mode?: 'submission' | 'screening' | 'review' + aiReviewers?: { aiWorkflowId: string }[] } const isInProgressStatus = (value: string | undefined): boolean => ( @@ -161,6 +164,45 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { [datas], ) + const aiReviewsColumn = useMemo | undefined>( + () => { + if (!props.aiReviewers?.length) { + return undefined + } + + return { + columnId: 'ai-reviews-table', + isExpand: true, + label: '', + renderer: (data: Screening, allRows: Screening[]) => { + const submissionPayload = { + id: data.submissionId ?? '', + virusScan: data.virusScan, + } as Pick + + if (!submissionPayload.id) { + return <> + } + + return ( + + ) + }, + type: 'element', + } as TableColumn + }, + [props.aiReviewers], + ) + + const appendAiColumn = (columns: TableColumn[]): TableColumn[] => ( + aiReviewsColumn ? [...columns, aiReviewsColumn] : columns + ) + const columns = useMemo[]>( () => { const tableMode = mode @@ -312,7 +354,7 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { } if (tableMode === 'submission') { - return baseColumns + return appendAiColumn(baseColumns) } if (tableMode === 'screening') { @@ -410,7 +452,7 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { const hasAnyMyAssignment = rows.some(row => Boolean(row.myReviewResourceId)) const canShowReopenActions = rows.some(row => computeReopenEligibility(row).canReopen) if (!hasAnyMyAssignment && !canShowReopenActions) { - return screeningColumns + return appendAiColumn(screeningColumns) } const actionColumn: TableColumn = { @@ -502,10 +544,10 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { type: 'element', } - return [ + return appendAiColumn([ ...screeningColumns, actionColumn, - ] + ]) } // mode === 'review' @@ -574,7 +616,7 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { const hasAnyMyAssignment = rows.some(row => Boolean(row.myReviewResourceId)) const canShowReopenActions = rows.some(row => computeReopenEligibility(row).canReopen) if (!hasAnyMyAssignment && !canShowReopenActions) { - return reviewColumns + return appendAiColumn(reviewColumns) } const actionColumn: TableColumn = { @@ -666,10 +708,10 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { type: 'element', } - return [ + return appendAiColumn([ ...reviewColumns, actionColumn, - ] + ]) }, [ challengeInfo, @@ -730,6 +772,8 @@ export const TableCheckpointSubmissions: FC = (props: Props) => { span { display: inline-flex; } + +.aiReviews { + margin: $sp-2 0; + + :global(.trigger) { + width: fit-content; + margin-left: auto; + } + + :global(.reviews-table) { + margin-left: auto; + width: 75%; + margin-bottom: -9px; + + @include ltelg { + width: auto; + margin-left: -1*$sp-4; + } + } +} diff --git a/src/apps/review/src/lib/components/TableIterativeReview/TableIterativeReview.tsx b/src/apps/review/src/lib/components/TableIterativeReview/TableIterativeReview.tsx index 1daadfef9..4dfb6e92a 100644 --- a/src/apps/review/src/lib/components/TableIterativeReview/TableIterativeReview.tsx +++ b/src/apps/review/src/lib/components/TableIterativeReview/TableIterativeReview.tsx @@ -44,6 +44,7 @@ import type { SubmissionRow } from '../common/types' import { resolveSubmissionReviewResult } from '../common/reviewResult' import { ProgressBar } from '../ProgressBar' import { TableWrapper } from '../TableWrapper' +import { CollapsibleAiReviewsRow } from '../CollapsibleAiReviewsRow' import styles from './TableIterativeReview.module.scss' @@ -57,6 +58,7 @@ interface Props { hideSubmissionColumn?: boolean isChallengeCompleted?: boolean hasPassedThreshold?: boolean + aiReviewers?: { aiWorkflowId: string }[] } interface ScoreMetadata { @@ -1335,6 +1337,25 @@ export const TableIterativeReview: FC = (props: Props) => { baseColumns.push(reviewerColumn) } + if (props.aiReviewers) { + baseColumns.push({ + columnId: 'ai-reviews-table', + isExpand: true, + label: '', + renderer: (submission: SubmissionInfo, allRows: SubmissionInfo[]) => ( + props.aiReviewers && ( + + ) + ), + type: 'element', + }) + } + return baseColumns }, [ approverColumn, @@ -1349,6 +1370,7 @@ export const TableIterativeReview: FC = (props: Props) => { reviewDateColumn, reviewerColumn, submissionColumn, + props.aiReviewers, ]) const columnsMobile = useMemo[][]>(() => ( @@ -1387,6 +1409,8 @@ export const TableIterativeReview: FC = (props: Props) => {
void hideHandleColumn?: boolean showScreeningColumns?: boolean + aiReviewers?: { aiWorkflowId: string }[] } interface SubmissionColumnConfig { @@ -785,6 +788,44 @@ export const TableSubmissionScreening: FC = (props: Props) => { return map }, [filteredChallengeSubmissions, props.screenings]) + const aiReviewersColumn = useMemo | undefined>( + () => { + if (!props.aiReviewers?.length) { + return undefined + } + + return { + columnId: 'ai-reviews-table', + isExpand: true, + label: '', + renderer: ( + data: Screening, + allRows: Screening[], + ) => { + const submissionPayload = submissionMetaById.get(data.submissionId) ?? { + id: data.submissionId ?? '', + virusScan: data.virusScan, + } + + if (!submissionPayload?.id) { + return <> + } + + return ( + } + defaultOpen={allRows ? !allRows.indexOf(data) : false} + /> + ) + }, + type: 'element', + } as TableColumn + }, + [props.aiReviewers, submissionMetaById], + ) + const primarySubmissionInfos = useMemo( () => props.screenings .map(screening => submissionMetaById.get(screening.submissionId)) @@ -1129,14 +1170,22 @@ export const TableSubmissionScreening: FC = (props: Props) => { const columns = useMemo[]>( () => { const base = [...baseColumns] - if (!showScreeningColumns) { - return appendActionColumn(base, actionColumn) - } - - const withScreening = [...base, ...screeningColumns] - return appendActionColumn(withScreening, actionColumn) + const columnsWithoutAction = showScreeningColumns + ? [...base, ...screeningColumns] + : base + const columnsWithAi = aiReviewersColumn + ? [...columnsWithoutAction, aiReviewersColumn] + : columnsWithoutAction + + return appendActionColumn(columnsWithAi, actionColumn) }, - [actionColumn, baseColumns, screeningColumns, showScreeningColumns], + [ + actionColumn, + aiReviewersColumn, + baseColumns, + screeningColumns, + showScreeningColumns, + ], ) const columnsMobile = useMemo[][]>( @@ -1178,6 +1227,8 @@ export const TableSubmissionScreening: FC = (props: Props) => {