> [!WARNING]
> I refactored a bunch of components into utility functions to make it
possible to display the `WorkflowStepHeader` component for **triggers**
in the `CommandMenuWorkflowRunViewStep` component. Previously, we were
asserting that we were displaying the header in `Output` and `Input`
tabs only for **actions**. Handling triggers too required a bunch of
changes. We can think of making a bigger refactor of this part.
In this PR:
- Only display the Flow for Workflow Runs; removed the Code Editor tab
- Allows users to see the Output of trigger nodes
- Prevent impossible states by manually setting the selected tab when
selecting a node
## Demo
### Success, Running and Not Executed steps
https://github.com/user-attachments/assets/c6bebd0f-5da2-4ccc-aef2-d9890eafa59a
### Failed step
https://github.com/user-attachments/assets/e1f4e13a-2f5e-4792-a089-928e4d6b1ac0
Closes https://github.com/twentyhq/core-team-issues/issues/709
- Updates on the JSON field input
- Previously, we were editing json fields in a textarea
- Now, we display a JSON visualizer and the user can click on an Edit
button to edit the JSON in Monaco
- The JSON field input has a special behavior for workflow run output.
We want the error to be displayed first in the visualizer. Displaying
the error in red was optional but makes the output clearer in the
context of a workflow run record board.
- Made the code editor transparent in the json field input
- Ensure workflow run's output is not considered readonly in
`packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts`;
we want the json visualizer to always be displayed in this specific case
## Demo
### Failed Workflow Run
https://github.com/user-attachments/assets/7a438d11-53fb-4425-a982-25bbea4ee7a8
### Any JSON field in the record table
https://github.com/user-attachments/assets/b5591abe-3483-4473-bd87-062a45e653e3
Closes https://github.com/twentyhq/core-team-issues/issues/539
- Wrap the content of Workflow View, Workflow Edit, and Workflow Run
side panels with a container making them take all the available height
- Remove the `StyledContainer` of code steps as it's redundant with the
global container
- Add the WorkflowStepHeader to the input and output tabs
- Make the JSON visualizer take all the available height in input and
output tabs
- Reuse the WorkflowStepBody component in the input and output tabs as
it applies proper background color
## Demo

