Skip to content

Commit c624467

Browse files
committed
PM-1905 - ai reviews
1 parent 5f34220 commit c624467

File tree

13 files changed

+430
-25
lines changed

13 files changed

+430
-25
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.wrap {
4+
font-family: "Nunito Sans", sans-serif;
5+
max-width: 100%;
6+
overflow: hidden;
7+
}
8+
9+
.reviewsTable {
10+
width: 100%;
11+
border-collapse: collapse;
12+
13+
th {
14+
border-top: 1px solid #A8A8A8;
15+
font-weight: bold;
16+
background: #E0E4E8;
17+
}
18+
19+
th, td {
20+
text-align: left;
21+
font-size: 14px;
22+
padding: $sp-2 $sp-4;
23+
border-bottom: 1px solid #A8A8A8;
24+
}
25+
26+
.scoreCol {
27+
text-align: right;
28+
29+
color: #0D61BF;
30+
}
31+
}
32+
33+
.aiReviewer {
34+
display: flex;
35+
align-items: center;
36+
gap: $sp-2;
37+
38+
.icon {
39+
display: flex;
40+
align-items: center;
41+
flex: 0 0;
42+
}
43+
44+
.workflowName {
45+
max-width: 200px;
46+
white-space: nowrap;
47+
overflow: hidden;
48+
text-overflow: ellipsis;
49+
}
50+
}
51+
52+
.result {
53+
display: flex;
54+
align-items: center;
55+
gap: $sp-2;
56+
57+
:global(.icon) {
58+
color: #C1294F;
59+
&:global(.passed) {
60+
color: $teal-160;
61+
}
62+
}
63+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { FC, useMemo } from 'react'
2+
import moment from 'moment'
3+
4+
import { CheckIcon, MinusCircleIcon } from '@heroicons/react/outline'
5+
6+
import { AiWorkflowRunsResponse, useFetchAiWorkflowsRuns } from '../../hooks'
7+
import { IconAiReview } from '../../assets/icons'
8+
import { TABLE_DATE_FORMAT } from '../../../config/index.config'
9+
10+
import styles from './AiReviewsTable.module.scss'
11+
12+
interface AiReviewsTableProps {
13+
submissionId: string
14+
reviewers: { aiWorkflowId: string }[]
15+
}
16+
17+
const AiReviewsTable: FC<AiReviewsTableProps> = props => {
18+
const aiWorkflowIds = useMemo(() => props.reviewers.map(r => r.aiWorkflowId), [props.reviewers])
19+
const { runs, isLoading }: AiWorkflowRunsResponse = useFetchAiWorkflowsRuns(props.submissionId, aiWorkflowIds)
20+
21+
return (
22+
<div className={styles.wrap}>
23+
<table className={styles.reviewsTable}>
24+
<tr>
25+
<th>AI Reviewer</th>
26+
<th>Review Date</th>
27+
<th>Score</th>
28+
<th>Result</th>
29+
</tr>
30+
31+
{!runs.length && isLoading && (
32+
<tr>
33+
<td colSpan={4}>Loading...</td>
34+
</tr>
35+
)}
36+
37+
{runs.map(run => (
38+
<tr key={run.id}>
39+
<td>
40+
<div className={styles.aiReviewer}>
41+
<span className={styles.icon}>
42+
<IconAiReview />
43+
</span>
44+
<span className={styles.workflowName} title={run.workflow.name}>
45+
{run.workflow.name}
46+
</span>
47+
</div>
48+
</td>
49+
<td>
50+
{run.status === 'SUCCESS' && (
51+
moment(run.completedAt)
52+
.local()
53+
.format(TABLE_DATE_FORMAT)
54+
)}
55+
</td>
56+
<td className={styles.scoreCol}>
57+
{run.status === 'SUCCESS' && run.score}
58+
</td>
59+
<td className={styles.resultCol}>
60+
{run.status === 'SUCCESS' && (
61+
<div className={styles.result}>
62+
{run.score >= run.workflow.scorecard.minimumPassingScore
63+
? (
64+
<>
65+
<CheckIcon className='icon icon-xl passed' />
66+
{' '}
67+
Passed
68+
</>
69+
)
70+
: (
71+
<>
72+
<MinusCircleIcon className='icon icon-xl' />
73+
{' '}
74+
Passed
75+
</>
76+
)}
77+
</div>
78+
)}
79+
</td>
80+
</tr>
81+
))}
82+
</table>
83+
</div>
84+
)
85+
}
86+
87+
export default AiReviewsTable
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as AiReviewsTable } from './AiReviewsTable'

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ interface SubmissionTabParams {
126126
isDownloadingSubmission: useDownloadSubmissionProps['isLoading']
127127
downloadSubmission: useDownloadSubmissionProps['downloadSubmission']
128128
isActiveChallenge: boolean
129+
aiReviewers: { aiWorkflowId: string }[]
129130
}
130131

