feat: add column sort, SQL formatter, table stats, insert dialog, saved queries & sessions monitor
- Column sort by header click in table view (ASC/DESC/none cycle, server-side) - SQL formatter with Format button and Shift+Alt+F keybinding (sql-formatter) - Table size and row count display in schema tree via pg_class - Insert row dialog with column type hints and auto-skip for identity columns - Saved queries (bookmarks) with CRUD backend, sidebar panel, and save dialog - Active sessions monitor (pg_stat_activity) with auto-refresh, cancel & terminate Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,8 @@ 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, Download } from "lucide-react";
|
||||
import { Save, RotateCcw, Filter, Loader2, Lock, Download, Plus } from "lucide-react";
|
||||
import { InsertRowDialog } from "./InsertRowDialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -31,14 +32,15 @@ export function TableDataView({ connectionId, schema, table }: Props) {
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(50);
|
||||
const [sortColumn, _setSortColumn] = useState<string | undefined>();
|
||||
const [sortDirection, _setSortDirection] = useState<string | undefined>();
|
||||
const [sortColumn, setSortColumn] = useState<string | undefined>();
|
||||
const [sortDirection, setSortDirection] = useState<string | undefined>();
|
||||
const [filter, setFilter] = useState("");
|
||||
const [appliedFilter, setAppliedFilter] = useState<string | undefined>();
|
||||
const [pendingChanges, setPendingChanges] = useState<
|
||||
Map<string, { rowIndex: number; colIndex: number; value: string | null }>
|
||||
>(new Map());
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [insertDialogOpen, setInsertDialogOpen] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data, isLoading, error } = useTableData({
|
||||
@@ -155,6 +157,15 @@ export function TableDataView({ connectionId, schema, table }: Props) {
|
||||
[data, table]
|
||||
);
|
||||
|
||||
const handleSort = useCallback(
|
||||
(column: string | undefined, direction: string | undefined) => {
|
||||
setSortColumn(column);
|
||||
setSortDirection(direction);
|
||||
setPage(1);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleApplyFilter = () => {
|
||||
setAppliedFilter(filter || undefined);
|
||||
setPage(1);
|
||||
@@ -207,6 +218,17 @@ export function TableDataView({ connectionId, schema, table }: Props) {
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
{!isReadOnly && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-6 gap-1 text-xs"
|
||||
onClick={() => setInsertDialogOpen(true)}
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
Insert Row
|
||||
</Button>
|
||||
)}
|
||||
{pendingChanges.size > 0 && (
|
||||
<>
|
||||
<Button
|
||||
@@ -253,6 +275,11 @@ export function TableDataView({ connectionId, schema, table }: Props) {
|
||||
rows={data.rows}
|
||||
onCellDoubleClick={handleCellDoubleClick}
|
||||
highlightedCells={highlightedCells}
|
||||
externalSort={{
|
||||
column: sortColumn,
|
||||
direction: sortDirection,
|
||||
onSort: handleSort,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -269,6 +296,19 @@ export function TableDataView({ connectionId, schema, table }: Props) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<InsertRowDialog
|
||||
open={insertDialogOpen}
|
||||
onOpenChange={setInsertDialogOpen}
|
||||
connectionId={connectionId}
|
||||
schema={schema}
|
||||
table={table}
|
||||
onSuccess={() => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["table-data", connectionId],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user