import { useEffect, useMemo, useRef, useState } from "react"; import { Menu } from "@base-ui/react/menu"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { AgGridReact } from "ag-grid-react"; import type { ColDef, ICellRendererParams } from "ag-grid-community"; import { ChevronDown, KeyRound, Link2, Pencil, RefreshCw, Save, Sparkles, X } from "lucide-react"; import { toast } from "sonner"; import { Button } from "../../components/ui/button"; import { ApiError, apiErrorMessage } from "../../api/client"; import { consolidateCatalogDescriptions, listCatalogColumns, startCatalogSync, startDescriptionGenerationRun, updateCatalogColumnMetadata, updateCatalogColumnSensitive, type CatalogColumn, type CatalogSyncRun, type CatalogTable, type DescriptionGenerationRun, type SensitivityAnalysisRequest, } from "../../api/catalog-databases"; import type { DatabaseNavigationState } from "./model"; import { FleetActionSelector, type FleetActionOption } from "./FleetActionSelector"; import { FleetLedgerDrawer } from "./FleetLedgerShell"; import { NO_METADATA_GENERATION_LLM_MODEL_MESSAGE } from "./MetadataGenerationModelSelector"; interface Props { databaseId: string; table: CatalogTable; databaseVersion: number; canManage: boolean; selectedMetadataModel: string | null; descriptionGenerationActive: boolean; onDescriptionGenerationRunStarted: (run: DescriptionGenerationRun) => void; onCatalogSyncRunStarted?: (run: CatalogSyncRun) => void; onNavigationStateChange: (state: DatabaseNavigationState) => void; onSuggestSensitive: (selection: SensitivityAnalysisRequest, scopeLabel: string) => Promise; catalogOperationActive?: boolean; onCatalogMetricsChanged?: () => void | Promise; presentation?: "legacy" | "fleet"; } interface GridContext { canManage: boolean; busy: boolean; presentation: "legacy" | "fleet"; onEdit: (column: CatalogColumn, origin: HTMLButtonElement) => void; onSensitiveChange: (column: CatalogColumn, sensitive: boolean) => void; } function KeyCell({ data }: ICellRendererParams) { if (!data) return null; return (
{data.isPrimaryKey ? PK{data.primaryKeyPosition && data.primaryKeyPosition > 1 ? ` ${data.primaryKeyPosition}` : ""} : null} {data.isForeignKey ? FK{data.foreignKeyCount > 1 ? ` ${data.foreignKeyCount}` : ""} : null}
); } function ActionCell({ data, context }: ICellRendererParams) { if (!data || !context) return null; return (
event.stopPropagation()}>
); } function SensitiveCell({ data, context }: ICellRendererParams) { if (!data || !context) return null; return (
event.stopPropagation()}> context.onSensitiveChange(data, event.target.checked)} onClick={(event) => event.stopPropagation()} />
); } function ColumnMetadataFieldMatrix({ column, description, generatedDescription, canManage, busy, onDescriptionChange, onGeneratedDescriptionChange, }: { column: CatalogColumn; description: string; generatedDescription: string; canManage: boolean; busy: boolean; onDescriptionChange: (value: string) => void; onGeneratedDescriptionChange: (value: string) => void; }) { const fieldClass = [ "thot-column-metadata-field flex min-h-0 flex-col gap-1.5", "text-sm font-semibold", ].join(" "); const editableClass = [ "min-h-28 flex-1 resize-none rounded-md border border-input bg-card px-3 py-2", "text-sm font-normal outline-none focus:border-primary/60 focus:ring-3 focus:ring-ring/15", ].join(" "); const readOnlyClass = [ "min-h-28 flex-1 resize-none rounded-md border border-input bg-muted/35 px-3 py-2", "text-sm font-normal", ].join(" "); return (