@flowuent-org/diagramming-core 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/TRANSLATION_FIX_SUMMARY.md +118 -0
  2. package/apps/diagramming/src/DiagramTabs.tsx +205 -205
  3. package/apps/diagramming/src/sample-workflow-content.ts +55 -54
  4. package/package.json +116 -116
  5. package/packages/diagrams/I18N_SETUP.md +126 -0
  6. package/packages/diagrams/NODE_DATA_UPDATE_API.md +430 -430
  7. package/packages/diagrams/README.md +443 -3
  8. package/packages/diagrams/UNDO_REDO_API.md +306 -306
  9. package/packages/diagrams/locales/en/translation.json +713 -0
  10. package/packages/diagrams/package.json +5 -23
  11. package/packages/diagrams/pnpm-lock.yaml +2606 -0
  12. package/packages/diagrams/project.json +42 -38
  13. package/packages/diagrams/rollup.config.js +5 -10
  14. package/packages/diagrams/src/index.ts +116 -113
  15. package/packages/diagrams/src/lib/atoms/CardEditableTitle.tsx +76 -76
  16. package/packages/diagrams/src/lib/components/automation/AutomationApiNode.tsx +24 -3
  17. package/packages/diagrams/src/lib/components/automation/AutomationEndNode.tsx +23 -2
  18. package/packages/diagrams/src/lib/components/automation/AutomationFormattingNode.tsx +24 -3
  19. package/packages/diagrams/src/lib/components/automation/AutomationStartNode.tsx +24 -3
  20. package/packages/diagrams/src/lib/contexts/onWorkflowNodeDelete.ts +1 -1
  21. package/packages/diagrams/src/lib/i18n.ts +42 -0
  22. package/packages/diagrams/src/lib/organisms/CustomEdge/useCreateBendPoint.tsx +121 -119
  23. package/packages/diagrams/src/lib/organisms/WorkFlowNode/NodeActionButtons.tsx +1 -1
  24. package/packages/diagrams/src/lib/templates/node-forms/CallForm.tsx +370 -370
  25. package/packages/diagrams/src/lib/types/node-types.ts +29 -29
  26. package/packages/diagrams/src/lib/utils/AutomationExecutionEngine.ts +1168 -1162
  27. package/packages/diagrams/tsconfig.lib.json +1 -3
  28. package/tsconfig.base.json +1 -1
