fix(table-paste): surface first batch paste error

Closes #4832
This commit is contained in:
t8y2 committed 2026-08-23 16:24:19 +00:00
1 parent e1eb44f626
commit 72bb880ee7
6 files changed
+74 -10

No files matched your search

@@ -149,7 +149,18 @@ import { formatSqlForDisplay, sqlFormatDialectForDbType } from "@/lib/sql/sqlFor
import { getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import { hasTreeNodeDatabaseContext } from "@/lib/sidebar/treeNodeContext";
import { defaultPasteTableMode, pasteTableModeCopiesData, supportsWholeRowTableDataCopy, tableClipboardMatchesTarget, tableClipboardMenuState, tableClipboardSourceContext, tableDataCopyColumnOptions, type TableClipboardContext, type TableClipboardTableContext } from "@/lib/table/tableClipboard";
import {
defaultPasteTableMode,
pasteTableModeCopiesData,
supportsWholeRowTableDataCopy,
tableClipboardMatchesTarget,
tableClipboardMenuState,
tableClipboardSourceContext,
tableDataCopyColumnOptions,
tablePasteFeedback,
type TableClipboardContext,
type TableClipboardTableContext,
} from "@/lib/table/tableClipboard";
import { selectedSidebarBatchTargets, selectedTreeNodesInVisibleOrder as orderSelectedTreeNodes } from "@/lib/sidebar/sidebarTreeSelection";
import { connectionPasteTargetGroupId, selectedConnectionClipboardTargets, selectedConnectionEditTarget, selectedConnectionMoveTargets } from "@/lib/sidebar/sidebarConnectionSelection";
import { connectionSupportsDatabaseUserAdmin, resolveDatabaseUserAdminProviderForConnection, type DatabaseUserIdentity } from "@/lib/database/databaseUserAdmin";
@@ -3831,6 +3842,7 @@ async function confirmPasteTable() {
showPasteDialog.value = false;
let successCount = 0;
let pasteFailCount = 0;
let firstPasteError: unknown;
let refreshFailCount = 0;
let refreshError: unknown;
let pasteCancelled = false;
@@ -3902,6 +3914,7 @@ async function confirmPasteTable() {
successCount++;
} catch (e: any) {
pasteFailCount++;
firstPasteError ??= e;
console.error(`Failed to paste table "${entry.sourceName}" -> "${targetName}":`, e);
}
}
@@ -3924,13 +3937,14 @@ async function confirmPasteTable() {
}
return;
}
if (pasteFailCount === 0) {
const pasteFeedback = tablePasteFeedback(successCount, pasteFailCount, firstPasteError);
if (pasteFeedback.failedCount === 0) {
if (connectionStore.treeClipboard === clipboardAtPasteStart) {
connectionStore.treeClipboard = null;
}
toast(t("contextMenu.batchPasteSuccess", { count: successCount }), 3000);
} else {
toast(t("contextMenu.batchPastePartialFail", { success: successCount, failed: pasteFailCount }), 5000);
toast(`${t("contextMenu.batchPastePartialFail", { success: pasteFeedback.successCount, failed: pasteFeedback.failedCount })}\n${t("contextMenu.tableOperationFailed", { message: translateBackendError(t, pasteFeedback.firstError) })}`, 5000);
}
if (refreshFailCount > 0) {
const refreshMessage = refreshError instanceof Error ? refreshError.message : String(refreshError);
@@ -41,4 +41,10 @@ describe("cross-database table paste", () => {
expect(runtimeSource).toMatch(/buildDuplicateTableStructurePlan\(\{[\s\S]*?connectionId: entry\.connectionId[\s\S]*?sourceName: entry\.sourceName[\s\S]*?tableComment: entry\.tableComment/);
expect(runtimeSource).toContain("sourceColumns = plan.sourceColumns");
});
it("shows the first paste failure reason without concatenating every error", () => {
expect(runtimeSource).toMatch(/pasteFailCount\+\+/);
expect(runtimeSource).toMatch(/firstPasteError \?\?= e/);
expect(runtimeSource).toMatch(/batchPastePartialFail[\s\S]*?tableOperationFailed[\s\S]*?translateBackendError\(t, pasteFeedback\.firstError\)/);
});
});