Skip to content

Commit 386af7d

Browse files
committed
PM-2135 - AI Workflows - scorecard header
1 parent 0b46c0f commit 386af7d

File tree

10 files changed

+296
-3
lines changed

10 files changed

+296
-3
lines changed
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

src/apps/review/src/lib/assets/icons/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { ReactComponent as IconReview } from './icon-phase-review.svg'
99
import { ReactComponent as IconAppeal } from './icon-phase-appeal.svg'
1010
import { ReactComponent as IconAppealResponse } from './icon-phase-appeal-response.svg'
1111
import { ReactComponent as IconPhaseWinners } from './icon-phase-winners.svg'
12+
import { ReactComponent as IconDeepseekAi } from './deepseek.svg'
13+
import { ReactComponent as IconClock } from './icon-clock.svg'
14+
import { ReactComponent as IconPremium } from './icon-premium.svg'
1215

1316
export * from './editor/bold'
1417
export * from './editor/code'
@@ -37,6 +40,9 @@ export {
3740
IconAppeal,
3841
IconAppealResponse,
3942
IconPhaseWinners,
43+
IconDeepseekAi,
44+
IconClock,
45+
IconPremium,
4046
}
4147

4248
export const phasesIcons = {

src/apps/review/src/lib/hooks/useFetchAiWorkflowRuns.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,21 @@ export interface AiWorkflow {
2525
name: string;
2626
description: string;
2727
scorecard?: Scorecard
28+
defUrl: string
29+
llm: {
30+
name: string
31+
description: string
32+
icon: string
33+
url: string
34+
provider: {
35+
name: string
36+
}
37+
}
2838
}
2939

3040
export interface AiWorkflowRun {
3141
id: string;
42+
startedAt: string;
3243
completedAt: string;
3344
status: AiWorkflowRunStatusEnum;
3445
score: number;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.modelNameWrap {
4+
display: flex;
5+
align-items: center;
6+
gap: $sp-4;
7+
}
8+
9+
.modelIcon {
10+
width: 60px;
11+
height: 60px;
12+
border-radius: $sp-1;
13+
border: 1px solid #A8A8A8;
14+
padding: $sp-1;
15+
16+
align-items: center;
17+
display: flex;
18+
justify-content: center;
19+
20+
flex: 0 0 auto;
21+
}
22+
23+
.modelName {
24+
display: flex;
25+
align-items: center;
26+
gap: $sp-3;
27+
28+
h3 {
29+
font-family: "Figtree", sans-serif;
30+
font-size: 26px;
31+
font-weight: 700;
32+
line-height: 30px;
33+
color: #0A0A0A;
34+
}
35+
36+
svg {
37+
display: block;
38+
width: 16px;
39+
height: 16px;
40+
}
41+
}
42+
43+
.modelDescription {
44+
margin-top: $sp-6;
45+
font-family: "Nunito Sans", sans-serif;
46+
font-size: 14px;
47+
line-height: 20px;
48+
color: #0A0A0A;
49+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { FC } from 'react'
2+
3+
import { BaseModal } from '~/libs/ui'
4+
import { AiWorkflow } from '~/apps/review/src/lib/hooks'
5+
import { IconExternalLink } from '~/apps/review/src/lib/assets/icons'
6+
7+
import styles from './AiModelModal.module.scss'
8+
9+
interface AiModelModalProps {
10+
model: AiWorkflow['llm']
11+
onClose: () => void
12+
}
13+
14+
const AiModelModal: FC<AiModelModalProps> = props => (
15+
<BaseModal
16+
spacer={false}
17+
open
18+
blockScroll
19+
onClose={props.onClose}
20+
size='lg'
21+
>
22+
<div className={styles.wrap}>
23+
<div className={styles.modelNameWrap}>
24+
<div className={styles.modelIcon}>
25+
<img src={props.model.icon} alt={props.model.name} />
26+
</div>
27+
<div className={styles.modelName}>
28+
<h3>{props.model.name}</h3>
29+
<a href={props.model.url} target='_blank' rel='noreferrer'>
30+
<IconExternalLink />
31+
</a>
32+
</div>
33+
</div>
34+
35+
<p className={styles.modelDescription}>
36+
{props.model.description}
37+
</p>
38+
</div>
39+
</BaseModal>
40+
)
41+
42+
export default AiModelModal
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as AiModelModal } from './AiModelModal'
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.wrap {
4+
width: 100%;
5+
}
6+
7+
.headerWrap {
8+
display: flex;
9+
align-items: flex-start;
10+
}
11+
12+
.workflowInfo {
13+
display: flex;
14+
align-items: flex-start;
15+
gap: $sp-4;
16+
}
17+
18+
.workflowIcon {
19+
width: 60px;
20+
height: 60px;
21+
border-radius: $sp-1;
22+
border: 1px solid #A8A8A8;
23+
padding: $sp-1;
24+
25+
align-items: center;
26+
display: flex;
27+
justify-content: center;
28+
29+
flex: 0 0 auto;
30+
}
31+
32+
.workflowName {
33+
display: flex;
34+
flex-direction: column;
35+
gap: $sp-2;
36+
37+
h3 {
38+
font-family: "Figtree", sans-serif;
39+
font-size: 26px;
40+
font-weight: 700;
41+
line-height: 30px;
42+
color: #0A0A0A;
43+
}
44+
45+
span {
46+
color: $link-blue-dark;
47+
font-family: "Nunito Sans", sans-serif;
48+
font-weight: bold;
49+
font-size: 16px;
50+
line-height: 22px;
51+
}
52+
}
53+
54+
.workflowRunStats {
55+
margin-left: auto;
56+
display: flex;
57+
flex-direction: column;
58+
gap: $sp-1;
59+
60+
flex: 0 0 auto;
61+
62+
> span {
63+
display: flex;
64+
align-items: center;
65+
gap: $sp-2;
66+
67+
font-family: "Nunito Sans", sans-serif;
68+
font-size: 14px;
69+
line-height: 19px;
70+
color: var(--GrayFontColor);
71+
}
72+
73+
strong {
74+
font-family: "Nunito Sans", sans-serif;
75+
font-size: 14px;
76+
font-weight: 700;
77+
line-height: 19px;
78+
color: var(--FontColor);
79+
}
80+
}
81+
82+
.workflowDescription {
83+
margin-top: $sp-6;
84+
font-family: "Nunito Sans", sans-serif;
85+
font-size: 14px;
86+
line-height: 20px;
87+
color: #0A0A0A;
88+
}
89+
90+
.workflowFileLink {
91+
margin-top: $sp-4;
92+
a {
93+
display: flex;
94+
align-items: center;
95+
gap: $sp-1;
96+
color: $link-blue-dark;
97+
font-family: "Nunito Sans", sans-serif;
98+
font-size: 14px;
99+
line-height: 20px;
100+
101+
svg {
102+
width: 12px;
103+
height: 12px;
104+
path {
105+
fill: $link-blue-dark;
106+
}
107+
}
108+
}
109+
110+
}

src/apps/review/src/pages/ai-scorecards/components/ScorecardHeader/ScorecardHeader.tsx

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,82 @@
1-
import { FC } from 'react'
1+
import { FC, useCallback, useMemo, useState } from 'react'
2+
import moment, { Duration } from 'moment'
23

34
import { AiScorecardContextModel } from '~/apps/review/src/lib/models'
45

56
import { useAiScorecardContext } from '../../AiScorecardContext'
7+
import { IconClock, IconDeepseekAi, IconPremium } from '../../../../lib/assets/icons'
8+
import { AiModelModal } from '../AiModelModal'
69

710
import styles from './ScorecardHeader.module.scss'
811

12+
const formatDuration = (duration: Duration): string => [
13+
!!duration.hours() && `${duration.hours()}h`,
14+
!!duration.minutes() && `${duration.minutes()}m`,
15+
!!duration.seconds() && `${duration.seconds()}s`,
16+
].filter(Boolean)
17+
.join(' ')
18+
919
const ScorecardHeader: FC = () => {
10-
const { workflow }: AiScorecardContextModel = useAiScorecardContext()
20+
const { workflow, workflowRun }: AiScorecardContextModel = useAiScorecardContext()
21+
const runDuration = useMemo(() => (
22+
workflowRun && moment.duration(
23+
+new Date(workflowRun.completedAt) - +new Date(workflowRun.startedAt),
24+
'milliseconds',
25+
)
26+
), [workflowRun])
27+
const [modelDetailsModalVisible, setModelDetailsModalVisible] = useState(false)
28+
29+
const toggleModelDetails = useCallback(() => {
30+
setModelDetailsModalVisible(wasVisible => !wasVisible)
31+
}, [])
32+
33+
if (!workflow || !workflowRun) {
34+
return <></>
35+
}
1136

1237
return (
1338
<div className={styles.wrap}>
14-
{workflow?.name}
39+
<div className={styles.headerWrap}>
40+
<div className={styles.workflowInfo}>
41+
<IconDeepseekAi className={styles.workflowIcon} />
42+
<div className={styles.workflowName}>
43+
<h3>{workflow.name}</h3>
44+
<span onClick={toggleModelDetails}>{workflow.llm.name}</span>
45+
</div>
46+
</div>
47+
<div className={styles.workflowRunStats}>
48+
<span>
49+
<IconPremium />
50+
<span>
51+
<strong>Minimum passing score:</strong>
52+
{' '}
53+
{workflow.scorecard?.minimumPassingScore.toFixed(2)}
54+
</span>
55+
</span>
56+
<span>
57+
<IconClock />
58+
<span>
59+
<strong>Duration:</strong>
60+
{' '}
61+
{!!runDuration && formatDuration(runDuration)}
62+
</span>
63+
</span>
64+
</div>
65+
</div>
66+
<p className={styles.workflowDescription}>
67+
{workflow.description}
68+
</p>
69+
{/* <div className={styles.workflowFileLink}>
70+
<a href={workflow.defUrl}>
71+
Workflow File
72+
{' '}
73+
<IconExternalLink />
74+
</a>
75+
</div> */}
76+
77+
{modelDetailsModalVisible && (
78+
<AiModelModal model={workflow.llm} onClose={toggleModelDetails} />
79+
)}
1580
</div>
1681
)
1782
}

0 commit comments

Comments
 (0)