-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Record Create action in the frontend #8514
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR adds Record Create action functionality to the workflow system, allowing users to create new records through workflow automation with a configurable form interface.
- Added
WorkflowEditActionFormRecordCreate
component with dynamic field rendering based on object metadata and variable tag inputs - Implemented CRUD action type handling in workflow diagram generation with
RECORD_CRUD.CREATE
support and appropriate icons - Added type guard
isWorkflowRecordCreateAction
for better type safety when handling record creation actions - Renamed workflow step types to action types throughout the codebase for consistency
- Needs attention: Form data type naming (SendEmailFormData), ts-expect-error fix, and activeObjectMetadataItems undefined checks
12 file(s) reviewed, 11 comment(s)
Edit PR Review Bot Settings | Greptile
case 'RECORD_CRUD.DELETE': | ||
case 'RECORD_CRUD.UPDATE': { | ||
return null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Missing icons for DELETE and UPDATE actions could make it harder for users to distinguish between action types. Consider adding distinct icons for these operations.
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Missing break statement after switch case could cause fallthrough to assertUnreachable. Add break or return statement.
type SendEmailFormData = { | ||
objectName: string; | ||
[field: string]: unknown; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Type name 'SendEmailFormData' is incorrect for this context - should be 'RecordCreateFormData' or similar
objectName: activeObjectMetadataItems[0].nameSingular, | ||
...props.action.settings.input.objectRecord, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Potential runtime error if activeObjectMetadataItems is empty - need to handle this case
// @ts-expect-error Temporary fix | ||
value={formField.value} | ||
onChange={(value) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: Fix type mismatch between VariableTagInput value prop and formField.value instead of using @ts-expect-error
@@ -30,29 +30,83 @@ export type WorkflowSendEmailStepSettings = BaseWorkflowStepSettings & { | |||
}; | |||
}; | |||
|
|||
type BaseWorkflowStep = { | |||
type ObjectRecord = Record<string, any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Using Record<string, any> for ObjectRecord loses type safety. Consider using a generic type parameter or more specific record type.
if (step.type === 'RECORD_CRUD') { | ||
nodeActionType = `RECORD_CRUD.${step.settings.input.type}`; | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No type checking on step.settings.input.type - could cause runtime errors if input type is undefined
settings: { | ||
input: { | ||
type: 'CREATE', | ||
objectName: activeObjectMetadataItems[0].nameSingular, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Potential crash if activeObjectMetadataItems is empty array. Add validation or provide fallback.
export const isWorkflowRecordCreateAction = ( | ||
action: WorkflowAction, | ||
): action is WorkflowRecordCreateAction => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Consider adding JSDoc documentation to explain the function's purpose and return type
stroke={props.color ?? 'currentColor'} | ||
strokeWidth={stroke} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: stroke attribute should use props.color ?? theme.something as fallback, currently no default if color prop is missing
value: item.nameSingular, | ||
})); | ||
|
||
const form = useForm<SendEmailFormData>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react hook form has been banned, let's use a normal state 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you have an example with WorkflowEditActionFormServerlessFunction
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand, the WorkflowEditActionFormServerlessFunction
component directly sends debounced updates to the backend.
The form I implemented is a bit more complex as I want to synchronously update the available fields based on the selected object type while still debouncing the server update. I wouldn't be able to fully rely on props.action
for that. Instead, I will have to create a useState
specifically for the object type.
I will use a useState
but I think we'll quickly want to create our own tiny form library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spotted a bug in the WorkflowEditActionFormServerlessFunction
component. I created an issue here: #8523.
We'll have to use a useState
in this component as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do the change on Monday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
...es/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx
Outdated
Show resolved
Hide resolved
Hey, I have tested the functionality, it works well I have 2 remarks:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, nice job, I have left some comments
packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx
Outdated
Show resolved
Hide resolved
<WorkflowEditActionFormRecordCreate | ||
action={stepDefinition.definition} | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
think we should avoid props spreading. It lower the readability of the code
just pass all
{...props} | |
stepId={stepId} | |
workflowVersion={workflowVersion} | |
readonly={readonly} | |
onTriggerUpdate={onTriggerUpdate} | |
onActionUpdate={onActionUpdate} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do the change on Monday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work easily. I wrote a type union for WorkflowEditTriggerDatabaseEventForm
props to have really strong types and prevent edge cases. This won't play well if I provide properties the usual way instead of spreading props.
Lines 48 to 59 in 65f47b6
type WorkflowEditTriggerDatabaseEventFormProps = | |
| { | |
trigger: WorkflowDatabaseEventTrigger; | |
readonly: true; | |
onTriggerUpdate?: undefined; | |
} | |
| { | |
trigger: WorkflowDatabaseEventTrigger; | |
readonly?: false; | |
onTriggerUpdate: (trigger: WorkflowDatabaseEventTrigger) => void; | |
}; | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component serves as a dispatcher. I think it's better to have strong types than to provide props as usually recommended.
Another way to keep the benefits without disabling the ESLint rule would be to have a single prop containing readonly
, onTriggerUpdate
, onActionUpdate
:
<WorkflowStepDetail
xxx={{ readonly: true }}
/>
<WorkflowStepDetail
xxx={{ onActionUpdate: yyy, onTriggerUpdate: zzz }}
/>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok for the
<WorkflowStepDetail
xxx={{ readonly: true }}
/>
<WorkflowStepDetail
xxx={{ onActionUpdate: yyy, onTriggerUpdate: zzz }}
/>
solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 👌
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just move the FormFieldInput to the other folder please ✅
type FormFieldInputProps = { | ||
recordFieldInputdId: string; | ||
label: string; | ||
value: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will probably come from a context but this is ok for now
isReadOnly?: boolean; | ||
}; | ||
|
||
export const FormFieldInput = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That component is not workflow specific. Could you move it next to FieldInput.tsx
🙏
@@ -0,0 +1,26 @@ | |||
import VariableTagInput from '@/workflow/search-variables/components/VariableTagInput'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need that component and the variables handling to be outside of workflows. But not for this PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will move the file next to the FieldInput file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thanks @Devessier for your contribution! |
In this PR:
<VariableTagInput />
component for every editable field; it's likely we'll change it soonCloses https://github.com/twentyhq/private-issues/issues/142
Demo:
CleanShot.2024-11-15.at.12.45.36.mp4