diff --git a/packages/twenty-front/src/modules/logic-functions/components/LogicFunctionTestInputInitEffect.tsx b/packages/twenty-front/src/modules/logic-functions/components/LogicFunctionTestInputInitEffect.tsx new file mode 100644 index 0000000000..8f36c4eade --- /dev/null +++ b/packages/twenty-front/src/modules/logic-functions/components/LogicFunctionTestInputInitEffect.tsx @@ -0,0 +1,60 @@ +import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue'; +import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState'; +import { logicFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/logicFunctionTestDataFamilyState'; +import { useEffect } from 'react'; +import { type InputJsonSchema } from 'twenty-shared/logic-function'; +import { isDefined } from 'twenty-shared/utils'; +import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow'; +import { useLogicFunctionForm } from '@/logic-functions/hooks/useLogicFunctionForm'; + +export const LogicFunctionTestInputInitEffect = ({ + logicFunctionId, +}: { + logicFunctionId: string; +}) => { + const { logicFunction } = useLogicFunctionForm({ logicFunctionId }); + + const toolInputSchema = logicFunction?.toolInputSchema; + + const logicFunctionTestData = useAtomFamilyStateValue( + logicFunctionTestDataFamilyState, + logicFunctionId, + ); + + const setLogicFunctionTestData = useSetAtomFamilyState( + logicFunctionTestDataFamilyState, + logicFunctionId, + ); + + useEffect(() => { + if (!logicFunctionTestData.shouldInitInput) { + return; + } + + if (!isDefined(toolInputSchema)) { + return; + } + + const schemaArray: InputJsonSchema[] = Array.isArray(toolInputSchema) + ? toolInputSchema + : [toolInputSchema]; + + const defaultInput = getFunctionInputFromInputSchema(schemaArray)[0]; + + if (!isDefined(defaultInput)) { + return; + } + + setLogicFunctionTestData((prev) => ({ + ...prev, + input: defaultInput as { [field: string]: any }, + shouldInitInput: false, + })); + }, [ + toolInputSchema, + logicFunctionTestData.shouldInitInput, + setLogicFunctionTestData, + ]); + + return null; +}; diff --git a/packages/twenty-front/src/modules/logic-functions/hooks/useExecuteLogicFunction.ts b/packages/twenty-front/src/modules/logic-functions/hooks/useExecuteLogicFunction.ts index 2771998589..e842dbc762 100644 --- a/packages/twenty-front/src/modules/logic-functions/hooks/useExecuteLogicFunction.ts +++ b/packages/twenty-front/src/modules/logic-functions/hooks/useExecuteLogicFunction.ts @@ -42,11 +42,19 @@ export const useExecuteLogicFunction = ({ logicFunctionTestDataFamilyState, logicFunctionId, ); + const setLogicFunctionTestData = useSetAtomFamilyState( logicFunctionTestDataFamilyState, logicFunctionId, ); + const updateLogicFunctionInput = (input: object) => { + setLogicFunctionTestData((prev) => ({ + ...prev, + input, + })); + }; + const executeLogicFunction = async () => { if (isExecuting) { return; @@ -94,5 +102,10 @@ export const useExecuteLogicFunction = ({ } }; - return { executeLogicFunction, isExecuting }; + return { + executeLogicFunction, + updateLogicFunctionInput, + logicFunctionTestData, + isExecuting, + }; }; diff --git a/packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionEditor.ts b/packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionForm.ts similarity index 82% rename from packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionEditor.ts rename to packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionForm.ts index dcd2b55dbd..ffdebda8be 100644 --- a/packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionEditor.ts +++ b/packages/twenty-front/src/modules/logic-functions/hooks/useLogicFunctionForm.ts @@ -1,4 +1,3 @@ -import { useExecuteLogicFunction } from '@/logic-functions/hooks/useExecuteLogicFunction'; import { type LogicFunctionFormValues, useLogicFunctionUpdateFormState, @@ -10,23 +9,16 @@ import { } from 'twenty-shared/logic-function'; import { useDebouncedCallback } from 'use-debounce'; -export const useLogicFunctionEditor = ({ +export const useLogicFunctionForm = ({ logicFunctionId, - executeCallback, }: { logicFunctionId: string; - executeCallback?: (result: object) => void; }) => { const { updateLogicFunction } = usePersistLogicFunction(); const { formValues, setFormValues, logicFunction, loading } = useLogicFunctionUpdateFormState({ logicFunctionId }); - const { executeLogicFunction, isExecuting } = useExecuteLogicFunction({ - logicFunctionId, - callback: executeCallback, - }); - const handleSave = useDebouncedCallback(async () => { await updateLogicFunction({ input: { @@ -74,7 +66,5 @@ export const useLogicFunctionEditor = ({ loading, handleSave, onChange, - executeLogicFunction, - isExecuting, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx index cdfe689095..386e882953 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/internal/__tests__/useCloseRecordTableCellNoGroup.test.tsx @@ -33,15 +33,15 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => { return ( - - {}} - > - + + {}} > { - - - + + + ); }; diff --git a/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx b/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx index adbbfd1a7f..b15c15118b 100644 --- a/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx +++ b/packages/twenty-front/src/modules/settings/logic-functions/components/tabs/SettingsLogicFunctionTestTab.tsx @@ -1,14 +1,12 @@ import { LogicFunctionExecutionResult } from '@/logic-functions/components/LogicFunctionExecutionResult'; import { LogicFunctionLogs } from '@/logic-functions/components/LogicFunctionLogs'; -import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue'; -import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState'; -import { logicFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/logicFunctionTestDataFamilyState'; import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; import { H2Title, IconPlayerPlay } from 'twenty-ui/display'; import { Button, CodeEditor, CoreEditorHeader } from 'twenty-ui/input'; import { Section } from 'twenty-ui/layout'; import { themeCssVariables } from 'twenty-ui/theme-constants'; +import { useExecuteLogicFunction } from '@/logic-functions/hooks/useExecuteLogicFunction'; const StyledInputsContainer = styled.div` display: flex; @@ -31,20 +29,18 @@ export const SettingsLogicFunctionTestTab = ({ isTesting?: boolean; }) => { const { t } = useLingui(); - const logicFunctionTestData = useAtomFamilyStateValue( - logicFunctionTestDataFamilyState, - logicFunctionId, - ); - const setLogicFunctionTestData = useSetAtomFamilyState( - logicFunctionTestDataFamilyState, - logicFunctionId, - ); - const onChange = (newInput: string) => { - setLogicFunctionTestData((prev) => ({ - ...prev, - input: JSON.parse(newInput), - })); + const { updateLogicFunctionInput, logicFunctionTestData } = + useExecuteLogicFunction({ + logicFunctionId, + }); + + const onChange = (value: string) => { + try { + updateLogicFunctionInput(JSON.parse(value)); + } catch { + // ignore invalid JSON while user is still typing + } }; return ( diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx index a5983ff26d..7cdc5f4f29 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx @@ -1,5 +1,5 @@ import { useGetAvailablePackages } from '@/logic-functions/hooks/useGetAvailablePackages'; -import { useLogicFunctionEditor } from '@/logic-functions/hooks/useLogicFunctionEditor'; +import { useLogicFunctionForm } from '@/logic-functions/hooks/useLogicFunctionForm'; import { useFullScreenModal } from '@/ui/layout/fullscreen/hooks/useFullScreenModal'; import { type BreadcrumbProps } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; @@ -22,9 +22,6 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS import { WorkflowCodeEditor } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowCodeEditor'; import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields'; import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowLogicFunctionTabListComponentId'; -import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue'; -import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState'; -import { logicFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/logicFunctionTestDataFamilyState'; import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; @@ -49,6 +46,8 @@ import { useIsMobile } from 'twenty-ui/utilities'; import { useDebouncedCallback } from 'use-debounce'; import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow'; import { themeCssVariables } from 'twenty-ui/theme-constants'; +import { LogicFunctionTestInputInitEffect } from '@/logic-functions/components/LogicFunctionTestInputInitEffect'; +import { useExecuteLogicFunction } from '@/logic-functions/hooks/useExecuteLogicFunction'; const StyledCodeEditorContainer = styled.div` display: flex; @@ -114,25 +113,24 @@ export const WorkflowEditActionCode = ({ }); }; - const { formValues, loading, executeLogicFunction, onChange, isExecuting } = - useLogicFunctionEditor({ - logicFunctionId, - executeCallback: updateOutputSchemaFromTestResult, - }); + const { formValues, loading, onChange } = useLogicFunctionForm({ + logicFunctionId, + }); + + const { + executeLogicFunction, + isExecuting, + logicFunctionTestData, + updateLogicFunctionInput, + } = useExecuteLogicFunction({ + logicFunctionId, + callback: updateOutputSchemaFromTestResult, + }); const { availablePackages } = useGetAvailablePackages({ id: logicFunctionId, }); - const logicFunctionTestData = useAtomFamilyStateValue( - logicFunctionTestDataFamilyState, - logicFunctionId, - ); - const setLogicFunctionTestData = useSetAtomFamilyState( - logicFunctionTestDataFamilyState, - logicFunctionId, - ); - const [functionInput, setFunctionInput] = useState( action.settings.input.logicFunctionInput, @@ -158,6 +156,7 @@ export const WorkflowEditActionCode = ({ newInput: newFunctionInput, oldInput: action.settings.input.logicFunctionInput, }); + const newMergedTestInput = mergeDefaultFunctionInputAndFunctionInput({ newInput: newFunctionInput, oldInput: logicFunctionTestData.input, @@ -165,10 +164,7 @@ export const WorkflowEditActionCode = ({ setFunctionInput(newMergedInput); - setLogicFunctionTestData((prev) => ({ - ...prev, - input: newMergedTestInput, - })); + updateLogicFunctionInput(newMergedTestInput); updateAction({ ...action, @@ -220,10 +216,8 @@ export const WorkflowEditActionCode = ({ path, value, ); - setLogicFunctionTestData((prev) => ({ - ...prev, - input: updatedTestFunctionInput, - })); + + updateLogicFunctionInput(updatedTestFunctionInput); }; const handleTestFunction = async () => { @@ -375,6 +369,7 @@ export const WorkflowEditActionCode = ({ return ( !loading && ( <> + { instanceId, ); - const { - formValues, - logicFunction, - loading, - onChange, - executeLogicFunction, - isExecuting, - } = useLogicFunctionEditor({ logicFunctionId }); + const { formValues, logicFunction, loading, onChange } = useLogicFunctionForm( + { logicFunctionId }, + ); + + const { executeLogicFunction, isExecuting } = useExecuteLogicFunction({ + logicFunctionId, + }); const handleTestFunction = async () => { navigate('#test');