feat: consolidate database management work

Add catalog-owned logical relationships and runtime snapshots, extend the database-management UI and validation coverage, and document the updated operational workflow.

Keep active sensitive-generation status in a tooltip and indicator, and update the layout E2E to follow the history action in its new database-scoped location.
This commit is contained in:
Codex
2026-09-01 14:46:55 +02:00
parent f586152636
commit 076c9742c5
73 changed files with 6966 additions and 610 deletions
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
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";
@@ -25,6 +25,7 @@ import type { DatabaseNavigationState } from "./model";
import { DatabaseColumns } from "./DatabaseColumns";
import { FleetActionSelector, type FleetActionOption } from "./FleetActionSelector";
import { FleetLedgerDrawer } from "./FleetLedgerShell";
import { NO_METADATA_GENERATION_LLM_MODEL_MESSAGE } from "./MetadataGenerationModelSelector";
interface Props {
database: CatalogDatabase;
@@ -39,10 +40,11 @@ interface Props {
onNestedNavigationChange?: (active: boolean) => void;
onRunStarted: (run: CatalogSyncRun) => void;
onOpenSync: () => void;
onOpenDescriptionHistory?: () => void;
onOpenSensitiveHistory?: () => void;
onDescriptionGenerationRunStarted: (run: DescriptionGenerationRun) => void;
onSuggestSensitive: (selection: SensitiveDataSuggestionRequest, scopeLabel: string) => Promise<void>;
onCatalogMetricsChanged?: () => void | Promise<void>;
metadataModelControl?: ReactNode;
presentation?: "legacy" | "fleet";
}
@@ -93,10 +95,11 @@ export function DatabaseTables({
onNestedNavigationChange,
onRunStarted,
onOpenSync,
onOpenDescriptionHistory,
onOpenSensitiveHistory,
onDescriptionGenerationRunStarted,
onSuggestSensitive,
onCatalogMetricsChanged,
metadataModelControl,
presentation = "legacy",
}: Props) {
const databaseId = database.id!;
@@ -314,7 +317,7 @@ export function DatabaseTables({
: selectedIds.length === 0
? "Select at least one table."
: !selectedMetadataModel
? "Choose a metadata model first."
? NO_METADATA_GENERATION_LLM_MODEL_MESSAGE
: descriptionGenerationActive || Boolean(currentRun)
? "Wait for the active catalog operation to finish."
: busy !== null
@@ -377,7 +380,7 @@ export function DatabaseTables({
: selectedIds.length === 0
? "Select at least one table."
: !selectedMetadataModel
? "Choose a metadata model first."
? NO_METADATA_GENERATION_LLM_MODEL_MESSAGE
: descriptionGenerationActive || Boolean(currentRun)
? "Wait for the active catalog operation to finish."
: busy !== null
@@ -487,8 +490,9 @@ export function DatabaseTables({
<div className="flex min-h-12 flex-wrap items-center gap-3 border-b border-border bg-muted/25 px-3 py-2">
<Button
type="button"
variant="outline"
className="thot-fleet-icon-action border-primary/35 bg-primary/8 shadow-sm"
variant="ghost"
size="sm"
className="thot-fleet-icon-action thot-fleet-back-action"
disabled={navigationBusy}
data-tooltip="Tables"
aria-label="Back to tables"
@@ -514,7 +518,6 @@ export function DatabaseTables({
bindingReady={bindingReady}
catalogOperationActive={Boolean(currentRun)}
onCatalogMetricsChanged={onCatalogMetricsChanged}
metadataModelControl={metadataModelControl}
presentation="fleet"
/>
</section>
@@ -712,7 +715,6 @@ export function DatabaseTables({
selectRef={fleetActionSelectRef}
onRun={runFleetAction}
onClear={clearSelection}
renderContext={(action) => action === "generate-descriptions" || action === "suggest-sensitive" ? metadataModelControl : null}
label="Table action"
/>
</>
@@ -751,7 +753,6 @@ export function DatabaseTables({
selectRef={fleetActionSelectRef}
onRun={runFleetAction}
onClear={clearSelection}
renderContext={(action) => action === "generate-descriptions" || action === "suggest-sensitive" ? metadataModelControl : null}
label="Table action"
/>
<input ref={searchInputRef} className="h-8 min-w-40 flex-1 rounded-md border border-input bg-background px-2.5 text-sm outline-none focus:border-primary/60 focus:ring-3 focus:ring-ring/15" aria-label="Search tables" placeholder="Search" value={search} onChange={(event) => setSearch(event.target.value)} />
@@ -773,6 +774,8 @@ export function DatabaseTables({
{currentRun ? <RefreshCw className="animate-spin" aria-hidden="true" /> : <History aria-hidden="true" />} Sync history
</Button>
) : null}
{onOpenDescriptionHistory ? <Button type="button" size="sm" variant="outline" onClick={onOpenDescriptionHistory}><History aria-hidden="true" /> Description history</Button> : null}
{onOpenSensitiveHistory ? <Button type="button" size="sm" variant="outline" onClick={onOpenSensitiveHistory}><History aria-hidden="true" /> Sensitive history</Button> : null}
</div>
{!bindingReady ? <div className="border-b border-border bg-muted/35 px-4 py-3 text-sm text-muted-foreground">Test the current database binding from Overview before synchronizing tables.</div> : null}
{fleetQueryError ?? <div className="relative min-h-[280px] flex-1">