diff --git a/frontend/webapp/containers/main/destinations/add-destination/connect-destination-modal-body/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/connect-destination-modal-body/index.tsx index ed970b343..94aa48242 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/connect-destination-modal-body/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/connect-destination-modal-body/index.tsx @@ -9,8 +9,8 @@ import { GET_DESTINATION_TYPE_DETAILS } from '@/graphql'; import { Body, Container, SideMenuWrapper } from '../styled'; import { Divider, SectionTitle } from '@/reuseable-components'; import { ConnectionNotification } from './connection-notification'; +import type { StepProps, DestinationInput, DestinationTypeItem, DestinationDetailsResponse, ConfiguredDestination } from '@/types'; import { useComputePlatform, useConnectDestinationForm, useConnectEnv, useDestinationFormData, useEditDestinationFormHandlers } from '@/hooks'; -import { StepProps, DestinationInput, DestinationTypeItem, DestinationDetailsResponse, ConfiguredDestination } from '@/types'; const SIDE_MENU_DATA: StepProps[] = [ { @@ -34,6 +34,7 @@ interface ConnectDestinationModalBodyProps { export function ConnectDestinationModalBody({ destination, onSubmitRef, onFormValidChange }: ConnectDestinationModalBodyProps) { const [destinationName, setDestinationName] = useState(''); const [showConnectionError, setShowConnectionError] = useState(false); + const [isFormDirty, setIsFormDirty] = useState(false); const { dynamicFields, exportedSignals, setDynamicFields, setExportedSignals } = useDestinationFormData(); @@ -41,7 +42,16 @@ export function ConnectDestinationModalBody({ destination, onSubmitRef, onFormVa const { refetch } = useComputePlatform(); const { buildFormDynamicFields } = useConnectDestinationForm(); - const { handleDynamicFieldChange, handleSignalChange } = useEditDestinationFormHandlers(setExportedSignals, setDynamicFields); + const { handleDynamicFieldChange, handleSignalChange } = useEditDestinationFormHandlers( + (...params) => { + setIsFormDirty(true); + setExportedSignals(...params); + }, + (...params) => { + setIsFormDirty(true); + setDynamicFields(...params); + }, + ); const addConfiguredDestination = useAppStore(({ addConfiguredDestination }) => addConfiguredDestination); @@ -96,6 +106,7 @@ export function ConnectDestinationModalBody({ destination, onSubmitRef, onFormVa }, [destination]); function onDynamicFieldChange(name: string, value: any) { + setIsFormDirty(true); setShowConnectionError(false); handleDynamicFieldChange(name, value); } @@ -161,26 +172,6 @@ export function ConnectDestinationModalBody({ destination, onSubmitRef, onFormVa } } - const actionButton = useMemo(() => { - if (!!destination?.testConnectionSupported) { - return ( - { - setShowConnectionError(true); - onFormValidChange(false); - }} - destination={{ - name: destinationName, - type: destination?.type || '', - exportedSignals, - fields: processFormFields(), - }} - /> - ); - } - return null; - }, [destination, destinationName, exportedSignals, processFormFields, onFormValidChange]); - if (!destination) return null; return ( @@ -190,7 +181,28 @@ export function ConnectDestinationModalBody({ destination, onSubmitRef, onFormVa - + setIsFormDirty(false)} + onError={() => { + setShowConnectionError(true); + onFormValidChange(false); + }} + /> + ) : undefined + } + /> void; + destination: DestinationInput; + isFormDirty: boolean; + clearFormDirty: () => void; + onError: () => void; } -const ActionButton = styled(Button)<{ $isTestConnectionSuccess?: boolean }>` +const ActionButton = styled(Button)<{ $success?: boolean }>` display: flex; align-items: center; gap: 8px; - background-color: ${({ $isTestConnectionSuccess }) => ($isTestConnectionSuccess ? 'rgba(129, 175, 101, 0.16)' : 'transparent')}; + background-color: ${({ $success }) => ($success ? 'rgba(129, 175, 101, 0.16)' : 'transparent')}; `; -const ActionButtonText = styled(Text)<{ $isTestConnectionSuccess?: boolean }>` +const ActionButtonText = styled(Text)<{ $success?: boolean }>` font-family: ${({ theme }) => theme.font_family.secondary}; font-weight: 500; text-decoration: underline; text-transform: uppercase; font-size: 14px; line-height: 157.143%; - color: ${({ theme, $isTestConnectionSuccess }) => ($isTestConnectionSuccess ? theme.text.success : theme.colors.white)}; + color: ${({ theme, $success }) => ($success ? theme.text.success : theme.colors.white)}; `; -const TestConnection: React.FC = ({ destination, onError }) => { - const [isTestConnectionSuccess, setIsTestConnectionSuccess] = useState(false); - const { testConnection, loading, error } = useTestConnection(); +const TestConnection: React.FC = ({ destination, isFormDirty, clearFormDirty, onError }) => { + const { testConnection, loading, data } = useTestConnection(); - const onButtonClick = async () => { - if (!destination) { - return; - } + const disabled = useMemo(() => !destination.fields.find((field) => !!field.value), [destination.fields]); + const success = useMemo(() => data?.testConnectionForDestination.succeeded || false, [data]); - const res = await testConnection(destination); - if (res) { - setIsTestConnectionSuccess(res.succeeded); - !res.succeeded && onError && onError(); + useEffect(() => { + if (data) { + clearFormDirty(); + if (!success) onError && onError(); } - }; + }, [data, success]); + return ( - - {isTestConnectionSuccess && checkmark} - {loading && } + testConnection(destination)} $success={success}> + {loading ? : success ? checkmark : null} - - {loading ? 'Checking' : isTestConnectionSuccess ? 'Connection ok' : 'Test Connection'} + + {loading ? 'Checking' : success ? 'Connection OK' : 'Test Connection'} ); diff --git a/frontend/webapp/hooks/destinations/useTestConnection.ts b/frontend/webapp/hooks/destinations/useTestConnection.ts index a5838db92..ddf4e7bac 100644 --- a/frontend/webapp/hooks/destinations/useTestConnection.ts +++ b/frontend/webapp/hooks/destinations/useTestConnection.ts @@ -10,33 +10,20 @@ interface TestConnectionResponse { reason: string; } -interface UseTestConnectionResult { - testConnection: ( - destination: DestinationInput - ) => Promise; - loading: boolean; - error?: Error; -} - -export const useTestConnection = (): UseTestConnectionResult => { - const [testConnectionMutation, { loading, error }] = useMutation< - { testConnectionForDestination: TestConnectionResponse }, - { destination: DestinationInput } - >(TEST_CONNECTION_MUTATION); +export const useTestConnection = () => { + const [testConnectionMutation, { loading, error, data }] = useMutation<{ testConnectionForDestination: TestConnectionResponse }, { destination: DestinationInput }>(TEST_CONNECTION_MUTATION, { + onError: (error, clientOptions) => { + console.error('Error testing connection:', error); + }, + onCompleted: (data, clientOptions) => { + console.log('Successfully tested connection:', data); + }, + }); - const testConnection = async ( - destination: DestinationInput - ): Promise => { - try { - const { data } = await testConnectionMutation({ - variables: { destination }, - }); - return data?.testConnectionForDestination; - } catch (err) { - console.error('Error testing connection:', err); - return undefined; - } + return { + testConnection: (destination: DestinationInput) => testConnectionMutation({ variables: { destination } }), + loading, + error, + data, }; - - return { testConnection, loading, error }; };