mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
adds double click to foreign keys on grid
This commit is contained in:
1 parent
831b0eff2f
commit
be4c29de5a
8 files changed
+163
-31
No files matched your search
@@ -198,7 +198,13 @@ const SupabaseGridLayout = forwardRef<SupabaseGridRef, SupabaseGridProps>((props
|
||||
onAddColumn={editable ? props.onAddColumn : undefined}
|
||||
headerActions={headerActions}
|
||||
/>
|
||||
<Grid ref={gridRef} {...gridProps} rows={data?.rows ?? []} updateRow={props.updateTableRow} />
|
||||
<Grid
|
||||
ref={gridRef}
|
||||
{...gridProps}
|
||||
rows={data?.rows ?? []}
|
||||
updateRow={props.updateTableRow}
|
||||
onEditForeignKeyColumnValue={props.onEditForeignKeyColumnValue}
|
||||
/>
|
||||
<Footer isLoading={isLoading || isRefetching} />
|
||||
<Shortcuts gridRef={gridRef} />
|
||||
{mounted &&
|
||||
|
||||
@@ -6,6 +6,7 @@ import { GridProps, SupaRow } from '../../types'
|
||||
import { useDispatch, useTrackedState } from '../../store'
|
||||
import RowRenderer from './RowRenderer'
|
||||
import AwesomeDebouncePromise from 'awesome-debounce-promise'
|
||||
import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
|
||||
function rowKeyGetter(row: SupaRow) {
|
||||
return row?.idx ?? -1
|
||||
@@ -14,13 +15,27 @@ function rowKeyGetter(row: SupaRow) {
|
||||
interface IGrid extends GridProps {
|
||||
rows: any[]
|
||||
updateRow: (previousRow: any, updatedData: any) => void
|
||||
onEditForeignKeyColumnValue: (args: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}) => void
|
||||
}
|
||||
|
||||
// [Joshen] Just for visibility this is causing some hook errors in the browser
|
||||
export const Grid = memo(
|
||||
forwardRef<DataGridHandle, IGrid>(
|
||||
(
|
||||
{ width, height, containerClass, gridClass, rowClass, rows, updateRow },
|
||||
{
|
||||
width,
|
||||
height,
|
||||
containerClass,
|
||||
gridClass,
|
||||
rowClass,
|
||||
rows,
|
||||
updateRow,
|
||||
onEditForeignKeyColumnValue,
|
||||
},
|
||||
ref: React.Ref<DataGridHandle> | undefined
|
||||
) => {
|
||||
const dispatch = useDispatch()
|
||||
@@ -60,6 +75,34 @@ export const Grid = memo(
|
||||
})
|
||||
}
|
||||
|
||||
const table = state.table
|
||||
|
||||
function getColumnForeignKey(columnName: string) {
|
||||
const foreignKey = table?.columns.find((x) => x.name == columnName)?.foreignKey ?? {}
|
||||
|
||||
return Boolean(
|
||||
foreignKey.targetTableSchema && foreignKey.targetTableName && foreignKey.targetColumnName
|
||||
)
|
||||
? foreignKey
|
||||
: undefined
|
||||
}
|
||||
|
||||
function onRowDoubleClick(row: any, column: any) {
|
||||
const foreignKey = getColumnForeignKey(column.name)
|
||||
|
||||
if (foreignKey) {
|
||||
onEditForeignKeyColumnValue({
|
||||
foreignKey: {
|
||||
target_column_name: foreignKey.targetColumnName!,
|
||||
target_table_name: foreignKey.targetTableName!,
|
||||
target_table_schema: foreignKey.targetTableSchema!,
|
||||
},
|
||||
row,
|
||||
column,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!columnHeaders || columnHeaders.length == 0) {
|
||||
return (
|
||||
<div
|
||||
@@ -92,6 +135,7 @@ export const Grid = memo(
|
||||
onRowsChange={onRowsChange}
|
||||
onSelectedCellChange={onSelectedCellChange}
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
onRowDoubleClick={onRowDoubleClick}
|
||||
className={gridClass}
|
||||
rowClass={rowClass}
|
||||
style={{ height: '100%' }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
import React from 'react'
|
||||
import { Dictionary } from './base'
|
||||
import { SupaRow, SupaTable } from './table'
|
||||
@@ -75,6 +76,11 @@ export interface SupabaseGridProps {
|
||||
refreshDocs: () => void
|
||||
onExpandJSONEditor: (column: string, row: SupaRow) => void
|
||||
updateTableRow: (previousRow: any, updatedData: any) => void
|
||||
onEditForeignKeyColumnValue: (args: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}) => void
|
||||
}
|
||||
|
||||
export interface SupabaseGridRef {
|
||||
|
||||
+7
-4
@@ -12,7 +12,6 @@ import { useStore } from 'hooks'
|
||||
import { IconLoader, SidePanel } from 'ui'
|
||||
|
||||
import ActionBar from '../../ActionBar'
|
||||
import { RowField } from '../RowEditor.types'
|
||||
import { useEncryptedColumns } from './ForeignRowSelector.utils'
|
||||
import SelectorGrid from './SelectorGrid'
|
||||
import { useState } from 'react'
|
||||
@@ -20,14 +19,18 @@ import Pagination from './Pagination'
|
||||
|
||||
export interface ForeignRowSelectorProps {
|
||||
visible: boolean
|
||||
referenceRow?: RowField
|
||||
foreignKey?: {
|
||||
target_table_schema: string
|
||||
target_table_name: string
|
||||
target_column_name: string
|
||||
}
|
||||
onSelect: (value: any) => void
|
||||
closePanel: () => void
|
||||
}
|
||||
|
||||
const ForeignRowSelector = ({
|
||||
visible,
|
||||
referenceRow,
|
||||
foreignKey,
|
||||
onSelect,
|
||||
closePanel,
|
||||
}: ForeignRowSelectorProps) => {
|
||||
@@ -38,7 +41,7 @@ const ForeignRowSelector = ({
|
||||
target_table_schema: schemaName,
|
||||
target_table_name: tableName,
|
||||
target_column_name: columnName,
|
||||
} = referenceRow?.foreignKey ?? {}
|
||||
} = foreignKey ?? {}
|
||||
|
||||
const tables = meta.tables.list()
|
||||
const table = tables.find((table) => table.schema === schemaName && table.name === tableName)
|
||||
|
||||
+1
-1
@@ -202,7 +202,7 @@ const RowEditor = ({
|
||||
|
||||
<ForeignRowSelector
|
||||
visible={isSelectingForeignKey}
|
||||
referenceRow={referenceRow}
|
||||
foreignKey={referenceRow?.foreignKey}
|
||||
onSelect={onSelectForeignRowValue}
|
||||
closePanel={() => {
|
||||
setIsSelectingForeignKey(false)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { find, isEmpty, isUndefined } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { find, isEmpty, isUndefined, noop } from 'lodash'
|
||||
import { Dictionary } from 'components/grid'
|
||||
import { Modal } from 'ui'
|
||||
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
|
||||
@@ -19,17 +20,24 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import JsonEdit from './RowEditor/JsonEditor/JsonEditor'
|
||||
import { JsonEditValue } from './RowEditor/RowEditor.types'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import ForeignRowSelector, {
|
||||
ForeignRowSelectorProps,
|
||||
} from './RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
|
||||
interface Props {
|
||||
export interface SidePanelEditorProps {
|
||||
selectedSchema: string
|
||||
selectedTable?: PostgresTable
|
||||
selectedRowToEdit?: Dictionary<any>
|
||||
selectedColumnToEdit?: PostgresColumn
|
||||
selectedTableToEdit?: PostgresTable
|
||||
selectedValueForJsonEdit?: JsonEditValue
|
||||
sidePanelKey?: 'row' | 'column' | 'table' | 'json'
|
||||
selectedForeignKeyToEdit?: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}
|
||||
sidePanelKey?: 'row' | 'column' | 'table' | 'json' | 'foreign-row-selector'
|
||||
isDuplicating?: boolean
|
||||
closePanel: () => void
|
||||
onRowCreated?: (row: Dictionary<any>) => void
|
||||
@@ -41,21 +49,22 @@ interface Props {
|
||||
onColumnSaved?: (hasEncryptedColumns?: boolean) => void
|
||||
}
|
||||
|
||||
const SidePanelEditor: FC<Props> = ({
|
||||
const SidePanelEditor = ({
|
||||
selectedSchema,
|
||||
selectedTable,
|
||||
selectedRowToEdit,
|
||||
selectedColumnToEdit,
|
||||
selectedTableToEdit,
|
||||
selectedValueForJsonEdit,
|
||||
selectedForeignKeyToEdit,
|
||||
sidePanelKey,
|
||||
isDuplicating = false,
|
||||
closePanel,
|
||||
onRowCreated = () => {},
|
||||
onRowUpdated = () => {},
|
||||
onTableCreated = () => {},
|
||||
onColumnSaved = () => {},
|
||||
}) => {
|
||||
onRowCreated = noop,
|
||||
onRowUpdated = noop,
|
||||
onTableCreated = noop,
|
||||
onColumnSaved = noop,
|
||||
}: SidePanelEditorProps) => {
|
||||
const { meta, ui } = useStore()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
@@ -156,6 +165,22 @@ const SidePanelEditor: FC<Props> = ({
|
||||
} catch (error: any) {}
|
||||
}
|
||||
|
||||
const onSaveForeignRow = async (value: any) => {
|
||||
if (selectedTable === undefined || selectedForeignKeyToEdit === undefined) return
|
||||
|
||||
try {
|
||||
const { row, column } = selectedForeignKeyToEdit
|
||||
const payload = { [column.name]: value }
|
||||
const identifiers = {} as Dictionary<any>
|
||||
selectedTable.primary_keys.forEach((column) => (identifiers[column.name] = row![column.name]))
|
||||
|
||||
const isNewRecord = false
|
||||
const configuration = { identifiers, rowIdx: row.idx }
|
||||
|
||||
saveRow(payload, isNewRecord, configuration, () => {})
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
const saveColumn = async (
|
||||
payload: CreateColumnPayload | UpdateColumnPayload,
|
||||
foreignKey: ExtendedPostgresRelationship | undefined,
|
||||
@@ -340,6 +365,12 @@ const SidePanelEditor: FC<Props> = ({
|
||||
closePanel={onClosePanel}
|
||||
onSaveJSON={onSaveJSON}
|
||||
/>
|
||||
<ForeignRowSelector
|
||||
visible={sidePanelKey === 'foreign-row-selector'}
|
||||
foreignKey={selectedForeignKeyToEdit?.foreignKey}
|
||||
closePanel={onClosePanel}
|
||||
onSelect={onSaveForeignRow}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
visible={isClosingPanel}
|
||||
header="Confirm to close"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC, useRef, useEffect, useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRouter } from 'next/router'
|
||||
import { find, isUndefined } from 'lodash'
|
||||
import { find, isUndefined, noop } from 'lodash'
|
||||
import type { PostgresColumn, PostgresRelationship, PostgresTable } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { QueryKey, useQueryClient } from '@tanstack/react-query'
|
||||
@@ -35,8 +35,9 @@ import {
|
||||
useForeignKeyConstraintsQuery,
|
||||
} from 'data/database/foreign-key-constraints-query'
|
||||
import { FOREIGN_KEY_DELETION_ACTION } from 'data/database/database-query-constants'
|
||||
import { ForeignRowSelectorProps } from './SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
|
||||
interface Props {
|
||||
export interface TableGridEditorProps {
|
||||
/** Theme for the editor */
|
||||
theme?: 'dark' | 'light'
|
||||
|
||||
@@ -44,7 +45,7 @@ interface Props {
|
||||
selectedTable: any // PostgresTable | SchemaView
|
||||
|
||||
/** Determines what side panel editor to show */
|
||||
sidePanelKey?: 'row' | 'column' | 'table' | 'json'
|
||||
sidePanelKey?: 'row' | 'column' | 'table' | 'json' | 'foreign-row-selector'
|
||||
/** Toggles if we're duplicating a table */
|
||||
isDuplicating: boolean
|
||||
/** Selected entities if we're editing a row, column or table */
|
||||
@@ -52,6 +53,11 @@ interface Props {
|
||||
selectedColumnToEdit?: PostgresColumn
|
||||
selectedTableToEdit?: PostgresTable
|
||||
selectedValueForJsonEdit?: JsonEditValue
|
||||
selectedForeignKeyToEdit?: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}
|
||||
|
||||
onAddRow: () => void
|
||||
onEditRow: (row: Dictionary<any>) => void
|
||||
@@ -59,10 +65,15 @@ interface Props {
|
||||
onEditColumn: (column: PostgresColumn) => void
|
||||
onDeleteColumn: (column: PostgresColumn) => void
|
||||
onExpandJSONEditor: (column: string, row: any) => void
|
||||
onEditForeignKeyColumnValue: (args: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}) => void
|
||||
onClosePanel: () => void
|
||||
}
|
||||
|
||||
const TableGridEditor: FC<Props> = ({
|
||||
const TableGridEditor = ({
|
||||
theme = 'dark',
|
||||
|
||||
selectedSchema,
|
||||
@@ -73,15 +84,17 @@ const TableGridEditor: FC<Props> = ({
|
||||
selectedColumnToEdit,
|
||||
selectedTableToEdit,
|
||||
selectedValueForJsonEdit,
|
||||
selectedForeignKeyToEdit,
|
||||
|
||||
onAddRow = () => {},
|
||||
onEditRow = () => {},
|
||||
onAddColumn = () => {},
|
||||
onEditColumn = () => {},
|
||||
onDeleteColumn = () => {},
|
||||
onExpandJSONEditor = () => {},
|
||||
onClosePanel = () => {},
|
||||
}) => {
|
||||
onAddRow = noop,
|
||||
onEditRow = noop,
|
||||
onAddColumn = noop,
|
||||
onEditColumn = noop,
|
||||
onDeleteColumn = noop,
|
||||
onExpandJSONEditor = noop,
|
||||
onEditForeignKeyColumnValue = noop,
|
||||
onClosePanel = noop,
|
||||
}: TableGridEditorProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const { meta, ui, vault } = useStore()
|
||||
const router = useRouter()
|
||||
@@ -382,6 +395,7 @@ const TableGridEditor: FC<Props> = ({
|
||||
onError={onError}
|
||||
onSqlQuery={onSqlQuery}
|
||||
onExpandJSONEditor={onExpandJSONEditor}
|
||||
onEditForeignKeyColumnValue={onEditForeignKeyColumnValue}
|
||||
/>
|
||||
{!isUndefined(selectedSchema) && (
|
||||
<SidePanelEditor
|
||||
@@ -392,6 +406,7 @@ const TableGridEditor: FC<Props> = ({
|
||||
selectedColumnToEdit={selectedColumnToEdit}
|
||||
selectedTableToEdit={selectedTableToEdit}
|
||||
selectedValueForJsonEdit={selectedValueForJsonEdit}
|
||||
selectedForeignKeyToEdit={selectedForeignKeyToEdit}
|
||||
sidePanelKey={sidePanelKey}
|
||||
onRowCreated={onRowCreated}
|
||||
onRowUpdated={onRowUpdated}
|
||||
|
||||
@@ -14,6 +14,7 @@ import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import { NextPageWithLayout, SchemaView } from 'types'
|
||||
import { JsonEditValue } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types'
|
||||
import { ProjectContextFromParamsProvider } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
|
||||
const TableEditorPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
@@ -30,11 +31,18 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
const [selectedColumnToDelete, setSelectedColumnToDelete] = useState<PostgresColumn>()
|
||||
const [selectedTableToDelete, setSelectedTableToDelete] = useState<PostgresTable>()
|
||||
|
||||
const [sidePanelKey, setSidePanelKey] = useState<'row' | 'column' | 'table' | 'json'>()
|
||||
const [sidePanelKey, setSidePanelKey] = useState<
|
||||
'row' | 'column' | 'table' | 'json' | 'foreign-row-selector'
|
||||
>()
|
||||
const [selectedRowToEdit, setSelectedRowToEdit] = useState<Dictionary<any>>()
|
||||
const [selectedColumnToEdit, setSelectedColumnToEdit] = useState<PostgresColumn>()
|
||||
const [selectedTableToEdit, setSelectedTableToEdit] = useState<PostgresTable>()
|
||||
const [selectedValueForJsonEdit, setSelectedValueForJsonEdit] = useState<JsonEditValue>()
|
||||
const [selectedForeignKeyToEdit, setSelectedForeignKeyToEdit] = useState<{
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}>()
|
||||
|
||||
const tables: PostgresTable[] = meta.tables.list()
|
||||
const views: SchemaView[] = meta.views.list()
|
||||
@@ -111,6 +119,23 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
setSelectedValueForJsonEdit({ column, row, jsonString: JSON.stringify(row[column]) || '' })
|
||||
}
|
||||
|
||||
const onEditForeignKeyColumnValue = ({
|
||||
foreignKey,
|
||||
row,
|
||||
column,
|
||||
}: {
|
||||
foreignKey: NonNullable<ForeignRowSelectorProps['foreignKey']>
|
||||
row: any
|
||||
column: any
|
||||
}) => {
|
||||
setSidePanelKey('foreign-row-selector')
|
||||
setSelectedForeignKeyToEdit({
|
||||
foreignKey,
|
||||
row,
|
||||
column,
|
||||
})
|
||||
}
|
||||
|
||||
const onClosePanel = () => {
|
||||
setSidePanelKey(undefined)
|
||||
}
|
||||
@@ -211,12 +236,14 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
selectedColumnToEdit={selectedColumnToEdit}
|
||||
selectedTableToEdit={selectedTableToEdit}
|
||||
selectedValueForJsonEdit={selectedValueForJsonEdit}
|
||||
selectedForeignKeyToEdit={selectedForeignKeyToEdit}
|
||||
onAddRow={onAddRow}
|
||||
onEditRow={onEditRow}
|
||||
onAddColumn={onAddColumn}
|
||||
onEditColumn={onEditColumn}
|
||||
onDeleteColumn={onDeleteColumn}
|
||||
onExpandJSONEditor={onExpandJSONEditor}
|
||||
onEditForeignKeyColumnValue={onEditForeignKeyColumnValue}
|
||||
onClosePanel={onClosePanel}
|
||||
theme={ui.themeOption == 'dark' ? 'dark' : 'light'}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user