131132
const renderSubmissionTab = ({
@@ -137,6 +138,7 @@ const renderSubmissionTab = ({
137138
isDownloadingSubmission,
138139
downloadSubmission,
139140
isActiveChallenge,
141+
aiReviewers,
140142
}: SubmissionTabParams): JSX.Element => {
141143
const isSubmissionTab = selectedTabNormalized === 'submission'
142144
const isTopgearSubmissionTab = selectedTabNormalized === 'topgearsubmission'
@@ -155,6 +157,7 @@ const renderSubmissionTab = ({
155157
if (canShowSubmissionList) {
156158
return (
157159
<TabContentSubmissions
160+
aiReviewers={aiReviewers}
158161
submissions={visibleSubmissions}
159162
isLoading={isLoadingSubmission}
160163
isDownloading={isDownloadingSubmission}
@@ -335,6 +338,9 @@ export const ChallengeDetailsContent: FC<Props> = (props: Props) => {
335338

336339
if (SUBMISSION_TAB_KEYS.has(selectedTabNormalized)) {
337340
return renderSubmissionTab({
341+
aiReviewers: (
342+
challengeInfo?.reviewers?.filter(r => !!r.aiWorkflowId) as { aiWorkflowId: string }[]
343+
) ?? [],
338344
downloadSubmission,
339345
isActiveChallenge: props.isActiveChallenge,
340346
isDownloadingSubmission,

src/apps/review/src/lib/components/ChallengeDetailsContent/TabContentSubmissions.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ import {
4141
} from '../../utils'
4242
import type { SubmissionHistoryPartition } from '../../utils'
4343
import { TABLE_DATE_FORMAT } from '../../../config/index.config'
44+
import { CollapsibleAiReviewsRow } from '../CollapsibleAiReviewsRow'
4445

4546
import styles from './TabContentSubmissions.module.scss'
4647

4748
interface Props {
49+
aiReviewers?: { aiWorkflowId: string }[]
4850
submissions: BackendSubmission[]
4951
isLoading: boolean
5052
isDownloading: IsRemovingType
@@ -340,6 +342,17 @@ export const TabContentSubmissions: FC<Props> = props => {
340342
},
341343
type: 'element',
342344
},
345+
...(!props.aiReviewers?.length ? [] : [{
346+
label: 'Reviewer',
347+
propertyName: 'submittedDate',
348+
renderer: (submission: BackendSubmission) => (
349+
<CollapsibleAiReviewsRow
350+
aiReviewers={props.aiReviewers!}
351+
submissionId={submission.id}
352+
/>
353+
),
354+
type: 'element',
355+
} as TableColumn<BackendSubmission>]),
343356
]
344357

345358
if (shouldShowHistoryActions) {
@@ -442,6 +455,7 @@ export const TabContentSubmissions: FC<Props> = props => {
442455
isDownloading={props.isDownloading}
443456
getRestriction={getHistoryRestriction}
444457
getSubmissionMeta={resolveSubmissionMeta}
458+
aiReviewers={props.aiReviewers}
445459
/>
446460
</TableWrapper>
447461
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.wrap {
4+
width: 100%;
5+
text-align: left;
6+
}
7+
8+
.reviewersDropown {
9+
display: flex;
10+
align-items: center;
11+
gap: $sp-2;
12+
13+
svg {
14+
color: #767676;
15+
}
16+
}
17+
18+
.table {
19+
margin-top: $sp-2;
20+
margin-left: -1 * $sp-4;
21+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { FC, useCallback, useState } from 'react'
2+
3+
import { IconOutline } from '~/libs/ui'
4+
5+
import { AiReviewsTable } from '../AiReviewsTable'
6+
7+
import styles from './CollapsibleAiReviewsRow.module.scss'
8+
9+
interface CollapsibleAiReviewsRowProps {
10+
aiReviewers: { aiWorkflowId: string }[]
11+
submissionId: string
12+
}
13+
14+
const CollapsibleAiReviewsRow: FC<CollapsibleAiReviewsRowProps> = props => {
15+
const aiReviewersCount = props.aiReviewers.length
16+
17+
const [isOpen, setIsOpen] = useState(false)
18+
19+
const toggleOpen = useCallback(() => {
20+
setIsOpen(wasOpen => !wasOpen)
21+
}, [])
22+
23+
return (
24+
<div className={styles.wrap}>
25+
<span className={styles.reviewersDropown} onClick={toggleOpen}>
26+
{aiReviewersCount}
27+
{' '}
28+
AI Reviewer
29+
{aiReviewersCount === 1 ? '' : 's'}
30+
<IconOutline.ChevronDownIcon className='icon-xl' />
31+
</span>
32+
{isOpen && (
33+
<div className={styles.table}>
34+
<AiReviewsTable
35+
reviewers={props.aiReviewers}
36+
submissionId={props.submissionId}
37+
/>
38+
</div>
39+
)}
40+
</div>
41+
)
42+
}
43+
44+
export default CollapsibleAiReviewsRow
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as CollapsibleAiReviewsRow } from './CollapsibleAiReviewsRow'

src/apps/review/src/lib/components/SubmissionHistoryModal/SubmissionHistoryModal.module.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,21 @@
146146
font-size: 14px;
147147
text-align: center;
148148
}
149+
150+
.reviewersDropown {
151+
display: flex;
152+
align-items: center;
153+
gap: $sp-2;
154+
155+
svg {
156+
color: #767676;
157+
}
158+
}
159+
160+
.table .aiReviewersTableRow.aiReviewersTableRow {
161+
padding: 0;
162+
}
163+
164+
.aiReviewersTable {
165+
margin-top: -1px;
166+
}

0 commit comments

Comments
 (0)