feat: add connection management UI

Add TypeScript types, typed Tauri invoke wrappers, Zustand store,
TanStack Query hooks, and connection components: ConnectionDialog
(create/edit/test), ConnectionList (sheet panel), ConnectionSelector
(toolbar dropdown).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 19:06:35 +03:00
parent 9b675babd5
commit 734b84b525
10 changed files with 878 additions and 0 deletions

75
src/stores/app-store.ts Normal file
View File

@@ -0,0 +1,75 @@
import { create } from "zustand";
import type { ConnectionConfig, Tab } from "@/types";
interface AppState {
connections: ConnectionConfig[];
activeConnectionId: string | null;
currentDatabase: string | null;
connectedIds: Set<string>;
tabs: Tab[];
activeTabId: string | null;
sidebarWidth: number;
pgVersion: string | null;
setConnections: (connections: ConnectionConfig[]) => void;
setActiveConnectionId: (id: string | null) => void;
setCurrentDatabase: (db: string | null) => void;
addConnectedId: (id: string) => void;
removeConnectedId: (id: string) => void;
setPgVersion: (version: string | null) => void;
addTab: (tab: Tab) => void;
closeTab: (id: string) => void;
setActiveTabId: (id: string | null) => void;
updateTab: (id: string, updates: Partial<Tab>) => void;
setSidebarWidth: (width: number) => void;
}
export const useAppStore = create<AppState>((set) => ({
connections: [],
activeConnectionId: null,
currentDatabase: null,
connectedIds: new Set(),
tabs: [],
activeTabId: null,
sidebarWidth: 260,
pgVersion: null,
setConnections: (connections) => set({ connections }),
setActiveConnectionId: (id) => set({ activeConnectionId: id }),
setCurrentDatabase: (db) => set({ currentDatabase: db }),
addConnectedId: (id) =>
set((state) => ({
connectedIds: new Set([...state.connectedIds, id]),
})),
removeConnectedId: (id) =>
set((state) => {
const next = new Set(state.connectedIds);
next.delete(id);
return { connectedIds: next };
}),
setPgVersion: (version) => set({ pgVersion: version }),
addTab: (tab) =>
set((state) => ({
tabs: [...state.tabs, tab],
activeTabId: tab.id,
})),
closeTab: (id) =>
set((state) => {
const tabs = state.tabs.filter((t) => t.id !== id);
const activeTabId =
state.activeTabId === id
? tabs.length > 0
? tabs[tabs.length - 1].id
: null
: state.activeTabId;
return { tabs, activeTabId };
}),
setActiveTabId: (id) => set({ activeTabId: id }),
updateTab: (id, updates) =>
set((state) => ({
tabs: state.tabs.map((t) => (t.id === id ? { ...t, ...updates } : t)),
})),
setSidebarWidth: (width) => set({ sidebarWidth: width }),
}));