DeleteSnippetsModal.tsx98 lines · main
1import { useParams } from 'common'
2import { useRouter } from 'next/router'
3import { toast } from 'sonner'
4import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
5
6import { useContentDeleteMutation } from '@/data/content/content-delete-mutation'
7import { Snippet } from '@/data/content/sql-folders-query'
8import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2'
9import { createTabId, useTabsStateSnapshot } from '@/state/tabs'
10
11export const DeleteSnippetsModal = ({
12 snippets,
13 visible,
14 onClose,
15}: {
16 visible: boolean
17 snippets: Snippet[]
18 onClose: () => void
19}) => {
20 const router = useRouter()
21 const { ref: projectRef, id } = useParams()
22 const tabs = useTabsStateSnapshot()
23 const snapV2 = useSqlEditorV2StateSnapshot()
24
25 const postDeleteCleanup = (ids: string[]) => {
26 if (!!id && ids.includes(id)) {
27 const openedSQLTabs = tabs.openTabs.filter((x) => x.startsWith('sql-') && !x.includes(id))
28 if (openedSQLTabs.length > 0) {
29 // [Joshen] For simplicity, just opening the first tab for now
30 const firstTabId = openedSQLTabs[0].split('sql-')[1]
31 router.push(`/project/${projectRef}/sql/${firstTabId}`)
32 } else {
33 router.push(`/project/${projectRef}/sql/new`)
34 }
35 }
36
37 if (ids.length > 0) ids.forEach((id) => snapV2.removeSnippet(id))
38 }
39
40 const { mutate: deleteContent, isPending: isDeleting } = useContentDeleteMutation({
41 onSuccess: (data) => {
42 toast.success(
43 `Successfully deleted ${snippets.length.toLocaleString()} quer${snippets.length > 1 ? 'ies' : 'y'}`
44 )
45
46 // Update Tabs state - currently unknown how to differentiate between sql and non-sql content
47 // so we're just deleting all tabs for with matching IDs
48 const tabIds = data.map((id) => createTabId('sql', { id }))
49 tabs.removeTabs(tabIds)
50
51 postDeleteCleanup(data)
52 onClose()
53 },
54 onError: (error, data) => {
55 if (error.message.includes('Contents not found')) {
56 postDeleteCleanup(data.ids)
57 onClose()
58 } else {
59 toast.error(`Failed to delete query: ${error.message}`)
60 }
61 },
62 })
63
64 const onConfirmDelete = () => {
65 if (!projectRef) return console.error('Project ref is required')
66 deleteContent({ projectRef, ids: snippets.map((x) => x.id) })
67 }
68
69 return (
70 <ConfirmationModal
71 size="small"
72 visible={visible}
73 title={`Confirm to delete ${snippets.length === 1 ? 'query' : `${snippets.length.toLocaleString()} quer${snippets.length > 1 ? 'ies' : 'y'}`}`}
74 confirmLabel={`Delete ${snippets.length.toLocaleString()} quer${snippets.length > 1 ? 'ies' : 'y'}`}
75 confirmLabelLoading="Deleting query"
76 loading={isDeleting}
77 variant="destructive"
78 onCancel={onClose}
79 onConfirm={onConfirmDelete}
80 alert={
81 (snippets[0]?.visibility as unknown as string) === 'project'
82 ? {
83 title: 'This SQL snippet will be lost forever',
84 description:
85 'Deleting this query will remove it for all members of the project team.',
86 }
87 : undefined
88 }
89 >
90 <p className="text-sm">
91 This action cannot be undone.{' '}
92 {snippets.length === 1
93 ? `Are you sure you want to delete '${snippets[0]?.name}'?`
94 : `Are you sure you want to delete the selected ${snippets.length} quer${snippets.length > 1 ? 'ies' : 'y'}?`}
95 </p>
96 </ConfirmationModal>
97 )
98}