@@ -1,370 +1,370 @@
1
- import { memo, useCallback, useState } from 'react';
2
- import { useTranslation } from 'react-i18next';
3
-
4
- import ExpressionInput from '../../atoms/ExpressionInput';
5
- import { useDiagram } from '../../contexts/DiagramProvider';
6
- import { FunctionSignature } from '../../types/FunctionSignature';
7
- import { RiArrowDownSLine } from 'react-icons/ri';
8
- import MenuList from '@mui/material/MenuList';
9
- import MenuItem from '@mui/material/MenuItem';
10
- import ListItemText from '@mui/material/ListItemText';
11
- import Typography from '@mui/material/Typography';
12
- import Button from '@mui/material/Button';
13
- import Dialog from '@mui/material/Dialog';
14
- import DialogActions from '@mui/material/DialogActions';
15
- import DialogContent from '@mui/material/DialogContent';
16
- import DialogTitle from '@mui/material/DialogTitle';
17
- import Stack from '@mui/material/Stack';
18
- import { Box, Divider, IconButton } from '@mui/material';
19
- import { useDeleteFunction } from '../../contexts/DiagramProvider';
20
- import DeleteIcon from '@mui/icons-material/Delete';
21
- import DialogContentText from '@mui/material/DialogContentText';
22
- import {
23
- CallFormData,
24
- CallFormProps,
25
- CustomFunctionSelectProps,
26
- FunctionArgument,
27
- FunctionReturnValue,
28
- } from '../../types/function-execution';
29
- import { Expression } from '../../types/ndoe-form-types';
30
- import { useFormValidation } from '../../hooks/useFormValidation';
31
- import ValidationError from '../../atoms/ValidationError';
32
-
33
- // Utility function to handle function execution and return value
34
- const executeFunctionAndGetReturnValue = (
35
- functionId: string,
36
- returnValueName: string,
37
- args: FunctionArgument[],
38
- availableFunctions: FunctionSignature[] | undefined,
39
- ): FunctionReturnValue => {
40
- if (!functionId || !returnValueName || !args || !args.every(arg => arg.value !== '')) {
41
- return {};
42
- }
43
-
44
- const selectedFunction = availableFunctions?.find(f => f.id === functionId);
45
- if (!selectedFunction) {
46
- return {};
47
- }
48
-
49
- try {
50
- const argNames = selectedFunction.args.map(arg => arg.name);
51
- const argValues = args.map(arg => {
52
- if (typeof arg.value === 'string') {
53
- try {
54
- return JSON.parse(arg.value);
55
- } catch {
56
- return arg.value;
57
- }
58
- }
59
- // If it's an Expression object, convert it to a string representation
60
- return String(arg.value);
61
- });
62
- // eslint-disable-next-line no-new-func
63
- const fn = new Function(...argNames, selectedFunction.code);
64
- const result = fn(...argValues);
65
- return { [returnValueName]: result };
66
- } catch (err) {
67
- return { [returnValueName]: 'Error: ' + (err instanceof Error ? err.message : String(err)) };
68
- }
69
- };
70
-
71
- const CustomFunctionSelect = memo(({
72
- options,
73
- value,
74
- onChange,
75
- }: CustomFunctionSelectProps) => {
76
- const [open, setOpen] = useState(false);
77
- const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
78
- const [deleteFuncId, setDeleteFuncId] = useState<string | null>(null);
79
- const deleteFunction = useDeleteFunction();
80
- const { t } = useTranslation();
81
-
82
- const handleClickOpen = useCallback(() => {
83
- setOpen(true);
84
- }, []);
85
-
86
- const handleClose = useCallback(() => {
87
- setOpen(false);
88
- }, []);
89
-
90
- const handleSelectFunction = useCallback(
91
- (func: FunctionSignature) => {
92
- onChange(func.id);
93
- handleClose();
94
- },
95
- [onChange, handleClose],
96
- );
97
-
98
- const handleDelete = useCallback((id: string) => {
99
- setDeleteFuncId(id);
100
- setDeleteDialogOpen(true);
101
- }, []);
102
-
103
- const handleConfirmDelete = useCallback(() => {
104
- if (deleteFuncId) {
105
- deleteFunction(deleteFuncId);
106
- }
107
- setDeleteDialogOpen(false);
108
- setDeleteFuncId(null);
109
- }, [deleteFuncId, deleteFunction]);
110
-
111
- const handleCloseDeleteDialog = useCallback(() => {
112
- setDeleteDialogOpen(false);
113
- }, []);
114
-
115
- const handleDeleteClick = useCallback((e: React.MouseEvent, id: string) => {
116
- e.stopPropagation();
117
- handleDelete(id);
118
- }, [handleDelete]);
119
-
120
- return (
121
- <>
122
- <Button
123
- onClick={handleClickOpen}
124
- variant="outlined"
125
- endIcon={<RiArrowDownSLine />}
126
- fullWidth
127
- sx={{
128
- backgroundColor: '#232323',
129
- color: '#FFFFFF',
130
- border: '1px solid #8E8E93',
131
- '&:hover': { border: '1px solid #000000' },
132
- }}
133
- >
134
- {value !== null
135
- ? options.find((func) => func.id === value)?.name ||
136
- t('callForm.selectFunction')
137
- : t('callForm.selectFunction')}
138
- </Button>
139
- <Dialog
140
- open={open}
141
- onClose={handleClose}
142
- fullWidth
143
- maxWidth="sm"
144
- sx={{
145
- '& .MuiDialog-paper': {
146
- backgroundColor: '#232323',
147
- color: '#FFFFFF',
148
- },
149
- }}
150
- >
151
- <DialogTitle>
152
- {t('callForm.selectFunction')}
153
- </DialogTitle>
154
- <DialogContent>
155
- <MenuList>
156
- {options.map((func) => (
157
- <MenuItem
158
- key={func.id}
159
- onClick={() => handleSelectFunction(func)}
160
- >
161
- <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
162
- <ListItemText sx={{ flex: 1 }}>
163
- {`${func.name}(${func.args.map(a => a.name).join(', ')})`}
164
- </ListItemText>
165
- <IconButton
166
- size="small"
167
- onClick={(e) => handleDeleteClick(e, func.id)}
168
- sx={{ color: '#fff' }}
169
- >
170
- <DeleteIcon fontSize="small" />
171
- </IconButton>
172
- </Box>
173
- </MenuItem>
174
- ))}
175
- </MenuList>
176
- </DialogContent>
177
- <DialogActions>
178
- <Button onClick={handleClose} color="secondary" sx={{ color: '#fff' }}>{t('button.cancel')}</Button>
179
- </DialogActions>
180
- </Dialog>
181
- <Dialog
182
- open={deleteDialogOpen}
183
- onClose={handleCloseDeleteDialog}
184
- maxWidth="xs"
185
- PaperProps={{ style: { background: '#232323', color: '#fff' } }}
186
- >
187
- <DialogTitle>Delete Function</DialogTitle>
188
- <DialogContent>
189
- <DialogContentText sx={{ color: '#fff' }}>
190
- Are you sure you want to delete this function?
191
- </DialogContentText>
192
- </DialogContent>
193
- <DialogActions>
194
- <Button onClick={handleCloseDeleteDialog} color="secondary" sx={{ color: '#fff' }}>Cancel</Button>
195
- <Button onClick={handleConfirmDelete} color="error" variant="contained">Delete</Button>
196
- </DialogActions>
197
- </Dialog>
198
- </>
199
- );
200
- });
201
-
202
- export const CallForm = memo(({
203
- formData,
204
- handleMultipleInputChanges,
205
- }: CallFormProps) => {
206
- const availableFunctions = useDiagram((state) => state.availableFunctions);
207
- const { t } = useTranslation();
208
-
209
- const {
210
- validationState,
211
- setFieldError,
212
- setFieldTouched,
213
- shouldShowFieldError,
214
- getFieldError,
215
- clearFieldError,
216
- } = useFormValidation();
217
-
218
- const handleFunctionChange = useCallback(
219
- (funcId: string) => {
220
- const selectedFunction = availableFunctions?.find((f) => f.id === funcId);
221
- const args = selectedFunction?.args || [];
222
- handleMultipleInputChanges({
223
- returnValueName: '',
224
- functionId: funcId,
225
- code: selectedFunction?.code || '',
226
- functionName: selectedFunction?.name || '',
227
- args: args.map((arg) => ({
228
- name: typeof arg === 'string' ? arg : arg.name,
229
- value: '' as string | Expression,
230
- dataType: 'String'
231
- })),
232
- });
233
- },
234
- [availableFunctions, handleMultipleInputChanges],
235
- );
236
-
237
- const handleArgChange = useCallback(
238
- (index: number, value: string | Expression) => {
239
- const updatedArgs = [...(formData.args || [])];
240
- updatedArgs[index] = { ...updatedArgs[index], value };
241
- handleMultipleInputChanges({
242
- args: updatedArgs,
243
- });
244
- },
245
- [formData.args, handleMultipleInputChanges],
246
- );
247
-
248
- // Find the selected function to get argument types
249
- const selectedFunction = availableFunctions?.find((f) => f.id === formData.functionId);
250
-
251
- return (
252
- <Stack spacing={1}>
253
- <Stack spacing={3} direction={'column'} sx={{ width: '100%' }}>
254
- {availableFunctions && (
255
- <CustomFunctionSelect
256
- options={availableFunctions}
257
- value={formData.functionId || null}
258
- onChange={(funcId: string) => {
259
- handleFunctionChange(funcId);
260
- }}
261
- />
262
- )}
263
- {getFieldError('functionId') && shouldShowFieldError('functionId') && (
264
- <ValidationError
265
- error={getFieldError('functionId')}
266
- show={true}
267
- />
268
- )}
269
- </Stack>
270
- <Divider variant={'inset'} />
271
- {formData.functionId && (
272
- <div>
273
- {formData.args?.map((arg, index) => (
274
- // Find the type for this argument from the selected function
275
- (() => {
276
- const argType = selectedFunction?.args?.find(a => a.name === arg.name)?.type || '';
277
- return (
278
- <Stack
279
- spacing={1}
280
- direction={'row'}
281
- sx={{ alignItems: 'center', mb: 2 }}
282
- key={arg.name}
283
- >
284
- <Typography
285
- variant="body1"
286
- sx={{ width: '80px', fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}
287
- >
288
- {arg.name}:
289
- </Typography>
290
- <ExpressionInput
291
- label=""
292
- value={arg.value as string}
293
- onChange={(newValue) => {
294
- handleArgChange(index, newValue);
295
- }}
296
- showDataTypeDropdown={true}
297
- dataType={arg.dataType || 'String'}
298
- onDataTypeChange={(dataType) => {
299
- const updatedArgs = [...(formData.args || [])];
300
- updatedArgs[index] = { ...updatedArgs[index], dataType };
301
- handleMultipleInputChanges({
302
- args: updatedArgs,
303
- });
304
- }}
305
- onValidationChange={(isValid, errors) => {
306
- // Handle validation from ExpressionInput
307
- if (!isValid && errors.length > 0) {
308
- setFieldError(`args[${index}].value`, errors[0]);
309
- } else {
310
- clearFieldError(`args[${index}].value`);
311
- }
312
- }}
313
- />
314
- {getFieldError(`args[${index}].value`) && (
315
- <ValidationError
316
- error={getFieldError(`args[${index}].value`)}
317
- show={true}
318
- />
319
- )}
320
- </Stack>
321
- );
322
- })()
323
- ))}
324
- </div>
325
- )}
326
- <Divider variant={'inset'} />
327
- {formData.functionId && (
328
- <Box>
329
- <label style={{ fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}>
330
- {t('callForm.returnValueName')}
331
- </label>
332
- <ExpressionInput
333
- label=""
334
- value={formData.returnValueName as string}
335
- onChange={(newValue) => {
336
- handleMultipleInputChanges({
337
- returnValueName: newValue,
338
- });
339
- }}
340
- showDataTypeDropdown={true}
341
- dataType={formData.returnValueDataType || 'String'}
342
- onDataTypeChange={(dataType) => {
343
- handleMultipleInputChanges({
344
- returnValueDataType: dataType,
345
- });
346
- }}
347
- onValidationChange={(isValid, errors) => {
348
- // Handle validation from ExpressionInput
349
- if (!isValid && errors.length > 0) {
350
- setFieldError('returnValueName', errors[0]);
351
- } else {
352
- clearFieldError('returnValueName');
353
- }
354
- }}
355
- />
356
- {getFieldError('returnValueName') && (
357
- <ValidationError
358
- error={getFieldError('returnValueName')}
359
- show={true}
360
- />
361
- )}
362
- </Box>
363
- )}
364
- </Stack>
365
- );
366
- });
367
-
368
- // Add display names for memoized components
369
- CustomFunctionSelect.displayName = 'CustomFunctionSelect';
370
- CallForm.displayName = 'CallForm';
1
+ import { memo, useCallback, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+
4
+ import ExpressionInput from '../../atoms/ExpressionInput';
5
+ import { useDiagram } from '@flowuent-labs/diagrams';
6
+ import { FunctionSignature } from '../../types/FunctionSignature';
7
+ import { RiArrowDownSLine } from 'react-icons/ri';
8
+ import MenuList from '@mui/material/MenuList';
9
+ import MenuItem from '@mui/material/MenuItem';
10
+ import ListItemText from '@mui/material/ListItemText';
11
+ import Typography from '@mui/material/Typography';
12
+ import Button from '@mui/material/Button';
13
+ import Dialog from '@mui/material/Dialog';
14
+ import DialogActions from '@mui/material/DialogActions';
15
+ import DialogContent from '@mui/material/DialogContent';
16
+ import DialogTitle from '@mui/material/DialogTitle';
17
+ import Stack from '@mui/material/Stack';
18
+ import { Box, Divider, IconButton } from '@mui/material';
19
+ import { useDeleteFunction } from '../../contexts/DiagramProvider';
20
+ import DeleteIcon from '@mui/icons-material/Delete';
21
+ import DialogContentText from '@mui/material/DialogContentText';
22
+ import {
23
+ CallFormData,
24
+ CallFormProps,
25
+ CustomFunctionSelectProps,
26
+ FunctionArgument,
27
+ FunctionReturnValue,
28
+ } from '../../types/function-execution';
29
+ import { Expression } from '../../types/ndoe-form-types';
30
+ import { useFormValidation } from '../../hooks/useFormValidation';
31
+ import ValidationError from '../../atoms/ValidationError';
32
+
33
+ // Utility function to handle function execution and return value
34
+ const executeFunctionAndGetReturnValue = (
35
+ functionId: string,
36
+ returnValueName: string,
37
+ args: FunctionArgument[],
38
+ availableFunctions: FunctionSignature[] | undefined,
39
+ ): FunctionReturnValue => {
40
+ if (!functionId || !returnValueName || !args || !args.every(arg => arg.value !== '')) {
41
+ return {};
42
+ }
43
+
44
+ const selectedFunction = availableFunctions?.find(f => f.id === functionId);
45
+ if (!selectedFunction) {
46
+ return {};
47
+ }
48
+
49
+ try {
50
+ const argNames = selectedFunction.args.map(arg => arg.name);
51
+ const argValues = args.map(arg => {
52
+ if (typeof arg.value === 'string') {
53
+ try {
54
+ return JSON.parse(arg.value);
55
+ } catch {
56
+ return arg.value;
57
+ }
58
+ }
59
+ // If it's an Expression object, convert it to a string representation
60
+ return String(arg.value);
61
+ });
62
+ // eslint-disable-next-line no-new-func
63
+ const fn = new Function(...argNames, selectedFunction.code);
64
+ const result = fn(...argValues);
65
+ return { [returnValueName]: result };
66
+ } catch (err) {
67
+ return { [returnValueName]: 'Error: ' + (err instanceof Error ? err.message : String(err)) };
68
+ }
69
+ };
70
+
71
+ const CustomFunctionSelect = memo(({
72
+ options,
73
+ value,
74
+ onChange,
75
+ }: CustomFunctionSelectProps) => {
76
+ const [open, setOpen] = useState(false);
77
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
78
+ const [deleteFuncId, setDeleteFuncId] = useState<string | null>(null);
79
+ const deleteFunction = useDeleteFunction();
80
+ const { t } = useTranslation();
81
+
82
+ const handleClickOpen = useCallback(() => {
83
+ setOpen(true);
84
+ }, []);
85
+
86
+ const handleClose = useCallback(() => {
87
+ setOpen(false);
88
+ }, []);
89
+
90
+ const handleSelectFunction = useCallback(
91
+ (func: FunctionSignature) => {
92
+ onChange(func.id);
93
+ handleClose();
94
+ },
95
+ [onChange, handleClose],
96
+ );
97
+
98
+ const handleDelete = useCallback((id: string) => {
99
+ setDeleteFuncId(id);
100
+ setDeleteDialogOpen(true);
101
+ }, []);
102
+
103
+ const handleConfirmDelete = useCallback(() => {
104
+ if (deleteFuncId) {
105
+ deleteFunction(deleteFuncId);
106
+ }
107
+ setDeleteDialogOpen(false);
108
+ setDeleteFuncId(null);
109
+ }, [deleteFuncId, deleteFunction]);
110
+
111
+ const handleCloseDeleteDialog = useCallback(() => {
112
+ setDeleteDialogOpen(false);
113
+ }, []);
114
+
115
+ const handleDeleteClick = useCallback((e: React.MouseEvent, id: string) => {
116
+ e.stopPropagation();
117
+ handleDelete(id);
118
+ }, [handleDelete]);
119
+
120
+ return (
121
+ <>
122
+ <Button
123
+ onClick={handleClickOpen}
124
+ variant="outlined"
125
+ endIcon={<RiArrowDownSLine />}
126
+ fullWidth
127
+ sx={{
128
+ backgroundColor: '#232323',
129
+ color: '#FFFFFF',
130
+ border: '1px solid #8E8E93',
131
+ '&:hover': { border: '1px solid #000000' },
132
+ }}
133
+ >
134
+ {value !== null
135
+ ? options.find((func) => func.id === value)?.name ||
136
+ t('callForm.selectFunction')
137
+ : t('callForm.selectFunction')}
138
+ </Button>
139
+ <Dialog
140
+ open={open}
141
+ onClose={handleClose}
142
+ fullWidth
143
+ maxWidth="sm"
144
+ sx={{
145
+ '& .MuiDialog-paper': {
146
+ backgroundColor: '#232323',
147
+ color: '#FFFFFF',
148
+ },
149
+ }}
150
+ >
151
+ <DialogTitle>
152
+ {t('callForm.selectFunction')}
153
+ </DialogTitle>
154
+ <DialogContent>
155
+ <MenuList>
156
+ {options.map((func) => (
157
+ <MenuItem
158
+ key={func.id}
159
+ onClick={() => handleSelectFunction(func)}
160
+ >
161
+ <Box sx={{ display: 'flex', alignItems: 'center', width: '100%' }}>
162
+ <ListItemText sx={{ flex: 1 }}>
163
+ {`${func.name}(${func.args.map(a => a.name).join(', ')})`}
164
+ </ListItemText>
165
+ <IconButton
166
+ size="small"
167
+ onClick={(e) => handleDeleteClick(e, func.id)}
168
+ sx={{ color: '#fff' }}
169
+ >
170
+ <DeleteIcon fontSize="small" />
171
+ </IconButton>
172
+ </Box>
173
+ </MenuItem>
174
+ ))}
175
+ </MenuList>
176
+ </DialogContent>
177
+ <DialogActions>
178
+ <Button onClick={handleClose} color="secondary" sx={{ color: '#fff' }}>{t('button.cancel')}</Button>
179
+ </DialogActions>
180
+ </Dialog>
181
+ <Dialog
182
+ open={deleteDialogOpen}
183
+ onClose={handleCloseDeleteDialog}
184
+ maxWidth="xs"
185
+ PaperProps={{ style: { background: '#232323', color: '#fff' } }}
186
+ >
187
+ <DialogTitle>Delete Function</DialogTitle>
188
+ <DialogContent>
189
+ <DialogContentText sx={{ color: '#fff' }}>
190
+ Are you sure you want to delete this function?
191
+ </DialogContentText>
192
+ </DialogContent>
193
+ <DialogActions>
194
+ <Button onClick={handleCloseDeleteDialog} color="secondary" sx={{ color: '#fff' }}>Cancel</Button>
195
+ <Button onClick={handleConfirmDelete} color="error" variant="contained">Delete</Button>
196
+ </DialogActions>
197
+ </Dialog>
198
+ </>
199
+ );
200
+ });
201
+
202
+ export const CallForm = memo(({
203
+ formData,
204
+ handleMultipleInputChanges,
205
+ }: CallFormProps) => {
206
+ const availableFunctions = useDiagram((state) => state.availableFunctions);
207
+ const { t } = useTranslation();
208
+
209
+ const {
210
+ validationState,
211
+ setFieldError,
212
+ setFieldTouched,
213
+ shouldShowFieldError,
214
+ getFieldError,
215
+ clearFieldError,
216
+ } = useFormValidation();
217
+
218
+ const handleFunctionChange = useCallback(
219
+ (funcId: string) => {
220
+ const selectedFunction = availableFunctions?.find((f) => f.id === funcId);
221
+ const args = selectedFunction?.args || [];
222
+ handleMultipleInputChanges({
223
+ returnValueName: '',
224
+ functionId: funcId,
225
+ code: selectedFunction?.code || '',
226
+ functionName: selectedFunction?.name || '',
227
+ args: args.map((arg) => ({
228
+ name: typeof arg === 'string' ? arg : arg.name,
229
+ value: '' as string | Expression,
230
+ dataType: 'String'
231
+ })),
232
+ });
233
+ },
234
+ [availableFunctions, handleMultipleInputChanges],
235
+ );
236
+
237
+ const handleArgChange = useCallback(
238
+ (index: number, value: string | Expression) => {
239
+ const updatedArgs = [...(formData.args || [])];
240
+ updatedArgs[index] = { ...updatedArgs[index], value };
241
+ handleMultipleInputChanges({
242
+ args: updatedArgs,
243
+ });
244
+ },
245
+ [formData.args, handleMultipleInputChanges],
246
+ );
247
+
248
+ // Find the selected function to get argument types
249
+ const selectedFunction = availableFunctions?.find((f) => f.id === formData.functionId);
250
+
251
+ return (
252
+ <Stack spacing={1}>
253
+ <Stack spacing={3} direction={'column'} sx={{ width: '100%' }}>
254
+ {availableFunctions && (
255
+ <CustomFunctionSelect
256
+ options={availableFunctions}
257
+ value={formData.functionId || null}
258
+ onChange={(funcId: string) => {
259
+ handleFunctionChange(funcId);
260
+ }}
261
+ />
262
+ )}
263
+ {getFieldError('functionId') && shouldShowFieldError('functionId') && (
264
+ <ValidationError
265
+ error={getFieldError('functionId')}
266
+ show={true}
267
+ />
268
+ )}
269
+ </Stack>
270
+ <Divider variant={'inset'} />
271
+ {formData.functionId && (
272
+ <div>
273
+ {formData.args?.map((arg, index) => (
274
+ // Find the type for this argument from the selected function
275
+ (() => {
276
+ const argType = selectedFunction?.args?.find(a => a.name === arg.name)?.type || '';
277
+ return (
278
+ <Stack
279
+ spacing={1}
280
+ direction={'row'}
281
+ sx={{ alignItems: 'center', mb: 2 }}
282
+ key={arg.name}
283
+ >
284
+ <Typography
285
+ variant="body1"
286
+ sx={{ width: '80px', fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}
287
+ >
288
+ {arg.name}:
289
+ </Typography>
290
+ <ExpressionInput
291
+ label=""
292
+ value={arg.value as string}
293
+ onChange={(newValue) => {
294
+ handleArgChange(index, newValue);
295
+ }}
296
+ showDataTypeDropdown={true}
297
+ dataType={arg.dataType || 'String'}
298
+ onDataTypeChange={(dataType) => {
299
+ const updatedArgs = [...(formData.args || [])];
300
+ updatedArgs[index] = { ...updatedArgs[index], dataType };
301
+ handleMultipleInputChanges({
302
+ args: updatedArgs,
303
+ });
304
+ }}
305
+ onValidationChange={(isValid, errors) => {
306
+ // Handle validation from ExpressionInput
307
+ if (!isValid && errors.length > 0) {
308
+ setFieldError(`args[${index}].value`, errors[0]);
309
+ } else {
310
+ clearFieldError(`args[${index}].value`);
311
+ }
312
+ }}
313
+ />
314
+ {getFieldError(`args[${index}].value`) && (
315
+ <ValidationError
316
+ error={getFieldError(`args[${index}].value`)}
317
+ show={true}
318
+ />
319
+ )}
320
+ </Stack>
321
+ );
322
+ })()
323
+ ))}
324
+ </div>
325
+ )}
326
+ <Divider variant={'inset'} />
327
+ {formData.functionId && (
328
+ <Box>
329
+ <label style={{ fontSize: '12px', color: '#FFFFFF', fontWeight: 'bold' }}>
330
+ {t('callForm.returnValueName')}
331
+ </label>
332
+ <ExpressionInput
333
+ label=""
334
+ value={formData.returnValueName as string}
335
+ onChange={(newValue) => {
336
+ handleMultipleInputChanges({
337
+ returnValueName: newValue,
338
+ });
339
+ }}
340
+ showDataTypeDropdown={true}
341
+ dataType={formData.returnValueDataType || 'String'}
342
+ onDataTypeChange={(dataType) => {
343
+ handleMultipleInputChanges({
344
+ returnValueDataType: dataType,
345
+ });
346
+ }}
347
+ onValidationChange={(isValid, errors) => {
348
+ // Handle validation from ExpressionInput
349
+ if (!isValid && errors.length > 0) {
350
+ setFieldError('returnValueName', errors[0]);
351
+ } else {
352
+ clearFieldError('returnValueName');
353
+ }
354
+ }}
355
+ />
356
+ {getFieldError('returnValueName') && (
357
+ <ValidationError
358
+ error={getFieldError('returnValueName')}
359
+ show={true}
360
+ />
361
+ )}
362
+ </Box>
363
+ )}
364
+ </Stack>
365
+ );
366
+ });
367
+
368
+ // Add display names for memoized components
369
+ CustomFunctionSelect.displayName = 'CustomFunctionSelect';
370
+ CallForm.displayName = 'CallForm';