mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
Fix database tables page delete table confirmation modal
This commit is contained in:
1 parent
8e3377492d
commit
0bb2bec2ea
2 files changed
+154
-129
No files matched your search
@@ -13,7 +13,6 @@ import {
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal'
|
||||
|
||||
const Header: FC<{
|
||||
filterString: string
|
||||
@@ -48,134 +47,109 @@ const Header: FC<{
|
||||
const TableList: FC<{
|
||||
onAddTable: () => void
|
||||
onEditTable: (table: any) => void
|
||||
onDeleteTable: (table: any) => void
|
||||
onOpenTable: (table: any) => void
|
||||
}> = observer(({ onAddTable = () => {}, onEditTable = () => {}, onOpenTable = () => {} }) => {
|
||||
const { ui, meta } = useStore()
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
const tables =
|
||||
filterString.length === 0
|
||||
? meta.tables.list((table: any) => table.schema === 'public')
|
||||
: meta.tables.list(
|
||||
(table: any) => table.schema === 'public' && table.name.includes(filterString)
|
||||
)
|
||||
}> = observer(
|
||||
({
|
||||
onAddTable = () => {},
|
||||
onEditTable = () => {},
|
||||
onDeleteTable = () => {},
|
||||
onOpenTable = () => {},
|
||||
}) => {
|
||||
const { meta } = useStore()
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
const tables =
|
||||
filterString.length === 0
|
||||
? meta.tables.list((table: any) => table.schema === 'public')
|
||||
: meta.tables.list(
|
||||
(table: any) => table.schema === 'public' && table.name.includes(filterString)
|
||||
)
|
||||
|
||||
async function onDelete(table: any) {
|
||||
confirmAlert({
|
||||
title: 'Confirm to delete',
|
||||
message: `Are you sure you want to delete "${table.name}" table? This action cannot be undone.`,
|
||||
onAsyncConfirm: async () => {
|
||||
try {
|
||||
const response: any = await meta.tables.del(table.id)
|
||||
if (response.error) {
|
||||
throw response.error
|
||||
} else {
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully removed ${table.name}.`,
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to delete ${table.name}: ${error.message}`,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{/* @ts-ignore */}
|
||||
<Header
|
||||
filterString={filterString}
|
||||
setFilterString={setFilterString}
|
||||
rightComponents={
|
||||
<Button icon={<IconPlus />} onClick={() => onAddTable()}>
|
||||
New
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-4">
|
||||
<Table
|
||||
head={[
|
||||
<Table.th key="name">Name</Table.th>,
|
||||
<Table.th key="schema">Schema</Table.th>,
|
||||
<Table.th key="description" className="hidden lg:table-cell">
|
||||
Description
|
||||
</Table.th>,
|
||||
<Table.th key="rows" className="hidden xl:table-cell">
|
||||
Rows (Estimated)
|
||||
</Table.th>,
|
||||
<Table.th key="size" className="hidden xl:table-cell">
|
||||
Size (Estimated)
|
||||
</Table.th>,
|
||||
<Table.th key="buttons"></Table.th>,
|
||||
]}
|
||||
body={tables.map((x: any, i: any) => (
|
||||
<Table.tr key={x.id}>
|
||||
<Table.td>
|
||||
<Typography.Text>{x.name}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
<Typography.Text>{x.schema}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" truncate max-w-sm hidden lg:table-cell">
|
||||
<Typography.Text>{x.comment}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" hidden xl:table-cell">
|
||||
<Typography.Text small code>
|
||||
{x.live_rows_estimate ?? x.live_row_count}
|
||||
</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" hidden xl:table-cell">
|
||||
<Typography.Text small code>
|
||||
{x.size}
|
||||
</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
iconRight={<IconColumns />}
|
||||
type="default"
|
||||
className="hover:border-gray-500 whitespace-nowrap"
|
||||
style={{ paddingTop: 3, paddingBottom: 3 }}
|
||||
onClick={() => {
|
||||
onOpenTable(x)
|
||||
}}
|
||||
>
|
||||
{x.columns.length} columns
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onEditTable(x)}
|
||||
icon={<IconEdit3 />}
|
||||
style={{ padding: 5 }}
|
||||
type="text"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => onDeleteTable(x)}
|
||||
icon={<IconTrash />}
|
||||
style={{ padding: 5 }}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
))}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{/* @ts-ignore */}
|
||||
<Header
|
||||
filterString={filterString}
|
||||
setFilterString={setFilterString}
|
||||
rightComponents={
|
||||
<Button icon={<IconPlus />} onClick={() => onAddTable()}>
|
||||
New
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full my-4">
|
||||
<Table
|
||||
head={[
|
||||
<Table.th key="name">Name</Table.th>,
|
||||
<Table.th key="schema">Schema</Table.th>,
|
||||
<Table.th key="description" className="hidden lg:table-cell">
|
||||
Description
|
||||
</Table.th>,
|
||||
<Table.th key="rows" className="hidden xl:table-cell">
|
||||
Rows (Estimated)
|
||||
</Table.th>,
|
||||
<Table.th key="size" className="hidden xl:table-cell">
|
||||
Size (Estimated)
|
||||
</Table.th>,
|
||||
<Table.th key="buttons"></Table.th>,
|
||||
]}
|
||||
body={tables.map((x: any, i: any) => (
|
||||
<Table.tr key={x.id}>
|
||||
<Table.td>
|
||||
<Typography.Text>{x.name}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
<Typography.Text>{x.schema}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" truncate max-w-sm hidden lg:table-cell">
|
||||
<Typography.Text>{x.comment}</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" hidden xl:table-cell">
|
||||
<Typography.Text small code>
|
||||
{x.live_rows_estimate ?? x.live_row_count}
|
||||
</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td className=" hidden xl:table-cell">
|
||||
<Typography.Text small code>
|
||||
{x.size}
|
||||
</Typography.Text>
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
iconRight={<IconColumns />}
|
||||
type="default"
|
||||
className="hover:border-gray-500 whitespace-nowrap"
|
||||
style={{ paddingTop: 3, paddingBottom: 3 }}
|
||||
onClick={() => {
|
||||
onOpenTable(x)
|
||||
}}
|
||||
>
|
||||
{x.columns.length} columns
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onEditTable(x)}
|
||||
icon={<IconEdit3 />}
|
||||
style={{ padding: 5 }}
|
||||
type="text"
|
||||
// tooltip={{
|
||||
// title: 'Edit Details',
|
||||
// position: 'top-left',
|
||||
// }}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => onDelete(x)}
|
||||
icon={<IconTrash />}
|
||||
style={{ padding: 5 }}
|
||||
type="text"
|
||||
// tooltip={{
|
||||
// title: 'Delete table',
|
||||
// position: 'top-left',
|
||||
// }}
|
||||
/>
|
||||
</div>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
))}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
export default TableList
|
||||
@@ -3,22 +3,27 @@ import { useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { isUndefined } from 'lodash'
|
||||
import { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
|
||||
import { Modal } from '@supabase/ui'
|
||||
|
||||
import { withAuth, useStore } from 'hooks'
|
||||
|
||||
import { DatabaseLayout } from 'components/layouts'
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import { TableList, ColumnList } from 'components/interfaces/Database'
|
||||
import { SidePanelEditor } from 'components/interfaces/TableGridEditor'
|
||||
|
||||
const DatabaseTables: NextPage = () => {
|
||||
const { meta } = useStore()
|
||||
const tables = meta.tables.list()
|
||||
const { meta, ui } = useStore()
|
||||
|
||||
const [isDeleting, setIsDeleting] = useState<boolean>(false)
|
||||
const [selectedTable, setSelectedTable] = useState<any>()
|
||||
const [sidePanelKey, setSidePanelKey] = useState<'column' | 'table'>()
|
||||
const [selectedColumnToEdit, setSelectedColumnToEdit] = useState<PostgresColumn>()
|
||||
const [selectedTableToEdit, setSelectedTableToEdit] = useState<PostgresTable>()
|
||||
|
||||
const [selectedColumnToDelete, setSelectedColumnToDelete] = useState<PostgresColumn>()
|
||||
const [selectedTableToDelete, setSelectedTableToDelete] = useState<PostgresTable>()
|
||||
|
||||
const onAddTable = () => {
|
||||
setSidePanelKey('table')
|
||||
setSelectedTableToEdit(undefined)
|
||||
@@ -29,6 +34,11 @@ const DatabaseTables: NextPage = () => {
|
||||
setSelectedTableToEdit(table)
|
||||
}
|
||||
|
||||
const onDeleteTable = (table: PostgresTable) => {
|
||||
setIsDeleting(true)
|
||||
setSelectedTableToDelete(table)
|
||||
}
|
||||
|
||||
const onAddColumn = () => {
|
||||
setSidePanelKey('column')
|
||||
setSelectedColumnToEdit(undefined)
|
||||
@@ -46,6 +56,30 @@ const DatabaseTables: NextPage = () => {
|
||||
|
||||
const onClosePanel = () => setSidePanelKey(undefined)
|
||||
|
||||
const onConfirmDeleteTable = async () => {
|
||||
try {
|
||||
if (isUndefined(selectedTableToDelete)) return
|
||||
|
||||
const response: any = await meta.tables.del(selectedTableToDelete.id)
|
||||
if (response.error) {
|
||||
throw response.error
|
||||
} else {
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully removed ${selectedTableToDelete.name}.`,
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to delete ${selectedTableToDelete?.name}: ${error.message}`,
|
||||
})
|
||||
} finally {
|
||||
setIsDeleting(false)
|
||||
setSelectedTableToDelete(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DatabaseLayout title="Database">
|
||||
<div className="p-4">
|
||||
@@ -53,6 +87,7 @@ const DatabaseTables: NextPage = () => {
|
||||
<TableList
|
||||
onAddTable={onAddTable}
|
||||
onEditTable={onEditTable}
|
||||
onDeleteTable={onDeleteTable}
|
||||
onOpenTable={setSelectedTable}
|
||||
/>
|
||||
) : (
|
||||
@@ -65,6 +100,22 @@ const DatabaseTables: NextPage = () => {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<ConfirmationModal
|
||||
danger
|
||||
visible={isDeleting && !isUndefined(selectedTableToDelete)}
|
||||
header={`Confirm deletion of table "${selectedTableToDelete?.name}"`}
|
||||
children={
|
||||
<Modal.Content>
|
||||
<p className="py-4 text-sm text-scale-1100">
|
||||
Are you sure you want to delete the selected table? This action cannot be undone.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={() => setIsDeleting(false)}
|
||||
onSelectConfirm={onConfirmDeleteTable}
|
||||
/>
|
||||
<SidePanelEditor
|
||||
sidePanelKey={sidePanelKey}
|
||||
selectedSchema="public"
|
||||
|
||||
Reference in new issue
Block a user