| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- import { useParams } from 'common'
- import { VectorBucket as VectorBucketIcon } from 'icons'
- import { ChevronRight, Search } from 'lucide-react'
- import { useRouter } from 'next/navigation'
- import { parseAsBoolean, useQueryState } from 'nuqs'
- import { useState } from 'react'
- import { Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
- import { Input } from 'ui-patterns/DataInputs/Input'
- import { PageContainer } from 'ui-patterns/PageContainer'
- import { PageSection, PageSectionContent, PageSectionTitle } from 'ui-patterns/PageSection'
- import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
- import { TimestampInfo } from 'ui-patterns/TimestampInfo'
- import { EmptyBucketState } from '../EmptyBucketState'
- import { CreateBucketButton } from '../NewBucketButton'
- import { CreateVectorBucketDialog } from './CreateVectorBucketDialog'
- import AlertError from '@/components/ui/AlertError'
- import { AlphaNotice } from '@/components/ui/AlphaNotice'
- import { useVectorBucketsQuery } from '@/data/storage/vector-buckets-query'
- import { createNavigationHandler } from '@/lib/navigation'
- /**
- * [Joshen] Low-priority refactor: We should use a virtualized table here as per how we do it
- * for the files buckets for consistency. Not pressing, just an optimization area.
- */
- export const VectorsBuckets = () => {
- const { ref: projectRef } = useParams()
- const router = useRouter()
- const [filterString, setFilterString] = useState('')
- const [visible, setVisible] = useQueryState(
- 'new',
- parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
- )
- const {
- data,
- error: bucketsError,
- isError: isErrorBuckets,
- isPending: isLoadingBuckets,
- isSuccess: isSuccessBuckets,
- } = useVectorBucketsQuery({ projectRef })
- const bucketsList = data?.vectorBuckets ?? []
- const filteredBuckets =
- filterString.length === 0
- ? bucketsList
- : bucketsList.filter((bucket) =>
- bucket.vectorBucketName.toLowerCase().includes(filterString.toLowerCase())
- )
- return (
- <>
- <PageContainer>
- <PageSection>
- <PageSectionContent className="flex flex-col gap-y-8">
- <AlphaNotice
- entity="Vector buckets"
- feedbackUrl="https://github.com/orgs/briven/discussions/40815"
- />
- {isLoadingBuckets && <GenericSkeletonLoader />}
- {isErrorBuckets && (
- <AlertError error={bucketsError} subject="Failed to retrieve vector buckets" />
- )}
- {isSuccessBuckets && (
- <>
- {bucketsList.length === 0 ? (
- <EmptyBucketState bucketType="vectors" onCreateBucket={() => setVisible(true)} />
- ) : (
- <div className="flex flex-col gap-y-4">
- <div className="py-0">
- <PageSectionTitle>Buckets</PageSectionTitle>
- </div>
- <div className="flex grow justify-between gap-x-2 items-center">
- <Input
- size="tiny"
- className="grow lg:grow-0 w-52"
- placeholder="Search for a bucket"
- value={filterString}
- onChange={(e) => setFilterString(e.target.value)}
- icon={<Search />}
- />
- <CreateBucketButton onClick={() => setVisible(true)} />
- </div>
- {isLoadingBuckets ? (
- <GenericSkeletonLoader />
- ) : (
- <Card>
- <Table>
- <TableHeader>
- <TableRow>
- {filteredBuckets.length > 0 && (
- <TableHead className="w-2 pr-1">
- <span className="sr-only">Icon</span>
- </TableHead>
- )}
- <TableHead>Name</TableHead>
- <TableHead>Created at</TableHead>
- <TableHead>
- <span className="sr-only">Actions</span>
- </TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {filteredBuckets.length === 0 && filterString.length > 0 && (
- <TableRow className="[&>td]:hover:bg-inherit">
- <TableCell colSpan={3}>
- <p className="text-sm text-foreground">No results found</p>
- <p className="text-sm text-foreground-lighter">
- Your search for "{filterString}" did not return any results
- </p>
- </TableCell>
- </TableRow>
- )}
- {filteredBuckets.map((bucket, idx: number) => {
- const id = `bucket-${idx}`
- const name = bucket.vectorBucketName
- // the creation time is in seconds, convert it to milliseconds
- const created = +bucket.creationTime * 1000
- const handleBucketNavigation = createNavigationHandler(
- `/project/${projectRef}/storage/vectors/buckets/${encodeURIComponent(name)}`,
- router
- )
- return (
- <TableRow
- key={id}
- className="relative cursor-pointer h-16 inset-focus"
- onClick={handleBucketNavigation}
- onAuxClick={handleBucketNavigation}
- onKeyDown={handleBucketNavigation}
- tabIndex={0}
- >
- <TableCell className="w-2 pr-1">
- <VectorBucketIcon size={16} className="text-foreground-muted" />
- </TableCell>
- <TableCell>
- <p className="whitespace-nowrap max-w-[512px] truncate">
- {name}
- </p>
- </TableCell>
- <TableCell>
- <p className="text-foreground-light">
- <TimestampInfo
- utcTimestamp={created}
- className="text-sm text-foreground-light"
- />
- </p>
- </TableCell>
- <TableCell>
- <div className="flex justify-end items-center h-full">
- <ChevronRight
- size={14}
- className="text-foreground-muted/60"
- />
- </div>
- </TableCell>
- </TableRow>
- )
- })}
- </TableBody>
- </Table>
- </Card>
- )}
- </div>
- )}
- </>
- )}
- </PageSectionContent>
- </PageSection>
- </PageContainer>
- <CreateVectorBucketDialog visible={visible} setVisible={setVisible} />
- </>
- )
- }
|