Fixes
https://discord.com/channels/1130383047699738754/1351906809417568376
---------
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
- Add validation for the `release` field of the releases collection
- The `release` must follow semver format
- The `slug` matches the `release` by default and must follow semver
format, too
- Order the releases properly
- `0.10.0` appears before `0.3.0`; I made the comparison against
`0000.0010.0000` and `0000.0003.0000`
- Set up images for release's contents
- Refactor the release notes of the version `0.43.0` to be editable in
Keystatic; it will serve as an example
## Demo
https://github.com/user-attachments/assets/d82851e9-11e7-4e27-b645-cf86a93d77bf
- Move the JsonTree component and the other components to twenty-ui
- Rely on a React Context to provide translations
## Future work
It would be good to migrate the `createRequiredContext` function to
`twenty-ui`. I didn't want to migrate it in this PR but would have liked
to use it.
The Keystatic's environment variables must be defined during the build.
@prastoin and I identified that the other environment variables aren't
defined during the build. We decided to add fake environment variables
directly in the Dockerfile to make the build pass. Later, the docker
image should be executed with the real environment variables that'll
make Keystatic work properly.
This PR introduces Keystatic to let us edit twenty.com's content with a
CMS. For now, we'll focus on creating release notes through Keystatic as
it uses quite simple Markdown. Other types of content will need some
refactoring to work with Keystatic.
https://github.com/user-attachments/assets/e9f85bbf-daff-4b41-bc97-d1baf63758b2
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
- Improve the type-safety of the objects mapping the id of a right
drawer or side panel view to a React component
- Improve the types of the `useTabList` hook to type the available tab
identifiers strictly
- Create a specialized `WorkflowRunDiagramCanvas` component to render a
`WorkflowRunDiagramCanvasEffect` component that opens
`RightDrawerPages.WorkflowRunStepView` when a step is selected
- Create a new side panel view specifically for workflow run step
details
- Create tab list in the new side panel; all the tabs are `Node`,
`Input` and `Output`
- Create a hook `useWorkflowSelectedNodeOrThrow` not to duplicate
throwing mechanisms
Closes https://github.com/twentyhq/core-team-issues/issues/432
## Demo
https://github.com/user-attachments/assets/8d5df7dc-0b99-49a2-9a54-d3eaee80a8e6
This PR introduces a new Recoil state to store the flow.
A few parts of the application need to know the definition of the
current flow. Previously, we stored the workflow version's ID and
fetched its definition with the `useWorkflowVersion` hook. However, we
must use another strategy to visualize workflow runs. Indeed, we now
store the definition of the workflow in the workflow run output when it
is executed. This is useful for draft versions, which can change between
the moment they were executed and the moment they are visualized.
[My last
PR](https://github.com/twentyhq/twenty/pull/10146#issuecomment-2665863910),
which introduced the workflow run visualizer, made it impossible to
debug an execution as it's not complete yet.
In this PR, I'm bringing back the raw output visualizer and hiding the
flow visualizer as it's been broken by the recent change to the output
format.
- Remove the demo test as I don't think it provides much value
- Re-run a test that was discarded because it failed due to a bug;
modified the test so the bug doesn't make the test fail
- Fixed all workflow tests
- Do not render a source handle for the leaf nodes
- Upgrade the `@xyflow/react` library
| Before | After |
|--------|--------|
| 
| 
|
## Other options considered
React Flow exposes a hook to get the connections of the current node. I
tried to use this hook – which makes things way simpler – but I couldn't
find a way to make it work in Storybook. I had two options: 1. Set up
React Flow to render the nodes properly, 2. Mock the hook in Storybook.
The first option was hard to achieve as the `<Reactflow />` component
renders a whole flow, and it doesn't play well with the idea of
rendering a single node in a story.
The second option seemed overkill as mocking modules with Storybook is
not straightforward. See
https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules.
I chose to keep the initial version of my code, written before I spot a
function simplifying the code. We can give it a look another time.
- Remove the `WorkflowDiagramBaseStepNode` component
- Create the `WorkflowDiagramStepNodeIcon` component to centralize the
icon put in workflow step nodes; that was the main task of the
`WorkflowDiagramBaseStepNode` component and the new
`WorkflowDiagramStepNodeIcon` component made it obsolete
- Update the `WorkflowDiagramStepNodeBase` component to be THE low level
component
Fixes
https://discord.com/channels/1130383047699738754/1333822806504247467
In this PR:
- Make the workflow step title input readonly when the visualizer is in
readonly mode
- Make all the fields of the Update Record and Delete Record readonly
when the visualizer is in readonly mode
- Create stories for the Create Record, Updated Record and Delete Record
actions; I'm checking for the default mode and several variants of the
disabled mode
- Set up mocks for the workflows and use them in msw handlers
Follow up:
- We use `readonly` and `disabled` alternatively; these are two
different states when talking about a HTML `<input />` element. I think
we should settle on a single word.
- Refactor the `<WorkflowSingleRecordPicker />` component to behave as
other selects
| Current component | Should look like |
|--------|--------|
| 
| 
|
**This PR implements a new layout for the visualizer, but the dimensions
of the nodes will change soon. I used hard-coded dimensions, like
`40px`, but I'll update them when I work on fixing the nodes' design. I
think we can merge this PR first and then fix the nodes' design.**
https://github.com/user-attachments/assets/580fa812-ee8e-4452-b6ac-ca55ecb31759
- Clean Playwright's configuration:
- Remove artificial 500ms delay between each step
- Group all tests under a `chrome` project relying on a `setup` project
to get an authentication state which all tests can reuse
- Changes on the `Sign up with invite link via email` test:
- Generate a new email for each test trial, as previously it was failing
when run many times
- Make deleting the account part of the test; if we write other tests
for account sign-up, we'll prefer to delete the accounts with an HTTP
call to speed up things
- Added some assertions to ensure we reached steps when expected, as we
removed the 500ms delay between each step, and it made some assertions
fail
- Wrote new tests for workflows:
- Created `Create workflow`, a test asserting we can create a workflow
from the record table
- Created `Create simple workflow`, a test asserting we can create a
simple flow; I will add more assertions to this test and write other
tests once this first PR is approved
- I make HTTP calls to delete and destroy workflows after they run to
keep the database clean
- Added a data-testid to ensure we focus elements from the Cmd+K; our
selectors are not strong – see `getByRole('textbox')` – and I preferred
to scope them to a root element
- Added an `aria-label` to a button
---------
Co-authored-by: prastoin <paul@twenty.com>
In this PR, I implemented or confirmed that the read-only mode works for
the following fields:
- [x] FormUuidFieldInput
- [x] FormRawJsonFieldInput
- [x] FormPhoneFieldInput
- [x] FormEmailsFieldInput
- [x] FormLinksFieldInput
- [x] FormAddressFieldInput
- [x] FormFullNameFieldInput
There are many fields so I will cut my work in several small PRs.
Here, I updated the following fields:
- [x] `FormBooleanFieldInput`
- [x] `FormCurrencyFieldInput`
- [x] `FormNumberFieldInput`
- [x] `FormDateFieldInput`
- [x] `FormDateTimeFieldInput`
- [x] `FormMultiSelectFieldInput`
- [x] `FormSelectFieldInput`
The updates in the components are relatively small. I wrote Storybook
tests, and this is why the PR is quite big.
The changes in the components should mostly the same.
I added a disabled state to some inputs.
I created a specialized `VariableChip` as its styles started diverging
from the original `SortOrFilterChip`.
- In the `formatFieldMetadataValue` function, allow people to call
TypeORM's `save()` method with unserialized JSON data.
- Create an `overrideWorkflowDraftVersion` mutation that takes a
workflow id and the id of the workflow version to use as the new draft
- If no draft exists yet, create one
- If a draft already exists, deactivate its serverless functions
- Duplicate every step. For serverless function steps, it includes
duplicating the functions
- Save the data of the step in DB
- Call the `overrideWorkflowDraftVersion` mutation in the old workflow
header and in the new Cmd+K actions
- I chose to not update the Apollo cache manually as the information of
the new draft are going to be automatically fetched once the user lands
on the workflow's show page. Note that we redirect the user to this page
after overriding the draft version.
- Create a really simple abstraction to unify the date and date time
fields. We might dissociate them sooner than expected.
- The _relative_ setting is ignored
- Implemented the `renderText` method for `VariableTag`. This method
returns the `variable` attribute of the node, i.e. `{{test}}`.
- Used the `editor.getText()` function to simply get the textual
representation of the field without relying on `editor.getJSON()` and
`parseEditorContent`.
- Implemented the RawJSON form field, which is heavily based on the
`TextVariableEditor` component.
- This component is inspired from the `RawJsonFieldInput` field,
especially the JSON validation.
Closes https://github.com/twentyhq/private-issues/issues/180
- Add an option to hide the input from the
`packages/twenty-front/src/modules/ui/input/components/internal/date/components/AbsoluteDatePickerHeader.tsx`
component
- Create a form field for dates
- Integrate the picker
- Create an input **without a mask** to let the user types a date and
validate it by pressing the Enter key
- Extract some utils to parse and format dates
- Refactor VariableTagInput to have a reusable low-level TipTap editor
- Create three primitive form fields:
- Text
- Number
- Boolean
## Notes
- We should automatically recognize the placeholder to use for every
FormFieldInput, as it's done for FieldInputs.
## Design decisions
Our main challenge was for variables and inputs to be able to
communicate between each other. We chose an API that adds some
duplication but remains simple and doesn't rely on "hacks" to work.
Common styles are centralized.
## Demo
"Workflow" mode with variables:

FormFieldInput mode, without variables:

Behavior difference between fields that can contain variables and static
content, and inputs that can have either a variable value or a static
value:

In this PR:
- Ensure the `setNestedValue` does a deep copy of the provided object
and doesn't mutate it directly.
- Store the form's state in a `useState` instead of relying entirely on
the backend's state. This makes the form more resilient to slow network
connections.
- Ensure the input settings are reset when selecting another function.
- The Inner component now expects the serverless functions data to be
resolved before being mounted, so I split it.
Closes https://github.com/twentyhq/twenty/issues/8523
This is the result of a long discussion we had here:
https://github.com/twentyhq/twenty/issues/8001.
The goal is to stop iOS from automatically zooming when the user focuses
on an input whose font size is less than 16px.
The options were:
1. Disable zoom for all devices
2. Disable zoom for devices detected as iOS devices, which doesn't
prevent users from zooming manually but fixes the auto-zoom bug
3. Set the font size of the inputs to be equal to or greater than
16px—this change would take a lot of time
To me, the second option is the best, as iOS prevents developers from
disabling zoom. They saw that it was overused and chose to restrict this
setting. Setting a `maximum-scale` doesn't prevent users from zooming,
but it fixes the initial bug we had.
My implementation can be seen as [progressive
enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement
): If we can detect that the user uses an iOS device, we'll set the
`maximum-scale` viewport property. Relying on the user agent is always
unstable, and the check might fail unpredictably. We might not disallow
auto-zoom for some iOS devices.
However, I think we can either:
- Invest some time to choose a more reliable user detection pattern if
the one I suggest is not sufficient ([we find many different checks on
the
internet](https://stackoverflow.com/questions/9038625/detect-if-device-is-ios),
I'm not sure which one is the best)
- Choose to apply the viewport setting to all devices and remove the JS
code. According to my tests, it doesn't prevent zooming on desktops.
However, it does on Android phones. I think it's not lovely to disallow
zoom, but if the team agrees that we should go this way, I won't
disagree.
I know my JavaScript code does not follow a pattern we want to spread in
the app. The synchronous script will run as soon as possible to ensure
the viewport is correctly set when the website launches. This shouldn't
be an example followed by others.
Thanks, @harshit078, for your help in thinking about the best option.
I'm tagging @lucasbordeau and @charlesBochet for a technical review.
I would appreciate if someone could test on a more recent iOS device
than mine.
Here is a demonstration of the behavior on iOS:
https://github.com/user-attachments/assets/d49fb65f-dd76-455c-9ac0-d4c002a7fe89
- Increase the dimensions of the ReactFlow nodes. This allows to ditch
scaling which made it hard to get the width of the nodes as they were
visually scaled by 1.3.
- Center the flow when the flow mounts and when the state of the right
drawer opens.
- Put the node type inside of the node so it doesn't overlap with the
arrow
- Make the edges non deletable
We'll have to make a refactor so the viewport can be animated properly:
https://github.com/twentyhq/twenty/issues/8387.
https://github.com/user-attachments/assets/69494a32-5403-4898-be75-7fc38058e263
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
- Use a label to make the whole card interactive
- Disallow the Toggle component to shrink; it used to on mobile devices
A focus indicator is missing for the Toggle component. We'll have to add
one.
Fixes:

The regression is due a recent change I made to the SelectControl
component: https://github.com/twentyhq/twenty/pull/8125.
The SelectControls get applied `text-align: center` due to the styles
`react-datepicker` applies to the header component. My grid
implementation makes the label take all the available width. I could
have let it take an `auto` width, but I think it's better to set the
`text-align` property and ensure the `SelectControl` component behaves
predictably.
In this PR:
- Add support for manual triggers in the backend
- Add a right drawer to let users select the type of trigger they want
- Create a specific right drawer for database event triggers
- Create a right drawer for manual triggers; let the user select where
the manual trigger should be made available
- Create a default trigger as soon as the user selects the type of
trigger they want. It prevents the user to see empty selects for record
type and event type. By default, the database event trigger will be set
to "company.created". It should be visible enough for users to
understand what happens and choose another record type or event type.
https://github.com/user-attachments/assets/29a21985-1823-4890-9eb3-e4f876459c7a
In this PR:
- Allow the `<ConfirmationModal />` to take additional buttons to
display between the cancel and the confirm buttons.
- Create a modal that's displayed when the user tries wants to use a
workflow version as draft while a draft version already exists. The
displayed modal contains a link to the current draft version and a
button to confirm the overriding of the current draft version.
A demo:
https://github.com/user-attachments/assets/6349f418-1b11-45b3-9f5e-061ca74c2966Closestwentyhq/private-issues#114
This PR:
- Uses `<label>` HTML elements when possible to represent labels
- Uses the new `useId()` React hook to get an identifier to link the
label with its input; it's more suitable than generating a UUID at every
render
Fixes#7281
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
In this PR:
- Refactored components to clarify their behavior. For example, I
renamed the `Workflow` component to `WorkflowVisualizer`. This moved
forward the issue #7010.
- Create two variants of several workflow-related components: one
version for editing and another for viewing. For instance, there is
`WorkflowDiagramCanvasEditable.tsx` and
`WorkflowDiagramCanvasReadonly.tsx`
- Implement the show page for workflow versions. On this page, we
display a readonly workflow visualizer. Users can click on nodes and it
will expand the right drawer.
- I added buttons in the header of the RecordShowPage for workflow
versions: users can activate, deactivate or use the currently viewed
version as the next draft.
**There are many cache desynchronisation and I'll fix them really
soon.**
## Demo
(Turn sound on)
https://github.com/user-attachments/assets/97fafa48-8902-4dab-8b39-f40848bf041e
- Allows the deletion of triggers and steps in workflows. If the
workflow can not be edited right now, we create a new draft version.
- The workflow right drawer can now render nothing. It's necessary to
behave that way because a deleted step will still be displayed for a
short amount of time in the drawer. The drawer will be filled with blank
content when it disappears.
https://github.com/user-attachments/assets/abd5184e-d3db-4fe7-8870-ccc78ff23d41Closes#7057
- Add the SAVE_EMAIL action. This action requires more setting
parameters than the Serverless Function action.
- Changed the way we computed the workflow diagram. It now preserves
some properties, like the `selected` property. That's necessary to not
close the right drawer when the workflow back-end data change.
- Added the possibility to set a label to a TextArea. This uses a
`<label>` HTML element and the `useId()` hook to create an id linking
the label with the input.
## Improvements
- This PR calls `fitView` when the Reactflow component inits. It tries
to fit the flow in a view with a fixed min and max zoom.
- Every time the WorkflowDiagramCanvas is rendered for a different
`workflowVersionId`, the `fitView` will be re-called. This is
implemented with a React `key`.
- The canvas will be re-rendered when an activated/deactivated version
is updated (and a new draft version is created.)
- It will also be re-rendered when the user selects another workflow
version and this doesn't cause the `WorkflowDiagramCanvas` component to
unmount. It happens if the user wants to go the previous or next
workflow or workflow version.
## Previous Behavior

## New Behavior

https://github.com/user-attachments/assets/cb73f456-58b1-49c3-bd31-a1650810e9dd
## Notes
Closes#7047
This PR is a simplification of #7151. We'll have to improve the way we
manage zoom in another PR.