Allow workflow field update when custom (#11619)

- Allow workflow field update when custom
- Also handle the case where object name is not defined in actions
This commit is contained in:
Thomas Trompette
2025-04-17 15:01:18 +02:00
committed by GitHub
parent 1401f80081
commit d2881bb4a2
9 changed files with 155 additions and 199 deletions

View File

@ -1,5 +1,5 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { Select } from '@/ui/input/components/Select';
@ -13,11 +13,11 @@ import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
import { FieldMetadataType } from '~/generated/graphql';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
type WorkflowEditActionCreateRecordProps = {
action: WorkflowCreateRecordAction;
@ -78,17 +78,19 @@ export const WorkflowEditActionCreateRecord = ({
const objectNameSingular = formData.objectName;
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const { objectMetadataItems } = useObjectMetadataItems();
const objectMetadataItem = objectMetadataItems.find(
(item) => item.nameSingular === objectNameSingular,
);
const { view: indexView } = useViewOrDefaultViewFromPrefetchedViews({
objectMetadataItemId: objectMetadataItem.id ?? '',
objectMetadataItemId: objectMetadataItem?.id ?? '',
});
const viewFields = indexView?.viewFields ?? [];
const inlineFieldMetadataItems = objectMetadataItem.fields
const inlineFieldMetadataItems = objectMetadataItem?.fields
.filter(
(fieldMetadataItem) =>
fieldMetadataItem.type !== FieldMetadataType.RELATION &&
@ -106,15 +108,16 @@ export const WorkflowEditActionCreateRecord = ({
})
.sort(sortByViewFieldPosition);
const inlineFieldDefinitions = inlineFieldMetadataItems.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
);
const inlineFieldDefinitions = isDefined(objectMetadataItem)
? inlineFieldMetadataItems?.map((fieldMetadataItem) =>
formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
)
: [];
const handleFieldChange = (
fieldName: keyof CreateRecordFormData,
@ -205,7 +208,7 @@ export const WorkflowEditActionCreateRecord = ({
<HorizontalSeparator noMargin />
{inlineFieldDefinitions.map((field) => {
{inlineFieldDefinitions?.map((field) => {
const currentValue = formData[field.metadata.fieldName] as JsonValue;
return (

View File

@ -11,10 +11,10 @@ import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-ac
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { isDefined } from 'twenty-shared/utils';
import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
type WorkflowEditActionDeleteRecordProps = {
action: WorkflowDeleteRecordAction;
@ -68,14 +68,9 @@ export const WorkflowEditActionDeleteRecord = ({
saveAction(newFormData);
};
const selectedObjectMetadataItemNameSingular = formData.objectName;
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
);
if (!isDefined(selectedObjectMetadataItem)) {
throw new Error('Should have found the metadata item');
}
const objectNameSingular = activeObjectMetadataItems.find(
(item) => item.nameSingular === formData.objectName,
)?.nameSingular;
const saveAction = useDebouncedCallback(
async (formData: DeleteRecordFormData) => {
@ -156,17 +151,19 @@ export const WorkflowEditActionDeleteRecord = ({
<HorizontalSeparator noMargin />
<FormSingleRecordPicker
label="Record"
onChange={(objectRecordId) =>
handleFieldChange('objectRecordId', objectRecordId)
}
objectNameSingular={formData.objectName}
defaultValue={formData.objectRecordId}
testId="workflow-edit-action-record-delete-object-record-id"
disabled={isFormDisabled}
VariablePicker={WorkflowVariablePicker}
/>
{isDefined(objectNameSingular) && (
<FormSingleRecordPicker
label="Record"
onChange={(objectRecordId) =>
handleFieldChange('objectRecordId', objectRecordId)
}
objectNameSingular={objectNameSingular}
defaultValue={formData.objectRecordId}
testId="workflow-edit-action-record-delete-object-record-id"
disabled={isFormDisabled}
VariablePicker={WorkflowVariablePicker}
/>
)}
</WorkflowStepBody>
</>
);

View File

@ -10,9 +10,9 @@ import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-a
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { isDefined } from 'twenty-shared/utils';
import { useDebouncedCallback } from 'use-debounce';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
import { useDebouncedCallback } from 'use-debounce';
type WorkflowEditActionFindRecordsProps = {
action: WorkflowFindRecordsAction;
@ -52,14 +52,10 @@ export const WorkflowEditActionFindRecords = ({
});
const isFormDisabled = actionOptions.readonly;
const selectedObjectMetadataItemNameSingular = formData.objectName;
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
);
if (!isDefined(selectedObjectMetadataItem)) {
throw new Error('Should have found the metadata item');
}
const selectedObjectMetadataItemNameSingular =
activeObjectMetadataItems.find(
(item) => item.nameSingular === formData.objectName,
)?.nameSingular ?? '';
const saveAction = useDebouncedCallback(
async (formData: FindRecordsFormData) => {
@ -119,7 +115,7 @@ export const WorkflowEditActionFindRecords = ({
label="Object"
fullWidth
disabled={isFormDisabled}
value={formData.objectName}
value={selectedObjectMetadataItemNameSingular}
emptyOption={{ label: 'Select an option', value: '' }}
options={availableMetadata}
onChange={(objectName) => {

View File

@ -14,11 +14,11 @@ import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-ac
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { isDefined } from 'twenty-shared/utils';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
import { SelectOption } from 'twenty-ui/input';
type WorkflowEditActionUpdateRecordProps = {
action: WorkflowUpdateRecordAction;
@ -94,16 +94,13 @@ export const WorkflowEditActionUpdateRecord = ({
saveAction(newFormData);
};
const selectedObjectMetadataItemNameSingular = formData.objectName;
const selectedObjectMetadataItem = activeObjectMetadataItems.find(
(item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
(item) => item.nameSingular === formData.objectName,
);
if (!isDefined(selectedObjectMetadataItem)) {
throw new Error('Should have found the metadata item');
}
const inlineFieldMetadataItems = selectedObjectMetadataItem.fields
const objectNameSingular = selectedObjectMetadataItem?.nameSingular;
const inlineFieldMetadataItems = selectedObjectMetadataItem?.fields
.filter(
(fieldMetadataItem) =>
!fieldMetadataItem.isSystem &&
@ -114,15 +111,16 @@ export const WorkflowEditActionUpdateRecord = ({
fieldMetadataItemA.name.localeCompare(fieldMetadataItemB.name),
);
const inlineFieldDefinitions = inlineFieldMetadataItems.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem: selectedObjectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
);
const inlineFieldDefinitions = isDefined(selectedObjectMetadataItem)
? inlineFieldMetadataItems?.map((fieldMetadataItem) =>
formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItem,
objectMetadataItem: selectedObjectMetadataItem,
showLabel: true,
labelWidth: 90,
}),
)
: [];
const saveAction = useDebouncedCallback(
async (formData: UpdateRecordFormData) => {
@ -209,39 +207,43 @@ export const WorkflowEditActionUpdateRecord = ({
<HorizontalSeparator noMargin />
<FormSingleRecordPicker
testId="workflow-edit-action-record-update-object-record-id"
label="Record"
onChange={(objectRecordId) =>
handleFieldChange('objectRecordId', objectRecordId)
}
objectNameSingular={formData.objectName}
defaultValue={formData.objectRecordId}
disabled={isFormDisabled}
VariablePicker={WorkflowVariablePicker}
/>
{isDefined(objectNameSingular) && (
<FormSingleRecordPicker
testId="workflow-edit-action-record-update-object-record-id"
label="Record"
onChange={(objectRecordId) =>
handleFieldChange('objectRecordId', objectRecordId)
}
objectNameSingular={objectNameSingular}
defaultValue={formData.objectRecordId}
disabled={isFormDisabled}
VariablePicker={WorkflowVariablePicker}
/>
)}
<FormMultiSelectFieldInput
testId="workflow-edit-action-record-update-fields-to-update"
label="Fields to update"
defaultValue={formData.fieldsToUpdate}
options={inlineFieldDefinitions.map((field) => ({
label: field.label,
value: field.metadata.fieldName,
icon: getIcon(field.iconName),
color: 'gray',
}))}
onChange={(fieldsToUpdate) =>
handleFieldChange('fieldsToUpdate', fieldsToUpdate)
}
placeholder="Select fields to update"
readonly={isFormDisabled}
/>
{isDefined(inlineFieldDefinitions) && (
<FormMultiSelectFieldInput
testId="workflow-edit-action-record-update-fields-to-update"
label="Fields to update"
defaultValue={formData.fieldsToUpdate}
options={inlineFieldDefinitions.map((field) => ({
label: field.label,
value: field.metadata.fieldName,
icon: getIcon(field.iconName),
color: 'gray',
}))}
onChange={(fieldsToUpdate) =>
handleFieldChange('fieldsToUpdate', fieldsToUpdate)
}
placeholder="Select fields to update"
readonly={isFormDisabled}
/>
)}
<HorizontalSeparator noMargin />
{formData.fieldsToUpdate.map((fieldName) => {
const fieldDefinition = inlineFieldDefinitions.find(
const fieldDefinition = inlineFieldDefinitions?.find(
(definition) => definition.metadata.fieldName === fieldName,
);