feat: refine metadata catalog workflows

This commit is contained in:
Codex
2026-09-02 15:58:23 +02:00
parent 4531746038
commit ae053961a3
31 changed files with 848 additions and 219 deletions
@@ -10,9 +10,11 @@ import { ApiError, apiErrorMessage } from "../../api/client";
import {
consolidateCatalogDescriptions,
listCatalogColumns,
startCatalogSync,
startDescriptionGenerationRun,
updateCatalogColumnMetadata,
type CatalogColumn,
type CatalogSyncRun,
type CatalogTable,
type DescriptionGenerationRun,
type SensitiveDataSuggestionRequest,
@@ -25,10 +27,12 @@ import { NO_METADATA_GENERATION_LLM_MODEL_MESSAGE } from "./MetadataGenerationMo
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: SensitiveDataSuggestionRequest, scopeLabel: string) => Promise<void>;
catalogOperationActive?: boolean;
@@ -88,10 +92,12 @@ function SensitiveCell({ data, context }: ICellRendererParams<CatalogColumn, unk
export function DatabaseColumns({
databaseId,
table,
databaseVersion,
canManage,
selectedMetadataModel,
descriptionGenerationActive,
onDescriptionGenerationRunStarted,
onCatalogSyncRunStarted,
onNavigationStateChange,
onSuggestSensitive,
catalogOperationActive = false,
@@ -285,15 +291,33 @@ export function DatabaseColumns({
};
type FleetColumnAction =
| "sync-columns"
| "generate-descriptions"
| "consolidate-descriptions"
| "suggest-sensitive"
| "save-sensitive";
const fleetActions: readonly FleetActionOption<FleetColumnAction>[] = [
{
id: "sync-columns",
label: "Synchronize columns for this table",
group: "Synchronization",
scopeLabel: "Selected table",
runLabel: "Synchronize",
disabled: !canManage || selectedIds.length === 0 || catalogOperationActive || busy,
disabledReason: !canManage
? "You do not have permission to synchronize the catalog."
: selectedIds.length === 0
? "Select at least one column."
: catalogOperationActive
? "Wait for the active catalog operation to finish."
: busy
? "Another action is running."
: undefined,
},
{
id: "generate-descriptions",
label: "Generate descriptions",
label: "Generate column descriptions",
group: "Descriptions",
runLabel: "Generate",
disabled: !canManage || selectedIds.length === 0 || !selectedMetadataModel || descriptionGenerationActive || catalogOperationActive || busy,
@@ -311,7 +335,7 @@ export function DatabaseColumns({
},
{
id: "consolidate-descriptions",
label: "Move generated to Description",
label: "Copy generated descriptions to Description",
group: "Descriptions",
runLabel: "Move",
disabled: !canManage || selectedIds.length === 0 || catalogOperationActive || busy,
@@ -345,14 +369,14 @@ export function DatabaseColumns({
},
{
id: "save-sensitive",
label: "Save sensitive-field changes",
label: "Save sensitive field changes",
group: "Sensitive data",
runLabel: "Save",
disabled: !canManage || changedSensitiveColumns.length === 0 || catalogOperationActive || busy,
disabledReason: !canManage
? "You do not have permission to update sensitive fields."
: changedSensitiveColumns.length === 0
? "No sensitive-field changes are waiting to be saved."
? "No sensitive field changes to save."
: catalogOperationActive
? "Wait for the active catalog operation to finish."
: busy
@@ -362,7 +386,8 @@ export function DatabaseColumns({
];
const runFleetAction = async (action: FleetColumnAction) => {
if (action === "generate-descriptions") await generateDescriptions();
if (action === "sync-columns") await synchronizeColumns();
else if (action === "generate-descriptions") await generateDescriptions();
else if (action === "consolidate-descriptions") await consolidateDescriptions();
else if (action === "suggest-sensitive") await suggestSensitive();
else await saveSensitive();
@@ -373,6 +398,22 @@ export function DatabaseColumns({
setSelectedIds([]);
};
const synchronizeColumns = async () => {
if (selectedIds.length === 0) return;
setBusy(true);
try {
const run = await startCatalogSync(databaseId, databaseVersion, "columns", [table.id]);
onCatalogSyncRunStarted?.(run);
gridRef.current?.api.deselectAll();
setSelectedIds([]);
toast.success("Column synchronization started");
} catch (error) {
toast.error(apiErrorMessage(error));
} finally {
setBusy(false);
}
};
const columns = useMemo<ColDef<CatalogColumn>[]>(() => [
{ field: "ordinalPosition", headerName: "#", width: 64, maxWidth: 64, filter: "agNumberColumnFilter" },
{ field: "sensitive", headerName: "Sensitive", headerTooltip: "Mark this column as sensitive; this does not select it for actions.", minWidth: 110, width: 110, sortable: false, filter: false, resizable: false, cellRenderer: SensitiveCell },
@@ -398,7 +439,7 @@ export function DatabaseColumns({
<p className="font-semibold text-destructive">Catalog columns could not be loaded.</p>
<p className="mt-1 leading-5 text-muted-foreground">{apiErrorMessage(error)}</p>
<Button type="button" variant="outline" className="mt-4" disabled={isFetching} onClick={() => void refetch()}>
<RefreshCw className={isFetching ? "animate-spin" : ""} aria-hidden="true" /> Retry columns
<RefreshCw className={isFetching ? "animate-spin" : ""} aria-hidden="true" /> Retry loading columns
</Button>
</div>
</div>
@@ -456,8 +497,9 @@ export function DatabaseColumns({
<Menu.Portal>
<Menu.Positioner side="bottom" align="start" sideOffset={4}>
<Menu.Popup className="z-50 min-w-64 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none">
<Menu.Item className="rounded-md px-3 py-2 text-sm outline-none data-[highlighted]:bg-muted data-[disabled]:opacity-45" disabled={!canManage || !selectedMetadataModel || descriptionGenerationActive || busy} onClick={() => void generateDescriptions()}>Generate {selectedIds.length === 1 ? "description" : "descriptions"}</Menu.Item>
<Menu.Item className="rounded-md px-3 py-2 text-sm outline-none data-[highlighted]:bg-muted data-[disabled]:opacity-45" disabled={!canManage || busy} onClick={() => void consolidateDescriptions()}>Move generated description to Description</Menu.Item>
<Menu.Item className="rounded-md px-3 py-2 text-sm outline-none data-[highlighted]:bg-muted data-[disabled]:opacity-45" disabled={!canManage || !selectedIds.length || catalogOperationActive || busy} onClick={() => void synchronizeColumns()}>Synchronize columns for this table</Menu.Item>
<Menu.Item className="rounded-md px-3 py-2 text-sm outline-none data-[highlighted]:bg-muted data-[disabled]:opacity-45" disabled={!canManage || !selectedMetadataModel || descriptionGenerationActive || busy} onClick={() => void generateDescriptions()}>Generate {selectedIds.length === 1 ? "column description" : "column descriptions"}</Menu.Item>
<Menu.Item className="rounded-md px-3 py-2 text-sm outline-none data-[highlighted]:bg-muted data-[disabled]:opacity-45" disabled={!canManage || busy} onClick={() => void consolidateDescriptions()}>Copy generated descriptions to Description</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>