-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
List of collections as a table with more info (#213)
* collections list as a table * collections pagination * collection search upd * moved a component * add an action menu to the collection table * upd * moved vectors configuration from DatasetsTableRow to a separate component, used it in the CollectionsList * some fixes * CollectionsList render test * fixes * display sparse vectors in VectorsConfigChip * optimizations
- Loading branch information
Showing
10 changed files
with
420 additions
and
80 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import React, { useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Link } from 'react-router-dom'; | ||
import { Box, MenuItem, TableCell, TableContainer, TableRow, Typography } from '@mui/material'; | ||
import { styled, useTheme } from '@mui/material/styles'; | ||
import { TableBodyWithGaps, TableHeadWithGaps, TableWithGaps } from '../Common/TableWithGaps'; | ||
import { Dot } from '../Common/Dot'; | ||
import DeleteDialog from './DeleteDialog'; | ||
import ActionsMenu from '../Common/ActionsMenu'; | ||
import VectorsConfigChip from '../Common/VectorsConfigChip'; | ||
|
||
const StyledLink = styled(Link)` | ||
text-decoration: none; | ||
color: inherit; | ||
&:hover { | ||
text-decoration: underline; | ||
} | ||
`; | ||
|
||
const CollectionTableRow = ({ collection, getCollectionsCall }) => { | ||
const [openDeleteDialog, setOpenDeleteDialog] = useState(false); | ||
const theme = useTheme(); | ||
|
||
return ( | ||
<TableRow> | ||
<TableCell> | ||
<Typography component={StyledLink} to={`/collections/${collection.name}`}> | ||
{collection.name} | ||
</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<Box | ||
sx={{ display: 'inline-flex', alignItems: 'center' }} | ||
component={StyledLink} | ||
to={`/collections/${collection.name}#info`} | ||
> | ||
<Dot color={collection.status} /> | ||
<Typography sx={{ ml: 1 }}>{collection.status}</Typography> | ||
</Box> | ||
</TableCell> | ||
<TableCell align="center"> | ||
<Typography component={StyledLink} to={`/collections/${collection.name}`}> | ||
{collection.points_count} | ||
</Typography> | ||
</TableCell> | ||
<TableCell align="center"> | ||
<Typography>{collection.segments_count}</Typography> | ||
</TableCell> | ||
<TableCell align="center"> | ||
<Typography>{collection.config.params.shard_number}</Typography> | ||
</TableCell> | ||
<TableCell> | ||
<VectorsConfigChip collectionConfigParams={collection.config.params} sx={{ justifyContent: 'center' }} /> | ||
</TableCell> | ||
<TableCell align="right"> | ||
<ActionsMenu> | ||
<MenuItem component={Link} to={`/collections/${collection.name}#snapshots`}> | ||
Take Snapshot | ||
</MenuItem> | ||
<MenuItem component={Link} to={`/collections/${collection.name}/visualize`}> | ||
Visualize | ||
</MenuItem> | ||
<MenuItem component={Link} to={`/collections/${collection.name}/graph`}> | ||
Graph | ||
</MenuItem> | ||
<MenuItem onClick={() => setOpenDeleteDialog(true)} sx={{ color: theme.palette.error.main }}> | ||
Delete | ||
</MenuItem> | ||
</ActionsMenu> | ||
<DeleteDialog | ||
open={openDeleteDialog} | ||
setOpen={setOpenDeleteDialog} | ||
collectionName={collection.name} | ||
getCollectionsCall={getCollectionsCall} | ||
/> | ||
</TableCell> | ||
</TableRow> | ||
); | ||
}; | ||
|
||
CollectionTableRow.propTypes = { | ||
collection: PropTypes.object.isRequired, | ||
getCollectionsCall: PropTypes.func.isRequired, | ||
}; | ||
|
||
const HeaderTableCell = styled(TableCell)` | ||
font-weight: bold; | ||
`; | ||
|
||
const CollectionsList = ({ collections, getCollectionsCall }) => { | ||
return ( | ||
<TableContainer> | ||
<TableWithGaps aria-label="simple table"> | ||
<TableHeadWithGaps> | ||
<TableRow> | ||
<HeaderTableCell width="25%">Name</HeaderTableCell> | ||
<HeaderTableCell width="12%">Status</HeaderTableCell> | ||
<HeaderTableCell width="12%" align="center"> | ||
Points (Approx) | ||
</HeaderTableCell> | ||
<HeaderTableCell width="12%" align="center"> | ||
Segments | ||
</HeaderTableCell> | ||
<HeaderTableCell width="12%" align="center"> | ||
Shards | ||
</HeaderTableCell> | ||
<HeaderTableCell width="20%" align="center"> | ||
Vectors Configuration | ||
<br /> | ||
(Name, Size, Distance) | ||
</HeaderTableCell> | ||
<HeaderTableCell width="7%" align="right"> | ||
Actions | ||
</HeaderTableCell> | ||
</TableRow> | ||
</TableHeadWithGaps> | ||
<TableBodyWithGaps> | ||
{collections.length > 0 && | ||
collections.map((collection) => ( | ||
<CollectionTableRow | ||
key={collection.name} | ||
collection={collection} | ||
getCollectionsCall={getCollectionsCall} | ||
/> | ||
))} | ||
</TableBodyWithGaps> | ||
</TableWithGaps> | ||
</TableContainer> | ||
); | ||
}; | ||
|
||
CollectionsList.propTypes = { | ||
collections: PropTypes.array.isRequired, | ||
getCollectionsCall: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default CollectionsList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import { MemoryRouter } from 'react-router-dom'; | ||
import CollectionsList from './CollectionsList'; | ||
import { describe, it, expect } from 'vitest'; | ||
|
||
vi.mock('../../context/client-context', () => ({ | ||
useClient: () => ({ | ||
client: { | ||
deleteCollection: vi.fn().mockResolvedValue({}), | ||
}, | ||
}), | ||
})); | ||
|
||
const COLLECTIONS = [ | ||
{ | ||
name: 'Collection 1', | ||
status: 'green', | ||
points_count: 1000, | ||
segments_count: 10, | ||
config: { | ||
params: { | ||
shard_number: 2, | ||
vectors: { | ||
size: 128, | ||
distance: 'cosine', | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: 'Collection 2', | ||
status: 'yellow', | ||
points_count: 500, | ||
segments_count: 5, | ||
config: { | ||
params: { | ||
shard_number: 1, | ||
vectors: { | ||
vector1: { | ||
size: 64, | ||
distance: 'euclidean', | ||
}, | ||
vector2: { | ||
size: 32, | ||
distance: 'manhattan', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
describe('CollectionsList', () => { | ||
it('should render CollectionsList with given data', () => { | ||
render( | ||
<MemoryRouter> | ||
<CollectionsList collections={COLLECTIONS} getCollectionsCall={() => {}} /> | ||
</MemoryRouter> | ||
); | ||
expect(screen.getByText('Collection 1')).toBeInTheDocument(); | ||
expect(screen.getByText('Collection 2')).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render CollectionTableRow with given data', () => { | ||
render( | ||
<MemoryRouter> | ||
<CollectionsList collections={COLLECTIONS} getCollectionsCall={() => {}} /> | ||
</MemoryRouter> | ||
); | ||
expect(screen.getByText('green')).toBeInTheDocument(); | ||
expect(screen.getByText('yellow')).toBeInTheDocument(); | ||
expect(screen.getByText('1000')).toBeInTheDocument(); | ||
expect(screen.getByText('500')).toBeInTheDocument(); | ||
expect(screen.getByText('128')).toBeInTheDocument(); | ||
expect(screen.getByText('cosine')).toBeInTheDocument(); | ||
expect(screen.getByText('64')).toBeInTheDocument(); | ||
expect(screen.getByText('euclidean')).toBeInTheDocument(); | ||
expect(screen.getByText('32')).toBeInTheDocument(); | ||
expect(screen.getByText('manhattan')).toBeInTheDocument(); | ||
}); | ||
}); |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Card, Grid } from '@mui/material'; | ||
|
||
const VectorsConfigChip = ({ collectionConfigParams, sx = {} }) => { | ||
return ( | ||
<> | ||
{collectionConfigParams.vectors.size && ( | ||
<Grid container component={Card} variant={'heading'} p={1} sx={{ ...sx }}> | ||
<Grid item align="center" mr={2}> | ||
default | ||
</Grid> | ||
<Grid item align="center" mr={2}> | ||
{collectionConfigParams.vectors.size} | ||
</Grid> | ||
<Grid item align="center" mr={2}> | ||
{collectionConfigParams.vectors.distance} | ||
</Grid> | ||
{/* model is not always present */} | ||
{collectionConfigParams.vectors.model && ( | ||
<Grid item align="center"> | ||
{collectionConfigParams.vectors.model} | ||
</Grid> | ||
)} | ||
</Grid> | ||
)} | ||
{!collectionConfigParams.vectors.size && | ||
Object.keys(collectionConfigParams.vectors).map((vector) => ( | ||
<Grid key={vector} container component={Card} variant={'heading'} p={1} sx={{ ...sx }}> | ||
<Grid item align="center" mr={2}> | ||
{vector} | ||
</Grid> | ||
<Grid item align="center" mr={2}> | ||
{collectionConfigParams.vectors[vector].size} | ||
</Grid> | ||
<Grid item align="center" mr={2}> | ||
{collectionConfigParams.vectors[vector].distance} | ||
</Grid> | ||
{/* model is not always present */} | ||
{collectionConfigParams.vectors[vector].model && ( | ||
<Grid item align="center"> | ||
{collectionConfigParams.vectors[vector].model} | ||
</Grid> | ||
)} | ||
</Grid> | ||
))} | ||
{collectionConfigParams.sparse_vectors && | ||
Object.keys(collectionConfigParams.sparse_vectors).map((vector) => ( | ||
<Grid key={vector} container component={Card} variant={'heading'} p={1} sx={{ ...sx }}> | ||
<Grid item align="center" mr={2}> | ||
{vector} | ||
</Grid> | ||
<Grid item align="center" mr={2}> | ||
Sparse | ||
</Grid> | ||
<Grid item align="center" mr={2}></Grid> | ||
</Grid> | ||
))} | ||
</> | ||
); | ||
}; | ||
|
||
VectorsConfigChip.propTypes = { | ||
collectionConfigParams: PropTypes.object.isRequired, | ||
sx: PropTypes.object, | ||
}; | ||
|
||
export default VectorsConfigChip; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.