diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 610e1b607..f60bebf5b 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -111,6 +111,8 @@ const setupRequired = ref(false); const showConnectionDialog = ref(false); const connectionDialogPrefill = ref(null); const showSettingsDialog = ref(false); +const settingsInitialTab = ref("editor"); +const settingsInitialSection = ref(undefined); const showQueryEditorDdlDialog = ref(false); const showDriverStore = ref(false); const showQuickOpen = ref(false); @@ -232,6 +234,12 @@ useVisibilityChange(); const appVersion = ref(""); const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic"); const updateNotificationsEnabled = computed(() => settingsStore.editorSettings.updateNotificationsEnabled); + +function openSettings(initialTab = "editor", initialSection?: string) { + settingsInitialTab.value = initialTab; + settingsInitialSection.value = initialSection; + showSettingsDialog.value = true; +} const toolbarAgentDriverUpdateCount = computed(() => (updateNotificationsEnabled.value ? agentDriverUpdateCount.value : 0)); const toolbarHasUpdateAvailable = computed(() => updateNotificationsEnabled.value && hasUpdateAvailable.value); const hasSqlFileConnections = computed(() => connectionStore.connections.some((c) => supportsSqlFileExecution(c.db_type))); @@ -1140,7 +1148,7 @@ function handleKeydown(e: KeyboardEvent) { if (isOpenSettingsShortcut(e, shortcuts)) { e.preventDefault(); e.stopPropagation(); - showSettingsDialog.value = true; + openSettings(); return; } if (isQuickOpenShortcut(e, shortcuts)) { @@ -1427,7 +1435,7 @@ onUnmounted(() => { @toggle-history="showHistory = !showHistory" @toggle-sql-library="toggleSqlLibrary" @open-github="openGitHub" - @open-settings="showSettingsDialog = true" + @open-settings="openSettings()" @open-driver-store="showDriverStore = !showDriverStore" @check-updates="checkUpdates()" @open-transfer="dialogs.showTransferDialog.value = true" @@ -1530,6 +1538,7 @@ onUnmounted(() => { ) " @structure-editor-close="activeTab && queryStore.closeTab(activeTab.id)" + @open-settings="openSettings" /> @@ -1576,6 +1585,8 @@ onUnmounted(() => { :show-connection-dialog="showConnectionDialog" :connection-prefill="connectionDialogPrefill" :show-settings-dialog="showSettingsDialog" + :settings-initial-tab="settingsInitialTab" + :settings-initial-section="settingsInitialSection" :app-version="appVersion" :show-danger-dialog="showDangerDialog" :danger-sql="dangerSql" diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 38225860f..333a78aaa 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -3,7 +3,7 @@ import { ref, watch, shallowRef, computed, onMounted, onUnmounted, nextTick } fr import type { Ref } from "vue"; import type { EditorView as EditorViewType } from "@codemirror/view"; import { useI18n } from "vue-i18n"; -import { AlertTriangle, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, Loader2, Moon, PackageSearch, Pencil, RefreshCw, RotateCcw, Settings, Sun, SunMoon, Terminal, Trash2, Upload, X } from "@lucide/vue"; +import { AlertTriangle, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, GripVertical, Loader2, Moon, PackageSearch, Pencil, RefreshCw, RotateCcw, Settings, Sun, SunMoon, Terminal, Trash2, Upload, X } from "@lucide/vue"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; @@ -62,7 +62,9 @@ import { eventToShortcut } from "@/lib/keyboardShortcuts"; import { SHORTCUT_DEFINITIONS, findShortcutConflict, normalizeShortcutSettings, type ShortcutActionId } from "@/lib/shortcutRegistry"; import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebarTableNameDisplay"; import { normalizeSqlFormatterSettings, type SqlFormatterSettings } from "@/lib/sqlFormatterConfig"; -import type { SqlSnippet } from "@/types/database"; +import { EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE, parseTableColumnTemplateFields, TABLE_COLUMN_TEMPLATE_DATABASE_TYPES } from "@/lib/tableColumnTemplates"; +import { combineDataTypeForDatabase, getDataTypeOptions, getDefaultLengthForType, isDataTypeLengthDisabled, splitDataType } from "@/lib/tableStructureEditorState"; +import type { DatabaseType, SqlSnippet } from "@/types/database"; import { uuid } from "@/lib/utils"; import { DEFAULT_SQL_SNIPPETS } from "@/lib/sqlCompletion"; import AiProviderLogo from "@/components/icons/AiProviderLogo.vue"; @@ -86,6 +88,7 @@ let pendingSystemFonts: Promise | null = null; const props = defineProps<{ open: boolean; initialTab?: string; + initialSection?: string; appVersion?: string; }>(); @@ -93,6 +96,76 @@ const emit = defineEmits<{ "update:open": [value: boolean]; }>(); +interface TableColumnTemplateOverrideRow { + id: string; + databaseType: DatabaseType; + dataType: string; +} + +interface TableColumnTemplateGridRow { + id: string; + name: string; + defaultValue: string; + required: boolean; + comment: string; + overrides: TableColumnTemplateOverrideRow[]; +} + +function tableColumnTemplateRowsFromSettings(lines: readonly string[]): TableColumnTemplateGridRow[] { + return parseTableColumnTemplateFields([...lines]).map((field) => ({ + id: uuid(), + name: field.name, + defaultValue: field.defaultValue ?? "", + required: !(field.isNullable ?? false), + comment: field.comment ?? "", + overrides: Object.entries(field.dataTypesByDatabase).map(([databaseType, dataType]) => ({ + id: uuid(), + databaseType: databaseType as DatabaseType, + dataType: dataType === EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE ? "" : dataType, + })), + })); +} + +function tableColumnTemplateRowsToSettings(rows: readonly TableColumnTemplateGridRow[]): string[] { + const seenNames = new Set(); + const settings: string[] = []; + for (const row of rows) { + const name = row.name.trim(); + if (!name) continue; + const key = name.toLowerCase(); + if (seenNames.has(key)) continue; + seenNames.add(key); + + const parts = [name]; + + const seenDatabaseTypes = new Set(); + for (const override of row.overrides) { + const dataType = override.dataType.trim(); + if (seenDatabaseTypes.has(override.databaseType)) continue; + seenDatabaseTypes.add(override.databaseType); + parts.push(`${override.databaseType}:${dataType || EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE}`); + } + if (!row.required) parts.push("required:false"); + const defaultValue = row.defaultValue.trim(); + if (defaultValue) parts.push(`default:${defaultValue}`); + const comment = row.comment.trim(); + if (comment) parts.push(`comment:${comment}`); + settings.push(parts.join(" | ")); + } + return settings; +} + +function createEmptyTableColumnTemplateRow(): TableColumnTemplateGridRow { + return { + id: uuid(), + name: "", + defaultValue: "", + required: true, + comment: "", + overrides: [], + }; +} + // Local edit state const editFontFamily = ref(settingsStore.editorSettings.fontFamily); const editFontSize = ref(settingsStore.editorSettings.fontSize); @@ -120,6 +193,11 @@ const editShowColumnTypesInHeader = ref(settingsStore.editorSettings.showColumnT const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions); const editInfiniteScroll = ref(settingsStore.editorSettings.infiniteScroll); const editInfiniteScrollMaxRows = ref(settingsStore.editorSettings.infiniteScrollMaxRows); +const editTableColumnTemplateRows = ref(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields)); +const editTableColumnTemplateDatabaseType = ref(TABLE_COLUMN_TEMPLATE_DATABASE_TYPES[0] ?? "mysql"); +const tableColumnTemplateSectionRef = ref(null); +const draggedTableColumnTemplateRowId = ref(null); +let tableColumnTemplatePointerDragCleanup: (() => void) | null = null; const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize); const editShortcuts = ref(normalizeShortcutSettings(settingsStore.editorSettings.shortcuts)); const editSqlFormatter = ref(normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter)); @@ -157,6 +235,13 @@ const disconnectTabHandlingModeDescriptionKey = computed(() => { return "disconnectTabHandlingModeCloseTabsDescription"; }); +const normalizedEditTableColumnTemplateFields = computed(() => tableColumnTemplateRowsToSettings(editTableColumnTemplateRows.value)); +const visibleTableColumnTemplateRows = computed(() => + editTableColumnTemplateRows.value.filter((row) => { + if (row.overrides.length === 0) return true; + return row.overrides.some((override) => override.databaseType === editTableColumnTemplateDatabaseType.value); + }), +); // --- Snippet state --- const editSnippets = ref(settingsStore.editorSettings.snippets.map((s) => ({ ...s }))); @@ -383,6 +468,7 @@ watch( editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll; editInfiniteScrollMaxRows.value = settingsStore.editorSettings.infiniteScrollMaxRows; + editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields); editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize; editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts); editSqlFormatter.value = normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter); @@ -445,6 +531,7 @@ function hasChanges(): boolean { editCompactColumnHeaderActions.value !== settingsStore.editorSettings.compactColumnHeaderActions || editInfiniteScroll.value !== settingsStore.editorSettings.infiniteScroll || editInfiniteScrollMaxRows.value !== settingsStore.editorSettings.infiniteScrollMaxRows || + JSON.stringify(normalizedEditTableColumnTemplateFields.value) !== JSON.stringify(settingsStore.editorSettings.tableColumnTemplateFields) || editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize || JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts) || JSON.stringify(editSqlFormatter.value) !== JSON.stringify(normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter)) || @@ -487,6 +574,7 @@ async function persistSettings() { compactColumnHeaderActions: editCompactColumnHeaderActions.value, infiniteScroll: editInfiniteScroll.value, infiniteScrollMaxRows: editInfiniteScrollMaxRows.value, + tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value, redisScanPageSize: editRedisScanPageSize.value, shortcuts: editShortcuts.value, sqlFormatter: normalizeSqlFormatterSettings(editSqlFormatter.value), @@ -570,6 +658,7 @@ function resetDefaultsForTab(tab: SettingsCategory) { editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions; editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll; editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows; + editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields); editExportBatchSize.value = DEFAULT_EDITOR_SETTINGS.exportBatchSize; editExportRowLimitEnabled.value = DEFAULT_EDITOR_SETTINGS.exportRowLimitEnabled; editExportRowLimit.value = DEFAULT_EDITOR_SETTINGS.exportRowLimit; @@ -607,6 +696,7 @@ function resetAllDefaults() { editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions; editInfiniteScroll.value = DEFAULT_EDITOR_SETTINGS.infiniteScroll; editInfiniteScrollMaxRows.value = DEFAULT_EDITOR_SETTINGS.infiniteScrollMaxRows; + editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields); editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize; editShortcuts.value = normalizeShortcutSettings(DEFAULT_EDITOR_SETTINGS.shortcuts); editSqlFormatter.value = normalizeSqlFormatterSettings(DEFAULT_EDITOR_SETTINGS.sqlFormatter); @@ -628,6 +718,130 @@ function resetAllDefaults() { editSnippets.value = DEFAULT_SQL_SNIPPETS.map((s) => ({ ...s })); } +function addTableColumnTemplateRow() { + const row = createEmptyTableColumnTemplateRow(); + row.overrides.push({ + id: uuid(), + databaseType: editTableColumnTemplateDatabaseType.value, + dataType: "", + }); + editTableColumnTemplateRows.value.push(row); +} + +function removeTableColumnTemplateRow(id: string) { + const row = editTableColumnTemplateRows.value.find((item) => item.id === id); + if (!row) return; + if (row.overrides.some((override) => override.databaseType === editTableColumnTemplateDatabaseType.value)) { + row.overrides = row.overrides.filter((override) => override.databaseType !== editTableColumnTemplateDatabaseType.value); + if (row.overrides.length > 0) return; + } + editTableColumnTemplateRows.value = editTableColumnTemplateRows.value.filter((item) => item.id !== id); +} + +function moveTableColumnTemplateRow(sourceId: string, targetId: string, placement: "before" | "after") { + if (!sourceId || sourceId === targetId) return; + const rows = [...editTableColumnTemplateRows.value]; + const sourceIndex = rows.findIndex((row) => row.id === sourceId); + const targetIndex = rows.findIndex((row) => row.id === targetId); + if (sourceIndex === -1 || targetIndex === -1) return; + const [source] = rows.splice(sourceIndex, 1); + if (!source) return; + const nextTargetIndex = rows.findIndex((row) => row.id === targetId); + const insertIndex = placement === "after" ? nextTargetIndex + 1 : nextTargetIndex; + rows.splice(nextTargetIndex === -1 ? rows.length : insertIndex, 0, source); + editTableColumnTemplateRows.value = rows; +} + +function cleanupTableColumnTemplatePointerDrag() { + tableColumnTemplatePointerDragCleanup?.(); + tableColumnTemplatePointerDragCleanup = null; + draggedTableColumnTemplateRowId.value = null; + document.body.style.cursor = ""; + document.body.style.userSelect = ""; +} + +function startTableColumnTemplateRowDrag(id: string, event: PointerEvent) { + if (event.button !== 0) return; + event.preventDefault(); + cleanupTableColumnTemplatePointerDrag(); + draggedTableColumnTemplateRowId.value = id; + document.body.style.cursor = "grabbing"; + document.body.style.userSelect = "none"; + + const onPointerMove = (moveEvent: PointerEvent) => { + const sourceId = draggedTableColumnTemplateRowId.value; + if (!sourceId) return; + const targetRow = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY)?.closest("[data-table-column-template-row-id]"); + const targetId = targetRow?.dataset.tableColumnTemplateRowId; + if (!targetRow || !targetId || targetId === sourceId) return; + const rect = targetRow.getBoundingClientRect(); + moveTableColumnTemplateRow(sourceId, targetId, moveEvent.clientY > rect.top + rect.height / 2 ? "after" : "before"); + }; + const onPointerUp = () => cleanupTableColumnTemplatePointerDrag(); + + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", onPointerUp, { once: true }); + window.addEventListener("pointercancel", onPointerUp, { once: true }); + tableColumnTemplatePointerDragCleanup = () => { + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerUp); + window.removeEventListener("pointercancel", onPointerUp); + }; +} + +function tableColumnTemplateTypeOptions(databaseType: DatabaseType): string[] { + return getDataTypeOptions(databaseType); +} + +function tableColumnTemplateDataTypeForSelectedDatabase(row: TableColumnTemplateGridRow): string { + return row.overrides.find((override) => override.databaseType === editTableColumnTemplateDatabaseType.value)?.dataType ?? ""; +} + +function tableColumnTemplateBaseTypeForSelectedDatabase(row: TableColumnTemplateGridRow): string { + return splitDataType(tableColumnTemplateDataTypeForSelectedDatabase(row)).baseType; +} + +function tableColumnTemplateLengthForSelectedDatabase(row: TableColumnTemplateGridRow): string { + return splitDataType(tableColumnTemplateDataTypeForSelectedDatabase(row)).params; +} + +function setTableColumnTemplateDataTypeForSelectedDatabase(row: TableColumnTemplateGridRow, value: string) { + const dataType = value.trim(); + const databaseType = editTableColumnTemplateDatabaseType.value; + const existing = row.overrides.find((override) => override.databaseType === databaseType); + if (!dataType) { + row.overrides = row.overrides.filter((override) => override.databaseType !== databaseType); + return; + } + if (existing) { + existing.dataType = dataType; + } else { + row.overrides.push({ id: uuid(), databaseType, dataType }); + } +} + +function setTableColumnTemplateBaseTypeForSelectedDatabase(row: TableColumnTemplateGridRow, value: string) { + const baseType = value.trim(); + if (!baseType) { + setTableColumnTemplateDataTypeForSelectedDatabase(row, ""); + return; + } + const databaseType = editTableColumnTemplateDatabaseType.value; + setTableColumnTemplateDataTypeForSelectedDatabase(row, combineDataTypeForDatabase(databaseType, baseType, getDefaultLengthForType(databaseType, baseType))); +} + +function setTableColumnTemplateLengthForSelectedDatabase(row: TableColumnTemplateGridRow, value: string) { + const databaseType = editTableColumnTemplateDatabaseType.value; + const baseType = tableColumnTemplateBaseTypeForSelectedDatabase(row); + if (!baseType || isDataTypeLengthDisabled(databaseType, baseType)) return; + setTableColumnTemplateDataTypeForSelectedDatabase(row, combineDataTypeForDatabase(databaseType, baseType, value)); +} + +function isTableColumnTemplateLengthDisabled(row: TableColumnTemplateGridRow): boolean { + const baseType = tableColumnTemplateBaseTypeForSelectedDatabase(row); + return !baseType || isDataTypeLengthDisabled(editTableColumnTemplateDatabaseType.value, baseType); +} + function onExecuteModeChange(v: any) { if (v === "all" || v === "current") editExecuteMode.value = v; } @@ -1133,6 +1347,13 @@ const passwordMessage = ref(""); const passwordError = ref(false); const changingPassword = ref(false); +async function scrollToInitialSettingsSection() { + await nextTick(); + if (props.initialSection === "tableColumnTemplates") { + tableColumnTemplateSectionRef.value?.scrollIntoView({ block: "center", behavior: "smooth" }); + } +} + watch( () => props.open, async (open) => { @@ -1154,11 +1375,19 @@ watch( syncAiEditState(); if (!isWeb && activeSettingsTab.value === "mcp") void refreshMcpStatus(); if (!isWeb && activeSettingsTab.value === "ai" && aiIsCodexCli.value) void ensureCodexMcpStatus(); + await scrollToInitialSettingsSection(); } }, { immediate: true }, ); +watch( + () => props.initialSection, + () => { + if (props.open) void scrollToInitialSettingsSection(); + }, +); + watch([webdavEndpoint, webdavUsername], () => { void refreshWebDavPasswordStatus(); }); @@ -1192,6 +1421,7 @@ onUnmounted(() => { window.clearInterval(webdavAutoUploadTimer); webdavAutoUploadTimer = undefined; } + cleanupTableColumnTemplatePointerDrag(); cleanupTruncationObservers(); }); @@ -2324,6 +2554,98 @@ watch( + + + +
+
{{ t("settings.tableStructureSection") }}
+
+
+
+ +

{{ t("settings.tableColumnTemplateFieldsDescription") }}

+
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ {{ t("settings.tableColumnTemplateColumn") }}{{ t("settings.tableColumnTemplateType") }}{{ t("settings.tableColumnTemplateLength") }}{{ t("settings.tableColumnTemplateDefault") }}{{ t("settings.tableColumnTemplateRequired") }}{{ t("settings.tableColumnTemplateComment") }} +
+ + + + + + + + + + + + + + + +
+
+
+
diff --git a/apps/desktop/src/components/layout/AppDialogs.vue b/apps/desktop/src/components/layout/AppDialogs.vue index 42a7fda05..6943aa6f4 100644 --- a/apps/desktop/src/components/layout/AppDialogs.vue +++ b/apps/desktop/src/components/layout/AppDialogs.vue @@ -26,6 +26,7 @@ const props = defineProps<{ connectionPrefill?: ConnectionDeepLinkDraft | null; showSettingsDialog: boolean; settingsInitialTab?: string; + settingsInitialSection?: string; appVersion?: string; showDangerDialog: boolean; dangerSql: string; @@ -107,7 +108,7 @@ watch( @connect-failed="emit('connectFailed', $event)" @open-driver-store="emit('openDriverStore')" /> - + (); const { t } = useI18n(); @@ -1106,6 +1107,7 @@ defineExpose({ focusSearch, refreshData, handleModRTarget, requestQueryEditorExe :table-name="activeTab.structureTableName || ''" @saved="(commentChanged) => emit('structureEditorSaved', commentChanged)" @close="emit('structureEditorClose')" + @open-settings="(initialTab, initialSection) => emit('openSettings', initialTab, initialSection)" /> diff --git a/apps/desktop/src/components/structure/TableStructureEditor.vue b/apps/desktop/src/components/structure/TableStructureEditor.vue index 3c3ea0f9f..74972ee2e 100644 --- a/apps/desktop/src/components/structure/TableStructureEditor.vue +++ b/apps/desktop/src/components/structure/TableStructureEditor.vue @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { AlertTriangle, Check, ChevronDown, ChevronUp, Copy, Database, Info, KeyRound, Loader2, Maximize2, Plus, RefreshCw, Save, SlidersHorizontal, Trash2, X } from "@lucide/vue"; +import { AlertTriangle, Check, ChevronDown, ChevronUp, Copy, Database, Info, KeyRound, Loader2, Maximize2, Plus, RefreshCw, Save, Settings, SlidersHorizontal, Trash2, X } from "@lucide/vue"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; @@ -22,6 +22,7 @@ import { type SqlHighlighter, createShikiSqlHighlighter } from "@/lib/sqlHighlig import { copyToClipboard } from "@/lib/clipboard"; import { queryTimeoutSecsForConnection } from "@/lib/queryTimeout"; import { type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/tableStructureEditorSql"; +import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/lib/tableColumnTemplates"; import { getTableMetadataCapabilities } from "@/lib/tableMetadataCapabilities"; import { canAddTableStructureColumn, getTableStructureCapabilities } from "@/lib/tableStructureCapabilities"; import { connectionObjectTreeQuerySchema, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect"; @@ -80,6 +81,7 @@ const props = defineProps<{ const emit = defineEmits<{ saved: [commentChanged: boolean]; close: []; + openSettings: [initialTab?: string, initialSection?: string]; }>(); const activeTab = ref("columns"); @@ -822,6 +824,20 @@ async function addColumn() { input?.select(); } +function applyColumnTemplate(templateId: string) { + if (!canAddColumn.value) return; + activeTab.value = "columns"; + const templateColumns = createTableColumnTemplateDrafts({ + templateId, + databaseType: databaseType.value, + columnNames: settingsStore.editorSettings.tableColumnTemplateFields, + existingColumnNames: columns.value.map((column) => column.name), + createId: uuid, + }); + if (!templateColumns.length) return; + columns.value.push(...templateColumns); +} + function removeNewColumn(column: EditableStructureColumn) { columns.value = columns.value.filter((item) => item.id !== column.id); } @@ -1354,6 +1370,18 @@ watch(activeTab, (tab) => { {{ t("structureEditor.addColumn") }} + + + + + + {{ t("structureEditor.configureColumnTemplates") }} +