diff --git a/frontend/webapp/containers/main/actions/action-drawer/index.tsx b/frontend/webapp/containers/main/actions/action-drawer/index.tsx index 7ba866df4..9204193a2 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/index.tsx +++ b/frontend/webapp/containers/main/actions/action-drawer/index.tsx @@ -23,7 +23,7 @@ const FormContainer = styled.div` export const ActionDrawer: React.FC = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); - const { formData, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useActionFormData(); + const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useActionFormData(); const { updateAction, deleteAction } = useActionCRUD({ onSuccess: (type) => { @@ -88,7 +88,7 @@ export const ActionDrawer: React.FC = () => { }; const handleSave = async (newTitle: string) => { - if (validateForm({ withAlert: true })) { + if (validateForm({ withAlert: true, alertTitle: ACTION.UPDATE })) { const title = newTitle !== item.type ? newTitle : ''; handleFormChange('name', title); await updateAction(id, { ...formData, name: title }); @@ -112,6 +112,7 @@ export const ActionDrawer: React.FC = () => { isUpdate action={thisAction} formData={formData} + formErrors={formErrors} handleFormChange={(...params) => { setIsFormDirty(true); handleFormChange(...params); diff --git a/frontend/webapp/containers/main/actions/action-form-body/index.tsx b/frontend/webapp/containers/main/actions/action-form-body/index.tsx index d2980da37..817e40d75 100644 --- a/frontend/webapp/containers/main/actions/action-form-body/index.tsx +++ b/frontend/webapp/containers/main/actions/action-form-body/index.tsx @@ -9,6 +9,7 @@ interface Props { isUpdate?: boolean; action: ActionOption; formData: ActionInput; + formErrors: Record; handleFormChange: (key: keyof ActionInput, val: any) => void; } @@ -23,7 +24,7 @@ const FieldTitle = styled(Text)` margin-bottom: 12px; `; -export const ActionFormBody: React.FC = ({ isUpdate, action, formData, handleFormChange }) => { +export const ActionFormBody: React.FC = ({ isUpdate, action, formData, formErrors, handleFormChange }) => { return ( {isUpdate && ( diff --git a/frontend/webapp/containers/main/actions/action-modal/index.tsx b/frontend/webapp/containers/main/actions/action-modal/index.tsx index 3aa6a5164..30ca157a7 100644 --- a/frontend/webapp/containers/main/actions/action-modal/index.tsx +++ b/frontend/webapp/containers/main/actions/action-modal/index.tsx @@ -1,5 +1,6 @@ +import React, { useState } from 'react'; +import { ACTION } from '@/utils'; import { ActionFormBody } from '../'; -import React, { useMemo, useState } from 'react'; import { CenterThis, ModalBody } from '@/styles'; import { useActionCRUD, useActionFormData } from '@/hooks/actions'; import { ACTION_OPTIONS, type ActionOption } from './action-options'; @@ -11,16 +12,10 @@ interface Props { } export const ActionModal: React.FC = ({ isOpen, onClose }) => { - const { formData, handleFormChange, resetFormData, validateForm } = useActionFormData(); + const { formData, formErrors, handleFormChange, resetFormData, validateForm } = useActionFormData(); const { createAction, loading } = useActionCRUD({ onSuccess: handleClose }); const [selectedItem, setSelectedItem] = useState(undefined); - const isFormOk = useMemo(() => !!selectedItem && validateForm(), [selectedItem, formData]); - - const handleSubmit = async () => { - createAction(formData); - }; - function handleClose() { resetFormData(); setSelectedItem(undefined); @@ -33,6 +28,15 @@ export const ActionModal: React.FC = ({ isOpen, onClose }) => { setSelectedItem(item); }; + const handleSubmit = async () => { + const isFormOk = validateForm({ withAlert: true, alertTitle: ACTION.CREATE }); + if (!isFormOk) return null; + + await createAction(formData); + + handleClose(); + }; + return ( = ({ isOpen, onClose }) => { variant: 'primary', label: 'DONE', onClick: handleSubmit, - disabled: !isFormOk || loading, + disabled: !selectedItem || loading, }, ]} /> @@ -64,7 +68,7 @@ export const ActionModal: React.FC = ({ isOpen, onClose }) => { ) : ( - + )} ) : null} diff --git a/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx b/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx index 936c7ef54..5688cd3a3 100644 --- a/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx @@ -1,11 +1,13 @@ import React, { useMemo, useState } from 'react'; +import { ACTION } from '@/utils'; +import buildCard from './build-card'; import styled from 'styled-components'; -import { safeJsonParse } from '@/utils'; import { useDrawerStore } from '@/store'; import { CardDetails } from '@/components'; -import type { ActualDestination } from '@/types'; +import buildDrawerItem from './build-drawer-item'; import OverviewDrawer from '../../overview/overview-drawer'; import { DestinationFormBody } from '../destination-form-body'; +import { OVERVIEW_ENTITY_TYPES, type ActualDestination } from '@/types'; import { useDestinationCRUD, useDestinationFormData, useDestinationTypes } from '@/hooks'; interface Props {} @@ -19,95 +21,87 @@ const FormContainer = styled.div` `; export const DestinationDrawer: React.FC = () => { - const selectedItem = useDrawerStore(({ selectedItem }) => selectedItem); - const [isEditing, setIsEditing] = useState(false); - const [isFormDirty, setIsFormDirty] = useState(false); + const { selectedItem, setSelectedItem } = useDrawerStore(); + const { destinations: destinationTypes } = useDestinationTypes(); - const { updateDestination, deleteDestination } = useDestinationCRUD(); - const { formData, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem, destinationTypeDetails, dynamicFields, setDynamicFields } = useDestinationFormData({ + const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem, destinationTypeDetails, dynamicFields, setDynamicFields } = useDestinationFormData({ destinationType: (selectedItem?.item as ActualDestination)?.destinationType?.type, preLoadedFields: (selectedItem?.item as ActualDestination)?.fields, // TODO: supportedSignals: thisDestination?.supportedSignals, // currently, the real "supportedSignals" is being used by "destination" passed as prop to "DestinationFormBody" }); - const cardData = useMemo(() => { - if (!selectedItem) return []; - - const buildMonitorsList = (exportedSignals: ActualDestination['exportedSignals']): string => - Object.keys(exportedSignals) - .filter((key) => exportedSignals[key]) - .join(', ') || 'N/A'; - - const buildDestinationFieldData = (parsedFields: Record) => - Object.entries(parsedFields).map(([key, value]) => { - const found = destinationTypeDetails?.fields?.find((field) => field.name === key); + const { updateDestination, deleteDestination } = useDestinationCRUD({ + onSuccess: (type) => { + setIsEditing(false); + setIsFormDirty(false); + + if (type === ACTION.DELETE) { + setSelectedItem(null); + } else { + const { item } = selectedItem as { item: ActualDestination }; + const { id } = item; + setSelectedItem({ id, type: OVERVIEW_ENTITY_TYPES.DESTINATION, item: buildDrawerItem(id, formData, item) }); + } + }, + }); - const { type } = safeJsonParse(found?.componentProperties, { type: '' }); - const secret = type === 'password' ? new Array(value.length).fill('•').join('') : ''; + const [isEditing, setIsEditing] = useState(false); + const [isFormDirty, setIsFormDirty] = useState(false); - return { - title: found?.displayName || key, - value: secret || value || 'N/A', - }; - }); + const cardData = useMemo(() => { + if (!selectedItem || !destinationTypeDetails) return []; - const { exportedSignals, destinationType, fields } = selectedItem.item as ActualDestination; - const parsedFields = safeJsonParse>(fields, {}); - const fieldsData = buildDestinationFieldData(parsedFields); + const { item } = selectedItem as { item: ActualDestination }; + const arr = buildCard(item, destinationTypeDetails); - return [{ title: 'Destination', value: destinationType.displayName || 'N/A' }, { title: 'Monitors', value: buildMonitorsList(exportedSignals) }, ...fieldsData]; + return arr; }, [selectedItem, destinationTypeDetails]); - const { destinations } = useDestinationTypes(); const thisDestination = useMemo(() => { - if (!destinations.length || !selectedItem || !isEditing) { + if (!destinationTypes.length || !selectedItem || !isEditing) { resetFormData(); return undefined; } const { item } = selectedItem as { item: ActualDestination }; - const found = destinations.map(({ items }) => items.filter(({ type }) => type === item.destinationType.type)).filter((arr) => !!arr.length)[0][0]; + const found = destinationTypes.map(({ items }) => items.filter(({ type }) => type === item.destinationType.type)).filter((arr) => !!arr.length)[0][0]; if (!found) return undefined; loadFormWithDrawerItem(selectedItem); return found; - }, [destinations, selectedItem, isEditing]); + }, [destinationTypes, selectedItem, isEditing]); if (!selectedItem?.item) return null; - const { id, item } = selectedItem; + const { id, item } = selectedItem as { id: string; item: ActualDestination }; const handleEdit = (bool?: boolean) => { - if (typeof bool === 'boolean') { - setIsEditing(bool); - } else { - setIsEditing(true); - } + setIsEditing(typeof bool === 'boolean' ? bool : true); }; const handleCancel = () => { - resetFormData(); setIsEditing(false); + setIsFormDirty(false); }; const handleDelete = async () => { - await deleteDestination(id as string); + await deleteDestination(id); }; const handleSave = async (newTitle: string) => { - if (validateForm({ withAlert: true })) { - const title = newTitle !== (item as ActualDestination).destinationType.displayName ? newTitle : ''; - - await updateDestination(id as string, { ...formData, name: title }); + if (validateForm({ withAlert: true, alertTitle: ACTION.UPDATE })) { + const title = newTitle !== item.destinationType.displayName ? newTitle : ''; + handleFormChange('name', title); + await updateDestination(id, { ...formData, name: title }); } }; return ( = () => { { setIsFormDirty(true); 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 021a7558b..27e31eab2 100644 --- a/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-form-body/index.tsx @@ -8,8 +8,8 @@ import { CheckboxList, Divider, Input, NotificationNote, SectionTitle } from '@/ interface Props { isUpdate?: boolean; destination?: DestinationTypeItem; - isFormOk: boolean; formData: DestinationInput; + formErrors: Record; handleFormChange: (key: keyof DestinationInput | string, val: any) => void; dynamicFields: DynamicField[]; setDynamicFields: Dispatch>; @@ -22,8 +22,9 @@ const Container = styled.div` padding: 0 4px; `; -export function DestinationFormBody({ isUpdate, destination, isFormOk, formData, handleFormChange, dynamicFields, setDynamicFields }: Props) { +export function DestinationFormBody({ isUpdate, destination, formData, formErrors, handleFormChange, dynamicFields, setDynamicFields }: Props) { const { supportedSignals, testConnectionSupported, displayName } = destination || {}; + const isFormOk = useMemo(() => !Object.keys(formErrors).length, [formErrors]); const [isFormDirty, setIsFormDirty] = useState(false); const [showConnectionError, setShowConnectionError] = useState(false); @@ -50,7 +51,7 @@ export function DestinationFormBody({ isUpdate, destination, isFormOk, formData, <> = ({ isOnboarding, isOpen, onClose }) => { const [selectedItem, setSelectedItem] = useState(); - const { createDestination } = useDestinationCRUD(); + const { createDestination, loading } = useDestinationCRUD(); const addConfiguredDestination = useAppStore(({ addConfiguredDestination }) => addConfiguredDestination); - const { formData, handleFormChange, resetFormData, validateForm, dynamicFields, setDynamicFields } = useDestinationFormData({ + const { formData, formErrors, handleFormChange, resetFormData, validateForm, dynamicFields, setDynamicFields } = useDestinationFormData({ supportedSignals: selectedItem?.supportedSignals, preLoadedFields: selectedItem?.fields, }); - const isFormOk = !!selectedItem && validateForm(); - const handleClose = () => { resetFormData(); setSelectedItem(undefined); @@ -59,6 +57,9 @@ export const DestinationModal: React.FC = ({ isOnboard }; const handleSubmit = async () => { + const isFormOk = validateForm({ withAlert: !isOnboarding, alertTitle: ACTION.CREATE }); + if (!isFormOk) return null; + if (isOnboarding) { const destinationTypeDetails = dynamicFields.map((field) => ({ title: field.title, @@ -81,7 +82,7 @@ export const DestinationModal: React.FC = ({ isOnboard addConfiguredDestination({ stored: storedDestination, form: formData }); } else { - createDestination(formData); + await createDestination(formData); } handleClose(); @@ -93,7 +94,7 @@ export const DestinationModal: React.FC = ({ isOnboard label: 'DONE', variant: 'primary' as const, onClick: handleSubmit, - disabled: !isFormOk, + disabled: !selectedItem || loading, }, ]; @@ -103,6 +104,7 @@ export const DestinationModal: React.FC = ({ isOnboard iconSrc: '/icons/common/arrow-white.svg', variant: 'secondary' as const, onClick: handleBack, + disabled: loading, }); } @@ -126,8 +128,8 @@ export const DestinationModal: React.FC = ({ isOnboard {!!selectedItem ? ( = () => { const { selectedItem, setSelectedItem } = useDrawerStore(); - const { formData, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useInstrumentationRuleFormData(); + const { formData, formErrors, handleFormChange, resetFormData, validateForm, loadFormWithDrawerItem } = useInstrumentationRuleFormData(); const { updateInstrumentationRule, deleteInstrumentationRule } = useInstrumentationRuleCRUD({ onSuccess: (type) => { @@ -85,7 +85,7 @@ export const RuleDrawer: React.FC = () => { }; const handleSave = async (newTitle: string) => { - if (validateForm({ withAlert: true })) { + if (validateForm({ withAlert: true, alertTitle: ACTION.UPDATE })) { const title = newTitle !== item.type ? newTitle : ''; handleFormChange('ruleName', title); await updateInstrumentationRule(id, { ...formData, ruleName: title }); @@ -109,6 +109,7 @@ export const RuleDrawer: React.FC = () => { isUpdate rule={thisRule} formData={formData} + formErrors={formErrors} handleFormChange={(...params) => { setIsFormDirty(true); handleFormChange(...params); diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx index fea52ec03..6e0f7f40c 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-form-body/index.tsx @@ -9,6 +9,7 @@ interface Props { isUpdate?: boolean; rule: RuleOption; formData: InstrumentationRuleInput; + formErrors: Record; handleFormChange: (key: keyof InstrumentationRuleInput, val: any) => void; } @@ -23,7 +24,7 @@ const FieldTitle = styled(Text)` margin-bottom: 12px; `; -export const RuleFormBody: React.FC = ({ isUpdate, rule, formData, handleFormChange }) => { +export const RuleFormBody: React.FC = ({ isUpdate, rule, formData, formErrors, handleFormChange }) => { return ( {isUpdate && ( diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx index 89a9530b3..dba7b8370 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-modal/index.tsx @@ -1,6 +1,7 @@ -import React, { useMemo, useState } from 'react'; -import { CenterThis, ModalBody } from '@/styles'; +import React, { useState } from 'react'; +import { ACTION } from '@/utils'; import { RuleFormBody } from '../'; +import { CenterThis, ModalBody } from '@/styles'; import { RULE_OPTIONS, RuleOption } from './rule-options'; import { useInstrumentationRuleCRUD, useInstrumentationRuleFormData } from '@/hooks'; import { AutocompleteInput, Divider, FadeLoader, Modal, NavigationButtons, NotificationNote, SectionTitle } from '@/reuseable-components'; @@ -11,16 +12,10 @@ interface Props { } export const RuleModal: React.FC = ({ isOpen, onClose }) => { - const { formData, handleFormChange, resetFormData, validateForm } = useInstrumentationRuleFormData(); + const { formData, formErrors, handleFormChange, resetFormData, validateForm } = useInstrumentationRuleFormData(); const { createInstrumentationRule, loading } = useInstrumentationRuleCRUD({ onSuccess: handleClose }); const [selectedItem, setSelectedItem] = useState(RULE_OPTIONS[0]); - const isFormOk = useMemo(() => !!selectedItem && validateForm(), [selectedItem, formData]); - - const handleSubmit = async () => { - createInstrumentationRule(formData); - }; - function handleClose() { resetFormData(); setSelectedItem(undefined); @@ -32,6 +27,15 @@ export const RuleModal: React.FC = ({ isOpen, onClose }) => { setSelectedItem(item); }; + const handleSubmit = async () => { + const isFormOk = validateForm({ withAlert: true, alertTitle: ACTION.CREATE }); + if (!isFormOk) return null; + + await createInstrumentationRule(formData); + + handleClose(); + }; + return ( = ({ isOpen, onClose }) => { variant: 'primary', label: 'DONE', onClick: handleSubmit, - disabled: !isFormOk || loading, + disabled: !selectedItem || loading, }, ]} /> @@ -64,7 +68,7 @@ export const RuleModal: React.FC = ({ isOpen, onClose }) => { ) : ( - + )} ) : null} diff --git a/frontend/webapp/hooks/actions/useActionFormData.ts b/frontend/webapp/hooks/actions/useActionFormData.ts index 1283d44ef..27f0439e1 100644 --- a/frontend/webapp/hooks/actions/useActionFormData.ts +++ b/frontend/webapp/hooks/actions/useActionFormData.ts @@ -16,7 +16,9 @@ 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) => ({ @@ -29,7 +31,8 @@ export function useActionFormData() { setFormData({ ...INITIAL }); }; - const validateForm = (params?: { withAlert?: boolean }) => { + const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { + const errors = {}; let ok = true; Object.entries(formData).forEach(([k, v]) => { @@ -37,7 +40,10 @@ export function useActionFormData() { case 'type': case 'signals': case 'details': - if (Array.isArray(v) ? !v.length : !v) ok = false; + if (Array.isArray(v) ? !v.length : !v) { + ok = false; + errors[k] = FORM_ALERTS.FIELD_IS_REQUIRED; + } break; default: @@ -48,11 +54,13 @@ export function useActionFormData() { if (!ok && params?.withAlert) { notify({ type: NOTIFICATION.WARNING, - title: ACTION.UPDATE, + title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); } + setFormErrors(errors); + return ok; }; @@ -94,6 +102,7 @@ export function useActionFormData() { return { formData, + formErrors, handleFormChange, resetFormData, validateForm, diff --git a/frontend/webapp/hooks/destinations/useDestinationFormData.ts b/frontend/webapp/hooks/destinations/useDestinationFormData.ts index 4cf324778..5f1652508 100644 --- a/frontend/webapp/hooks/destinations/useDestinationFormData.ts +++ b/frontend/webapp/hooks/destinations/useDestinationFormData.ts @@ -32,6 +32,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp const { buildFormDynamicFields } = useConnectDestinationForm(); const [formData, setFormData] = useState({ ...INITIAL }); + const [formErrors, setFormErrors] = useState>({}); const [dynamicFields, setDynamicFields] = useState([]); const t = destinationType || formData.type; @@ -110,19 +111,27 @@ export function useDestinationFormData(params?: { destinationType?: string; supp setFormData({ ...INITIAL }); }; - const validateForm = (params?: { withAlert?: boolean }) => { + const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { + const errors = {}; let ok = true; - ok = dynamicFields.every((field) => (field.required ? !!field.value : true)); + dynamicFields.forEach(({ name, value, required }) => { + if (required && !value) { + ok = false; + errors[name] = FORM_ALERTS.FIELD_IS_REQUIRED; + } + }); if (!ok && params?.withAlert) { notify({ type: NOTIFICATION.WARNING, - title: ACTION.UPDATE, + title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); } + setFormErrors(errors); + return ok; }; @@ -147,6 +156,7 @@ export function useDestinationFormData(params?: { destinationType?: string; supp return { formData, + formErrors, handleFormChange, resetFormData, validateForm, diff --git a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts index d5d88e47e..552fd2fe4 100644 --- a/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts +++ b/frontend/webapp/hooks/instrumentation-rules/useInstrumentationRuleFormData.ts @@ -2,11 +2,7 @@ import { useState } from 'react'; import { useNotify } from '../notification/useNotify'; import type { DrawerBaseItem } from '@/store'; import { ACTION, FORM_ALERTS, NOTIFICATION } from '@/utils'; -import { - PayloadCollectionType, - type InstrumentationRuleInput, - type InstrumentationRuleSpec, -} from '@/types'; +import { PayloadCollectionType, type InstrumentationRuleInput, type InstrumentationRuleSpec } from '@/types'; const INITIAL: InstrumentationRuleInput = { ruleName: '', @@ -24,7 +20,9 @@ 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) => ({ @@ -37,16 +35,18 @@ export function useInstrumentationRuleFormData() { setFormData({ ...INITIAL }); }; - const validateForm = (params?: { withAlert?: boolean }) => { + const validateForm = (params?: { withAlert?: boolean; alertTitle?: string }) => { + const errors = {}; let ok = true; Object.entries(formData).forEach(([k, v]) => { switch (k) { case 'payloadCollection': - const hasNoneSelected = !Object.values( - v as InstrumentationRuleInput['payloadCollection'] - ).filter((val) => !!val).length; - ok = !hasNoneSelected; + const hasNoneSelected = !Object.values(v as InstrumentationRuleInput['payloadCollection']).filter((val) => !!val).length; + if (hasNoneSelected) { + ok = false; + errors[k] = FORM_ALERTS.FIELD_IS_REQUIRED; + } break; default: @@ -57,17 +57,18 @@ export function useInstrumentationRuleFormData() { if (!ok && params?.withAlert) { notify({ type: NOTIFICATION.WARNING, - title: ACTION.UPDATE, + title: params.alertTitle, message: FORM_ALERTS.REQUIRED_FIELDS, }); } + setFormErrors(errors); + return ok; }; const loadFormWithDrawerItem = (drawerItem: DrawerBaseItem) => { - const { ruleName, notes, disabled, payloadCollection } = - drawerItem.item as InstrumentationRuleSpec; + const { ruleName, notes, disabled, payloadCollection } = drawerItem.item as InstrumentationRuleSpec; const updatedData: InstrumentationRuleInput = { ...INITIAL, @@ -78,26 +79,10 @@ export function useInstrumentationRuleFormData() { if (payloadCollection) { updatedData['payloadCollection'] = { - [PayloadCollectionType.HTTP_REQUEST]: !!payloadCollection[ - PayloadCollectionType.HTTP_REQUEST - ] - ? {} - : null, - [PayloadCollectionType.HTTP_RESPONSE]: !!payloadCollection[ - PayloadCollectionType.HTTP_RESPONSE - ] - ? {} - : null, - [PayloadCollectionType.DB_QUERY]: !!payloadCollection[ - PayloadCollectionType.DB_QUERY - ] - ? {} - : null, - [PayloadCollectionType.MESSAGING]: !!payloadCollection[ - PayloadCollectionType.MESSAGING - ] - ? {} - : null, + [PayloadCollectionType.HTTP_REQUEST]: !!payloadCollection[PayloadCollectionType.HTTP_REQUEST] ? {} : null, + [PayloadCollectionType.HTTP_RESPONSE]: !!payloadCollection[PayloadCollectionType.HTTP_RESPONSE] ? {} : null, + [PayloadCollectionType.DB_QUERY]: !!payloadCollection[PayloadCollectionType.DB_QUERY] ? {} : null, + [PayloadCollectionType.MESSAGING]: !!payloadCollection[PayloadCollectionType.MESSAGING] ? {} : null, }; } @@ -106,6 +91,7 @@ export function useInstrumentationRuleFormData() { return { formData, + formErrors, handleFormChange, resetFormData, validateForm, diff --git a/frontend/webapp/types/destinations.ts b/frontend/webapp/types/destinations.ts index 4a83057fe..08e90393d 100644 --- a/frontend/webapp/types/destinations.ts +++ b/frontend/webapp/types/destinations.ts @@ -46,7 +46,10 @@ export interface DestinationDetailsField { export type DynamicField = { name: string; componentType: 'input' | 'dropdown' | 'multi_input' | 'textarea'; + type: string; title: string; + value?: any; + required?: boolean; [key: string]: any; }; diff --git a/frontend/webapp/utils/constants/string.tsx b/frontend/webapp/utils/constants/string.tsx index 5bc91baca..7c2f3e5a1 100644 --- a/frontend/webapp/utils/constants/string.tsx +++ b/frontend/webapp/utils/constants/string.tsx @@ -33,7 +33,8 @@ export const ACTION = { }; export const FORM_ALERTS = { - REQUIRED_FIELDS: 'Required fields are missing!', + REQUIRED_FIELDS: 'Required fields are missing', + FIELD_IS_REQUIRED: 'This field is required', }; export const NOTIFICATION: {