@@ -10,6 +10,7 @@ import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState';
1010import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState' ;
1111import { deleteEvaluation , getListEvaluationsQueryKey } from '@nemo/sdk/generated/platform/api' ;
1212import { Button , Flex , Text } from '@nvidia/foundations-react-core' ;
13+ import { type EvalJobRow , evalJobDetailRoute } from '@studio/api/evaluation/utils' ;
1314import { BulkDeleteModal } from '@studio/components/BulkDeleteModal' ;
1415import {
1516 evaluatorScores ,
@@ -20,20 +21,30 @@ import type { AgentEvaluationRow } from '@studio/routes/agents/AgentDetailRoute/
2021import { getEvaluationDetailRoute } from '@studio/routes/utils' ;
2122import { useQueryClient } from '@tanstack/react-query' ;
2223import { FlaskConical , Trash } from 'lucide-react' ;
23- import { type ComponentProps , type FC , useCallback , useState } from 'react' ;
24+ import { type ComponentProps , type FC , useCallback , useMemo , useState } from 'react' ;
2425import { useNavigate } from 'react-router' ;
2526
2627interface EvaluationsTableProps {
2728 workspace : string ;
2829 evaluations : AgentEvaluationRow [ ] ;
30+ /** All evaluator jobs for the agent (any status), used to link a published evaluation back to
31+ * the job that produced it. Absent until the reverse join finds a match. */
32+ jobs : EvalJobRow [ ] ;
2933}
3034
3135/** Every published evaluation for the agent, ungrouped. */
32- export const EvaluationsTable : FC < EvaluationsTableProps > = ( { workspace, evaluations } ) => {
36+ export const EvaluationsTable : FC < EvaluationsTableProps > = ( { workspace, evaluations, jobs } ) => {
3337 const navigate = useNavigate ( ) ;
3438 const queryClient = useQueryClient ( ) ;
3539 const dataViewState = useStudioDataViewState ( ) ;
3640 const [ deleteRows , setDeleteRows ] = useState < AgentEvaluationRow [ ] > ( [ ] ) ;
41+ // Reverse of JobsTable's job -> evaluation link: a completed job carries the evaluation it
42+ // published to, so index by that name to recover the job from an evaluation row.
43+ const jobByEvaluation = useMemo ( ( ) => {
44+ const map : Record < string , EvalJobRow > = { } ;
45+ for ( const job of jobs ) if ( job . evaluationName ) map [ job . evaluationName ] = job ;
46+ return map ;
47+ } , [ jobs ] ) ;
3748
3849 const handleDelete = useCallback (
3950 async ( rows : AgentEvaluationRow [ ] ) => {
@@ -55,7 +66,7 @@ export const EvaluationsTable: FC<EvaluationsTableProps> = ({ workspace, evaluat
5566
5667 const makeColumns : ComponentProps < typeof StudioDataView < AgentEvaluationRow > > [ 'makeColumns' ] =
5768 useCallback (
58- ( { accessor } , { rowSelectionColumn } ) => [
69+ ( { accessor } , { rowSelectionColumn, rowActionsColumn } ) => [
5970 rowSelectionColumn ( { size : ROW_SELECTION_COLUMN_SIZE } ) ,
6071 accessor ( 'name' , {
6172 header : 'Evaluation' ,
@@ -109,8 +120,21 @@ export const EvaluationsTable: FC<EvaluationsTableProps> = ({ workspace, evaluat
109120 cell : ( { row } ) =>
110121 row . original . created_at ? < RelativeTime datetime = { row . original . created_at } /> : '—' ,
111122 } ) ,
123+ rowActionsColumn ( {
124+ rowActions : ( row ) => {
125+ const job = jobByEvaluation [ row . name ] ;
126+ return job
127+ ? [
128+ {
129+ children : 'View job' ,
130+ onSelect : ( ) => navigate ( evalJobDetailRoute ( workspace , job ) ) ,
131+ } ,
132+ ]
133+ : false ;
134+ } ,
135+ } ) ,
112136 ] ,
113- [ ]
137+ [ jobByEvaluation , navigate , workspace ]
114138 ) ;
115139
116140 return (
0 commit comments