Skip to content

Commit e4be6d9

Browse files
committed
Add mobile view, fix issues from PR feedback
1 parent 6eb0aa6 commit e4be6d9

File tree

4 files changed

+152
-43
lines changed

4 files changed

+152
-43
lines changed

src/apps/review/src/lib/components/Scorecard/ScorecardAttachments/ScorecardAttachments.module.scss

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
@import '@libs/ui/styles/includes';
22

3-
.container {
3+
.tableWrapper {
44
min-height: calc($content-height - 250px);
5+
font-size: 14px;
6+
7+
&:global(.enhanced-table) {
8+
table {
9+
th,
10+
td {
11+
text-align: left;
12+
background-color: white;
13+
}
14+
15+
}
16+
}
517
}
618

719
.tableCell {
@@ -44,4 +56,32 @@
4456
.artifactIcon {
4557
stroke: #00797A;
4658
}
47-
}
59+
}
60+
61+
.noAttachmentText {
62+
text-align: center;
63+
}
64+
65+
.mobileRow {
66+
padding: 16px 8px;
67+
border-bottom: 1px solid #A8A8A8;
68+
}
69+
70+
.mobileHeader {
71+
display: flex;
72+
gap: 12px;
73+
}
74+
75+
.mobileExpanded {
76+
padding: 16px 20px 0px 32px;
77+
}
78+
79+
.rowItem {
80+
display: flex;
81+
justify-content: space-between;
82+
}
83+
84+
.rowItemHeading {
85+
font-weight: 700;
86+
color: #0a0a0a;
87+
}

src/apps/review/src/lib/components/Scorecard/ScorecardAttachments/ScorecardAttachments.tsx

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { FC, useCallback, useMemo } from 'react'
1+
import { FC, useCallback, useMemo, useState } from 'react'
22
import { noop } from 'lodash'
33
import classNames from 'classnames'
44
import moment from 'moment'
55

66
import { IconOutline, Table, TableColumn } from '~/libs/ui'
77
import { useReviewsContext } from '~/apps/review/src/pages/reviews/ReviewsContext'
8+
import { useWindowSize, WindowSize } from '~/libs/shared'
89

