index.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { useParams } from 'common'
  2. import { VectorBucket as VectorBucketIcon } from 'icons'
  3. import { ChevronRight, Search } from 'lucide-react'
  4. import { useRouter } from 'next/navigation'
  5. import { parseAsBoolean, useQueryState } from 'nuqs'
  6. import { useState } from 'react'
  7. import { Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
  8. import { Input } from 'ui-patterns/DataInputs/Input'
  9. import { PageContainer } from 'ui-patterns/PageContainer'
  10. import { PageSection, PageSectionContent, PageSectionTitle } from 'ui-patterns/PageSection'
  11. import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
  12. import { TimestampInfo } from 'ui-patterns/TimestampInfo'
  13. import { EmptyBucketState } from '../EmptyBucketState'
  14. import { CreateBucketButton } from '../NewBucketButton'
  15. import { CreateVectorBucketDialog } from './CreateVectorBucketDialog'
  16. import AlertError from '@/components/ui/AlertError'
  17. import { AlphaNotice } from '@/components/ui/AlphaNotice'
  18. import { useVectorBucketsQuery } from '@/data/storage/vector-buckets-query'
  19. import { createNavigationHandler } from '@/lib/navigation'
  20. /**
  21. * [Joshen] Low-priority refactor: We should use a virtualized table here as per how we do it
  22. * for the files buckets for consistency. Not pressing, just an optimization area.
  23. */
  24. export const VectorsBuckets = () => {
  25. const { ref: projectRef } = useParams()
  26. const router = useRouter()
  27. const [filterString, setFilterString] = useState('')
  28. const [visible, setVisible] = useQueryState(
  29. 'new',
  30. parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
  31. )
  32. const {
  33. data,
  34. error: bucketsError,
  35. isError: isErrorBuckets,
  36. isPending: isLoadingBuckets,
  37. isSuccess: isSuccessBuckets,
  38. } = useVectorBucketsQuery({ projectRef })
  39. const bucketsList = data?.vectorBuckets ?? []
  40. const filteredBuckets =
  41. filterString.length === 0
  42. ? bucketsList
  43. : bucketsList.filter((bucket) =>
  44. bucket.vectorBucketName.toLowerCase().includes(filterString.toLowerCase())
  45. )
  46. return (
  47. <>
  48. <PageContainer>
  49. <PageSection>
  50. <PageSectionContent className="flex flex-col gap-y-8">
  51. <AlphaNotice
  52. entity="Vector buckets"
  53. feedbackUrl="https://github.com/orgs/briven/discussions/40815"
  54. />
  55. {isLoadingBuckets && <GenericSkeletonLoader />}
  56. {isErrorBuckets && (
  57. <AlertError error={bucketsError} subject="Failed to retrieve vector buckets" />
  58. )}
  59. {isSuccessBuckets && (
  60. <>
  61. {bucketsList.length === 0 ? (
  62. <EmptyBucketState bucketType="vectors" onCreateBucket={() => setVisible(true)} />
  63. ) : (
  64. <div className="flex flex-col gap-y-4">
  65. <div className="py-0">
  66. <PageSectionTitle>Buckets</PageSectionTitle>
  67. </div>
  68. <div className="flex grow justify-between gap-x-2 items-center">
  69. <Input
  70. size="tiny"
  71. className="grow lg:grow-0 w-52"
  72. placeholder="Search for a bucket"
  73. value={filterString}
  74. onChange={(e) => setFilterString(e.target.value)}
  75. icon={<Search />}
  76. />
  77. <CreateBucketButton onClick={() => setVisible(true)} />
  78. </div>
  79. {isLoadingBuckets ? (
  80. <GenericSkeletonLoader />
  81. ) : (
  82. <Card>
  83. <Table>
  84. <TableHeader>
  85. <TableRow>
  86. {filteredBuckets.length > 0 && (
  87. <TableHead className="w-2 pr-1">
  88. <span className="sr-only">Icon</span>
  89. </TableHead>
  90. )}
  91. <TableHead>Name</TableHead>
  92. <TableHead>Created at</TableHead>
  93. <TableHead>
  94. <span className="sr-only">Actions</span>
  95. </TableHead>
  96. </TableRow>
  97. </TableHeader>
  98. <TableBody>
  99. {filteredBuckets.length === 0 && filterString.length > 0 && (
  100. <TableRow className="[&>td]:hover:bg-inherit">
  101. <TableCell colSpan={3}>
  102. <p className="text-sm text-foreground">No results found</p>
  103. <p className="text-sm text-foreground-lighter">
  104. Your search for "{filterString}" did not return any results
  105. </p>
  106. </TableCell>
  107. </TableRow>
  108. )}
  109. {filteredBuckets.map((bucket, idx: number) => {
  110. const id = `bucket-${idx}`
  111. const name = bucket.vectorBucketName
  112. // the creation time is in seconds, convert it to milliseconds
  113. const created = +bucket.creationTime * 1000
  114. const handleBucketNavigation = createNavigationHandler(
  115. `/project/${projectRef}/storage/vectors/buckets/${encodeURIComponent(name)}`,
  116. router
  117. )
  118. return (
  119. <TableRow
  120. key={id}
  121. className="relative cursor-pointer h-16 inset-focus"
  122. onClick={handleBucketNavigation}
  123. onAuxClick={handleBucketNavigation}
  124. onKeyDown={handleBucketNavigation}
  125. tabIndex={0}
  126. >
  127. <TableCell className="w-2 pr-1">
  128. <VectorBucketIcon size={16} className="text-foreground-muted" />
  129. </TableCell>
  130. <TableCell>
  131. <p className="whitespace-nowrap max-w-[512px] truncate">
  132. {name}
  133. </p>
  134. </TableCell>
  135. <TableCell>
  136. <p className="text-foreground-light">
  137. <TimestampInfo
  138. utcTimestamp={created}
  139. className="text-sm text-foreground-light"
  140. />
  141. </p>
  142. </TableCell>
  143. <TableCell>
  144. <div className="flex justify-end items-center h-full">
  145. <ChevronRight
  146. size={14}
  147. className="text-foreground-muted/60"
  148. />
  149. </div>
  150. </TableCell>
  151. </TableRow>
  152. )
  153. })}
  154. </TableBody>
  155. </Table>
  156. </Card>
  157. )}
  158. </div>
  159. )}
  160. </>
  161. )}
  162. </PageSectionContent>
  163. </PageSection>
  164. </PageContainer>
  165. <CreateVectorBucketDialog visible={visible} setVisible={setVisible} />
  166. </>
  167. )
  168. }