DeleteSnippetsModal.tsx98 lines · main
| 1 | import { useParams } from 'common' |
| 2 | import { useRouter } from 'next/router' |
| 3 | import { toast } from 'sonner' |
| 4 | import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' |
| 5 | |
| 6 | import { useContentDeleteMutation } from '@/data/content/content-delete-mutation' |
| 7 | import { Snippet } from '@/data/content/sql-folders-query' |
| 8 | import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2' |
| 9 | import { createTabId, useTabsStateSnapshot } from '@/state/tabs' |
| 10 | |
| 11 | export 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 | } |