Skip to content

Commit

Permalink
feat: basic foreign key value selection
Browse files Browse the repository at this point in the history
  • Loading branch information
alaister authored and joshenlim committed Mar 15, 2023
1 parent 0af811b commit edd36ad
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 150 deletions.
10 changes: 8 additions & 2 deletions packages/ui/src/components/SidePanel/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,16 @@ export function Separator() {
return <div className={__styles.separator}></div>
}

export function Content({ children }: { children: React.ReactNode }) {
export function Content({
children,
className,
}: {
children: React.ReactNode
className?: string
}) {
let __styles = styleHandler('sidepanel')

return <div className={__styles.content}>{children}</div>
return <div className={[__styles.content, className].join(' ').trim()}>{children}</div>
}

SidePanel.Content = Content
Expand Down

This file was deleted.

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
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
}
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
Loading

0 comments on commit edd36ad

Please sign in to comment.