From 08917ba54a342e1649ae82ef258cb57e2b36b0f2 Mon Sep 17 00:00:00 2001 From: Guoyu Su Date: Fri, 4 Sep 2026 20:42:12 +0800 Subject: [PATCH] feat(export): copy background task failure details --- .../export/ExportProgressPopover.vue | 66 +++++- .../__tests__/ExportProgressPopover.spec.ts | 210 ++++++++++++++++++ .../export/failureDetailCopyText.ts | 10 + apps/desktop/src/i18n/locales/en.ts | 3 + apps/desktop/src/i18n/locales/es.ts | 3 + apps/desktop/src/i18n/locales/it.ts | 3 + apps/desktop/src/i18n/locales/ja.ts | 3 + apps/desktop/src/i18n/locales/ko.ts | 3 + apps/desktop/src/i18n/locales/pt-BR.ts | 3 + apps/desktop/src/i18n/locales/tr.ts | 3 + apps/desktop/src/i18n/locales/zh-CN.ts | 3 + apps/desktop/src/i18n/locales/zh-TW.ts | 3 + 12 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 apps/desktop/src/components/export/failureDetailCopyText.ts diff --git a/apps/desktop/src/components/export/ExportProgressPopover.vue b/apps/desktop/src/components/export/ExportProgressPopover.vue index 6327000b8a..1c10930553 100644 --- a/apps/desktop/src/components/export/ExportProgressPopover.vue +++ b/apps/desktop/src/components/export/ExportProgressPopover.vue @@ -3,11 +3,13 @@ import { computed, onBeforeUnmount, onMounted, ref } from "vue"; import { useI18n } from "vue-i18n"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; -import { Loader2, CheckCircle2, XCircle, AlertCircle, X, FileDown, DatabaseBackup, FileCode2, ArrowRightLeft, Layers3, ChevronRight, FolderOpen } from "@lucide/vue"; +import { Loader2, Check, CheckCircle2, XCircle, AlertCircle, X, FileDown, DatabaseBackup, FileCode2, ArrowRightLeft, Layers3, ChevronRight, FolderOpen, Copy } from "@lucide/vue"; import { formatDataTransferDuration, useExportTracker, type ExportTask } from "@/composables/useExportTracker"; +import { dataTransferFailureCopyText, sqlFileFailureCopyText } from "@/components/export/failureDetailCopyText"; import { translateBackendError } from "@/i18n/backend-errors"; import { useToast } from "@/composables/useToast"; import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; +import { copyToClipboard } from "@/lib/common/clipboard"; import * as api from "@/lib/backend/api"; const { t } = useI18n(); @@ -17,17 +19,22 @@ const open = ref(false); const showAll = ref(false); const expandedFailureTaskIds = ref([]); const revealingTaskIds = ref([]); +const copiedFailureDetailKey = ref(""); const currentTime = ref(Date.now()); const MAX_VISIBLE = 5; let elapsedTimer: ReturnType | undefined; +let copiedFailureDetailTimer: ReturnType | undefined; +let unmounted = false; onMounted(() => { elapsedTimer = setInterval(() => { currentTime.value = Date.now(); }, 1000); }); onBeforeUnmount(() => { + unmounted = true; if (elapsedTimer) clearInterval(elapsedTimer); + if (copiedFailureDetailTimer) clearTimeout(copiedFailureDetailTimer); }); const reversedTasks = computed(() => { @@ -243,6 +250,31 @@ function hasUnlistedTaskError(task: ExportTask) { return true; } +function dataTransferFailureDetailKey(taskId: string, table: string): string { + return `data-transfer:${taskId}:${table}`; +} + +function sqlFileFailureDetailKey(taskId: string, fileIndex: number | undefined, statementIndex: number): string { + return `sql-file:${taskId}:${fileIndex ?? -1}:${statementIndex}`; +} + +async function copyFailureDetail(text: string, key: string): Promise { + try { + await copyToClipboard(text); + if (unmounted) return; + copiedFailureDetailKey.value = key; + if (copiedFailureDetailTimer) clearTimeout(copiedFailureDetailTimer); + copiedFailureDetailTimer = setTimeout(() => { + if (copiedFailureDetailKey.value === key) copiedFailureDetailKey.value = ""; + copiedFailureDetailTimer = undefined; + }, 2000); + toast(t("exportProgress.failureDetailCopied")); + } catch (error: unknown) { + if (unmounted) return; + toast(t("exportProgress.failureDetailCopyFailed", { message: translateBackendError(t, error) }), 5000); + } +} + function openTask(task: ExportTask): void { task.onOpen?.(); } @@ -307,7 +339,19 @@ function openTask(task: ExportTask): void {