diff --git a/src/renderer/components/Experiment/Eval/EvalJobsTable.tsx b/src/renderer/components/Experiment/Eval/EvalJobsTable.tsx index d72c74318744419021b23aa1f678c28316ed79b1..111fe2a7468ab06bac259f33b468f22cbd8c4f8a 100644 --- a/src/renderer/components/Experiment/Eval/EvalJobsTable.tsx +++ b/src/renderer/components/Experiment/Eval/EvalJobsTable.tsx @@ -8,6 +8,7 @@ import { Table, Typography, Link, + Checkbox, } from '@mui/joy'; import { ChartColumnBigIcon, @@ -89,6 +90,7 @@ function RenderScore({ score }) { } const EvalJobsTable = () => { + const [selected, setSelected] = useState<readonly string[]>([]); const [viewOutputFromJob, setViewOutputFromJob] = useState(-1); const [openCSVModal, setOpenCSVModal] = useState(false); const [openPlotModal, setOpenPlotModal] = useState(false); @@ -149,11 +151,53 @@ const EvalJobsTable = () => { setFileName={setFileNameForDetailedReport} fileName={fileNameForDetailedReport} /> - <Typography level="h3">Executions</Typography> + <Box + sx={{ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'baseline', + }} + > + <Typography level="h3">Executions</Typography> + {selected.length > 1 && ( + <Typography + level="body-sm" + startDecorator={<ChartColumnIncreasingIcon size="20px" />} + onClick={() => { + alert('this feature coming soon'); + }} + sx={{ cursor: 'pointer' }} + > + <>Compare Selected Evals</> + </Typography> + )} + </Box> <Sheet sx={{ overflowY: 'scroll' }}> <Table stickyHeader> <thead> <tr> + <th + style={{ width: 48, textAlign: 'center', padding: '6px 6px' }} + > + <Checkbox + size="sm" + indeterminate={ + selected.length > 0 && selected.length !== jobs.length + } + checked={selected.length === jobs.length} + onChange={(event) => { + setSelected( + event.target.checked ? jobs.map((row) => row.id) : [] + ); + }} + color={ + selected.length > 0 || selected.length === jobs.length + ? 'primary' + : undefined + } + sx={{ verticalAlign: 'text-bottom' }} + /> + </th> <th width="50px">Id</th> <th>Eval</th> <th>Progress</th> @@ -164,6 +208,24 @@ const EvalJobsTable = () => { <tbody> {jobs?.map((job) => ( <tr key={job.id}> + <td style={{ textAlign: 'center', width: 120 }}> + <Checkbox + size="sm" + checked={selected.includes(job?.id)} + color={selected.includes(job?.id) ? 'primary' : undefined} + onChange={(event) => { + setSelected((ids) => + event.target.checked + ? ids.concat(job?.id) + : ids.filter((itemId) => itemId !== job?.id) + ); + }} + slotProps={{ + checkbox: { sx: { textAlign: 'left' } }, + }} + sx={{ verticalAlign: 'text-bottom' }} + /> + </td> <td>{job.id}</td> <td> <Typography level="title-md"> @@ -179,7 +241,6 @@ const EvalJobsTable = () => { <td> <JobProgress job={job} /> </td> - <td> <RenderScore score={job?.job_data?.score} /> {job?.job_data?.additional_output_path && @@ -219,7 +280,6 @@ const EvalJobsTable = () => { </Link> )} </td> - <td> <ButtonGroup variant="soft"