fix(import): preserve selected Excel sheet during execution
This commit is contained in:
parent
cda8c4c8e2
commit
177545dca8
|
|
@ -13,7 +13,7 @@ import { AlertTriangle, ArrowLeft, ArrowRight, Check, CheckCircle2, FileJson, Fi
|
|||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { autoMapImportColumns, nextTableImportWizardStep, previousTableImportWizardStep, requiredImportTargetColumns, suggestImportTargetDataTypes, validateImportMappings, type TableImportWizardStep } from "@/lib/table/tableImport";
|
||||
import { autoMapImportColumns, buildTableImportParseOptions, nextTableImportWizardStep, previousTableImportWizardStep, requiredImportTargetColumns, suggestImportTargetDataTypes, validateImportMappings, type TableImportWizardStep } from "@/lib/table/tableImport";
|
||||
import { getDataTypeOptions } from "@/lib/table/tableStructureEditorState";
|
||||
import { tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import type { ColumnInfo } from "@/types/database";
|
||||
|
|
@ -180,17 +180,7 @@ const createColumnSummaries = computed(() =>
|
|||
targetDataType: mapping.targetDataType || "",
|
||||
})),
|
||||
);
|
||||
const parseOptions = computed<api.TableImportParseOptions>(() => ({
|
||||
delimiter: sourceFormat.value === "tsv" ? "\\t" : sourceFormat.value === "csv" ? "," : delimiter.value,
|
||||
encoding: isDelimitedFormat(sourceFormat.value) ? textEncoding.value : null,
|
||||
titleRow: titleRow.value,
|
||||
dataStartRow: dataStartRow.value,
|
||||
lastDataRow: lastDataRow.value,
|
||||
trimValues: trimValues.value,
|
||||
emptyStringAsNull: emptyStringAsNull.value,
|
||||
sheetName: sourceFormat.value === "excel" ? selectedSheet.value || null : null,
|
||||
jsonShape: sourceFormat.value === "json" ? jsonShape.value : null,
|
||||
}));
|
||||
const parseOptions = computed<api.TableImportParseOptions>(() => taskParseOptions(sourceFormat.value, selectedSheet.value));
|
||||
const terminalStatus = computed(() => progress.value?.status && ["done", "error", "cancelled"].includes(progress.value.status));
|
||||
|
||||
function resetState() {
|
||||
|
|
@ -268,17 +258,18 @@ function uniqueTableName(baseName: string, usedNames: Set<string>): string {
|
|||
}
|
||||
|
||||
function taskParseOptions(format: api.TableImportSourceFormat, sheetName = ""): api.TableImportParseOptions {
|
||||
return {
|
||||
delimiter: format === "tsv" ? "\\t" : format === "csv" ? "," : delimiter.value,
|
||||
encoding: isDelimitedFormat(format) ? textEncoding.value : null,
|
||||
return buildTableImportParseOptions({
|
||||
format,
|
||||
delimiter: delimiter.value,
|
||||
textEncoding: textEncoding.value,
|
||||
titleRow: titleRow.value,
|
||||
dataStartRow: dataStartRow.value,
|
||||
lastDataRow: lastDataRow.value,
|
||||
trimValues: trimValues.value,
|
||||
emptyStringAsNull: emptyStringAsNull.value,
|
||||
sheetName: format === "excel" ? sheetName || null : null,
|
||||
jsonShape: format === "json" ? jsonShape.value : null,
|
||||
};
|
||||
sheetName,
|
||||
jsonShape: jsonShape.value,
|
||||
});
|
||||
}
|
||||
|
||||
function importParseOptions(format: api.TableImportSourceFormat, currentPreview: api.TableImportPreview, sheetName = ""): api.TableImportParseOptions {
|
||||
|
|
@ -622,7 +613,8 @@ async function startImport() {
|
|||
filePath: currentPreview.filePath,
|
||||
sourceRef: currentPreview.sourceRef || null,
|
||||
sourceFormat: sourceFormat.value,
|
||||
parseOptions: importParseOptions(sourceFormat.value, currentPreview),
|
||||
// Execution must parse the same worksheet that produced the preview and mappings.
|
||||
parseOptions: importParseOptions(sourceFormat.value, currentPreview, selectedSheet.value),
|
||||
mappings: mappedColumns.value,
|
||||
mode: targetMode.value === "create" ? "append" : importMode.value,
|
||||
createTable: targetMode.value === "create",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { autoMapImportColumns, nextTableImportWizardStep, previousTableImportWizardStep, requiredImportTargetColumns, suggestImportTargetDataTypes, validateImportMappings } from "@/lib/table/tableImport";
|
||||
import { autoMapImportColumns, buildTableImportParseOptions, nextTableImportWizardStep, previousTableImportWizardStep, requiredImportTargetColumns, suggestImportTargetDataTypes, validateImportMappings } from "@/lib/table/tableImport";
|
||||
|
||||
describe("tableImport", () => {
|
||||
it("auto maps exact and normalized column names", () => {
|
||||
|
|
@ -54,6 +54,22 @@ describe("tableImport", () => {
|
|||
expect(previousTableImportWizardStep("source")).toBe("source");
|
||||
});
|
||||
|
||||
it("keeps the selected Excel worksheet in execution parse options", () => {
|
||||
const baseSettings = {
|
||||
delimiter: ",",
|
||||
textEncoding: "auto" as const,
|
||||
titleRow: 1,
|
||||
dataStartRow: 2,
|
||||
lastDataRow: 0,
|
||||
trimValues: false,
|
||||
emptyStringAsNull: true,
|
||||
jsonShape: "auto" as const,
|
||||
};
|
||||
|
||||
expect(buildTableImportParseOptions({ ...baseSettings, format: "excel", sheetName: "Second" }).sheetName).toBe("Second");
|
||||
expect(buildTableImportParseOptions({ ...baseSettings, format: "csv", sheetName: "Second" }).sheetName).toBeNull();
|
||||
});
|
||||
|
||||
it("suggests create-table data types from preview rows", () => {
|
||||
expect(
|
||||
suggestImportTargetDataTypes(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { DatabaseType } from "@/types/database";
|
||||
import type { TableImportJsonShape, TableImportParseOptions, TableImportSourceFormat, TableImportTextEncoding } from "@/lib/backend/api";
|
||||
|
||||
export const IMPORT_SKIP_TARGET = "";
|
||||
|
||||
|
|
@ -26,6 +27,34 @@ export type TableImportWizardStep = "source" | "options" | "mapping" | "review"
|
|||
|
||||
export const TABLE_IMPORT_WIZARD_STEPS: TableImportWizardStep[] = ["source", "options", "mapping", "review", "execution"];
|
||||
|
||||
export interface TableImportParseSettings {
|
||||
format: TableImportSourceFormat;
|
||||
delimiter: string;
|
||||
textEncoding: TableImportTextEncoding;
|
||||
titleRow: number;
|
||||
dataStartRow: number;
|
||||
lastDataRow: number;
|
||||
trimValues: boolean;
|
||||
emptyStringAsNull: boolean;
|
||||
sheetName?: string;
|
||||
jsonShape: TableImportJsonShape;
|
||||
}
|
||||
|
||||
export function buildTableImportParseOptions(settings: TableImportParseSettings): TableImportParseOptions {
|
||||
const isDelimited = settings.format === "csv" || settings.format === "tsv" || settings.format === "delimited";
|
||||
return {
|
||||
delimiter: settings.format === "tsv" ? "\\t" : settings.format === "csv" ? "," : settings.delimiter,
|
||||
encoding: isDelimited ? settings.textEncoding : null,
|
||||
titleRow: settings.titleRow,
|
||||
dataStartRow: settings.dataStartRow,
|
||||
lastDataRow: settings.lastDataRow,
|
||||
trimValues: settings.trimValues,
|
||||
emptyStringAsNull: settings.emptyStringAsNull,
|
||||
sheetName: settings.format === "excel" ? settings.sheetName || null : null,
|
||||
jsonShape: settings.format === "json" ? settings.jsonShape : null,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeImportColumnName(name: string): string {
|
||||
return name.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3060,6 +3060,18 @@ mod tests {
|
|||
assert_eq!(xlsx_sheet_names(&path.to_string_lossy()).unwrap(), vec!["First", "Second"]);
|
||||
assert_eq!(parsed.columns, vec!["name"]);
|
||||
assert_eq!(parsed.rows, vec![vec![serde_json::json!("Ada")]]);
|
||||
assert_eq!(
|
||||
mapping_indexes(
|
||||
&parsed,
|
||||
&[TableImportColumnMapping {
|
||||
source_column: "name".to_string(),
|
||||
target_column: "display_name".to_string(),
|
||||
target_data_type: None,
|
||||
}],
|
||||
)
|
||||
.unwrap(),
|
||||
vec![(0, "display_name".to_string())]
|
||||
);
|
||||
let _ = std::fs::remove_file(path);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue