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:
@@ -6,7 +6,7 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { History, Plus, RefreshCw } from "lucide-react";
|
||||
import { History, RefreshCw } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { ApiError, apiErrorMessage } from "../api/client";
|
||||
@@ -209,11 +209,7 @@ export function DatabaseManagementPage({
|
||||
() => queryClient.invalidateQueries({ queryKey: ["catalog-metrics"] }),
|
||||
[queryClient],
|
||||
);
|
||||
const availableWorkspaces = useMemo(
|
||||
() => rows.filter((row) => row.workspaceAvailable && !row.configured),
|
||||
[rows],
|
||||
);
|
||||
const editable = screen.kind === "add" || screen.kind === "edit";
|
||||
const editable = screen.kind === "configure" || screen.kind === "edit";
|
||||
const configurationDirty = Boolean(
|
||||
editable
|
||||
&& draft
|
||||
@@ -221,8 +217,9 @@ export function DatabaseManagementPage({
|
||||
);
|
||||
const dirty = Boolean(editable && draft && (configurationDirty || hasSecretChanges(draft)));
|
||||
const busy = busyAction !== null;
|
||||
const navigationDirty = screen.kind === "tables" ? tablesNavigationState.dirty : dirty;
|
||||
const navigationBusy = screen.kind === "tables" ? tablesNavigationState.busy : busy;
|
||||
const catalogChildVisible = screen.kind === "tables" || screen.kind === "relationships";
|
||||
const navigationDirty = catalogChildVisible ? tablesNavigationState.dirty : dirty;
|
||||
const navigationBusy = catalogChildVisible ? tablesNavigationState.busy : busy;
|
||||
|
||||
useEffect(() => {
|
||||
onNavigationStateChange?.({ dirty: navigationDirty, busy: navigationBusy });
|
||||
@@ -263,9 +260,14 @@ export function DatabaseManagementPage({
|
||||
}, [queryClient]);
|
||||
|
||||
const restoreListFocus = useCallback(() => {
|
||||
const originLabel = originRef.current?.getAttribute("aria-label");
|
||||
window.setTimeout(() => {
|
||||
if (originRef.current?.isConnected) {
|
||||
originRef.current.focus();
|
||||
} else if (originLabel) {
|
||||
const matchingAction = [...document.querySelectorAll<HTMLButtonElement>("button")]
|
||||
.find((button) => button.getAttribute("aria-label") === originLabel);
|
||||
(matchingAction ?? searchInputRef.current)?.focus();
|
||||
} else {
|
||||
searchInputRef.current?.focus();
|
||||
}
|
||||
@@ -307,7 +309,6 @@ export function DatabaseManagementPage({
|
||||
mode: DatabaseFormMode,
|
||||
row: CatalogDatabase,
|
||||
origin: HTMLElement,
|
||||
workspaceLocked = false,
|
||||
) => {
|
||||
const nextDraft = draftFrom(row);
|
||||
originRef.current = origin;
|
||||
@@ -320,7 +321,6 @@ export function DatabaseManagementPage({
|
||||
setScreen({
|
||||
kind: mode,
|
||||
workspaceId: row.workspaceId,
|
||||
...(mode === "add" ? { workspaceLocked } : {}),
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -329,7 +329,7 @@ export function DatabaseManagementPage({
|
||||
}, [openForm]);
|
||||
|
||||
const editRow = useCallback((row: CatalogDatabase, origin: HTMLButtonElement) => {
|
||||
openForm(row.configured ? "edit" : "add", row, origin, !row.configured);
|
||||
openForm(row.configured ? "edit" : "configure", row, origin);
|
||||
}, [openForm]);
|
||||
|
||||
const deleteRow = useCallback((row: CatalogDatabase, origin: HTMLButtonElement) => {
|
||||
@@ -349,8 +349,9 @@ export function DatabaseManagementPage({
|
||||
setScreen({ kind: "tables", workspaceId: row.workspaceId });
|
||||
}, []);
|
||||
|
||||
const openRelationships = useCallback((row: CatalogDatabase) => {
|
||||
const openRelationships = useCallback((row: CatalogDatabase, origin?: HTMLElement) => {
|
||||
if (!row.configured || !row.id) return;
|
||||
if (origin) originRef.current = origin;
|
||||
setDraft(null);
|
||||
setBaseline("");
|
||||
setFormSource(null);
|
||||
@@ -372,25 +373,6 @@ export function DatabaseManagementPage({
|
||||
setScreen({ kind: "view", workspaceId: row.workspaceId });
|
||||
}, []);
|
||||
|
||||
const addDatabase = useCallback((origin: HTMLElement) => {
|
||||
const workspace = availableWorkspaces[0];
|
||||
if (!workspace || !canManage) return;
|
||||
openForm("add", workspace, origin, false);
|
||||
}, [availableWorkspaces, canManage, openForm]);
|
||||
|
||||
const changeWorkspace = useCallback((workspaceId: string) => {
|
||||
const workspace = availableWorkspaces.find((row) => row.workspaceId === workspaceId);
|
||||
if (!workspace) return;
|
||||
if (dirty && !window.confirm("Discard changes and choose another workspace?")) return;
|
||||
const nextDraft = draftFrom(workspace);
|
||||
setDraft(nextDraft);
|
||||
setBaseline(configurationFingerprint(nextDraft));
|
||||
setFormSource({ id: workspace.id, version: workspace.version });
|
||||
setStale(false);
|
||||
setPartialSecretFailure(null);
|
||||
setScreen({ kind: "add", workspaceId, workspaceLocked: false });
|
||||
}, [availableWorkspaces, dirty]);
|
||||
|
||||
const changeField = useCallback((field: "databaseName" | "schema", value: string) => {
|
||||
setDraft((current) => current ? { ...current, [field]: value } : current);
|
||||
}, []);
|
||||
@@ -624,7 +606,7 @@ export function DatabaseManagementPage({
|
||||
showList();
|
||||
toast.info("This database configuration has already been deleted");
|
||||
} else if (screen.kind === "edit" && !latest.configured) {
|
||||
setScreen({ kind: "add", workspaceId: latest.workspaceId, workspaceLocked: true });
|
||||
setScreen({ kind: "configure", workspaceId: latest.workspaceId });
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(apiErrorMessage(error));
|
||||
@@ -669,22 +651,40 @@ export function DatabaseManagementPage({
|
||||
}, [queryClient]);
|
||||
|
||||
const openDescriptionGenerationHistory = useCallback(() => {
|
||||
const run = observedActiveDescriptionGenerationRun
|
||||
?? descriptionGenerationRuns[0]
|
||||
?? activeDescriptionGenerationRun;
|
||||
const scopedRuns = activeRow ? descriptionGenerationRuns.filter((item) => item.databaseId === activeRow.id) : descriptionGenerationRuns;
|
||||
const scopedActiveRun = observedActiveDescriptionGenerationRun
|
||||
&& (!activeRow || observedActiveDescriptionGenerationRun.databaseId === activeRow.id)
|
||||
? observedActiveDescriptionGenerationRun
|
||||
: undefined;
|
||||
const scopedSelectedRun = activeDescriptionGenerationRun
|
||||
&& (!activeRow || activeDescriptionGenerationRun.databaseId === activeRow.id)
|
||||
? activeDescriptionGenerationRun
|
||||
: undefined;
|
||||
const run = scopedActiveRun
|
||||
?? scopedRuns[0]
|
||||
?? scopedSelectedRun;
|
||||
if (run) setActiveDescriptionGenerationRun(run);
|
||||
setSensitiveDataSuggestionHistoryDrawerOpen(false);
|
||||
setDescriptionGenerationDrawerOpen(true);
|
||||
}, [activeDescriptionGenerationRun, descriptionGenerationRuns, observedActiveDescriptionGenerationRun]);
|
||||
}, [activeDescriptionGenerationRun, activeRow, descriptionGenerationRuns, observedActiveDescriptionGenerationRun]);
|
||||
|
||||
const openSensitiveDataSuggestionHistory = useCallback(() => {
|
||||
const run = observedActiveSensitiveDataSuggestionRun
|
||||
?? sensitiveDataSuggestionRuns[0]
|
||||
?? activeSensitiveDataSuggestionRun;
|
||||
const scopedRuns = activeRow ? sensitiveDataSuggestionRuns.filter((item) => item.databaseId === activeRow.id) : sensitiveDataSuggestionRuns;
|
||||
const scopedActiveRun = observedActiveSensitiveDataSuggestionRun
|
||||
&& (!activeRow || observedActiveSensitiveDataSuggestionRun.databaseId === activeRow.id)
|
||||
? observedActiveSensitiveDataSuggestionRun
|
||||
: undefined;
|
||||
const scopedSelectedRun = activeSensitiveDataSuggestionRun
|
||||
&& (!activeRow || activeSensitiveDataSuggestionRun.databaseId === activeRow.id)
|
||||
? activeSensitiveDataSuggestionRun
|
||||
: undefined;
|
||||
const run = scopedActiveRun
|
||||
?? scopedRuns[0]
|
||||
?? scopedSelectedRun;
|
||||
if (run) setActiveSensitiveDataSuggestionRun(run);
|
||||
setDescriptionGenerationDrawerOpen(false);
|
||||
setSensitiveDataSuggestionHistoryDrawerOpen(true);
|
||||
}, [activeSensitiveDataSuggestionRun, observedActiveSensitiveDataSuggestionRun, sensitiveDataSuggestionRuns]);
|
||||
}, [activeRow, activeSensitiveDataSuggestionRun, observedActiveSensitiveDataSuggestionRun, sensitiveDataSuggestionRuns]);
|
||||
|
||||
const descriptionGenerationTerminated = useCallback(async (run: DescriptionGenerationRun) => {
|
||||
await Promise.all([
|
||||
@@ -902,6 +902,7 @@ export function DatabaseManagementPage({
|
||||
isLoading={metadataModelsQuery.isLoading}
|
||||
selectedModel={selectedMetadataModel}
|
||||
onSelectedModelChange={setSelectedMetadataModel}
|
||||
variant="compact"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -943,6 +944,7 @@ export function DatabaseManagementPage({
|
||||
/>
|
||||
<DescriptionGenerationDrawer
|
||||
open={descriptionGenerationDrawerOpen}
|
||||
databaseId={activeRow?.id ?? null}
|
||||
run={activeDescriptionGenerationRun}
|
||||
modelLabel={metadataModels.find(
|
||||
(model) => model.id === activeDescriptionGenerationRun?.modelId,
|
||||
@@ -953,6 +955,7 @@ export function DatabaseManagementPage({
|
||||
/>
|
||||
<SensitiveDataSuggestionHistoryDrawer
|
||||
open={sensitiveDataSuggestionHistoryDrawerOpen}
|
||||
databaseId={activeRow?.id ?? null}
|
||||
run={activeSensitiveDataSuggestionRun}
|
||||
modelLabel={metadataModels.find(
|
||||
(model) => model.id === activeSensitiveDataSuggestionRun?.modelId,
|
||||
@@ -980,7 +983,6 @@ export function DatabaseManagementPage({
|
||||
eyebrow="Catalog configuration"
|
||||
title={databaseFormTitle(
|
||||
fleetFormMode,
|
||||
fleetFormMode === "add" && Boolean(screen.kind === "add" && screen.workspaceLocked),
|
||||
"drawer",
|
||||
)}
|
||||
description={`${activeRow.workspaceName} · ${activeRow.workspaceId}`}
|
||||
@@ -993,8 +995,6 @@ export function DatabaseManagementPage({
|
||||
presentation="drawer"
|
||||
mode={fleetFormMode}
|
||||
row={activeRow}
|
||||
availableWorkspaces={availableWorkspaces}
|
||||
workspaceLocked={fleetFormMode === "add" && Boolean(screen.kind === "add" && screen.workspaceLocked)}
|
||||
draft={draft}
|
||||
dirty={dirty}
|
||||
canManage={canManage}
|
||||
@@ -1005,7 +1005,6 @@ export function DatabaseManagementPage({
|
||||
partialSecretFailure={partialSecretFailure}
|
||||
headingRef={formHeadingRef}
|
||||
onBack={backToList}
|
||||
onWorkspaceChange={changeWorkspace}
|
||||
onFieldChange={changeField}
|
||||
onTransportChange={changeTransport}
|
||||
onBindingChange={changeBinding}
|
||||
@@ -1020,6 +1019,8 @@ export function DatabaseManagementPage({
|
||||
onOpenRelationships={() => openRelationships(activeRow)}
|
||||
onSync={(scope) => void syncDatabase(scope)}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
activeSyncRun={currentActiveRun}
|
||||
/>
|
||||
</FleetLedgerDrawer>
|
||||
@@ -1040,10 +1041,11 @@ export function DatabaseManagementPage({
|
||||
onNestedNavigationChange={setTableNestedNavigationActive}
|
||||
onRunStarted={rememberSyncRun}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
onDescriptionGenerationRunStarted={rememberDescriptionGenerationRun}
|
||||
onSuggestSensitive={suggestActiveDatabaseSensitiveFields}
|
||||
onCatalogMetricsChanged={invalidateCatalogMetrics}
|
||||
metadataModelControl={fleetMetadataModelControl}
|
||||
/>
|
||||
) : screen.kind === "relationships" && relationshipsVisible ? (
|
||||
<DatabaseRelationships
|
||||
@@ -1055,6 +1057,10 @@ export function DatabaseManagementPage({
|
||||
onOpenTables={() => openTables(activeRow)}
|
||||
onRunStarted={rememberSyncRun}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
onCatalogMetricsChanged={invalidateCatalogMetrics}
|
||||
onNavigationStateChange={setTablesNavigationState}
|
||||
/>
|
||||
) : isError && rows.length === 0 ? (
|
||||
<div className="grid h-full place-items-center p-6">
|
||||
@@ -1078,6 +1084,7 @@ export function DatabaseManagementPage({
|
||||
onSearchChange={setSearch}
|
||||
onView={viewRow}
|
||||
onOpenTables={(row, origin) => openTables(row, origin)}
|
||||
onOpenRelationships={(row, origin) => openRelationships(row, origin)}
|
||||
onEdit={editRow}
|
||||
onDelete={deleteRow}
|
||||
onOpenSync={openSync}
|
||||
@@ -1088,7 +1095,6 @@ export function DatabaseManagementPage({
|
||||
onGenerateDescriptions={generateDatabaseDescriptions}
|
||||
onSuggestSensitive={suggestDatabaseSensitiveFields}
|
||||
onDeleteMetadataSelected={deleteSelectedMetadata}
|
||||
metadataModelControl={fleetMetadataModelControl}
|
||||
onRefresh={refreshList}
|
||||
/>
|
||||
);
|
||||
@@ -1118,28 +1124,7 @@ export function DatabaseManagementPage({
|
||||
)}
|
||||
actions={(
|
||||
<>
|
||||
<Button type="button" variant="outline" size="sm" className="thot-fleet-nav-action thot-fleet-nav-action--below" data-tooltip="Description history" data-fleet-action="secondary" onClick={openDescriptionGenerationHistory}>
|
||||
<History aria-hidden="true" /> Description history
|
||||
{observedActiveDescriptionGenerationRun ? <span className="size-1.5 rounded-full bg-warning" aria-hidden="true" /> : null}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" className="thot-fleet-nav-action thot-fleet-nav-action--below" data-tooltip="Sensitive history" data-fleet-action="secondary" onClick={openSensitiveDataSuggestionHistory}>
|
||||
<History aria-hidden="true" /> Sensitive history
|
||||
{observedActiveSensitiveDataSuggestionRun ? <span className="size-1.5 rounded-full bg-warning" aria-hidden="true" /> : null}
|
||||
</Button>
|
||||
{(screen.kind === "list" || formVisible) ? (
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
className="thot-fleet-nav-action thot-fleet-nav-action--below"
|
||||
data-tooltip="Add database"
|
||||
data-fleet-action="primary"
|
||||
disabled={!canManage || availableWorkspaces.length === 0}
|
||||
title={availableWorkspaces.length === 0 ? "Every available workspace is already configured" : undefined}
|
||||
onClick={(event) => addDatabase(event.currentTarget)}
|
||||
>
|
||||
<Plus aria-hidden="true" /> Add database
|
||||
</Button>
|
||||
) : null}
|
||||
{fleetMetadataModelControl}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
@@ -1205,40 +1190,16 @@ export function DatabaseManagementPage({
|
||||
selectedModel={selectedMetadataModel}
|
||||
onSelectedModelChange={setSelectedMetadataModel}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="whitespace-nowrap disabled:opacity-70"
|
||||
aria-label="Run descriptions generation history"
|
||||
disabled={!canManage}
|
||||
title={observedActiveDescriptionGenerationRun
|
||||
? "Description generation is active"
|
||||
: "View description generation history"}
|
||||
onClick={openDescriptionGenerationHistory}
|
||||
>
|
||||
<History />
|
||||
Run descriptions generation history
|
||||
{observedActiveDescriptionGenerationRun ? (
|
||||
<span aria-hidden="true" className="size-1.5 rounded-full bg-primary" />
|
||||
) : null}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="whitespace-nowrap disabled:opacity-70"
|
||||
aria-label="Run sensitive suggestions history"
|
||||
disabled={!canManage}
|
||||
title={observedActiveSensitiveDataSuggestionRun
|
||||
? "Sensitive suggestion generation is active"
|
||||
: "View sensitive suggestion history"}
|
||||
onClick={openSensitiveDataSuggestionHistory}
|
||||
>
|
||||
<History />
|
||||
Run sensitive suggestions history
|
||||
{observedActiveSensitiveDataSuggestionRun ? (
|
||||
<span aria-hidden="true" className="size-1.5 rounded-full bg-primary" />
|
||||
) : null}
|
||||
</Button>
|
||||
{screen.kind === "list" ? <>
|
||||
<Button type="button" variant="outline" className="whitespace-nowrap disabled:opacity-70" aria-label="Run descriptions generation history" disabled={!canManage} title="View description generation history" onClick={openDescriptionGenerationHistory}>
|
||||
<History /> Run descriptions generation history
|
||||
</Button>
|
||||
<Button type="button" variant="outline" className="whitespace-nowrap disabled:opacity-70" aria-label="Run sensitive suggestions history" disabled={!canManage} title={observedActiveSensitiveDataSuggestionRun ? "Sensitive suggestion generation is active" : "View sensitive suggestion history"} onClick={openSensitiveDataSuggestionHistory}>
|
||||
<History />
|
||||
{observedActiveSensitiveDataSuggestionRun ? <span aria-hidden="true" className="size-2 rounded-full bg-primary" /> : null}
|
||||
Run sensitive suggestions history
|
||||
</Button>
|
||||
</> : null}
|
||||
</div>
|
||||
{screen.kind === "list" ? (
|
||||
<div
|
||||
@@ -1249,14 +1210,6 @@ export function DatabaseManagementPage({
|
||||
<Button type="button" variant="outline" disabled={isFetching} onClick={() => void refreshList()}>
|
||||
<RefreshCw className={isFetching ? "animate-spin" : ""} /> Refresh
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={!canManage || availableWorkspaces.length === 0}
|
||||
title={availableWorkspaces.length === 0 ? "Every available workspace is already configured" : undefined}
|
||||
onClick={(event) => addDatabase(event.currentTarget)}
|
||||
>
|
||||
<Plus /> Add database
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1308,8 +1261,6 @@ export function DatabaseManagementPage({
|
||||
<DatabaseForm
|
||||
mode={screen.kind as DatabaseFormMode}
|
||||
row={activeRow}
|
||||
availableWorkspaces={availableWorkspaces}
|
||||
workspaceLocked={screen.kind === "add" && Boolean(screen.workspaceLocked)}
|
||||
draft={draft}
|
||||
dirty={dirty}
|
||||
canManage={canManage}
|
||||
@@ -1320,7 +1271,6 @@ export function DatabaseManagementPage({
|
||||
partialSecretFailure={partialSecretFailure}
|
||||
headingRef={formHeadingRef}
|
||||
onBack={backToList}
|
||||
onWorkspaceChange={changeWorkspace}
|
||||
onFieldChange={changeField}
|
||||
onTransportChange={changeTransport}
|
||||
onBindingChange={changeBinding}
|
||||
@@ -1335,6 +1285,8 @@ export function DatabaseManagementPage({
|
||||
onOpenRelationships={() => openRelationships(activeRow)}
|
||||
onSync={(scope) => void syncDatabase(scope)}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
activeSyncRun={currentActiveRun}
|
||||
/>
|
||||
) : null}
|
||||
@@ -1352,6 +1304,8 @@ export function DatabaseManagementPage({
|
||||
onNavigationStateChange={setTablesNavigationState}
|
||||
onRunStarted={rememberSyncRun}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
onDescriptionGenerationRunStarted={rememberDescriptionGenerationRun}
|
||||
onSuggestSensitive={suggestActiveDatabaseSensitiveFields}
|
||||
onCatalogMetricsChanged={invalidateCatalogMetrics}
|
||||
@@ -1367,6 +1321,10 @@ export function DatabaseManagementPage({
|
||||
onOpenTables={() => openTables(activeRow)}
|
||||
onRunStarted={rememberSyncRun}
|
||||
onOpenSync={() => openSync(activeRow)}
|
||||
onOpenDescriptionHistory={openDescriptionGenerationHistory}
|
||||
onOpenSensitiveHistory={openSensitiveDataSuggestionHistory}
|
||||
onCatalogMetricsChanged={invalidateCatalogMetrics}
|
||||
onNavigationStateChange={setTablesNavigationState}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1381,6 +1339,7 @@ export function DatabaseManagementPage({
|
||||
/>
|
||||
<DescriptionGenerationDrawer
|
||||
open={descriptionGenerationDrawerOpen}
|
||||
databaseId={activeRow?.id ?? null}
|
||||
run={activeDescriptionGenerationRun}
|
||||
modelLabel={metadataModels.find(
|
||||
(model) => model.id === activeDescriptionGenerationRun?.modelId,
|
||||
@@ -1391,6 +1350,7 @@ export function DatabaseManagementPage({
|
||||
/>
|
||||
<SensitiveDataSuggestionHistoryDrawer
|
||||
open={sensitiveDataSuggestionHistoryDrawerOpen}
|
||||
databaseId={activeRow?.id ?? null}
|
||||
run={activeSensitiveDataSuggestionRun}
|
||||
modelLabel={metadataModels.find(
|
||||
(model) => model.id === activeSensitiveDataSuggestionRun?.modelId,
|
||||
|
||||
Reference in New Issue
Block a user