From ebc6a7e51a2578f0fd5a4d33cd9d864532357dc2 Mon Sep 17 00:00:00 2001 From: "A.Shakhmatov" Date: Wed, 11 Feb 2026 21:23:07 +0300 Subject: [PATCH] feat: add CSV/JSON export buttons to query results and table data view Export dropdown (CSV/JSON) appears in the WorkspacePanel toolbar when query results are available, and in the TableDataView toolbar for table data. Uses the Tauri save dialog for file path selection. Co-Authored-By: Claude Opus 4.6 --- src/components/table-viewer/TableDataView.tsx | 56 +++++++++++++++++- src/components/workspace/WorkspacePanel.tsx | 57 ++++++++++++++++++- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/components/table-viewer/TableDataView.tsx b/src/components/table-viewer/TableDataView.tsx index 26fb53c..11298ba 100644 --- a/src/components/table-viewer/TableDataView.tsx +++ b/src/components/table-viewer/TableDataView.tsx @@ -9,7 +9,15 @@ import { getTableColumns } from "@/lib/tauri"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAppStore } from "@/stores/app-store"; import { toast } from "sonner"; -import { Save, RotateCcw, Filter, Loader2, Lock } from "lucide-react"; +import { Save, RotateCcw, Filter, Loader2, Lock, Download } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { exportCsv, exportJson } from "@/lib/tauri"; +import { save } from "@tauri-apps/plugin-dialog"; interface Props { connectionId: string; @@ -123,6 +131,30 @@ export function TableDataView({ connectionId, schema, table }: Props) { setPendingChanges(new Map()); }; + const handleExport = useCallback( + async (format: "csv" | "json") => { + if (!data || data.columns.length === 0) return; + const ext = format === "csv" ? "csv" : "json"; + const path = await save({ + title: `Export as ${ext.toUpperCase()}`, + defaultPath: `${table}.${ext}`, + filters: [{ name: ext.toUpperCase(), extensions: [ext] }], + }); + if (!path) return; + try { + if (format === "csv") { + await exportCsv(path, data.columns, data.rows as unknown[][]); + } else { + await exportJson(path, data.columns, data.rows as unknown[][]); + } + toast.success(`Exported to ${path}`); + } catch (err) { + toast.error("Export failed", { description: String(err) }); + } + }, + [data, table] + ); + const handleApplyFilter = () => { setAppliedFilter(filter || undefined); setPage(1); @@ -153,6 +185,28 @@ export function TableDataView({ connectionId, schema, table }: Props) { > Apply + {data && data.columns.length > 0 && ( + + + + + + handleExport("csv")}> + Export CSV + + handleExport("json")}> + Export JSON + + + + )} {pendingChanges.size > 0 && ( <> + {result && result.columns.length > 0 && ( + + + + + + handleExport("csv")}> + Export CSV + + handleExport("json")}> + Export JSON + + + + )} Ctrl+Enter to execute