import { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { useSaveConnection, useTestConnection } from "@/hooks/use-connections"; import { toast } from "sonner"; import type { ConnectionConfig } from "@/types"; import { ENVIRONMENTS } from "@/lib/environment"; import { Loader2, X } from "lucide-react"; type InputMode = "fields" | "dsn"; function parseDsn(dsn: string): Partial | null { try { const trimmed = dsn.trim(); // Support both postgres:// and postgresql:// const match = trimmed.match( /^(?:postgres(?:ql)?):\/\/(?:([^:@]*?)(?::([^@]*?))?@)?([^/:?]+)(?::(\d+))?(?:\/([^?]*))?(?:\?(.*))?$/ ); if (!match) return null; const [, user, password, host, port, database, queryStr] = match; const result: Partial = {}; if (host) result.host = decodeURIComponent(host); if (port) result.port = parseInt(port, 10); if (user) result.user = decodeURIComponent(user); if (password !== undefined) result.password = decodeURIComponent(password); if (database) result.database = decodeURIComponent(database); if (queryStr) { const params = new URLSearchParams(queryStr); const ssl = params.get("sslmode"); if (ssl) result.ssl_mode = ssl; } return result; } catch { return null; } } function buildDsn(config: ConnectionConfig): string { const ssl = config.ssl_mode ?? "prefer"; const user = encodeURIComponent(config.user); const pass = config.password ? `:${encodeURIComponent(config.password)}` : ""; const auth = config.user ? `${user}${pass}@` : ""; return `postgresql://${auth}${config.host}:${config.port}/${encodeURIComponent(config.database)}?sslmode=${ssl}`; } const CONNECTION_COLORS = [ { name: "Red", value: "#ef4444" }, { name: "Orange", value: "#f97316" }, { name: "Yellow", value: "#eab308" }, { name: "Green", value: "#22c55e" }, { name: "Cyan", value: "#06b6d4" }, { name: "Blue", value: "#3b82f6" }, { name: "Purple", value: "#a855f7" }, { name: "Pink", value: "#ec4899" }, ]; interface Props { open: boolean; onOpenChange: (open: boolean) => void; connection?: ConnectionConfig | null; } const emptyConfig: ConnectionConfig = { id: "", name: "", host: "localhost", port: 5432, user: "postgres", password: "", database: "postgres", ssl_mode: "prefer", color: undefined, environment: undefined, }; export function ConnectionDialog({ open, onOpenChange, connection }: Props) { const [form, setForm] = useState(emptyConfig); const [mode, setMode] = useState("fields"); const [dsn, setDsn] = useState(""); const [dsnError, setDsnError] = useState(null); const saveMutation = useSaveConnection(); const testMutation = useTestConnection(); useEffect(() => { if (open) { const config = connection ?? { ...emptyConfig, id: crypto.randomUUID() }; setForm(config); setMode("fields"); setDsn(connection ? buildDsn(config) : ""); setDsnError(null); } }, [open, connection]); const update = (field: keyof ConnectionConfig, value: string | number) => { setForm((f) => ({ ...f, [field]: value })); }; const handleTest = () => { testMutation.mutate(form, { onSuccess: (version) => { toast.success("Connection successful", { description: version }); }, onError: (err) => { toast.error("Connection failed", { description: String(err) }); }, }); }; const handleSave = () => { if (!form.name.trim()) { toast.error("Name is required"); return; } saveMutation.mutate(form, { onSuccess: () => { toast.success("Connection saved"); onOpenChange(false); }, onError: (err) => { toast.error("Save failed", { description: String(err) }); }, }); }; return ( {connection ? "Edit Connection" : "New Connection"}
update("name", e.target.value)} placeholder="My Database" />
{mode === "dsn" ? (
{ const value = e.target.value; setDsn(value); if (value.trim()) { const parsed = parseDsn(value); if (parsed) { setForm((f) => ({ ...f, ...parsed })); setDsnError(null); } else { setDsnError("Invalid DSN format"); } } else { setDsnError(null); } }} placeholder="postgresql://user:password@host:5432/database?sslmode=prefer" /> {dsnError && (

{dsnError}

)}

postgresql://user:password@host:port/database?sslmode=prefer

) : ( <>
update("host", e.target.value)} />
update("port", parseInt(e.target.value) || 5432)} />
update("user", e.target.value)} />
update("password", e.target.value)} />
update("database", e.target.value)} />
)}
{CONNECTION_COLORS.map((c) => (
); }