diff --git a/package-lock.json b/package-lock.json
index 9ae83f26..0558f241 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2972,9 +2972,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001578",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001578.tgz",
- "integrity": "sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg==",
+ "version": "1.0.30001651",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
+ "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==",
"funding": [
{
"type": "opencollective",
@@ -10925,9 +10925,9 @@
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
},
"caniuse-lite": {
- "version": "1.0.30001578",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001578.tgz",
- "integrity": "sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg=="
+ "version": "1.0.30001651",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz",
+ "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg=="
},
"canvas-color-tracker": {
"version": "1.2.1",
diff --git a/src/components/Collections/CollectionsList.jsx b/src/components/Collections/CollectionsList.jsx
new file mode 100644
index 00000000..54033c04
--- /dev/null
+++ b/src/components/Collections/CollectionsList.jsx
@@ -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 (
+
+
+
+ {collection.name}
+
+
+
+
+
+ {collection.status}
+
+
+
+
+ {collection.points_count}
+
+
+
+ {collection.segments_count}
+
+
+ {collection.config.params.shard_number}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+CollectionTableRow.propTypes = {
+ collection: PropTypes.object.isRequired,
+ getCollectionsCall: PropTypes.func.isRequired,
+};
+
+const HeaderTableCell = styled(TableCell)`
+ font-weight: bold;
+`;
+
+const CollectionsList = ({ collections, getCollectionsCall }) => {
+ return (
+
+
+
+
+ Name
+ Status
+
+ Points (Approx)
+
+
+ Segments
+
+
+ Shards
+
+
+ Vectors Configuration
+
+ (Name, Size, Distance)
+
+
+ Actions
+
+
+
+
+ {collections.length > 0 &&
+ collections.map((collection) => (
+
+ ))}
+
+
+
+ );
+};
+
+CollectionsList.propTypes = {
+ collections: PropTypes.array.isRequired,
+ getCollectionsCall: PropTypes.func.isRequired,
+};
+
+export default CollectionsList;
diff --git a/src/components/Collections/SearchBar.jsx b/src/components/Collections/SearchBar.jsx
index 65929f1a..ce195ea0 100644
--- a/src/components/Collections/SearchBar.jsx
+++ b/src/components/Collections/SearchBar.jsx
@@ -26,6 +26,7 @@ function InputWithIcon({ value, setValue, actions }) {
}
+ size={'small'}
sx={{ maxWidth: 500 }}
/>
diff --git a/src/components/Collections/collectionList.test.jsx b/src/components/Collections/collectionList.test.jsx
new file mode 100644
index 00000000..1e19a372
--- /dev/null
+++ b/src/components/Collections/collectionList.test.jsx
@@ -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(
+
+ {}} />
+
+ );
+ expect(screen.getByText('Collection 1')).toBeInTheDocument();
+ expect(screen.getByText('Collection 2')).toBeInTheDocument();
+ });
+
+ it('should render CollectionTableRow with given data', () => {
+ render(
+
+ {}} />
+
+ );
+ 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();
+ });
+});
diff --git a/src/components/InfoBanner.jsx b/src/components/Common/InfoBanner.jsx
similarity index 100%
rename from src/components/InfoBanner.jsx
rename to src/components/Common/InfoBanner.jsx
diff --git a/src/components/Common/VectorsConfigChip.jsx b/src/components/Common/VectorsConfigChip.jsx
new file mode 100644
index 00000000..a10c6fd1
--- /dev/null
+++ b/src/components/Common/VectorsConfigChip.jsx
@@ -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 && (
+
+
+ default
+
+
+ {collectionConfigParams.vectors.size}
+
+
+ {collectionConfigParams.vectors.distance}
+
+ {/* model is not always present */}
+ {collectionConfigParams.vectors.model && (
+
+ {collectionConfigParams.vectors.model}
+
+ )}
+
+ )}
+ {!collectionConfigParams.vectors.size &&
+ Object.keys(collectionConfigParams.vectors).map((vector) => (
+
+
+ {vector}
+
+
+ {collectionConfigParams.vectors[vector].size}
+
+
+ {collectionConfigParams.vectors[vector].distance}
+
+ {/* model is not always present */}
+ {collectionConfigParams.vectors[vector].model && (
+
+ {collectionConfigParams.vectors[vector].model}
+
+ )}
+
+ ))}
+ {collectionConfigParams.sparse_vectors &&
+ Object.keys(collectionConfigParams.sparse_vectors).map((vector) => (
+
+
+ {vector}
+
+
+ Sparse
+
+
+
+ ))}
+ >
+ );
+};
+
+VectorsConfigChip.propTypes = {
+ collectionConfigParams: PropTypes.object.isRequired,
+ sx: PropTypes.object,
+};
+
+export default VectorsConfigChip;
diff --git a/src/components/Datasets/DatasetsTableRow.jsx b/src/components/Datasets/DatasetsTableRow.jsx
index 43051799..0972e035 100644
--- a/src/components/Datasets/DatasetsTableRow.jsx
+++ b/src/components/Datasets/DatasetsTableRow.jsx
@@ -2,9 +2,10 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types';
import prettyBytes from 'pretty-bytes';
import { useTheme } from '@mui/material/styles';
-import { Box, Card, CircularProgress, Grid, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material';
+import { Box, CircularProgress, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material';
import { Download, FolderZip } from '@mui/icons-material';
import ImportDatasetDialog from './ImportDatasetDialog';
+import VectorsConfigChip from '../Common/VectorsConfigChip';
export const DatasetsTableRow = ({ dataset, importDataset }) => {
const theme = useTheme();
@@ -63,39 +64,7 @@ export const DatasetsTableRow = ({ dataset, importDataset }) => {
{prettyBytes(dataset.size)}
- {dataset.vectors.size && (
-
-
- default
-
-
- {dataset.vectors.size}
-
-
- {dataset.vectors.distance}
-
-
- {dataset.vectors.model}
-
-
- )}
- {!dataset.vectors.size &&
- Object.keys(dataset.vectors).map((vector) => (
-
-
- {vector}
-
-
- {dataset.vectors[vector].size}
-
-
- {dataset.vectors[vector].distance}
-
-
- {dataset.vectors[vector].model}
-
-
- ))}
+
{dataset.vectorCount}
diff --git a/src/components/Snapshots/SnapshotsTab.jsx b/src/components/Snapshots/SnapshotsTab.jsx
index 6eef126b..aa3d2a1c 100644
--- a/src/components/Snapshots/SnapshotsTab.jsx
+++ b/src/components/Snapshots/SnapshotsTab.jsx
@@ -8,7 +8,7 @@ import PhotoCamera from '@mui/icons-material/PhotoCamera';
import { TableWithGaps, TableHeadWithGaps, TableBodyWithGaps } from '../Common/TableWithGaps';
import { SnapshotsTableRow } from './SnapshotsTableRow';
import { pumpFile, updateProgress } from '../../common/utils';
-import InfoBanner from '../InfoBanner';
+import InfoBanner from '../Common/InfoBanner';
export const SnapshotsTab = ({ collectionName }) => {
const { client: qdrantClient } = useClient();
diff --git a/src/components/Snapshots/SnapshotsUpload.jsx b/src/components/Snapshots/SnapshotsUpload.jsx
index de31bef9..7636e9cf 100644
--- a/src/components/Snapshots/SnapshotsUpload.jsx
+++ b/src/components/Snapshots/SnapshotsUpload.jsx
@@ -4,7 +4,7 @@ import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';
-import IconButton from '@mui/material/IconButton';
+import Button from '@mui/material/Button';
import UploadFile from '@mui/icons-material/UploadFile';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
@@ -28,9 +28,9 @@ export const SnapshotsUpload = ({ onComplete, sx }) => {
return (
-
-
-
+ }>
+ Upload snapshot
+