From 9b973807783f23974c7b91240b118e78ccb17732 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 27 Aug 2024 14:26:53 +0300 Subject: [PATCH 1/6] chore: wip --- .../add-destination-modal/index.tsx | 8 ++- .../configured-destinations-list/index.tsx | 2 +- .../connect-destination-modal-body/index.tsx | 55 ++++++++++++++++--- .../dynamic-form-fields/index.tsx | 5 +- .../destinations/useConnectDestinationForm.ts | 2 +- .../reuseable-components/dropdown/index.tsx | 12 ++-- 6 files changed, 68 insertions(+), 16 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/add-destination-modal/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/add-destination-modal/index.tsx index afadab7f3..27aa87bfb 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/add-destination-modal/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/add-destination-modal/index.tsx @@ -13,9 +13,11 @@ function ModalActionComponent({ onNext, onBack, item, + isFormValid, }: { onNext: () => void; onBack: () => void; + isFormValid?: boolean; item: DestinationTypeItem | undefined; }) { return ( @@ -33,6 +35,7 @@ function ModalActionComponent({ label: 'DONE', onClick: onNext, variant: 'primary', + disabled: !isFormValid, }, ] : [] @@ -47,6 +50,7 @@ export function AddDestinationModal({ }: AddDestinationModalProps) { const submitRef = useRef<() => void | null>(null); const [selectedItem, setSelectedItem] = useState(); + const [isFormValid, setIsFormValid] = useState(false); function handleNextStep(item: DestinationTypeItem) { setSelectedItem(item); @@ -55,8 +59,9 @@ export function AddDestinationModal({ function renderModalBody() { return selectedItem ? ( ) : ( @@ -78,6 +83,7 @@ export function AddDestinationModal({ setSelectedItem(undefined)} + isFormValid={isFormValid} item={selectedItem} /> } diff --git a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx index a989f4164..f79ffe5a2 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx @@ -115,7 +115,7 @@ function ConfiguredDestinationsListItem({ item: ConfiguredDestination; }) { const [expand, setExpand] = React.useState(false); - + console.log({ item }); function renderSupportedSignals(item: ConfiguredDestination) { const supportedSignals = item.exportedSignals; const signals = Object.keys(supportedSignals); 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 3f59b33a1..25dfedbe0 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 @@ -64,11 +64,13 @@ const NotificationNoteWrapper = styled.div` interface ConnectDestinationModalBodyProps { destination: DestinationTypeItem | undefined; onSubmitRef: React.MutableRefObject<(() => void) | null>; + onFormValidChange: (isValid: boolean) => void; } export function ConnectDestinationModalBody({ destination, onSubmitRef, + onFormValidChange, }: ConnectDestinationModalBodyProps) { const [formData, setFormData] = useState>({}); const [destinationName, setDestinationName] = useState(''); @@ -118,11 +120,13 @@ export function ConnectDestinationModalBody({ if (destination.fields && field?.name in destination.fields) { return { ...field, - initialValue: destination.fields[field.name], + value: destination.fields[field.name], + // initialValue: destination.fields[field.name], }; } return field; }); + setDynamicFields(newDynamicFields); } }, [data, destination]); @@ -132,8 +136,26 @@ export function ConnectDestinationModalBody({ onSubmitRef.current = handleSubmit; }, [formData, destinationName, exportedSignals]); + useEffect(() => { + const isFormValid = + dynamicFields.every( + (field) => + formData[field.name] !== undefined && formData[field.name] !== '' + ) && Object.keys(formData).length > 0; + + onFormValidChange(isFormValid); + }, [formData]); + function handleDynamicFieldChange(name: string, value: any) { setFormData((prev) => ({ ...prev, [name]: value })); + setDynamicFields((prev) => { + return prev.map((field) => { + if (field.name === name) { + return { ...field, value }; + } + return field; + }); + }); } function handleSignalChange(signal: string, value: boolean) { @@ -141,34 +163,47 @@ export function ConnectDestinationModalBody({ } async function handleSubmit() { - const fields = Object.entries(formData).map(([name, value]) => ({ - key: name, - value, + // Helper function to process field values + function processFieldValue(field) { + return field.componentType === 'dropdown' + ? field.value.value + : field.value; + } + + // Prepare fields for the request body + const fields = dynamicFields.map((field) => ({ + key: field.name, + value: processFieldValue(field), })); + // Function to store configured destination function storeConfiguredDestination() { const destinationTypeDetails = dynamicFields.map((field) => ({ title: field.title, - value: formData[field.name], + value: processFieldValue(field), })); + // Add 'Destination name' as the first item destinationTypeDetails.unshift({ title: 'Destination name', value: destinationName, }); + // Construct the configured destination object const storedDestination: ConfiguredDestination = { exportedSignals, destinationTypeDetails, type: destination?.type || '', imageUrl: destination?.imageUrl || '', - category: '', + category: '', // Could be handled in a more dynamic way if needed displayName: destination?.displayName || '', }; + // Dispatch action to store the destination dispatch(addConfiguredDestination(storedDestination)); } + // Prepare the request body const body: DestinationInput = { name: destinationName, type: destination?.type || '', @@ -176,7 +211,13 @@ export function ConnectDestinationModalBody({ fields, }; - await connectEnv(body, storeConfiguredDestination); + try { + // Await connection and store the configured destination if successful + await connectEnv(body, storeConfiguredDestination); + } catch (error) { + console.error('Failed to submit destination configuration:', error); + // Handle error (e.g., show notification or alert) + } } if (!destination) return null; diff --git a/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx index 813602dd8..6fd9f3edd 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx @@ -16,6 +16,7 @@ export function DynamicConnectDestinationFormFields({ fields: any[]; onChange: (name: string, value: any) => void; }) { + console.log({ fields }); return fields?.map((field: any) => { switch (field.componentType) { case INPUT_TYPES.INPUT: @@ -32,7 +33,9 @@ export function DynamicConnectDestinationFormFields({ onChange(field.name, option.value)} + onSelect={(option) => + onChange(field.name, { id: option.id, value: option.value }) + } /> ); case INPUT_TYPES.MULTI_INPUT: diff --git a/frontend/webapp/hooks/destinations/useConnectDestinationForm.ts b/frontend/webapp/hooks/destinations/useConnectDestinationForm.ts index 8165c1194..7f95908c5 100644 --- a/frontend/webapp/hooks/destinations/useConnectDestinationForm.ts +++ b/frontend/webapp/hooks/destinations/useConnectDestinationForm.ts @@ -37,7 +37,7 @@ export function useConnectDestinationForm() { title: displayName, onSelect: () => {}, options, - selectedOption: options[0], + placeholder: 'Select an option', ...componentPropertiesJson, }; diff --git a/frontend/webapp/reuseable-components/dropdown/index.tsx b/frontend/webapp/reuseable-components/dropdown/index.tsx index b43634117..1e7fafbb5 100644 --- a/frontend/webapp/reuseable-components/dropdown/index.tsx +++ b/frontend/webapp/reuseable-components/dropdown/index.tsx @@ -10,10 +10,11 @@ import { useOnClickOutside } from '@/hooks'; interface DropdownProps { options: DropdownOption[]; - selectedOption: DropdownOption | undefined; + value: DropdownOption | undefined; onSelect: (option: DropdownOption) => void; title?: string; tooltip?: string; + placeholder?: string; } const Container = styled.div` @@ -105,10 +106,11 @@ const OpenDropdownIcon = styled(Image)<{ isOpen: boolean }>` const Dropdown: React.FC = ({ options, - selectedOption, + value, onSelect, title, tooltip, + placeholder, }) => { const [isOpen, setIsOpen] = useState(false); const [searchTerm, setSearchTerm] = useState(''); @@ -143,7 +145,7 @@ const Dropdown: React.FC = ({ )} setIsOpen(!isOpen)}> - {selectedOption?.value} + {value?.value || placeholder} = ({ {filteredOptions.map((option) => ( handleSelect(option)} > {option.value} - {option.id === selectedOption?.id && ( + {option.id === value?.id && ( Date: Tue, 27 Aug 2024 14:34:20 +0300 Subject: [PATCH 2/6] chore: wip --- .../destinations/configured-destination-fields/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/webapp/components/destinations/configured-destination-fields/index.tsx b/frontend/webapp/components/destinations/configured-destination-fields/index.tsx index 5c8cd899e..22d2f94fe 100644 --- a/frontend/webapp/components/destinations/configured-destination-fields/index.tsx +++ b/frontend/webapp/components/destinations/configured-destination-fields/index.tsx @@ -30,8 +30,12 @@ const ItemValue = styled(Text)` export const ConfiguredDestinationFields: React.FC< ConfiguredDestinationFieldsProps > = ({ details }) => { - const parseValue = (value: string) => { + const parseValue = (value: any) => { try { + if (typeof value === 'string') { + return value; + } + const parsed = JSON.parse(value); if (Array.isArray(parsed)) { From f9e2f86a48f5952bde047c77e67f8bbc71dfdc69 Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 27 Aug 2024 15:01:36 +0300 Subject: [PATCH 3/6] chore: wip --- .../connect-destination-modal-body/index.tsx | 38 +++++++++++-------- .../dynamic-form-fields/index.tsx | 1 - .../reuseable-components/input-list/index.tsx | 9 ++++- .../key-value-input-list/index.tsx | 9 ++++- 4 files changed, 38 insertions(+), 19 deletions(-) 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 25dfedbe0..d91324649 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 @@ -72,7 +72,6 @@ export function ConnectDestinationModalBody({ onSubmitRef, onFormValidChange, }: ConnectDestinationModalBodyProps) { - const [formData, setFormData] = useState>({}); const [destinationName, setDestinationName] = useState(''); const [showConnectionError, setShowConnectionError] = useState(false); const [dynamicFields, setDynamicFields] = useState([]); @@ -134,20 +133,19 @@ export function ConnectDestinationModalBody({ useEffect(() => { // Assign handleSubmit to the onSubmitRef so it can be triggered externally onSubmitRef.current = handleSubmit; - }, [formData, destinationName, exportedSignals]); + }, [dynamicFields, destinationName, exportedSignals]); useEffect(() => { - const isFormValid = - dynamicFields.every( - (field) => - formData[field.name] !== undefined && formData[field.name] !== '' - ) && Object.keys(formData).length > 0; + const isFormValid = dynamicFields.every((field) => + field.required ? field.value : true + ); + + console.log({ isFormValid, dynamicFields }); onFormValidChange(isFormValid); - }, [formData]); + }, [dynamicFields]); function handleDynamicFieldChange(name: string, value: any) { - setFormData((prev) => ({ ...prev, [name]: value })); setDynamicFields((prev) => { return prev.map((field) => { if (field.name === name) { @@ -162,8 +160,7 @@ export function ConnectDestinationModalBody({ setExportedSignals((prev) => ({ ...prev, [signal]: value })); } - async function handleSubmit() { - // Helper function to process field values + function processFormFields() { function processFieldValue(field) { return field.componentType === 'dropdown' ? field.value.value @@ -171,10 +168,22 @@ export function ConnectDestinationModalBody({ } // Prepare fields for the request body - const fields = dynamicFields.map((field) => ({ + return dynamicFields.map((field) => ({ key: field.name, value: processFieldValue(field), })); + } + + async function handleSubmit() { + // Helper function to process field values + function processFieldValue(field) { + return field.componentType === 'dropdown' + ? field.value.value + : field.value; + } + + // Prepare fields for the request body + const fields = processFormFields(); // Function to store configured destination function storeConfiguredDestination() { @@ -240,10 +249,7 @@ export function ConnectDestinationModalBody({ name: destinationName, type: destination?.type || '', exportedSignals, - fields: Object.entries(formData).map(([name, value]) => ({ - key: name, - value, - })), + fields: processFormFields(), }} /> ) : ( diff --git a/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx index 6fd9f3edd..b935a558c 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/dynamic-form-fields/index.tsx @@ -16,7 +16,6 @@ export function DynamicConnectDestinationFormFields({ fields: any[]; onChange: (name: string, value: any) => void; }) { - console.log({ fields }); return fields?.map((field: any) => { switch (field.componentType) { case INPUT_TYPES.INPUT: diff --git a/frontend/webapp/reuseable-components/input-list/index.tsx b/frontend/webapp/reuseable-components/input-list/index.tsx index 0ea34d78a..43d6fffe5 100644 --- a/frontend/webapp/reuseable-components/input-list/index.tsx +++ b/frontend/webapp/reuseable-components/input-list/index.tsx @@ -10,6 +10,7 @@ interface InputListProps { initialValues?: string[]; title?: string; tooltip?: string; + required?: boolean; onChange: (values: string[]) => void; } @@ -55,19 +56,20 @@ const Title = styled(Text)` font-size: 14px; opacity: 0.8; line-height: 22px; - margin-bottom: 4px; `; const HeaderWrapper = styled.div` display: flex; align-items: center; gap: 6px; + margin-bottom: 4px; `; const InputList: React.FC = ({ initialValues = [''], title, tooltip, + required, onChange, }) => { const [inputs, setInputs] = useState(initialValues); @@ -102,6 +104,11 @@ const InputList: React.FC = ({ {title} + {!required && ( + + (optional) + + )} {tooltip && ( void; } @@ -23,6 +24,7 @@ const HeaderWrapper = styled.div` display: flex; align-items: center; gap: 6px; + margin-bottom: 4px; `; const Row = styled.div` @@ -61,13 +63,13 @@ const Title = styled(Text)` font-size: 14px; opacity: 0.8; line-height: 22px; - margin-bottom: 4px; `; export const KeyValueInputsList: React.FC = ({ initialKeyValuePairs = [{ key: '', value: '' }], title, tooltip, + required, onChange, }) => { const [keyValuePairs, setKeyValuePairs] = @@ -124,6 +126,11 @@ export const KeyValueInputsList: React.FC = ({ {title} + {!required && ( + + (optional) + + )} {tooltip && ( Date: Tue, 27 Aug 2024 15:10:01 +0300 Subject: [PATCH 4/6] chore: wip --- .../destinations/configured-destination-fields/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/webapp/components/destinations/configured-destination-fields/index.tsx b/frontend/webapp/components/destinations/configured-destination-fields/index.tsx index 22d2f94fe..a813a0427 100644 --- a/frontend/webapp/components/destinations/configured-destination-fields/index.tsx +++ b/frontend/webapp/components/destinations/configured-destination-fields/index.tsx @@ -32,11 +32,10 @@ export const ConfiguredDestinationFields: React.FC< > = ({ details }) => { const parseValue = (value: any) => { try { - if (typeof value === 'string') { - return value; - } - const parsed = JSON.parse(value); + if (typeof parsed === 'string') { + return parsed; + } if (Array.isArray(parsed)) { return parsed @@ -44,6 +43,7 @@ export const ConfiguredDestinationFields: React.FC< if (typeof item === 'object' && item !== null) { return `${item.key}: ${item.value}`; } + return item; }) .join(', '); From 722b3766c4e1fa3d57c68b7df506ea0b45c98efd Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 27 Aug 2024 15:42:19 +0300 Subject: [PATCH 5/6] chore: wip --- .../add-destination/choose-destination-menu/index.tsx | 2 +- .../configured-destinations-list/index.tsx | 2 +- .../connect-destination-modal-body/index.tsx | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/webapp/containers/main/destinations/add-destination/choose-destination-menu/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/choose-destination-menu/index.tsx index 5c7474c74..1e31c9dac 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/choose-destination-menu/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/choose-destination-menu/index.tsx @@ -64,7 +64,7 @@ const DestinationFilterComponent: React.FC = ({ diff --git a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx index f79ffe5a2..a989f4164 100644 --- a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx +++ b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx @@ -115,7 +115,7 @@ function ConfiguredDestinationsListItem({ item: ConfiguredDestination; }) { const [expand, setExpand] = React.useState(false); - console.log({ item }); + function renderSupportedSignals(item: ConfiguredDestination) { const supportedSignals = item.exportedSignals; const signals = Object.keys(supportedSignals); 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 d91324649..ab10247f7 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 @@ -140,12 +140,11 @@ export function ConnectDestinationModalBody({ field.required ? field.value : true ); - console.log({ isFormValid, dynamicFields }); - onFormValidChange(isFormValid); }, [dynamicFields]); function handleDynamicFieldChange(name: string, value: any) { + setShowConnectionError(false); setDynamicFields((prev) => { return prev.map((field) => { if (field.name === name) { @@ -244,7 +243,10 @@ export function ConnectDestinationModalBody({ actionButton={ destination.testConnectionSupported ? ( setShowConnectionError(true)} + onError={() => { + setShowConnectionError(true); + onFormValidChange(false); + }} destination={{ name: destinationName, type: destination?.type || '', From 6ae83846d0f2aab7796dc74891095a02b730154c Mon Sep 17 00:00:00 2001 From: alonkeyval Date: Tue, 27 Aug 2024 15:51:25 +0300 Subject: [PATCH 6/6] chore: fixed pr commentd --- .../add-destination/connect-destination-modal-body/index.tsx | 1 - 1 file changed, 1 deletion(-) 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 ab10247f7..f316b7e14 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 @@ -120,7 +120,6 @@ export function ConnectDestinationModalBody({ return { ...field, value: destination.fields[field.name], - // initialValue: destination.fields[field.name], }; } return field;