910
import { AiWorkflowRunArtifact,
1011
AiWorkflowRunArtifactDownloadResponse,
@@ -23,8 +24,8 @@ interface ScorecardAttachmentsProps {
2324

2425
const ScorecardAttachments: FC<ScorecardAttachmentsProps> = (props: ScorecardAttachmentsProps) => {
2526
const className = props.className
26-
// const { width: screenWidth }: WindowSize = useWindowSize()
27-
// const isTablet = useMemo(() => screenWidth <= 1000, [screenWidth])
27+
const { width: screenWidth }: WindowSize = useWindowSize()
28+
const isTablet = useMemo(() => screenWidth <= 1000, [screenWidth])
2829
const { workflowId, workflowRun }: ReviewsContextModel = useReviewsContext()
2930
const { artifacts }: AiWorkflowRunAttachmentsResponse
3031
= useFetchAiWorkflowsRunAttachments(workflowId, workflowRun?.id)
@@ -105,28 +106,94 @@ const ScorecardAttachments: FC<ScorecardAttachmentsProps> = (props: ScorecardAtt
105106
type: 'element',
106107
},
107108
],
109+
[createDownloadHandler, isDownloading],
110+
)
111+
112+
const [openRow, setOpenRow] = useState<number | undefined>(undefined)
113+
const toggleRow = useCallback(
114+
(id: number) => () => {
115+
setOpenRow(prev => (prev === id ? undefined : id))
116+
},
108117
[],
109118
)
119+
const renderMobileRow = (attachment: AiWorkflowRunArtifact): JSX.Element => {
120+
const isExpired = attachment.expired
121+
const downloading = isDownloading
122+
const isOpen = openRow === attachment.id
123+
124+
return (
125+
<div key={attachment.id} className={styles.mobileRow}>
126+
{/* Top collapsed row */}
127+
<div className={styles.mobileHeader}>
128+
<IconOutline.ChevronDownIcon
129+
onClick={toggleRow(attachment.id)}
130+
className={classNames(styles.chevron, {
131+
[styles.open]: isOpen,
132+
})}
133+
width={20}
134+
/>
135+
<div
136+
className={classNames(styles.filenameCell, {
137+
[styles.expired]: isExpired,
138+
[styles.downloading]: downloading,
139+
})}
140+
onClick={!isExpired ? createDownloadHandler(attachment.id) : undefined}
141+
>
142+
{attachment.name}
143+
</div>
144+
145+
</div>
146+
147+
{/* Expanded content */}
148+
{isOpen && (
149+
<div className={styles.mobileExpanded}>
150+
<div className={styles.rowItem}>
151+
<span className={styles.rowItemHeading}>Type:</span>
152+
<div className={styles.artifactType}>
153+
<IconOutline.CubeIcon className={styles.artifactIcon} width={20} />
154+
Artifact
155+
</div>
156+
</div>
157+
158+
<div className={styles.rowItem}>
159+
<span className={styles.rowItemHeading}>Size:</span>
160+
{formatFileSize(attachment.size_in_bytes)}
161+
</div>
162+
163+
<div className={styles.rowItem}>
164+
<span className={styles.rowItemHeading}>Date:</span>
165+
{moment(attachment.created_at)
166+
.local()
167+
.format(TABLE_DATE_FORMAT)}
168+
</div>
169+
</div>
170+
)}
171+
</div>
172+
)
173+
}
110174

111175
return (
112176
<TableWrapper
113177
className={classNames(
114-
styles.container,
178+
styles.tableWrapper,
115179
className,
116180
'enhanced-table',
117181
)}
118182
>
119-
{artifacts ? (
183+
{!artifacts || artifacts.length === 0 ? (
184+
<div className={styles.noAttachmentText}>No attachments</div>
185+
) : isTablet ? (
186+
<div className={styles.mobileList}>
187+
{artifacts.map(renderMobileRow)}
188+
</div>
189+
) : (
120190
<Table
121191
columns={columns}
122192
data={artifacts}
123193
disableSorting
124194
onToggleSort={noop}
125195
removeDefaultSort
126-
className='enhanced-table-desktop'
127196
/>
128-
) : (
129-
<div>No attachments</div>
130197
)}
131198

132199
</TableWrapper>

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

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useCallback, useEffect, useState } from 'react'
22
import useSWR, { SWRResponse } from 'swr'
33

44
import { EnvironmentConfig } from '~/config'
@@ -204,40 +204,38 @@ export function useFetchAiWorkflowsRunAttachments(
204204

205205
export function useDownloadAiWorkflowsRunArtifact(
206206
workflowId?: string,
207-
runId?: string | undefined,
207+
runId?: string,
208208
): AiWorkflowRunArtifactDownloadResponse {
209209
const [isDownloading, setIsDownloading] = useState(false)
210210

211-
const download = async (artifactId: number): Promise<void> => {
212-
if (!workflowId || !runId || !artifactId) return
211+
const download = useCallback(
212+
async (artifactId: number): Promise<void> => {
213+
if (!workflowId || !runId || !artifactId) return
213214

214-
try {
215215
setIsDownloading(true)
216-
217216
const url = `${TC_API_BASE_URL}/workflows/${workflowId}/runs/${runId}/attachments/${artifactId}/zip`
218217

219-
xhrGetBlobAsync<Blob>(url)
220-
.then(blob => {
221-
const objectUrl = window.URL.createObjectURL(blob)
222-
const link = document.createElement('a')
223-
link.href = objectUrl
224-
link.download = `artifact-${artifactId}.zip`
225-
226-
document.body.appendChild(link)
227-
link.click()
228-
link.remove()
229-
230-
window.URL.revokeObjectURL(objectUrl)
231-
})
232-
.catch(err => {
233-
handleError(err as Error)
234-
})
235-
} catch (err) {
236-
handleError(err as Error)
237-
} finally {
238-
setIsDownloading(false)
239-
}
240-
}
218+
try {
219+
const blob = await xhrGetBlobAsync<Blob>(url)
220+
221+
const objectUrl = window.URL.createObjectURL(blob)
222+
const link = document.createElement('a')
223+
link.href = objectUrl
224+
link.download = `artifact-${artifactId}.zip`
225+
226+
document.body.appendChild(link)
227+
link.click()
228+
link.remove()
229+
230+
window.URL.revokeObjectURL(objectUrl)
231+
} catch (err) {
232+
handleError(err as Error)
233+
} finally {
234+
setIsDownloading(false)
235+
}
236+
},
237+
[workflowId, runId],
238+
)
241239

242240
return {
243241
download,

src/apps/review/src/pages/reviews/components/AiReviewViewer/AiReviewViewer.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { Tabs } from '~/apps/review/src/lib'
44
import { ScorecardViewer } from '~/apps/review/src/lib/components/Scorecard'
55
import { ScorecardAttachments } from '~/apps/review/src/lib/components/Scorecard/ScorecardAttachments'
66
import {
7+
AiWorkflowRunAttachmentsResponse,
78
AiWorkflowRunItemsResponse,
89
AiWorkflowRunStatusEnum,
10+
useFetchAiWorkflowsRunAttachments,
911
useFetchAiWorkflowsRunItems,
1012
} from '~/apps/review/src/lib/hooks'
1113
import { ReviewsContextModel, SelectOption } from '~/apps/review/src/lib/models'
@@ -15,15 +17,17 @@ import { useReviewsContext } from '../../ReviewsContext'
1517

1618
import styles from './AiReviewViewer.module.scss'
1719

18-
const tabItems: SelectOption[] = [
19-
{ label: 'Scorecard', value: 'scorecard' },
20-
{ label: 'Attachments', value: 'attachments' },
21-
]
22-
2320
const AiReviewViewer: FC = () => {
2421
const { scorecard, workflowId, workflowRun }: ReviewsContextModel = useReviewsContext()
2522
const [selectedTab, setSelectedTab] = useState('scorecard')
2623
const { runItems }: AiWorkflowRunItemsResponse = useFetchAiWorkflowsRunItems(workflowId, workflowRun?.id)
24+
const { totalCount }: AiWorkflowRunAttachmentsResponse
25+
= useFetchAiWorkflowsRunAttachments(workflowId, workflowRun?.id)
26+
27+
const tabItems: SelectOption[] = [
28+
{ label: 'Scorecard', value: 'scorecard' },
29+
{ label: `Attachments (${totalCount ?? 0})`, value: 'attachments' },
30+
]
2731
const isFailedRun = useMemo(() => (
2832
workflowRun && [
2933
AiWorkflowRunStatusEnum.CANCELLED,

0 commit comments

Comments
 (0)