diff --git a/packages/ui/src/components/SidePanel/SidePanel.tsx b/packages/ui/src/components/SidePanel/SidePanel.tsx
index b9595e7fec31b..6dc69c307e913 100644
--- a/packages/ui/src/components/SidePanel/SidePanel.tsx
+++ b/packages/ui/src/components/SidePanel/SidePanel.tsx
@@ -126,10 +126,16 @@ export function Separator() {
return
}
-export function Content({ children }: { children: React.ReactNode }) {
+export function Content({
+ children,
+ className,
+}: {
+ children: React.ReactNode
+ className?: string
+}) {
let __styles = styleHandler('sidepanel')
- return {children}
+ return {children}
}
SidePanel.Content = Content
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignKeySelector.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignKeySelector.tsx
deleted file mode 100644
index 1fb0e7ddb0bc1..0000000000000
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignKeySelector.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React, { useEffect, useState } from 'react'
-import { SidePanel, IconLoader, IconXCircle } from 'ui'
-import type { PostgresRelationship } from '@supabase/postgres-meta'
-
-import ActionBar from '../ActionBar'
-import InputField from './InputField'
-import { RowField } from './RowEditor.types'
-import { generateRowFieldsWithoutColumnMeta } from './RowEditor.utils'
-
-export interface ForeignKeySelectorProps {
- visible: boolean
- referenceRow?: { loading: boolean; foreignKey: any; row: any }
- closePanel: () => void
-}
-
-const ForeignKeySelector = ({ visible, referenceRow, closePanel }: ForeignKeySelectorProps) => {
- const loading = referenceRow?.loading ?? true
- const foreignKey: PostgresRelationship = referenceRow?.foreignKey ?? undefined
- const row = referenceRow?.row ?? undefined
- const [rowFields, setRowFields] = useState([])
-
- useEffect(() => {
- if (visible) {
- if (!foreignKey) {
- setRowFields([])
- } else if (row) {
- const rowFields = generateRowFieldsWithoutColumnMeta(row)
- setRowFields(rowFields)
- }
- }
- }, [visible, referenceRow])
-
- return (
-
- Selecting foreign key for{' '}
-
- {foreignKey?.target_table_schema ?? ''}.{foreignKey?.target_table_name ?? ''}
-
-
- }
- hideFooter={false}
- onCancel={closePanel}
- customFooter={}
- >
-
-
- {loading ? (
-
-
-
Loading reference row
-
- ) : !row ? (
-
-
-
- Unable to find the corresponding row in {foreignKey?.target_table_schema}.
- {foreignKey?.target_table_name}
-
-
- ) : (
-
- {rowFields.map((field: RowField) => {
- return
- })}
-
- )}
-
-
-
- )
-}
-
-export default ForeignKeySelector
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
new file mode 100644
index 0000000000000..48094b4001c23
--- /dev/null
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
@@ -0,0 +1,117 @@
+import { PostgresTable } from '@supabase/postgres-meta'
+import { parseSupaTable } from 'components/grid'
+import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { useTableRowsQuery } from 'data/table-rows/table-rows-query'
+import { useStore } from 'hooks'
+import { IconLoader, SidePanel } from 'ui'
+
+import ActionBar from '../../ActionBar'
+import { RowField } from '../RowEditor.types'
+import { useEncryptedColumns } from './ForeignRowSelector.utils'
+import SelectorTable from './SelectorTable'
+
+export interface ForeignRowSelectorProps {
+ visible: boolean
+ referenceRow?: RowField
+ onSelect: (value: any) => void
+ closePanel: () => void
+}
+
+const ForeignRowSelector = ({
+ visible,
+ referenceRow,
+ onSelect,
+ closePanel,
+}: ForeignRowSelectorProps) => {
+ const { meta } = useStore()
+ const { project } = useProjectContext()
+
+ const {
+ target_table_schema: schemaName,
+ target_table_name: tableName,
+ target_column_name: columnName,
+ } = referenceRow?.foreignKey ?? {}
+
+ const tables = meta.tables.list()
+ const table = tables.find((table) => table.schema === schemaName && table.name === tableName)
+ const encryptedColumns = useEncryptedColumns({ schemaName, tableName })
+
+ const supaTable =
+ table &&
+ parseSupaTable(
+ {
+ table: table as PostgresTable,
+ columns: (table as PostgresTable).columns ?? [],
+ primaryKeys: (table as PostgresTable).primary_keys,
+ relationships: (table as PostgresTable).relationships,
+ },
+ encryptedColumns
+ )
+
+ const { data, isLoading, isSuccess, isError } = useTableRowsQuery(
+ {
+ queryKey: [schemaName, tableName],
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ table: supaTable,
+ // sorts,
+ // filters,
+ // page: state.page,
+ // limit: state.rowsPerPage,
+ },
+ {
+ keepPreviousData: true,
+ }
+ )
+
+ return (
+
+ Selecting foreign key from{' '}
+
+ {schemaName}.{tableName}
+
+
+ }
+ hideFooter={false}
+ onCancel={closePanel}
+ customFooter={}
+ >
+
+
+ {isLoading && (
+
+ )}
+
+ {isError && (
+
+
+ Unable to load rows from{' '}
+
+ {schemaName}.{tableName}
+
+ . Please try again or contact support.
+
+
+ )}
+
+ {isSuccess && supaTable && (
+
onSelect(row[columnName ?? ''])}
+ />
+ )}
+
+
+
+ )
+}
+
+export default ForeignRowSelector
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.utils.ts b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.utils.ts
new file mode 100644
index 0000000000000..7bb0a05c1547d
--- /dev/null
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.utils.ts
@@ -0,0 +1,36 @@
+import { useFlag, useStore } from 'hooks'
+import { useEffect, useState } from 'react'
+
+export interface UseEncryptedColumnsArgs {
+ schemaName?: string
+ tableName?: string
+}
+
+export function useEncryptedColumns({ schemaName, tableName }: UseEncryptedColumnsArgs) {
+ const isVaultEnabled = useFlag('vaultExtension')
+ const { vault } = useStore()
+
+ const [encryptedColumns, setEncryptedColumns] = useState([])
+
+ useEffect(() => {
+ let isMounted = true
+
+ const getEncryptedColumns = async () => {
+ if (schemaName !== undefined && tableName !== undefined && isVaultEnabled) {
+ const columns = await vault.listEncryptedColumns(schemaName, tableName)
+
+ if (isMounted) {
+ setEncryptedColumns(columns)
+ }
+ }
+ }
+
+ getEncryptedColumns()
+
+ return () => {
+ isMounted = false
+ }
+ }, [schemaName, tableName, isVaultEnabled])
+
+ return encryptedColumns
+}
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/SelectorTable.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/SelectorTable.tsx
new file mode 100644
index 0000000000000..92d58b964ff12
--- /dev/null
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/SelectorTable.tsx
@@ -0,0 +1,59 @@
+import DataGrid, { Column } from '@supabase/react-data-grid'
+import * as Tooltip from '@radix-ui/react-tooltip'
+import { SupaRow, SupaTable } from 'components/grid'
+import { IconKey } from 'ui'
+
+export interface SelectorTableProps {
+ table: SupaTable
+ rows: SupaRow[]
+ onRowSelect: (row: SupaRow) => void
+}
+
+const columnRender = (name: string, isPrimaryKey = false) => {
+ return (
+
+ {isPrimaryKey && (
+
+
+
+
+
+
+
+
+
+ Primary key
+
+
+
+ )}
+
+
{name}
+
+ )
+}
+
+const formatter = (column: string, row: any) => {
+ return (
+
+ {JSON.stringify(row[column])}
+
+ )
+}
+
+const SelectorTable = ({ table, rows, onRowSelect }: SelectorTableProps) => {
+ const columns: Column[] = table.columns.map((column) => ({
+ key: column.name,
+ name: column.name,
+ formatter: ({ row }: any) => formatter(column.name, row),
+ headerRenderer: () => columnRender(column.name, column.isPrimaryKey),
+ resizable: true,
+ minWidth: 120,
+ }))
+
+ return (
+
+ )
+}
+
+export default SelectorTable
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.tsx
index aeab5f0991344..11d77b70a01d3 100644
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.tsx
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.tsx
@@ -8,14 +8,14 @@ import ActionBar from '../ActionBar'
import HeaderTitle from './HeaderTitle'
import InputField from './InputField'
import JsonEdit from './JsonEditor'
-import ForeignKeySelector from './ForeignKeySelector'
+import ForeignRowSelector from './ForeignRowSelector/ForeignRowSelector'
import {
generateRowFields,
validateFields,
generateRowObjectFromFields,
generateUpdateRowPayload,
} from './RowEditor.utils'
-import { JsonEditValue, ReferenceRow, RowField } from './RowEditor.types'
+import { JsonEditValue, RowField } from './RowEditor.types'
export interface RowEditorProps {
row?: Dictionary
@@ -39,7 +39,7 @@ const RowEditor = ({
const [selectedValueForJsonEdit, setSelectedValueForJsonEdit] = useState()
const [isSelectingForeignKey, setIsSelectingForeignKey] = useState(false)
- const [referenceRow, setReferenceRow] = useState()
+ const [referenceRow, setReferenceRow] = useState()
const isNewRecord = isUndefined(row)
const isEditingJson = !isUndefined(selectedValueForJsonEdit)
@@ -72,47 +72,18 @@ const RowEditor = ({
updateEditorDirty()
}
- const onSelectForeignKey = async (row: RowField) => {
+ const onOpenForeignRowSelector = async (row: RowField) => {
setIsSelectingForeignKey(true)
+ setReferenceRow(row)
+ }
+
+ const onSelectForeignRowValue = (value: any) => {
+ if (!referenceRow) return
+
+ onUpdateField({ [referenceRow.name]: value })
- // Possible low prio refactor: Shift fetching reference row retrieval to ForeignKeySelector
- // in a useEffect, rather than trying to manage a loading state in this method
- // if (!row.value) {
- // ui.setNotification({
- // category: 'error',
- // message: `Please enter a value in the ${row.name} field first`,
- // duration: 4000,
- // })
- // }
- // const foreignKey = row.foreignKey
- // setReferenceRow({ loading: true, foreignKey, row: undefined })
- // setIsSelectingForeignKey(true)
-
- // if (foreignKey) {
- // const schema = foreignKey.target_table_schema
- // const table = foreignKey.target_table_name
- // const column = foreignKey.target_column_name
-
- // const query = new Query()
- // .from(table, schema)
- // .select()
- // .match({ [column]: row.value })
- // .toSql()
- // const res = await meta.query(query)
- // if (res.error) {
- // setReferenceRow({ loading: false, foreignKey, row: undefined })
- // return ui.setNotification({ category: 'error', message: res.error.message })
- // }
- // if (res.length === 0) {
- // setReferenceRow({ loading: false, foreignKey, row: undefined })
- // return ui.setNotification({
- // category: 'error',
- // message: `Unable to find the corresponding row in ${foreignKey.target_table_schema}.${foreignKey.target_table_name} where ${foreignKey.target_column_name} equals ${row.value}`,
- // duration: 4000,
- // })
- // }
- // setReferenceRow({ loading: false, foreignKey, row: res[0] })
- // }
+ setIsSelectingForeignKey(false)
+ setReferenceRow(undefined)
}
const onSaveChanges = (e: React.FormEvent) => {
@@ -175,7 +146,7 @@ const RowEditor = ({
errors={errors}
onUpdateField={onUpdateField}
onEditJson={setSelectedValueForJsonEdit}
- onSelectForeignKey={() => onSelectForeignKey(field)}
+ onSelectForeignKey={() => onOpenForeignRowSelector(field)}
/>
)
})}
@@ -200,7 +171,7 @@ const RowEditor = ({
errors={errors}
onUpdateField={onUpdateField}
onEditJson={setSelectedValueForJsonEdit}
- onSelectForeignKey={() => onSelectForeignKey(field)}
+ onSelectForeignKey={() => onOpenForeignRowSelector(field)}
/>
)
})}
@@ -220,9 +191,10 @@ const RowEditor = ({
}}
/>
- {
setIsSelectingForeignKey(false)
setReferenceRow(undefined)
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types.ts b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types.ts
index fe7eade0c1f6d..b909379875457 100644
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types.ts
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types.ts
@@ -6,12 +6,6 @@ export interface JsonEditValue {
jsonString: string
}
-export interface ReferenceRow {
- loading: boolean
- foreignKey: any // ForeignKey
- row: any // RowField
-}
-
export interface RowField {
id: string
name: string
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils.ts b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils.ts
index 7bbe183f5f7ea..919fe7421be6f 100644
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils.ts
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils.ts
@@ -70,26 +70,6 @@ export const validateFields = (fields: RowField[]) => {
return errors
}
-// Currently only used in ReferenceRowViewer for rows outside of public schema
-export const generateRowFieldsWithoutColumnMeta = (row: any): RowField[] => {
- const properties = Object.keys(row)
- return properties.map((property) => {
- return {
- id: uuidv4(),
- value: row[property] || '',
- name: property,
- comment: '',
- format: '',
- enums: [],
- defaultValue: '',
- foreignKey: undefined,
- isNullable: false,
- isIdentity: false,
- isPrimaryKey: false,
- }
- })
-}
-
const parseValue = (originalValue: string, format: string) => {
try {
if (originalValue === null || originalValue.length === 0) {