diff --git a/apps/desktop/src/components/diff/DataCompareDialog.vue b/apps/desktop/src/components/diff/DataCompareDialog.vue
index 19764e1b30..d4ee57133a 100644
--- a/apps/desktop/src/components/diff/DataCompareDialog.vue
+++ b/apps/desktop/src/components/diff/DataCompareDialog.vue
@@ -32,7 +32,7 @@ import {
import * as api from "@/lib/backend/api";
import { executeWithProductionSqlGuard } from "@/lib/database/productionExecutionGuard";
import TableMultiSelect from "@/components/diff/TableMultiSelect.vue";
-import { ArrowLeftRight, CheckSquare, ChevronDown, ChevronRight, Copy, GitCompareArrows, Loader2, Play, Square } from "@lucide/vue";
+import { ArrowLeftRight, CheckSquare, ChevronDown, ChevronRight, Copy, GitCompareArrows, Loader2, Play, RotateCcw, Square } from "@lucide/vue";
const PREVIEW_LIMIT_OPTIONS = [50, 100, 200, 500];
const SYNC_EXECUTE_BATCH_SIZE = 500;
@@ -510,7 +510,7 @@ async function rebuildSyncPlan() {
}
function startCompare(): void {
- if (!canCompare.value || comparing.value) return;
+ if (!canCompare.value || comparing.value || executing.value) return;
const tasks = buildCompareTasks();
if (tasks.length === 0) {
toast(t("dataCompare.noComparableTables"), 5000);
@@ -785,8 +785,11 @@ onBeforeUnmount(() => {
-
-
+
+
+ S
+ {{ t("diff.source") }}
+
{
-
-
+
+
+ T
+ {{ t("diff.target") }}
+
{
+
{{ compareProgressLabel || t("diff.progress.comparing") }}
{{ t("diff.syncProgress", { current: executedCount, total: executeTotal }) }}
diff --git a/apps/desktop/src/components/diff/__tests__/DataCompareDialog.prefill.spec.ts b/apps/desktop/src/components/diff/__tests__/DataCompareDialog.prefill.spec.ts
index 96b8c96cfb..d918b2f48e 100644
--- a/apps/desktop/src/components/diff/__tests__/DataCompareDialog.prefill.spec.ts
+++ b/apps/desktop/src/components/diff/__tests__/DataCompareDialog.prefill.spec.ts
@@ -13,6 +13,7 @@ const mocks = vi.hoisted(() => ({
listTables: vi.fn().mockResolvedValue([{ name: "CODEX_7467_META", table_type: "TABLE" }]),
getColumns: vi.fn().mockResolvedValue([{ name: "ID", data_type: "NUMBER", is_primary_key: true }]),
buildDataCompareSyncPlan: vi.fn(),
+ executeBatch: vi.fn(),
}));
const sessionMocks = vi.hoisted(() => ({
@@ -58,6 +59,7 @@ vi.mock("@/lib/backend/api", () => ({
listTables: mocks.listTables,
getColumns: mocks.getColumns,
buildDataCompareSyncPlan: mocks.buildDataCompareSyncPlan,
+ executeBatch: mocks.executeBatch,
}));
const mountedApps: App[] = [];
@@ -259,6 +261,45 @@ describe("DataCompareDialog session restore", () => {
expect(document.querySelector("textarea[readonly]")?.value).toBe("INSERT 1;\nINSERT 2;");
});
+ it("starts another comparison and distinguishes source and target panels after results", async () => {
+ mountSessionDialog(completedSession());
+
+ await flushAsyncSetup();
+
+ const recompareButton = [...document.querySelectorAll("button")].find((button) => button.textContent?.includes(i18n.global.t("dataCompare.recompare")));
+ expect(recompareButton).toBeDefined();
+ expect([...document.querySelectorAll("div")].filter((element) => element.classList.contains("border-blue-500/35"))).toHaveLength(1);
+ expect([...document.querySelectorAll("div")].filter((element) => element.classList.contains("border-emerald-500/35"))).toHaveLength(1);
+
+ recompareButton?.click();
+ await flushAsyncSetup();
+
+ expect(mocks.ensureConnected).toHaveBeenCalledWith("oracle-11g");
+ expect(mocks.ensureConnected).toHaveBeenCalledWith("oracle-jdbc-11g");
+ });
+
+ it("disables recompare while sync SQL is still executing against the target", async () => {
+ mocks.executeBatch.mockImplementation(() => new Promise(() => {}));
+ mountSessionDialog(completedSession());
+
+ await flushAsyncSetup();
+ mocks.ensureConnected.mockClear();
+
+ const executeButton = [...document.querySelectorAll("button")].find((button) => button.textContent?.includes(i18n.global.t("diff.executeSync")));
+ expect(executeButton).toBeDefined();
+ executeButton?.click();
+ await flushAsyncSetup();
+
+ const recompareButton = [...document.querySelectorAll("button")].find((button) => button.textContent?.includes(i18n.global.t("dataCompare.recompare")));
+ expect(recompareButton).toBeDefined();
+ expect(recompareButton?.disabled).toBe(true);
+
+ recompareButton?.click();
+ await flushAsyncSetup();
+
+ expect(mocks.ensureConnected).not.toHaveBeenCalledWith("oracle-11g");
+ });
+
it("persists the latest selection plan when the dialog closes during planning", async () => {
const session = completedSession();
let resolvePlan!: (plan: DataCompareSession["syncPlan"]) => void;
diff --git a/apps/desktop/src/i18n/locales/az.ts b/apps/desktop/src/i18n/locales/az.ts
index 9e162c6033..5b83d06d66 100644
--- a/apps/desktop/src/i18n/locales/az.ts
+++ b/apps/desktop/src/i18n/locales/az.ts
@@ -6633,6 +6633,7 @@ export default withEnglishFallback({
rowCounts: "Mənbədə {source} sətir, hədəfdə {target} sətir; tam müqayisə tamamlandı",
previewLimitOption: "Önbaxış: {count}",
compare: "Verilənləri müqayisə et",
+ recompare: "Yenidən müqayisə et",
summary: "Əlavə edilib: {added}, çıxarılıb: {removed}, dəyişdirilib: {modified}",
batchSummary: "{tables} cədvəl müqayisə edilib: {different} fərqli, {same} eyni, {failed} uğursuz; əlavə edilib: {added}, çıxarılıb: {removed}, dəyişdirilib: {modified}",
comparingTable: "Müqayisə edilir {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts
index 36eff94317..f4312564cf 100644
--- a/apps/desktop/src/i18n/locales/en.ts
+++ b/apps/desktop/src/i18n/locales/en.ts
@@ -6820,6 +6820,7 @@ export default {
rowCounts: "Source {source} rows, target {target} rows; full compare completed",
previewLimitOption: "Preview {count}",
compare: "Compare Data",
+ recompare: "Compare Again",
summary: "Added {added}, removed {removed}, modified {modified}",
batchSummary: "Compared {tables} tables: {different} different, {same} identical, {failed} failed; added {added}, removed {removed}, modified {modified}",
comparingTable: "Comparing {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts
index 677e6bf8f5..9c4a8ee090 100644
--- a/apps/desktop/src/i18n/locales/es.ts
+++ b/apps/desktop/src/i18n/locales/es.ts
@@ -6393,6 +6393,7 @@ export default withEnglishFallback({
rowCounts: "Origen {source} filas, destino {target} filas; comparación completa terminada",
previewLimitOption: "Vista previa {count}",
compare: "Comparar datos",
+ recompare: "Comparar de nuevo",
summary: "Agregadas {added}, eliminadas {removed}, modificadas {modified}",
batchSummary: "Se compararon {tables} tablas: {different} con diferencias, {same} iguales, {failed} con error; agregadas {added}, eliminadas {removed}, modificadas {modified}",
comparingTable: "Comparando {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts
index a9f3ace671..19b09cf6fe 100644
--- a/apps/desktop/src/i18n/locales/it.ts
+++ b/apps/desktop/src/i18n/locales/it.ts
@@ -6393,6 +6393,7 @@ export default withEnglishFallback({
rowCounts: "Sorgente {source} righe, destinazione {target} righe; confronto completo completato",
previewLimitOption: "Anteprima {count}",
compare: "Confronta Dati",
+ recompare: "Confronta di nuovo",
summary: "Aggiunte {added}, rimosse {removed}, modificate {modified}",
batchSummary: "Confrontate {tables} tabelle: {different} diverse, {same} identiche, {failed} non riuscite; aggiunte {added}, rimosse {removed}, modificate {modified}",
comparingTable: "Confronto di {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts
index 2a826d69cc..364bb7df0e 100644
--- a/apps/desktop/src/i18n/locales/ja.ts
+++ b/apps/desktop/src/i18n/locales/ja.ts
@@ -6431,6 +6431,7 @@ export default withEnglishFallback({
rowCounts: "ソース {source}行、ターゲット {target}行、完全比較が完了しました",
previewLimitOption: "{count}件プレビュー",
compare: "データを比較",
+ recompare: "再比較",
summary: "追加 {added}、削除 {removed}、変更 {modified}",
batchSummary: "{tables}テーブルを比較: {different}不一致、{same}一致、{failed}失敗; 追加 {added}、削除 {removed}、変更 {modified}",
comparingTable: "比較中 {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/ko.ts b/apps/desktop/src/i18n/locales/ko.ts
index acb03698d8..9942b322d7 100644
--- a/apps/desktop/src/i18n/locales/ko.ts
+++ b/apps/desktop/src/i18n/locales/ko.ts
@@ -6143,6 +6143,7 @@ export default withEnglishFallback({
rowCounts: "소스 {source}행, 대상 {target}행; 전체 비교 완료",
previewLimitOption: "미리보기 {count}",
compare: "데이터 비교",
+ recompare: "다시 비교",
summary: "추가 {added}, 제거 {removed}, 수정 {modified}",
batchSummary: "테이블 {tables}개 비교: {different}개 차이, {same}개 동일, {failed}개 실패; 추가 {added}, 제거 {removed}, 수정 {modified}",
comparingTable: "{current}/{total} 비교 중: {table}",
diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts
index 9f26665b59..4a1d6b2e42 100644
--- a/apps/desktop/src/i18n/locales/pt-BR.ts
+++ b/apps/desktop/src/i18n/locales/pt-BR.ts
@@ -6395,6 +6395,7 @@ export default withEnglishFallback({
rowCounts: "Origem com {source} linhas, destino com {target} linhas; comparação completa concluída",
previewLimitOption: "Pré-visualizar {count}",
compare: "Comparar Dados",
+ recompare: "Comparar novamente",
summary: "{added} adicionados, {removed} removidos, {modified} modificados",
batchSummary: "{tables} tabelas comparadas: {different} diferentes, {same} idênticas, {failed} com falha; {added} adicionados, {removed} removidos, {modified} modificados",
comparingTable: "Comparando {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/tr.ts b/apps/desktop/src/i18n/locales/tr.ts
index 4ea9051462..38f65486c1 100644
--- a/apps/desktop/src/i18n/locales/tr.ts
+++ b/apps/desktop/src/i18n/locales/tr.ts
@@ -6515,6 +6515,7 @@ export default withEnglishFallback({
rowCounts: "Kaynak {source} satır, hedef {target} satır; tam karşılaştırma tamamlandı",
previewLimitOption: "Önizleme {count}",
compare: "Verileri Karşılaştır",
+ recompare: "Tekrar karşılaştır",
summary: "{added} eklendi, {removed} kaldırıldı, {modified} değiştirildi",
batchSummary: "{tables} tablo karşılaştırıldı: {different} farklı, {same} aynı, {failed} başarısız; {added} eklendi, {removed} kaldırıldı, {modified} değiştirildi",
comparingTable: "Karşılaştırılıyor {current}/{total}: {table}",
diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts
index e6f23ae47d..1600b09b48 100644
--- a/apps/desktop/src/i18n/locales/zh-CN.ts
+++ b/apps/desktop/src/i18n/locales/zh-CN.ts
@@ -6794,6 +6794,7 @@ export default withEnglishFallback({
rowCounts: "源表 {source} 行,目标表 {target} 行;已完成全量比较",
previewLimitOption: "预览 {count}",
compare: "开始比较数据",
+ recompare: "重新比较",
summary: "新增 {added} 行,删除 {removed} 行,修改 {modified} 行",
batchSummary: "已比较 {tables} 张表:存在差异 {different} 张,一致 {same} 张,失败 {failed} 张;累计新增 {added} 行,删除 {removed} 行,修改 {modified} 行",
comparingTable: "正在比较 {current}/{total}:{table}",
diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts
index 1ce6f5fbee..86cd0a8bb1 100644
--- a/apps/desktop/src/i18n/locales/zh-TW.ts
+++ b/apps/desktop/src/i18n/locales/zh-TW.ts
@@ -5713,6 +5713,7 @@ export default withEnglishFallback({
rowCounts: "來源 {source} 列,目標 {target} 列;已完成完整比較",
previewLimitOption: "預覽 {count}",
compare: "開始比較資料",
+ recompare: "重新比較",
summary: "新增 {added} 列,刪除 {removed} 列,修改 {modified} 列",
batchSummary: "已比較 {tables} 張資料表:{different} 張有差異,{same} 張一致,{failed} 張失敗;新增 {added} 列,刪除 {removed} 列,修改 {modified} 列",
comparingTable: "正在比較 {current}/{total}:{table}",