Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useTableFilter } from '@/components/grid/hooks/useTableFilter'
import type { SupaRow } from '@/components/grid/types'
import { useDatabaseColumnDeleteMutation } from '@/data/database-columns/database-column-delete-mutation'
import { TableLike } from '@/data/table-editor/table-editor-types'
import { useMaterializedViewDeleteMutation } from '@/data/materialized-views/materialized-view-delete-mutation'
import { Entity } from '@/data/table-editor/table-editor-types'
import { useTableRowDeleteAllMutation } from '@/data/table-rows/table-row-delete-all-mutation'
import { useTableRowDeleteMutation } from '@/data/table-rows/table-row-delete-mutation'
import { useTableRowTruncateMutation } from '@/data/table-rows/table-row-truncate-mutation'
import { useTableDeleteMutation } from '@/data/tables/table-delete-mutation'
import { useViewDeleteMutation } from '@/data/views/view-delete-mutation'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { useGetImpersonatedRoleState } from '@/state/role-impersonation-state'
import { useTableEditorStateSnapshot } from '@/state/table-editor'

export type DeleteConfirmationDialogsProps = {
selectedTable?: TableLike
selectedTable?: Entity
onTableDeleted?: () => void
}

Expand Down Expand Up @@ -69,6 +71,32 @@ const DeleteConfirmationDialogs = ({
},
})

const { mutate: deleteView } = useViewDeleteMutation({
onSuccess: async () => {
toast.success(`Successfully deleted view "${selectedTable?.name}"`)
onTableDeleted?.()
},
onError: (error) => {
toast.error(`Failed to delete ${selectedTable?.name}: ${error.message}`)
},
onSettled: () => {
snap.closeConfirmationDialog()
},
})

const { mutate: deleteMaterializedView } = useMaterializedViewDeleteMutation({
onSuccess: async () => {
toast.success(`Successfully deleted materialized view "${selectedTable?.name}"`)
onTableDeleted?.()
},
onError: (error) => {
toast.error(`Failed to delete ${selectedTable?.name}: ${error.message}`)
},
onSettled: () => {
snap.closeConfirmationDialog()
},
})

const { mutate: deleteRows, isPending: isDeletingRows } = useTableRowDeleteMutation({
onSuccess: () => {
if (snap.confirmationDialog?.type === 'row') {
Expand Down Expand Up @@ -121,7 +149,10 @@ const DeleteConfirmationDialogs = ({
: 0

const isDeleteWithCascade =
snap.confirmationDialog?.type === 'column' || snap.confirmationDialog?.type === 'table'
snap.confirmationDialog?.type === 'column' ||
snap.confirmationDialog?.type === 'table' ||
snap.confirmationDialog?.type === 'view' ||
snap.confirmationDialog?.type === 'materialized-view'
? snap.confirmationDialog.isDeleteWithCascade
: false

Expand Down Expand Up @@ -156,6 +187,34 @@ const DeleteConfirmationDialogs = ({
})
}

const onConfirmDeleteView = async () => {
if (snap.confirmationDialog?.type !== 'view') return
if (!project || !selectedTable) return

deleteView({
projectRef: project.ref,
connectionString: project.connectionString,
id: selectedTable.id,
name: selectedTable.name,
schema: selectedTable.schema,
cascade: isDeleteWithCascade,
})
}

const onConfirmDeleteMaterializedView = async () => {
if (snap.confirmationDialog?.type !== 'materialized-view') return
if (!project || !selectedTable) return

deleteMaterializedView({
projectRef: project.ref,
connectionString: project.connectionString,
id: selectedTable.id,
name: selectedTable.name,
schema: selectedTable.schema,
cascade: isDeleteWithCascade,
})
}

const getImpersonatedRoleState = useGetImpersonatedRoleState()

const onConfirmDeleteRow = async () => {
Expand Down Expand Up @@ -320,6 +379,26 @@ const DeleteConfirmationDialogs = ({
</div>
</ConfirmationModal>

<DropEntityConfirmationModal
visible={snap.confirmationDialog?.type === 'view'}
entityLabel="view"
entityName={selectedTable?.name}
isDeleteWithCascade={isDeleteWithCascade}
onToggleCascade={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
onCancel={() => snap.closeConfirmationDialog()}
onConfirm={onConfirmDeleteView}
/>

<DropEntityConfirmationModal
visible={snap.confirmationDialog?.type === 'materialized-view'}
entityLabel="materialized view"
entityName={selectedTable?.name}
isDeleteWithCascade={isDeleteWithCascade}
onToggleCascade={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
onCancel={() => snap.closeConfirmationDialog()}
onConfirm={onConfirmDeleteMaterializedView}
/>

<ConfirmationModal
variant={'destructive'}
size="small"
Expand Down Expand Up @@ -352,3 +431,85 @@ const DeleteConfirmationDialogs = ({
}

export default DeleteConfirmationDialogs

type DropEntityConfirmationModalProps = {
visible: boolean
entityLabel: 'view' | 'materialized view'
entityName?: string
isDeleteWithCascade: boolean
onToggleCascade: () => void
onCancel: () => void
onConfirm: () => void
}

const DropEntityConfirmationModal = ({
visible,
entityLabel,
entityName,
isDeleteWithCascade,
onToggleCascade,
onCancel,
onConfirm,
}: DropEntityConfirmationModalProps) => {
const checkboxId = `checkbox-cascade-${entityLabel.replace(/\s+/g, '-')}`
return (
<ConfirmationModal
variant="destructive"
size="small"
visible={visible}
title={
<span className="wrap-break-word">{`Confirm deletion of ${entityLabel} "${entityName ?? ''}"`}</span>
}
confirmLabel="Delete"
confirmLabelLoading="Deleting"
onCancel={onCancel}
onConfirm={onConfirm}
>
<div className="space-y-4">
<p className="text-sm text-foreground-light">
Are you sure you want to delete this {entityLabel}? This action cannot be undone.
</p>
<div className="items-top flex space-x-2">
<Checkbox
id={checkboxId}
checked={isDeleteWithCascade}
onCheckedChange={onToggleCascade}
/>
<div className="grid gap-1.5 leading-none">
<label
htmlFor={checkboxId}
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Drop {entityLabel} with cascade?
</label>
<p className="text-sm text-foreground-muted">
Deletes the {entityLabel} and its dependent objects
</p>
</div>
</div>
{isDeleteWithCascade && (
<Alert variant="warning">
<AlertTitle>
Warning: Dropping with cascade may result in unintended consequences
</AlertTitle>
<AlertDescription>
All dependent objects will be removed, as will any objects that depend on them,
recursively.
</AlertDescription>
<AlertDescription className="mt-4">
<Button asChild size="tiny" type="default" icon={<ExternalLink />}>
<Link
href="https://www.postgresql.org/docs/current/ddl-depend.html"
target="_blank"
rel="noreferrer"
>
About dependency tracking
</Link>
</Button>
</AlertDescription>
</Alert>
)}
</div>
</ConfirmationModal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const TableGridEditor = ({
</SupabaseGrid>

<DeleteConfirmationDialogs
selectedTable={isTableSelected ? selectedTable : undefined}
selectedTable={selectedTable}
onTableDeleted={onTableDeleted}
/>
</TableEditorTableStateContextProvider>
Expand Down
Loading