import { useParams } from 'common' import { Copy, Download, Edit, Globe, Lock, MoreVertical, Trash } from 'lucide-react' import Link from 'next/link' import { type CSSProperties } from 'react' import { toast } from 'sonner' import { Badge, Button, cn, copyToClipboard, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Tooltip, TooltipContent, TooltipTrigger, TreeViewItemVariant, } from 'ui' import { useExportAllRowsAsCsv, useExportAllRowsAsSql } from './ExportAllRows' import { useTableFilter } from '@/components/grid/hooks/useTableFilter' import { buildTableEditorUrl } from '@/components/grid/BrivenGrid.utils' import { getEntityLintDetails } from '@/components/interfaces/TableGridEditor/TableEntity.utils' import { EntityTypeIcon } from '@/components/ui/EntityTypeIcon' import { InlineLink } from '@/components/ui/InlineLink' import { getTableDefinition } from '@/data/database/table-definition-query' import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants' import { Entity } from '@/data/entity-types/entity-types-infinite-query' import { useProjectLintsQuery } from '@/data/lint/lint-query' import { EditorTablePageLink } from '@/data/prefetchers/project.$ref.editor.$id' import type { TableApiAccessData, TableApiAccessMap, } from '@/data/privileges/table-api-access-query' import { useTableRowsCountQuery } from '@/data/table-rows/table-rows-count-query' import { useQuerySchemaState } from '@/hooks/misc/useSchemaQueryState' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { formatSql } from '@/lib/formatSql' import { useRoleImpersonationStateSnapshot, type RoleImpersonationState, } from '@/state/role-impersonation-state' import { useTableEditorStateSnapshot } from '@/state/table-editor' import { createTabId, useTabsStateSnapshot } from '@/state/tabs' export interface EntityListItemProps { id: number | string projectRef: string item: Entity isLocked: boolean isActive?: boolean style?: CSSProperties onExportCLI: () => void apiAccessMap?: TableApiAccessMap } // [jordi] Used to determine the entity is a table and not a view or other unsupported entity type function isTableLikeEntityListItem(entity: { type?: string }) { return entity?.type === ENTITY_TYPE.TABLE || entity?.type === ENTITY_TYPE.PARTITIONED_TABLE } export const EntityListItem = ({ id, projectRef, item: entity, isLocked, isActive: _isActive, style, onExportCLI, apiAccessMap, }: EntityListItemProps) => { const { data: project } = useSelectedProjectQuery() const snap = useTableEditorStateSnapshot() const { selectedSchema } = useQuerySchemaState() const tabId = createTabId(entity.type, { id: entity.id }) const tabs = useTabsStateSnapshot() const isPreview = tabs.previewTabId === tabId const isActive = Number(id) === entity.id const canEdit = isActive && !isLocked const { filters } = useTableFilter() const roleImpersonationState = useRoleImpersonationStateSnapshot() const { data: countData } = useTableRowsCountQuery( { projectRef, tableId: entity.id, filters, enforceExactCount: false, roleImpersonationState: roleImpersonationState as RoleImpersonationState, }, { enabled: isTableLikeEntityListItem(entity) && isActive, } ) const rowCount = countData?.count const { data: lints = [] } = useProjectLintsQuery({ projectRef: project?.ref, }) const tableHasRlsDisabledLint: boolean = getEntityLintDetails( entity.name, 'rls_disabled_in_public', ['ERROR'], lints, selectedSchema ).hasLint const tableHasRlsEnabledNoPolicyLint: boolean = getEntityLintDetails( entity.name, 'rls_enabled_no_policy', ['ERROR', 'WARN', 'INFO'], lints, selectedSchema ).hasLint const viewHasLints: boolean = getEntityLintDetails( entity.name, 'security_definer_view', ['ERROR', 'WARN'], lints, selectedSchema ).hasLint const materializedViewHasLints: boolean = getEntityLintDetails( entity.name, 'materialized_view_in_api', ['ERROR', 'WARN'], lints, selectedSchema ).hasLint const foreignTableHasLints: boolean = getEntityLintDetails( entity.name, 'foreign_table_in_api', ['ERROR', 'WARN'], lints, selectedSchema ).hasLint const apiAccessData = apiAccessMap?.[entity.name] const formatTooltipText = (entityType: string) => { const text = Object.entries(ENTITY_TYPE) .find(([, value]) => value === entityType)?.[0] ?.toLowerCase() ?.split('_') ?.join(' ') || '' // Return sentence case (capitalize first letter only) return text.charAt(0).toUpperCase() + text.slice(1) } const { exportCsv, confirmationModal: exportCsvConfirmationModal } = useExportAllRowsAsCsv({ enabled: true, projectRef, connectionString: project?.connectionString ?? null, entity, type: 'fetch_all', totalRows: rowCount, }) const { exportSql, confirmationModal: exportSqlConfirmationModal } = useExportAllRowsAsSql({ enabled: true, projectRef, connectionString: project?.connectionString ?? null, entity, type: 'fetch_all', totalRows: rowCount, }) return ( { e.preventDefault() const tabId = createTabId(entity.type, { id: entity.id }) tabs.makeTabPermanent(tabId) }} > <> {isActive &&
} {formatTooltipText(entity.type)}
{entity.name}
{canEdit && (