diff --git a/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx b/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx index ab36f0ebb..df8dbfbd7 100644 --- a/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx @@ -12,7 +12,7 @@ interface Props { formData: DestinationInput; formErrors: Record; validateForm: () => boolean; - handleFormChange: (key: keyof DestinationInput | string, val: any) => void; + handleFormChange: (key: keyof DestinationInput, val: any) => void; dynamicFields: DynamicField[]; setDynamicFields: Dispatch>; } diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index fef65a4fc..e6f721462 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -1,7 +1,6 @@ -import { useState } from 'react'; -import { useNotify } from '../notification/useNotify'; import { DrawerBaseItem } from '@/store'; -import { ACTION, FORM_ALERTS, NOTIFICATION } from '@/utils'; +import { useGenericForm, useNotify } from '@/hooks'; +import { FORM_ALERTS, NOTIFICATION } from '@/utils'; import type { ActionDataParsed, ActionInput } from '@/types'; const INITIAL: ActionInput = { @@ -16,21 +15,7 @@ const INITIAL: ActionInput = { export function useActionFormData() { const notify = useNotify(); - - const [formData, setFormData] = useState({ ...INITIAL }); - const [formErrors, setFormErrors] = useState>({}); - - const handleFormChange = (key: keyof typeof INITIAL, val: any) => { - setFormData((prev) => ({ - ...prev, - [key]: val, - })); - }; - - const resetFormData = () => { - setFormData({ ...INITIAL }); - setFormErrors({}); - }; + const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { const errors = {}; @@ -60,7 +45,7 @@ export function useActionFormData() { }); } - setFormErrors(errors); + handleErrorChange(undefined, undefined, errors); return ok; }; @@ -98,7 +83,7 @@ export function useActionFormData() { } }); - setFormData(updatedData); + handleFormChange(undefined, undefined, updatedData); }; return { diff --git a/frontend/webapp/hooks/common/index.ts b/frontend/webapp/hooks/common/index.ts index 71b90efaa..370760930 100644 --- a/frontend/webapp/hooks/common/index.ts +++ b/frontend/webapp/hooks/common/index.ts @@ -1,4 +1,5 @@ export * from './useContainerSize'; +export * from './useGenericForm'; export * from './useOnClickOutside'; export * from './useKeyDown'; export * from './useTimeAgo'; diff --git a/frontend/webapp/hooks/common/useGenericForm.ts b/frontend/webapp/hooks/common/useGenericForm.ts new file mode 100644 index 000000000..35d8f46da --- /dev/null +++ b/frontend/webapp/hooks/common/useGenericForm.ts @@ -0,0 +1,44 @@ +import { useState } from 'react'; + +export const useGenericForm =
>(initialFormData: Form) => { + const [formData, setFormData] = useState({ ...initialFormData }); + const [formErrors, setFormErrors] = useState>>({}); + + const handleFormChange = (key?: keyof typeof formData, val?: any, obj?: typeof formData) => { + if (!!key) { + // this is for cases where the form contains objects such as "exportedSignals", + // the object's child is targeted with a ".dot" for example: "exportedSignals.logs" + + const [parentKey, childKey] = (key as string).split('.'); + + if (!!childKey) { + setFormData((prev) => ({ ...prev, [parentKey]: { ...prev[parentKey], [childKey]: val } })); + } else { + setFormData((prev) => ({ ...prev, [parentKey]: val })); + } + } else if (!!obj) { + setFormData({ ...obj }); + } + }; + + const handleErrorChange = (key?: keyof typeof formErrors, val?: string, obj?: typeof formErrors) => { + if (!!key) { + setFormErrors((prev) => ({ ...prev, [key]: val })); + } else if (!!obj) { + setFormErrors({ ...obj }); + } + }; + + const resetFormData = () => { + setFormData({ ...initialFormData }); + setFormErrors({}); + }; + + return { + formData, + formErrors, + handleFormChange, + handleErrorChange, + resetFormData, + }; +}; diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 28ca622e8..3f4edefd3 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { DrawerBaseItem } from '@/store'; import { useQuery } from '@apollo/client'; import { GET_DESTINATION_TYPE_DETAILS } from '@/graphql'; -import { useConnectDestinationForm, useNotify } from '@/hooks'; +import { useConnectDestinationForm, useGenericForm, useNotify } from '@/hooks'; import { ACTION, FORM_ALERTS, NOTIFICATION, safeJsonParse } from '@/utils'; import { type DynamicField, @@ -29,10 +29,9 @@ export function useDestinationFormData(params?: { destinationType?: string; supp const { destinationType, supportedSignals, preLoadedFields } = params || {}; const notify = useNotify(); - const { buildFormDynamicFields } = useConnectDestinationForm(); + const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); - const [formData, setFormData] = useState({ ...INITIAL }); - const [formErrors, setFormErrors] = useState>({}); + const { buildFormDynamicFields } = useConnectDestinationForm(); const [dynamicFields, setDynamicFields] = useState([]); const t = destinationType || formData.type; @@ -87,31 +86,6 @@ export function useDestinationFormData(params?: { destinationType?: string; supp }); }, [supportedSignals]); - function handleFormChange(key: keyof typeof INITIAL | string, val: any) { - // this is for a case where "exportedSignals" have been changed, it's an object so they children are targeted as: "exportedSignals.logs" - const [parentKey, childKey] = key.split('.'); - - if (!!childKey) { - setFormData((prev) => ({ - ...prev, - [parentKey]: { - ...prev[parentKey], - [childKey]: val, - }, - })); - } else { - setFormData((prev) => ({ - ...prev, - [parentKey]: val, - })); - } - } - - const resetFormData = () => { - setFormData({ ...INITIAL }); - setFormErrors({}); - }; - const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { const errors = {}; let ok = true; @@ -131,7 +105,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp }); } - setFormErrors(errors); + handleErrorChange(undefined, undefined, errors); return ok; }; @@ -152,7 +126,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp fields: Object.entries(safeJsonParse(fields, {})).map(([key, value]: [string, string]) => ({ key, value })), }; - setFormData(updatedData); + handleFormChange(undefined, undefined, updatedData); }; return { diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index 0c39e9c66..c1718e65f 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -1,7 +1,6 @@ -import { useState } from 'react'; -import { useNotify } from '../notification/useNotify'; import type { DrawerBaseItem } from '@/store'; -import { ACTION, FORM_ALERTS, NOTIFICATION } from '@/utils'; +import { useGenericForm, useNotify } from '@/hooks'; +import { FORM_ALERTS, NOTIFICATION } from '@/utils'; import { PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types'; const INITIAL: InstrumentationRuleInput = { @@ -20,21 +19,7 @@ const INITIAL: InstrumentationRuleInput = { export function useInstrumentationRuleFormData() { const notify = useNotify(); - - const [formData, setFormData] = useState({ ...INITIAL }); - const [formErrors, setFormErrors] = useState>({}); - - const handleFormChange = (key: keyof typeof INITIAL, val: any) => { - setFormData((prev) => ({ - ...prev, - [key]: val, - })); - }; - - const resetFormData = () => { - setFormData({ ...INITIAL }); - setFormErrors({}); - }; + const { formData, formErrors, handleFormChange, handleErrorChange, resetFormData } = useGenericForm(INITIAL); const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { const errors = {}; @@ -63,7 +48,7 @@ export function useInstrumentationRuleFormData() { }); } - setFormErrors(errors); + handleErrorChange(undefined, undefined, errors); return ok; }; @@ -87,7 +72,7 @@ export function useInstrumentationRuleFormData() { }; } - setFormData(updatedData); + handleFormChange(undefined, undefined, updatedData); }; return {