DeleteVectorBucketModal.tsx106 lines · main
1import { useState } from 'react'
2import { toast } from 'sonner'
3
4import { useS3VectorsWrapperInstance } from './useS3VectorsWrapperInstance'
5import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
6import { useFDWDeleteMutation } from '@/data/fdw/fdw-delete-mutation'
7import { useVectorBucketDeleteMutation } from '@/data/storage/vector-bucket-delete-mutation'
8import { deleteVectorBucketIndex } from '@/data/storage/vector-bucket-index-delete-mutation'
9import { useVectorBucketsIndexesQuery } from '@/data/storage/vector-buckets-indexes-query'
10import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
11
12export interface DeleteVectorBucketModalProps {
13 visible: boolean
14 bucketName?: string
15 onCancel: () => void
16 onSuccess: () => void
17}
18
19export const DeleteVectorBucketModal = ({
20 visible,
21 bucketName,
22 onCancel,
23 onSuccess,
24}: DeleteVectorBucketModalProps) => {
25 const { data: project } = useSelectedProjectQuery()
26 // Has to be a state because we're using a promise.all to delete the indexes
27 const [isDeletingIndexes, setIsDeletingIndexes] = useState(false)
28
29 const { data: vectorBucketWrapper, meta: wrapperMeta } = useS3VectorsWrapperInstance({
30 bucketId: bucketName,
31 })
32
33 const { mutate: deleteFDW } = useFDWDeleteMutation()
34
35 const { mutateAsync: deleteBucket, isPending: isDeletingBucket } = useVectorBucketDeleteMutation({
36 onSuccess: async () => {
37 toast.success(`Bucket "${bucketName}" deleted successfully`)
38 if (vectorBucketWrapper) {
39 deleteFDW({
40 projectRef: project?.ref,
41 connectionString: project?.connectionString,
42 wrapper: vectorBucketWrapper,
43 wrapperMeta: wrapperMeta!,
44 })
45 }
46 onSuccess()
47 },
48 })
49
50 const { data: { indexes = [] } = {}, isPending: isLoadingIndexes } = useVectorBucketsIndexesQuery(
51 {
52 projectRef: project?.ref,
53 vectorBucketName: bucketName,
54 }
55 )
56
57 const onConfirmDelete = async () => {
58 if (!project?.ref) return console.error('Project ref is required')
59 if (!bucketName) return console.error('No bucket is selected')
60
61 try {
62 setIsDeletingIndexes(true)
63 // delete all indexes from the bucket first
64 const promises = indexes.map((index) =>
65 deleteVectorBucketIndex({
66 projectRef: project?.ref,
67 bucketName: bucketName,
68 indexName: index.indexName,
69 })
70 )
71 await Promise.all(promises)
72
73 await deleteBucket({ projectRef: project?.ref, bucketName })
74 } catch (error) {
75 toast.error(
76 `Failed to delete bucket: ${error instanceof Error ? error.message : 'Unknown error'}`
77 )
78 } finally {
79 setIsDeletingIndexes(false)
80 }
81 }
82
83 return (
84 <TextConfirmModal
85 visible={visible}
86 size="medium"
87 variant="destructive"
88 title={`Delete bucket “${bucketName}”`}
89 loading={isDeletingBucket || isDeletingIndexes || isLoadingIndexes}
90 confirmPlaceholder="Type bucket name"
91 confirmString={bucketName ?? ''}
92 confirmLabel="Delete bucket"
93 onCancel={onCancel}
94 onConfirm={onConfirmDelete}
95 alert={{
96 title: 'You cannot recover this bucket once deleted',
97 description: 'This action cannot be undone',
98 }}
99 >
100 <p className="text-sm">
101 Your bucket <span className="font-bold text-foreground">{bucketName}</span> and all of its
102 contents will be permanently deleted.
103 </p>
104 </TextConfirmModal>
105 )
106}