forked from supabase/supabase
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: basic foreign key value selection
- Loading branch information
Showing
8 changed files
with
237 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 0 additions & 77 deletions
77
...io/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignKeySelector.tsx
This file was deleted.
Oops, something went wrong.
117 changes: 117 additions & 0 deletions
117
...faces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<SidePanel | ||
visible={visible} | ||
size="large" | ||
header={ | ||
<div> | ||
Selecting foreign key from{' '} | ||
<code className="text-sm"> | ||
{schemaName}.{tableName} | ||
</code> | ||
</div> | ||
} | ||
hideFooter={false} | ||
onCancel={closePanel} | ||
customFooter={<ActionBar hideApply backButtonLabel="Close" closePanel={closePanel} />} | ||
> | ||
<SidePanel.Content className="h-full !px-0"> | ||
<div className="h-full"> | ||
{isLoading && ( | ||
<div className="flex h-full py-6 flex-col items-center justify-center space-y-2"> | ||
<IconLoader className="animate-spin" /> | ||
<p className="text-sm text-scale-1100">Loading rows</p> | ||
</div> | ||
)} | ||
|
||
{isError && ( | ||
<div className="flex h-full py-6 flex-col items-center justify-center"> | ||
<p className="text-sm text-scale-1100"> | ||
Unable to load rows from{' '} | ||
<code> | ||
{schemaName}.{tableName} | ||
</code> | ||
. Please try again or contact support. | ||
</p> | ||
</div> | ||
)} | ||
|
||
{isSuccess && supaTable && ( | ||
<SelectorTable | ||
table={supaTable} | ||
rows={data?.rows ?? []} | ||
onRowSelect={(row) => onSelect(row[columnName ?? ''])} | ||
/> | ||
)} | ||
</div> | ||
</SidePanel.Content> | ||
</SidePanel> | ||
) | ||
} | ||
|
||
export default ForeignRowSelector |
36 changes: 36 additions & 0 deletions
36
.../TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string[]>([]) | ||
|
||
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 | ||
} |
59 changes: 59 additions & 0 deletions
59
...interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/SelectorTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className="flex h-full items-center justify-center gap-2"> | ||
{isPrimaryKey && ( | ||
<Tooltip.Root delayDuration={0}> | ||
<Tooltip.Trigger> | ||
<div className="text-brand-900"> | ||
<IconKey size="tiny" strokeWidth={2} /> | ||
</div> | ||
</Tooltip.Trigger> | ||
<Tooltip.Content side="bottom"> | ||
<Tooltip.Arrow className="radix-tooltip-arrow" /> | ||
<div className="rounded bg-scale-100 py-1 px-2 leading-none shadow border border-scale-200"> | ||
<span className="text-xs text-scale-1200">Primary key</span> | ||
</div> | ||
</Tooltip.Content> | ||
</Tooltip.Root> | ||
)} | ||
|
||
<span className="font-mono">{name}</span> | ||
</div> | ||
) | ||
} | ||
|
||
const formatter = (column: string, row: any) => { | ||
return ( | ||
<div className="group sb-grid-select-cell__formatter overflow-hidden"> | ||
<span className="font-mono text-xs truncate">{JSON.stringify(row[column])}</span> | ||
</div> | ||
) | ||
} | ||
|
||
const SelectorTable = ({ table, rows, onRowSelect }: SelectorTableProps) => { | ||
const columns: Column<SupaRow>[] = 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 ( | ||
<DataGrid columns={columns} rows={rows} style={{ height: '100%' }} onRowClick={onRowSelect} /> | ||
) | ||
} | ||
|
||
export default SelectorTable |
Oops, something went wrong.