import { ident, joinSqlFragments, safeSql, type SafeSqlFragment, } from '@supabase/pg-meta/src/pg-format' import { PermissionAction } from '@supabase/shared-types/out/constants' import { Check, ChevronsUpDown } from 'lucide-react' import { useEffect, useState } from 'react' import { UseFormReturn } from 'react-hook-form' import { Button, cn, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, FormControl, FormField, FormItem, FormLabel, FormMessage, Input, Popover, PopoverContent, PopoverTrigger, RadioGroup, RadioGroupLargeItem, ScrollArea, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, } from 'ui' import { MultiSelector, MultiSelectorContent, MultiSelectorItem, MultiSelectorList, MultiSelectorTrigger, } from 'ui-patterns/multi-select' import { useDatabaseRolesQuery } from '@/data/database-roles/database-roles-query' import { useTablesQuery } from '@/data/tables/tables-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' interface PolicyDetailsV2Props { schema: string searchString?: string selectedTable?: string isEditing: boolean form: UseFormReturn<{ name: string table: string behavior: string command: string roles: string }> onUpdateCommand: (command: string) => void onRolesChange?: (fragment: SafeSqlFragment) => void authContext: 'database' | 'realtime' } export const PolicyDetailsV2 = ({ schema, searchString, selectedTable, isEditing, form, onUpdateCommand, onRolesChange, authContext, }: PolicyDetailsV2Props) => { const { data: project } = useSelectedProjectQuery() const [open, setOpen] = useState(false) const { can: canUpdatePolicies } = useAsyncCheckPermissions( PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables' ) const { data: tables, isSuccess: isSuccessTables } = useTablesQuery({ projectRef: project?.ref, connectionString: project?.connectionString, schema: schema, sortByProperty: 'name', includeColumns: true, }) const { data: dbRoles } = useDatabaseRolesQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const formattedRoles = (dbRoles ?? []) .map((role) => { return { id: role.id, name: role.name, value: role.name, disabled: false, } }) .sort((a, b) => a.name.localeCompare(b.name)) useEffect(() => { if (!isEditing && selectedTable === undefined) { const table = tables?.find( (table) => table.schema === schema && (table.id.toString() === searchString || table.name === searchString) ) if (table) { form.setValue('table', table.name) } else if (isSuccessTables && tables.length > 0) { form.setValue('table', tables[0].name) } } }, [isEditing, form, searchString, tables, isSuccessTables, selectedTable]) return ( <>
( Policy Name )} /> ( Table on clause {authContext === 'database' && ( event.stopPropagation()}> No tables found 7 ? 'h-[200px]' : ''}> {(tables ?? []).map((table) => ( { form.setValue('table', table.name) setOpen(false) }} onClick={() => { form.setValue('table', table.name) setOpen(false) }} > {field.value === table.name ? : ''} {table.name} ))} )} {authContext === 'realtime' && ( )} )} /> ( Policy Behavior as clause )} /> ( Policy Command for clause { form.setValue('command', value) onUpdateCommand(value) }} className={cn('flex flex-wrap gap-3', isEditing && 'opacity-50')} > {[ 'select', 'insert', ...(authContext === 'database' ? ['update', 'delete', 'all'] : []), ].map((x) => ( ))} )} /> ( Target Roles to clause { field.onChange(roles.join(', ')) onRolesChange?.( roles.length === 0 ? safeSql`public` : joinSqlFragments( roles.map((r) => ident(r)), ', ' ) ) }} disabled={!canUpdatePolicies} values={field.value.length === 0 ? [] : field.value?.split(', ')} size="small" > {formattedRoles.map((role) => ( {role.name} ))} )} />
) }