diff --git a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx index 8f6ba0328170..659d6bb4c0bb 100644 --- a/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx +++ b/airbyte-webapp/src/components/CreateConnectionContent/CreateConnectionContent.tsx @@ -1,6 +1,6 @@ import { faRedoAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import React, { Suspense, useMemo } from "react"; +import React, { Suspense, useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; import styled from "styled-components"; @@ -13,6 +13,7 @@ import { LogsRequestError } from "core/request/LogsRequestError"; import { useAnalyticsService } from "hooks/services/Analytics/useAnalyticsService"; import { useCreateConnection, ValuesProps } from "hooks/services/useConnectionHook"; import ConnectionForm from "views/Connection/ConnectionForm"; +import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig"; import { DestinationRead, SourceRead, WebBackendConnectionRead } from "../../core/request/AirbyteClient"; import { useDiscoverSchema } from "../../hooks/services/useSourceHook"; @@ -52,14 +53,21 @@ const CreateConnectionContent: React.FC = ({ const { schema, isLoading, schemaErrorStatus, catalogId, onDiscoverSchema } = useDiscoverSchema(source.sourceId); + const [connectionFormValues, setConnectionFormValues] = useState(); + const connection = useMemo( () => ({ + name: connectionFormValues?.name ?? "", + namespaceDefinition: connectionFormValues?.namespaceDefinition, + namespaceFormat: connectionFormValues?.namespaceFormat, + prefix: connectionFormValues?.prefix, + schedule: connectionFormValues?.schedule, syncCatalog: schema, destination, source, catalogId, }), - [schema, destination, source, catalogId] + [connectionFormValues, schema, destination, source, catalogId] ); const onSubmitConnectionStep = async (values: ValuesProps) => { @@ -126,6 +134,7 @@ const CreateConnectionContent: React.FC = ({ } onSubmit={onSubmitConnectionStep} + onChangeValues={setConnectionFormValues} /> )} diff --git a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx index e541012864f4..6eb75031cc3f 100644 --- a/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx +++ b/airbyte-webapp/src/pages/ConnectionPage/pages/ConnectionItemPage/components/ReplicationView.tsx @@ -1,7 +1,7 @@ import { faSyncAlt } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FormikHelpers } from "formik"; -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useAsyncFn } from "react-use"; import styled from "styled-components"; @@ -20,6 +20,7 @@ import { } from "hooks/services/useConnectionHook"; import { equal } from "utils/objects"; import ConnectionForm from "views/Connection/ConnectionForm"; +import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig"; interface ReplicationViewProps { onAfterSaveSchema: () => void; @@ -52,6 +53,7 @@ export const ReplicationView: React.FC = ({ onAfterSaveSch const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); const [activeUpdatingSchemaMode, setActiveUpdatingSchemaMode] = useState(false); const [saved, setSaved] = useState(false); + const [connectionFormValues, setConnectionFormValues] = useState(); const { mutateAsync: updateConnection } = useUpdateConnection(); const { mutateAsync: resetConnection } = useResetConnection(); @@ -65,7 +67,24 @@ export const ReplicationView: React.FC = ({ onAfterSaveSch [connectionId] ); - const connection = activeUpdatingSchemaMode ? connectionWithRefreshCatalog : initialConnection; + const connection = useMemo(() => { + if (activeUpdatingSchemaMode && connectionWithRefreshCatalog) { + // merge connectionFormValues (unsaved previous form state) with the refreshed connection data: + // 1. if there is a namespace definition, format, prefix, or schedule in connectionFormValues, + // use those and fill in the rest from the database + // 2. otherwise, use the values from the database + // 3. if none of the above, use the default values. + return { + ...connectionWithRefreshCatalog, + namespaceDefinition: + connectionFormValues?.namespaceDefinition ?? connectionWithRefreshCatalog.namespaceDefinition, + namespaceFormat: connectionFormValues?.namespaceFormat ?? connectionWithRefreshCatalog.namespaceFormat, + prefix: connectionFormValues?.prefix ?? connectionWithRefreshCatalog.prefix, + schedule: connectionFormValues?.schedule ?? connectionWithRefreshCatalog.schedule, + }; + } + return initialConnection; + }, [activeUpdatingSchemaMode, connectionWithRefreshCatalog, initialConnection, connectionFormValues]); const onSubmit = async (values: ValuesProps, formikHelpers?: FormikHelpers) => { if (!connection) { @@ -125,7 +144,7 @@ export const ReplicationView: React.FC = ({ onAfterSaveSch await refreshCatalog(); }; - const onExitRefreshCatalogMode = () => { + const onCancelConnectionFormEdit = () => { setActiveUpdatingSchemaMode(false); }; @@ -158,9 +177,10 @@ export const ReplicationView: React.FC = ({ onAfterSaveSch onSubmit={onSubmitForm} onReset={onReset} successMessage={saved && } - onCancel={onExitRefreshCatalogMode} + onCancel={onCancelConnectionFormEdit} editSchemeMode={activeUpdatingSchemaMode} additionalSchemaControl={renderUpdateSchemaButton()} + onChangeValues={setConnectionFormValues} /> ) : ( diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx index f4d1a992befc..76ed789b7afe 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/ConnectionForm.tsx @@ -1,7 +1,8 @@ -import { Field, FieldProps, Form, Formik, FormikHelpers } from "formik"; +import { Field, FieldProps, Form, Formik, FormikHelpers, useFormikContext } from "formik"; import React, { useCallback, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useToggle } from "react-use"; +import { useDebounce } from "react-use"; import styled from "styled-components"; import { ControlLabels, DropDown, DropDownRow, H5, Input, Label } from "components"; @@ -93,6 +94,19 @@ interface ConnectionFormSubmitResult { export type ConnectionFormMode = "create" | "edit" | "readonly"; +function FormValuesChangeTracker({ onChangeValues }: { onChangeValues?: (values: T) => void }) { + // Grab values from context + const { values } = useFormikContext(); + useDebounce( + () => { + onChangeValues?.(values); + }, + 200, + [values, onChangeValues] + ); + return null; +} + interface ConnectionFormProps { onSubmit: (values: ConnectionFormValues) => Promise; className?: string; @@ -101,6 +115,7 @@ interface ConnectionFormProps { onReset?: (connectionId?: string) => void; onDropDownSelect?: (item: DropDownRow.IDataItem) => void; onCancel?: () => void; + onChangeValues?: (values: FormikConnectionFormValues) => void; /** Should be passed when connection is updated with withRefreshCatalog flag */ editSchemeMode?: boolean; @@ -124,6 +139,7 @@ const ConnectionForm: React.FC = ({ editSchemeMode, additionalSchemaControl, connection, + onChangeValues, }) => { const { openConfirmationModal, closeConfirmationModal } = useConfirmationModalService(); const destDefinition = useGetDestinationDefinitionSpecification(connection.destination.destinationDefinitionId); @@ -204,6 +220,7 @@ const ConnectionForm: React.FC = ({ {({ isSubmitting, setFieldValue, isValid, dirty, resetForm, values }) => ( + {!isEditMode && ( diff --git a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx index 2e6f6e3e568f..2852f086004e 100644 --- a/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx +++ b/airbyte-webapp/src/views/Connection/ConnectionForm/formConfig.tsx @@ -234,7 +234,7 @@ const useInitialValues = ( const initialValues: FormikConnectionFormValues = { name: connection.name ?? `${connection.source.name} <> ${connection.destination.name}`, syncCatalog: initialSchema, - schedule: connection.connectionId ? connection.schedule ?? null : DEFAULT_SCHEDULE, + schedule: connection.connectionId || connection.schedule ? connection.schedule ?? null : DEFAULT_SCHEDULE, prefix: connection.prefix || "", namespaceDefinition: connection.namespaceDefinition || NamespaceDefinitionType.source, namespaceFormat: connection.namespaceFormat ?? SOURCE_NAMESPACE_TAG,