From bc6dcf839551855125e9a47b3c68f3f1d4e133bb Mon Sep 17 00:00:00 2001 From: onenewcode Date: Sun, 11 Oct 2026 19:37:30 +0800 Subject: [PATCH] fix(export): use table name for single-table exports --- .../desktop/src/components/export/DatabaseExportDialog.vue | 4 ++-- .../src/lib/export/__tests__/databaseExportOptions.spec.ts | 7 ++++++- apps/desktop/src/lib/export/databaseExportOptions.ts | 5 +++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/export/DatabaseExportDialog.vue b/apps/desktop/src/components/export/DatabaseExportDialog.vue index 82458bafe1..001cf71694 100644 --- a/apps/desktop/src/components/export/DatabaseExportDialog.vue +++ b/apps/desktop/src/components/export/DatabaseExportDialog.vue @@ -24,7 +24,7 @@ import { isQueryTimeoutErrorMessage } from "@/lib/sql/queryError"; import type { SqlInsertDialect, SqlInsertMode } from "@/lib/export/sqlInsertMode"; import { revealExportedPath } from "@/lib/export/exportPath"; import { translateBackendError } from "@/i18n/backend-errors"; -import { loadSavedDatabaseExportOptions, MAX_SPLIT_SQL_PART_MB, MIN_SPLIT_SQL_PART_MB, saveDatabaseExportOptions, sortDatabaseTableNames } from "@/lib/export/databaseExportOptions"; +import { databaseExportFileStem, loadSavedDatabaseExportOptions, MAX_SPLIT_SQL_PART_MB, MIN_SPLIT_SQL_PART_MB, saveDatabaseExportOptions, sortDatabaseTableNames } from "@/lib/export/databaseExportOptions"; import { notifyExportComplete } from "@/lib/export/exportReveal"; const { t } = useI18n(); @@ -403,7 +403,7 @@ async function startExport() { if (isTauriRuntime()) { try { - const safeName = sanitizeFileName(database.value || "database"); + const safeName = sanitizeFileName(databaseExportFileStem(database.value || "database", selectedTables.value) || "database"); const path = await promptExportSavePath({ defaultFileName: `${safeName}.${splitSqlOutput.value ? "zip" : "sql"}`, filters: [{ name: splitSqlOutput.value ? "ZIP" : "SQL", extensions: [splitSqlOutput.value ? "zip" : "sql"] }], diff --git a/apps/desktop/src/lib/export/__tests__/databaseExportOptions.spec.ts b/apps/desktop/src/lib/export/__tests__/databaseExportOptions.spec.ts index c3806c5a2b..834c082538 100644 --- a/apps/desktop/src/lib/export/__tests__/databaseExportOptions.spec.ts +++ b/apps/desktop/src/lib/export/__tests__/databaseExportOptions.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { DEFAULT_DATABASE_EXPORT_OPTIONS, DATABASE_EXPORT_OPTIONS_STORAGE_KEY, loadSavedDatabaseExportOptions, saveDatabaseExportOptions, sortDatabaseTableNames } from "../databaseExportOptions"; +import { databaseExportFileStem, DEFAULT_DATABASE_EXPORT_OPTIONS, DATABASE_EXPORT_OPTIONS_STORAGE_KEY, loadSavedDatabaseExportOptions, saveDatabaseExportOptions, sortDatabaseTableNames } from "../databaseExportOptions"; function createMemoryStorage(initial: Record = {}) { const store = new Map(Object.entries(initial)); @@ -12,6 +12,11 @@ function createMemoryStorage(initial: Record = {}) { } describe("databaseExportOptions", () => { + it("uses the selected table as the filename stem for a single-table export", () => { + expect(databaseExportFileStem("app", ["orders"])).toBe("orders"); + expect(databaseExportFileStem("app", ["orders", "users"])).toBe("app"); + expect(databaseExportFileStem("app", [])).toBe("app"); + }); it("returns default options when storage is empty or contains invalid json", () => { const emptyStorage = createMemoryStorage(); expect(loadSavedDatabaseExportOptions(emptyStorage)).toEqual(DEFAULT_DATABASE_EXPORT_OPTIONS); diff --git a/apps/desktop/src/lib/export/databaseExportOptions.ts b/apps/desktop/src/lib/export/databaseExportOptions.ts index 9dd34d3972..3d233db0d6 100644 --- a/apps/desktop/src/lib/export/databaseExportOptions.ts +++ b/apps/desktop/src/lib/export/databaseExportOptions.ts @@ -82,3 +82,8 @@ export function sortDatabaseTableNames(names: string[]): string[] { // Pin the collation so the order does not shift with the OS locale. return [...names].sort((a, b) => a.localeCompare(b, "en", { sensitivity: "base" })); } + +/** Use the selected table as the suggested filename when exporting one table. */ +export function databaseExportFileStem(database: string, selectedTables: readonly string[]): string { + return selectedTables.length === 1 ? selectedTables[0]! : database; +}