DeleteSnippetsModal.tsx 3.4 KB

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