Skip to content
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

Merged
merged 11 commits into from
Nov 18, 2024
Merged

Add Record Create action in the frontend #8514

merged 11 commits into from
Nov 18, 2024

Conversation

Devessier
Copy link
Contributor

@Devessier Devessier commented Nov 15, 2024

In this PR:

  • Updated the front-end types for workflows to include CRUD actions and global naming changes
  • Allow users to create a Record Create action
  • Scaffold the edit for Record Create action; for now, I render a <VariableTagInput /> component for every editable field; it's likely we'll change it soon

Closes https://github.com/twentyhq/private-issues/issues/142

Demo:

CleanShot.2024-11-15.at.12.45.36.mp4

Copy link
Contributor

@greptile-apps greptile-apps bot left a 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

Comment on lines +90 to +93
case 'RECORD_CRUD.DELETE':
case 'RECORD_CRUD.UPDATE': {
return null;
}
Copy link
Contributor

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.

Comment on lines 94 to 95
}
}
Copy link
Contributor

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.

Comment on lines +24 to +27
type SendEmailFormData = {
objectName: string;
[field: string]: unknown;
};
Copy link
Contributor

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

Comment on lines 46 to 47
objectName: activeObjectMetadataItems[0].nameSingular,
...props.action.settings.input.objectRecord,
Copy link
Contributor

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

Comment on lines 152 to 154
// @ts-expect-error Temporary fix
value={formField.value}
onChange={(value) => {
Copy link
Contributor

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>;
Copy link
Contributor

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.

Comment on lines +39 to +41
if (step.type === 'RECORD_CRUD') {
nodeActionType = `RECORD_CRUD.${step.settings.input.type}`;
} else {
Copy link
Contributor

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,
Copy link
Contributor

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.

Comment on lines +6 to +8
export const isWorkflowRecordCreateAction = (
action: WorkflowAction,
): action is WorkflowRecordCreateAction => {
Copy link
Contributor

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

Comment on lines +20 to +21
stroke={props.color ?? 'currentColor'}
strokeWidth={stroke}
Copy link
Contributor

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>({
Copy link
Contributor

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 👍

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@martmull
Copy link
Contributor

martmull commented Nov 15, 2024

Hey, I have tested the functionality, it works well I have 2 remarks:

Copy link
Contributor

@martmull martmull left a 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

<WorkflowEditActionFormRecordCreate
action={stepDefinition.definition}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
Copy link
Contributor

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

Suggested change
{...props}
stepId={stepId}
workflowVersion={workflowVersion}
readonly={readonly}
onTriggerUpdate={onTriggerUpdate}
onActionUpdate={onActionUpdate}

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

type WorkflowEditTriggerDatabaseEventFormProps =
| {
trigger: WorkflowDatabaseEventTrigger;
readonly: true;
onTriggerUpdate?: undefined;
}
| {
trigger: WorkflowDatabaseEventTrigger;
readonly?: false;
onTriggerUpdate: (trigger: WorkflowDatabaseEventTrigger) => void;
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, the props object is a type union. If I provide the properties of this object individually, I will loose the type union and, for instance, props.readonly will be a boolean.

CleanShot 2024-11-18 at 15 01 41@2x

Copy link
Contributor Author

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 }}
/>

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👌

Copy link
Contributor

@thomtrp thomtrp left a 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;
Copy link
Contributor

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 = ({
Copy link
Contributor

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';
Copy link
Contributor

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!

Copy link
Contributor Author

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.

Copy link
Contributor

@martmull martmull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Weiko Weiko merged commit c17e18b into main Nov 18, 2024
19 checks passed
@Weiko Weiko deleted the add-create-record-action branch November 18, 2024 17:23
Copy link

Thanks @Devessier for your contribution!
This marks your 27th PR on the repo. You're top 2% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants