import { useCallback, useEffect, useState } from "react"; import { IConnectionOptions, IEditConnection } from "@components/Library/types"; import { ArrowPathIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { getRouteApi, useNavigate } from "@tanstack/react-router"; import { AlertIcon, AlertModal } from "@components/Library/AlertModal"; import { enqueueSnackbar } from "notistack"; import { useDeleteConnection, useGetConnection, useGetConversations, useUpdateConnection, useRefreshConnectionSchema, } from "@/hooks"; import { Button } from "../Catalyst/button"; import { Transition } from "@headlessui/react"; import { ChevronDownIcon } from "@heroicons/react/20/solid"; import { Switch } from "@components/Catalyst/switch"; function classNames(...classes: string[]) { return classes.filter(Boolean).join(" "); } const SchemaEditor = ({ options, setOptions, }: { options: IConnectionOptions; setOptions: (newOptions: IConnectionOptions) => void; }) => { const [expanded, setExpanded] = useState( Object.fromEntries(options.schemas.map((schema) => [schema.name, false])) ); return (
{options.schemas.map((schema, schema_index) => schema.tables.length === 0 ? null : (
// Check/Uncheck schema and its tables setOptions({ schemas: options.schemas.map((prev_schema, prev_idx) => prev_idx === schema_index ? { ...prev_schema, enabled: checked, tables: prev_schema.tables.map((table) => ({ ...table, enabled: checked, })), } : prev_schema ), }) } />
setExpanded((prev) => ({ ...prev, [schema.name]: !prev[schema.name], })) } > {schema.name}
{schema.tables.map((table, table_index) => (
// Check/Uncheck table setOptions({ schemas: options.schemas.map( (prev_schema, prev_idx) => prev_idx === schema_index ? { ...prev_schema, tables: prev_schema.tables.map( (table, inner_table_idx) => inner_table_idx === table_index ? { ...table, enabled: checked, } : table ), } : prev_schema ), }) } /> {table.name}
))}
) )}
); }; const connectionRouteApi = getRouteApi("/_app/connection/$connectionId"); export const ConnectionEditor = () => { const navigate = useNavigate(); const { connectionId } = connectionRouteApi.useParams(); const [unsavedChanges, setUnsavedChanges] = useState(false); const [showCancelAlert, setShowCancelAlert] = useState(false); const [showDeleteAlert, setShowDeleteAlert] = useState(false); const { data, isLoading } = useGetConnection(connectionId); const { data: conversationsData } = useGetConversations(); const relatedConversations = conversationsData?.filter( (conversation) => conversation.connection_id === connectionId ) ?? []; const connection = data; const { mutate: deleteConnection } = useDeleteConnection({ onSuccess() { navigate({ to: "/" }); }, }); const { mutate: updateConnection } = useUpdateConnection({ onSuccess() { navigate({ to: "/" }); }, }); const { mutate: refreshSchema, isPending: isRefreshing } = useRefreshConnectionSchema((data) => { setEditFields((prev) => ({ ...prev, options: data.options, })); }); // Form state const [editFields, setEditFields] = useState({ name: "", dsn: "", }); useEffect(() => { setEditFields((prev) => ({ name: connection?.name || prev.name, dsn: connection?.dsn || prev.dsn, options: connection?.options || prev.options, })); }, [connection]); if (!connectionId) { enqueueSnackbar({ variant: "error", message: "No connection id provided - something went wrong", }); } // Handle navigating back only if there are no unsaved changes const handleBack = useCallback(() => { if (unsavedChanges) { setShowCancelAlert(true); } else { navigate({ to: "/" }); } }, [navigate, unsavedChanges]); // Handle navigating back when escape is pressed useEffect(() => { const handleKeyPress = (event: { key: string }) => { if (event.key === "Escape") { handleBack(); } }; // Add an event listener for the "Escape" key press document.addEventListener("keydown", handleKeyPress); // Clean up the event listener when the component unmounts return () => { document.removeEventListener("keydown", handleKeyPress); }; }, [handleBack, unsavedChanges]); function handleDelete() { if (!connectionId) return; deleteConnection(connectionId); } function handleSubmit() { if (!unsavedChanges) { navigate({ to: "/" }); // Return to previous page return; } if (!connectionId) return; updateConnection({ id: connectionId, payload: { name: editFields.name, ...(editFields.dsn !== connection?.dsn && { dsn: editFields.dsn }), // only add dsn if it changed options: editFields.options, }, }); } return (
{ setShowCancelAlert(false); history.back(); }} onCancel={() => { setShowCancelAlert(false); }} /> { setShowDeleteAlert(false); handleDelete(); }} onCancel={() => { setShowDeleteAlert(false); }} />
Edit connection
{ setEditFields({ ...editFields, name: e.target.value }); setUnsavedChanges(true); }} className={classNames( isLoading ? "animate-pulse bg-gray-900 text-gray-400" : "bg-white/5 text-white", "block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-white/10 focus:ring-2 focus:ring-inset focus:ring-indigo-500 sm:text-sm sm:leading-6" )} />
{ setEditFields({ ...editFields, dsn: e.target.value }); setUnsavedChanges(true); }} className={classNames( isLoading ? "animate-pulse bg-gray-900 text-gray-400" : "bg-white/5 text-white", "block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-white/10 focus:ring-2 focus:ring-inset focus:ring-indigo-500 sm:text-sm sm:leading-6" )} />
{editFields.options && ( { setEditFields((prev) => ({ ...prev, options: newOptions, })); setUnsavedChanges(true); }} /> )}
); };