diff --git a/frontend/src/base/components/environment/ConnectionDetailsSection.jsx b/frontend/src/base/components/environment/ConnectionDetailsSection.jsx index 5b7e0c6..6bcd20d 100644 --- a/frontend/src/base/components/environment/ConnectionDetailsSection.jsx +++ b/frontend/src/base/components/environment/ConnectionDetailsSection.jsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useEffect } from "react"; import PropTypes from "prop-types"; import { Typography, Input, Select, Form } from "antd"; @@ -13,49 +13,49 @@ const ConnectionDetailsSection = memo( ({ connectionId, dbSelectionInfo, - handleConnectionNameDesc, + connectionDetailsForm, handleCardClick, mappedDataSources, dbUsage, }) => { + // Populate form values when connection data is loaded + // Apply collapseSpaces since setFieldsValue bypasses the normalize prop + useEffect(() => { + connectionDetailsForm.setFieldsValue({ + name: collapseSpaces(dbSelectionInfo.name || ""), + description: dbSelectionInfo.description, + }); + }, [ + connectionDetailsForm, + connectionId, + dbSelectionInfo.name, + dbSelectionInfo.description, + ]); + return (
Connection Details
-
+ - // collapse any run of 2+ spaces only when followed by non-space - collapseSpaces(value) - } + name="name" + normalize={collapseSpaces} rules={[ { required: true, message: "Please enter the connection name" }, { validator: validateFormFieldName }, ]} required > - - handleConnectionNameDesc("name", e.target.value) - } - /> + - - handleConnectionNameDesc("description", e.target.value) - } - /> + @@ -90,7 +90,7 @@ ConnectionDetailsSection.propTypes = { description: PropTypes.string, icon: PropTypes.string, }).isRequired, - handleConnectionNameDesc: PropTypes.func.isRequired, + connectionDetailsForm: PropTypes.object.isRequired, handleCardClick: PropTypes.func.isRequired, mappedDataSources: PropTypes.array.isRequired, dbUsage: PropTypes.shape({ diff --git a/frontend/src/base/components/environment/CreateConnection.jsx b/frontend/src/base/components/environment/CreateConnection.jsx index 3b8df5c..81ff920 100644 --- a/frontend/src/base/components/environment/CreateConnection.jsx +++ b/frontend/src/base/components/environment/CreateConnection.jsx @@ -1,6 +1,7 @@ import { useEffect, useState, useCallback, useMemo } from "react"; import Cookies from "js-cookie"; import PropTypes from "prop-types"; +import { Form } from "antd"; import { useAxiosPrivate } from "../../../service/axios-service.js"; import { orgStore } from "../../../store/org-store.js"; @@ -19,6 +20,7 @@ import { } from "./environment-api-service.js"; import "./environment.css"; import { useNotificationService } from "../../../service/notification-service.js"; +import { collapseSpaces } from "./helper"; const CreateConnection = ({ setIsModalOpen, @@ -54,6 +56,11 @@ const CreateConnection = ({ const [isCredentialsRevealed, setIsCredentialsRevealed] = useState(false); const [isRevealLoading, setIsRevealLoading] = useState(false); const { notify } = useNotificationService(); + const [connectionDetailsForm] = Form.useForm(); + + // Watch form fields reactively for hasDetailsChanged comparison + const formName = Form.useWatch("name", connectionDetailsForm); + const formDescription = Form.useWatch("description", connectionDetailsForm); // Initialize encryption service useEffect(() => { @@ -118,16 +125,17 @@ const CreateConnection = ({ getConnectionFields(); }, [getConnectionFields]); - const handleConnectionNameDesc = useCallback((name, value) => { - setDbSelectionInfo((prev) => ({ ...prev, [name]: value })); - }, []); - const handleCreateOrUpdate = useCallback(async () => { setIsCreateOrUpdateLoading(true); try { + // Get name and description directly from the form (source of truth) + const { name, description } = connectionDetailsForm.getFieldsValue(); + // Prepare connection data const connectionData = { ...dbSelectionInfo, + name, + description, connection_details: { ...inputFields, ...(["postgres", "snowflake"].includes( @@ -199,6 +207,8 @@ const CreateConnection = ({ } }, [ connectionId, + connectionDetailsForm, + hasDetailsChanged, dbSelectionInfo, inputFields, connType, @@ -232,7 +242,10 @@ const CreateConnection = ({ icon: db_icon, }; setDbSelectionInfo(selectionInfo); - setOriginalDbSelectionInfo({ ...selectionInfo }); + setOriginalDbSelectionInfo({ + ...selectionInfo, + name: collapseSpaces(selectionInfo.name || ""), + }); // Process connection details to handle JSON objects for textarea fields const processedConnectionDetails = { ...connection_details }; @@ -398,10 +411,10 @@ const CreateConnection = ({ const hasDetailsChanged = useMemo(() => { if (!connectionId || !originalDbSelectionInfo) return false; return ( - dbSelectionInfo.name !== originalDbSelectionInfo.name || - dbSelectionInfo.description !== originalDbSelectionInfo.description + formName !== originalDbSelectionInfo.name || + formDescription !== originalDbSelectionInfo.description ); - }, [connectionId, dbSelectionInfo, originalDbSelectionInfo]); + }, [connectionId, formName, formDescription, originalDbSelectionInfo]); const mappedDataSources = useMemo( () => @@ -428,7 +441,7 @@ const CreateConnection = ({ { // Clear input fields when datasource changes to prevent old fields from being sent setInputFields({});