PolicyTableRowHeader.tsx150 lines · main
| 1 | import { PermissionAction } from '@supabase/shared-types/out/constants' |
| 2 | import { useParams } from 'common' |
| 3 | import { noop } from 'lodash' |
| 4 | import { Lock, Table } from 'lucide-react' |
| 5 | import { AiIconAnimation, Badge, CardTitle } from 'ui' |
| 6 | |
| 7 | import type { PolicyTable } from './PolicyTableRow.types' |
| 8 | import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' |
| 9 | import { ButtonTooltip } from '@/components/ui/ButtonTooltip' |
| 10 | import { EditorTablePageLink } from '@/data/prefetchers/project.$ref.editor.$id' |
| 11 | import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' |
| 12 | import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state' |
| 13 | import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' |
| 14 | |
| 15 | interface PolicyTableRowHeaderProps { |
| 16 | table: PolicyTable |
| 17 | isLocked: boolean |
| 18 | hasApiAccess: boolean |
| 19 | isLoadingApiAccess: boolean |
| 20 | onSelectToggleRLS: (table: PolicyTable) => void |
| 21 | onSelectCreatePolicy: (table: PolicyTable) => void |
| 22 | } |
| 23 | |
| 24 | export const PolicyTableRowHeader = ({ |
| 25 | table, |
| 26 | isLocked, |
| 27 | hasApiAccess, |
| 28 | isLoadingApiAccess, |
| 29 | onSelectToggleRLS = noop, |
| 30 | onSelectCreatePolicy, |
| 31 | }: PolicyTableRowHeaderProps) => { |
| 32 | const { ref } = useParams() |
| 33 | const aiSnap = useAiAssistantStateSnapshot() |
| 34 | const { openSidebar } = useSidebarManagerSnapshot() |
| 35 | |
| 36 | const { can: canCreatePolicies } = useAsyncCheckPermissions( |
| 37 | PermissionAction.TENANT_SQL_ADMIN_WRITE, |
| 38 | 'policies' |
| 39 | ) |
| 40 | const { can: canToggleRLS } = useAsyncCheckPermissions( |
| 41 | PermissionAction.TENANT_SQL_ADMIN_WRITE, |
| 42 | 'tables' |
| 43 | ) |
| 44 | |
| 45 | const isRealtimeSchema = table.schema === 'realtime' |
| 46 | const isRealtimeMessagesTable = isRealtimeSchema && table.name === 'messages' |
| 47 | const isTableLocked = isRealtimeSchema ? !isRealtimeMessagesTable : isLocked |
| 48 | |
| 49 | return ( |
| 50 | <div id={table.id.toString()} className="flex w-full items-center justify-between"> |
| 51 | <div className="flex gap-x-4 text-left flex-wrap"> |
| 52 | <EditorTablePageLink |
| 53 | projectRef={ref} |
| 54 | id={String(table.id)} |
| 55 | className="flex items-center gap-3 flex-wrap" |
| 56 | > |
| 57 | <Table strokeWidth={1.5} size={16} className="text-foreground-muted" /> |
| 58 | <CardTitle className="m-0 normal-case">{table.name}</CardTitle> |
| 59 | {!table.rls_enabled && ( |
| 60 | <Badge variant="warning" className="shrink-0"> |
| 61 | RLS Disabled |
| 62 | </Badge> |
| 63 | )} |
| 64 | {!isLoadingApiAccess && !hasApiAccess && ( |
| 65 | <Badge variant="default" className="shrink-0"> |
| 66 | API Disabled |
| 67 | </Badge> |
| 68 | )} |
| 69 | </EditorTablePageLink> |
| 70 | {isTableLocked && ( |
| 71 | <Badge> |
| 72 | <span className="flex gap-2 items-center text-xs uppercase text-foreground-lighter"> |
| 73 | <Lock size={12} /> Locked |
| 74 | </span> |
| 75 | </Badge> |
| 76 | )} |
| 77 | </div> |
| 78 | {!isTableLocked && ( |
| 79 | <div className="flex-1"> |
| 80 | <div className="flex flex-row justify-end gap-x-2"> |
| 81 | {!isRealtimeMessagesTable && ( |
| 82 | <ButtonTooltip |
| 83 | type="default" |
| 84 | disabled={!canToggleRLS} |
| 85 | onClick={() => onSelectToggleRLS(table)} |
| 86 | data-testid={`${table.name}-toggle-rls`} |
| 87 | tooltip={{ |
| 88 | content: { |
| 89 | side: 'bottom', |
| 90 | text: !canToggleRLS |
| 91 | ? 'You need additional permissions to toggle RLS' |
| 92 | : undefined, |
| 93 | }, |
| 94 | }} |
| 95 | > |
| 96 | {table.rls_enabled ? 'Disable RLS' : 'Enable RLS'} |
| 97 | </ButtonTooltip> |
| 98 | )} |
| 99 | <ButtonTooltip |
| 100 | type="default" |
| 101 | disabled={!canToggleRLS || !canCreatePolicies} |
| 102 | onClick={() => onSelectCreatePolicy(table)} |
| 103 | data-testid={`${table.name}-create-policy`} |
| 104 | tooltip={{ |
| 105 | content: { |
| 106 | side: 'bottom', |
| 107 | text: !canToggleRLS |
| 108 | ? !canToggleRLS || !canCreatePolicies |
| 109 | ? 'You need additional permissions to create RLS policies' |
| 110 | : undefined |
| 111 | : undefined, |
| 112 | }, |
| 113 | }} |
| 114 | > |
| 115 | Create policy |
| 116 | </ButtonTooltip> |
| 117 | |
| 118 | <ButtonTooltip |
| 119 | type="default" |
| 120 | className="px-1" |
| 121 | onClick={() => { |
| 122 | openSidebar(SIDEBAR_KEYS.AI_ASSISTANT) |
| 123 | aiSnap.newChat({ |
| 124 | name: 'Create new policy', |
| 125 | initialInput: `Create and name a new policy for the ${table.schema} schema on the ${table.name} table that ...`, |
| 126 | }) |
| 127 | }} |
| 128 | tooltip={{ |
| 129 | content: { |
| 130 | side: 'bottom', |
| 131 | text: |
| 132 | !canToggleRLS || !canCreatePolicies |
| 133 | ? 'You need additional permissions to create RLS policies' |
| 134 | : 'Create with Briven Assistant', |
| 135 | }, |
| 136 | }} |
| 137 | aria-label={ |
| 138 | !canToggleRLS || !canCreatePolicies |
| 139 | ? 'You need additional permissions to create RLS policies' |
| 140 | : 'Create with Briven Assistant' |
| 141 | } |
| 142 | > |
| 143 | <AiIconAnimation size={16} /> |
| 144 | </ButtonTooltip> |
| 145 | </div> |
| 146 | </div> |
| 147 | )} |
| 148 | </div> |
| 149 | ) |
| 150 | } |