diff --git a/studio/components/interfaces/Database/Tables/ColumnList.tsx b/studio/components/interfaces/Database/Tables/ColumnList.tsx
index 83b62b42089..5d3e8691e7d 100644
--- a/studio/components/interfaces/Database/Tables/ColumnList.tsx
+++ b/studio/components/interfaces/Database/Tables/ColumnList.tsx
@@ -1,4 +1,4 @@
-import { FC, ReactNode, useState } from 'react'
+import { FC, useState } from 'react'
import { observer } from 'mobx-react-lite'
import {
Input,
@@ -10,66 +10,40 @@ import {
IconTrash,
} from '@supabase/ui'
-import Table from '../../../to-be-cleaned/Table'
+import { useStore } from 'hooks'
+import Table from 'components/to-be-cleaned/Table'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
+import { PostgresTable } from '@supabase/postgres-meta'
-const Header: FC<{
- filterString: string
- filterPlaceholder: string
- leftComponents: ReactNode
- rightComponents: ReactNode
- setFilterString: (value: string) => void
-}> = ({
- filterString,
- setFilterString,
- filterPlaceholder = 'Filter',
- leftComponents,
- rightComponents,
-}) => (
-
-
-
{leftComponents}
-
- setFilterString(e.target.value)}
- icon={}
- />
-
-
-
{rightComponents}
-
-)
-
-const ColumnList: FC<{
- selectedTable: any
+interface Props {
+ selectedTable: PostgresTable
onSelectBack: () => void
onAddColumn: () => void
onEditColumn: (column: any) => void
onDeleteColumn: (column: any) => void
-}> = ({
+}
+
+const ColumnList: FC = ({
selectedTable,
onSelectBack = () => {},
onAddColumn = () => {},
onEditColumn = () => {},
onDeleteColumn = () => {},
}) => {
+ const { meta } = useStore()
const [filterString, setFilterString] = useState('')
const columns =
filterString.length === 0
? selectedTable.columns
: selectedTable.columns.filter((column: any) => column.name.includes(filterString))
+ const isLocked = meta.excludedSchemas.includes(selectedTable?.schema ?? '')
+
return (
<>
-
+
{selectedTable.name}
- }
- rightComponents={
-
} onClick={() => onAddColumn()}>
- New
-
- }
- />
+
+ setFilterString(e.target.value)}
+ icon={}
+ />
+
+
+ {!isLocked && (
+
+ } onClick={() => onAddColumn()}>
+ New column
+
+
+ )}
+
{columns.length === 0 ? (
@@ -111,8 +96,12 @@ const ColumnList: FC<{
{x.name}
-
- {x.comment}
+
+ {x.comment !== null ? (
+ {x.comment}
+ ) : (
+ No description
+ )}
{x.data_type}
@@ -127,12 +116,14 @@ const ColumnList: FC<{
icon={}
style={{ padding: 5 }}
type="text"
+ disabled={isLocked}
/>
diff --git a/studio/components/interfaces/Database/Tables/TableList.tsx b/studio/components/interfaces/Database/Tables/TableList.tsx
index 1edbc0f357a..c4773d1196d 100644
--- a/studio/components/interfaces/Database/Tables/TableList.tsx
+++ b/studio/components/interfaces/Database/Tables/TableList.tsx
@@ -1,155 +1,195 @@
-import { FC, ReactNode, useState } from 'react'
+import { FC, useState } from 'react'
import { observer } from 'mobx-react-lite'
import {
Button,
IconPlus,
Input,
+ Listbox,
IconSearch,
IconTrash,
IconEdit3,
IconColumns,
+ IconLock,
} from '@supabase/ui'
+import { partition } from 'lodash'
import { useStore } from 'hooks'
import Table from 'components/to-be-cleaned/Table'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
+import { PostgresTable, PostgresSchema } from '@supabase/postgres-meta'
-const Header: FC<{
- filterString: string
- filterPlaceholder: string
- leftComponents: ReactNode
- rightComponents: ReactNode
- setFilterString: (value: string) => void
-}> = ({
- filterString,
- setFilterString,
- filterPlaceholder = 'Filter',
- leftComponents,
- rightComponents,
-}) => (
-
-
-
{leftComponents}
-
- setFilterString(e.target.value)}
- icon={}
- />
-
-
-
{rightComponents}
-
-)
-
-const TableList: FC<{
+interface Props {
+ selectedSchema: string
+ onSelectSchema: (schema: string) => void
onAddTable: () => void
onEditTable: (table: any) => void
onDeleteTable: (table: any) => void
onOpenTable: (table: any) => void
-}> = observer(
- ({
- onAddTable = () => {},
- onEditTable = () => {},
- onDeleteTable = () => {},
- onOpenTable = () => {},
- }) => {
- const { meta } = useStore()
- const [filterString, setFilterString] = useState('')
- 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)
- )
+}
- return (
- <>
-
- {/* @ts-ignore */}
- } onClick={() => onAddTable()}>
- New
-
- }
- />
-
- {tables.length === 0 ? (
-
- ) : (
-
-
Name,
- Schema,
-
- Description
- ,
-
- Rows (Estimated)
- ,
-
- Size (Estimated)
- ,
- ,
- ]}
- body={tables.map((x: any, i: any) => (
-
-
- {x.name}
-
-
- {x.schema}
-
-
- {x.comment}
-
-
- {x.live_rows_estimate ?? x.live_row_count}
-
-
- {x.size}
-
-
-
- }
- type="default"
- className="whitespace-nowrap hover:border-gray-500"
- style={{ paddingTop: 3, paddingBottom: 3 }}
- onClick={() => {
- onOpenTable(x)
- }}
- >
- {x.columns.length} columns
-
-
-
-
+const TableList: FC = ({
+ selectedSchema,
+ onSelectSchema = () => {},
+ onAddTable = () => {},
+ onEditTable = () => {},
+ onDeleteTable = () => {},
+ onOpenTable = () => {},
+}) => {
+ const { meta } = useStore()
+
+ const [filterString, setFilterString] = useState('')
+
+ const schemas: PostgresSchema[] = meta.schemas.list()
+ const [protectedSchemas, openSchemas] = partition(schemas, (schema) =>
+ meta.excludedSchemas.includes(schema?.name ?? '')
+ )
+
+ const allTables: PostgresTable[] = meta.tables.list(
+ (table: PostgresTable) => table.schema === selectedSchema
+ )
+ const tables =
+ filterString.length === 0
+ ? allTables
+ : // @ts-ignore
+ allTables.filter((table: PostgresTable) => table.name.includes(filterString))
+
+ // @ts-ignore
+ const schema = schemas.find((schema) => schema.name === selectedSchema)
+ const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
+
+ return (
+ <>
+
+
+
+
}
+ >
+
+ Schemas
+
+ {/* @ts-ignore */}
+ {openSchemas.map((schema) => (
+
schema}
+ >
+ {schema.name}
+
))}
+
+ Protected schemas
+
+ {protectedSchemas.map((schema) => (
+
schema}
+ >
+ {schema.name}
+
+ ))}
+
+
+
+ setFilterString(e.target.value)}
+ icon={}
/>
+
+ {!isLocked && (
+
+ } onClick={() => onAddTable()}>
+ New table
+
+
)}
- >
- )
- }
-)
+
+ {tables.length === 0 ? (
+
+ ) : (
+
+
Name,
+
+ Description
+ ,
+
+ Rows (Estimated)
+ ,
+
+ Size (Estimated)
+ ,
+ ,
+ ]}
+ body={tables.map((x: any, i: any) => (
+
+
+ {x.name}
+
+
+ {x.comment !== null ? (
+ {x.comment}
+ ) : (
+ No description
+ )}
+
+
+ {x.live_rows_estimate ?? x.live_row_count}
+
+
+ {x.size}
+
+
+
+ }
+ className="whitespace-nowrap hover:border-gray-500"
+ style={{ paddingTop: 3, paddingBottom: 3 }}
+ onClick={() => onOpenTable(x)}
+ >
+ {x.columns.length} columns
+
+ }
+ style={{ padding: 5 }}
+ disabled={isLocked}
+ onClick={() => onEditTable(x)}
+ />
+ }
+ style={{ padding: 5 }}
+ disabled={isLocked}
+ onClick={() => onDeleteTable(x)}
+ />
+
+
+
+ ))}
+ />
+
+ )}
+ >
+ )
+}
-export default TableList
+export default observer(TableList)
diff --git a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
index 5a91a18caf6..d7ada6fa5b6 100644
--- a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
+++ b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
@@ -104,8 +104,8 @@ const TableEditorMenu: FC = ({
{/* Schema selection dropdown */}
- {meta.schemas.isLoading ? (
-
+ {!meta.schemas.isInitialized ? (
+
Loading schemas...
@@ -121,7 +121,7 @@ const TableEditorMenu: FC
= ({
}}
>
- Schemas
+ Schemas
{/* @ts-ignore */}
{openSchemas.map((schema) => (
@@ -129,9 +129,9 @@ const TableEditorMenu: FC = ({
key={schema.id}
value={schema.name}
label={schema.name}
- addOnBefore={() => schema}
+ addOnBefore={() => schema}
>
- {schema.name}
+ {schema.name}
))}
= ({
value="protected-schemas"
label="Protected schemas"
>
- Protected schemas
+ Protected schemas
{protectedSchemas.map((schema) => (
schema}
+ addOnBefore={() => schema}
>
- {schema.name}
+ {schema.name}
))}
diff --git a/studio/pages/project/[ref]/database/tables.tsx b/studio/pages/project/[ref]/database/tables.tsx
index 541095f6f05..ae22d2c1945 100644
--- a/studio/pages/project/[ref]/database/tables.tsx
+++ b/studio/pages/project/[ref]/database/tables.tsx
@@ -16,8 +16,10 @@ const DatabaseTables: NextPageWithLayout = () => {
const { meta, ui } = useStore()
const [isDeleting, setIsDeleting] = useState(false)
+ const [selectedSchema, setSelectedSchema] = useState('public')
const [selectedTable, setSelectedTable] = useState()
const [sidePanelKey, setSidePanelKey] = useState<'column' | 'table'>()
+
const [selectedColumnToEdit, setSelectedColumnToEdit] = useState()
const [selectedTableToEdit, setSelectedTableToEdit] = useState()
@@ -121,6 +123,8 @@ const DatabaseTables: NextPageWithLayout = () => {
{isUndefined(selectedTable) ? (