# Introduction
Migrating `viewFilter` to v2 in order to migrate later the field update
side effect on view to v2 too
## What's done
- Created flat-view-filter
- flat view filter runner
- flat view filter builder
- create view filter service v2 and input transpilers
- refactor the existing view filter resolver to fix standard (
BREAKING_CHANGE on graphql api update especially ) REST stays the same
- refactored the front to consume the mutations autogenerated
## New generic tools
### Compare two flat entity
Introducing a new util to compare two flat entity, it's strictly typed
and will be added to the generic builder in a following PR
This will ease flat entity addition as won't required to create a
specific abstraction for comparison
Generic builder will expect specific constant: properties to compare and
properties to stringify
### Transform flat entity for comparison
Forked and refactor the initial existing method for flat entity business
scope and type safety
## Coverage
Migrated existing integration tests to fit new contract API
This PR does not add strong coverage on validation exceptions
Deadlines are too short
close https://github.com/twentyhq/core-team-issues/issues/1666
## Problem
With Twenty usage growing, we are facing challenges on server side to
respond to the demand. The current bottleneck we are facing is server
CPU.
While investigating CPU performances, we figured out that loading all
relation fields was the biggest issue.
### About graphql query response size
**Example:** on `People Index` Table page:
we query: `person.company` and we query **all non relation fields** on
company relation field.
`{ person { company { id, name, domainName, employees, address ... } }`
However we only need **company.id, company.name and company.domainName**
to be able to render the Company Chip in the company column (RELATION)
in the table.
`{ person { company { id, name, domainName } }`
We initially assumed that the querying all non relation fields was not
an issue because it was not adding additional load on database (which is
also partially true: if a field is containing a huge json this will add
load on postgres as data needs to be transfered)
This assumption is wrong on CPU side:
- when loading one to many relations (company.people), this quickly ads
up and we end up with response of 20kB quite quickly, even without
adding any custom field on person.
- even when loading a many to one relation, if the user is storing big
data in a given field (note.body for instance, or workflowRun.state),
this starts also being an issue.
- Worst case scenario are starting to happen: on a production workspace
with 20 active workflows. Loading workflow table while displaying
workflow runs commands (20 workflows x 60 workflowRuns x
workflowRun.state which is a big JSON) result in a response of... 125MB.
### Why is it an issue?
When yoga (graphql engine) parses a response to send it back in server
response, it's using `JSON.parse `(or stringify depending on the case).
Parsing data is CPU instensive (as you need to validate, transform) and
this is more or less `O(n)`.
This means returning 125MB is very intense on the CPU and will likely
use 100% of the CPU for ~1sec in our production servers.
NodeJs (nodeV8) is single threaded. It's able to process multiple
requests in parallel but to do that, it will cut them in "microTasks"
and process each microTasks (that can belong to different requests) one
after the other. Exactly like a single threaded CPU would allocate some
time to a process and then to the next one, etc.
This means that this big response request will actually block the other
request.
As a result, all requests are slow and our infrastructure is multi
tenant so some customers are impacting others.
This is even a bigger issue when our health checks start to fail and
containers are starting being considered as unhealthy and killed by the
orchestrator
## How to fix this
1. On Front end side, we should only query what we need. In this PR, I'm
forcing the relations to only query: `id`, `labelIdentifier`,
`imageIdentifier`
2. (later) as we are API first, we also need to do something to protect
the servers from this side too. To do that, Graphql APIs can associate a
cost to each graphql field (maybe 1 for a test fixed, 2 for a JSON
field, 10 for relations, etc...) and we can throttle based on that.
## Changes in this PR
It's mainly about refactor the tooling to generate `RecordGqlFields`
(`generateDepthOneGqlFields`). this tooling is used to generate the list
of fields we want to query. Most of the time we want to query a record
with one level of nesting.
1) Refactor to remove duplicate code => `generateDepthOne` become
`generateDepth` and can handle both `depth = 0 | 1`
2) Introduce `generateDepthRecordGqlFieldsFromFields.ts` which is the
base and can give you a list of gqlFields based on FieldMetadataItems
3) Introduce `generateDepthRecordGqlFieldsFromObject` which is a
shortcut for an object
4) Introduce `generateDepthRecordGqlFieldsFromRecords` which is the
intersection between `generateDepthRecordGqlFieldsFromObject` and a
given record (useful for cache tooling)
5) Replace all usages + introduce a hook
`useGenerateDepthRecordGqlFieldsFromObject` to ease usage
## Summary
1. This change introduces the ability to limit how many values a
multi-value field can contain (for example: maximum number of emails,
phone numbers, links, or array items).
2. It centralizes the limit as a shared constant and type, validates
settings on the server, updates front-end types and components to
consume the setting, and adds a small settings UI so workspace admins
can change the limit per field.
3. Default behavior is preserved: if no max is configured, the existing
default (10) is used.
## **Fixes Issue:** [#14740
](https://github.com/twentyhq/twenty/issues/14740)
## Manual Test Screenshot
<img width="383" height="451" alt="Screenshot 2025-10-08 002413"
src="https://github.com/user-attachments/assets/a7704af6-10ef-4d10-b8c9-a9eeca03bfd9"
/>
### Values in fields
https://github.com/user-attachments/assets/7f59c4f7-3aca-4f83-8c04-3d44988316e3
Let me know if any changes are needed
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Storybook had issues. This PR intends to solve them
Main things to care :
I had to change the way the provider was given to the Calendar view. Not
sure how it may impact the final component, so if someone with some
knowledge about it could take a look that would be great
TODO:
Only one remaining test to fix : prefetch loading. Couldn't find out how
to solve this issue. When you open the story you see that the companies
are not fetched.
## Scope
- Refactor SingleRecordPicker to handle morphItem and match Multiple one
- fix query generation to take morph into account
Left:
- double check SingleRecordPicker in Workflows / Form
- Fix tests / stories
This PR follows the multiSelect PR merged previously. It will enable
morph relation Many to One to be handled from the table, using a
singleSelect picker
Main point : I decided to change the singleSelect API to take an array
of **objectMetadataName** instead of only one to deal with both our
usecases.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
## 📝 Summary
Added **infinite scroll support** in the **IconPicker** dropdown by
extending `DropdownMenuItemsContainer` with an optional `onScroll`
handler.
Fixes#14419
---
## 🔄 Changes
- Updated `DropdownMenuItemsContainer` to accept an optional `onScroll`
prop.
- Implemented lazy-loading of icons in `IconPicker` (loads 50 more icons
when the user scrolls).
- **Note**: Infinite scroll is **disabled** if a `maxIconsVisible` prop
is passed — in that case the list is static.
- No new UI elements were added
---
## ✅ How it Works
- Scroll event is captured via `onScroll`.
- When the user scrolls near the bottom:
```ts
target.scrollTop + target.clientHeight >= target.scrollHeight - 10
---
## Demo ->
[Screencast from 2025-09-30
20-47-03.webm](https://github.com/user-attachments/assets/d8906ebd-3cf1-4a89-a10d-a049b098c94f)
Attached screen recording of infinite scroll in action.
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
fix:Ability to add a new option for a multi-select on the fly
Issues:#13877
---------
Co-authored-by: ehconitin <nitinkoche03@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
## Performance short term fix
Done in this PR:
workflowVersions and workflowRuns are heavy object. I'm preventing
loading their data when they are loaded as relations. This will reduce
the work on backend
## Long term fix
Todo:
We should make sure workflow data is ligher
Related issue: https://github.com/twentyhq/core-team-issues/issues/1585
## Summary
Added a new "Add a node" button to the command menu that enables users
to insert workflow nodes through the command interface. This enhancement
improves the workflow creation experience by providing an additional,
accessible way to add nodes to workflows.
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
**Issue**: https://github.com/twentyhq/core-team-issues/issues/1587
### Changes Made
- Modified scroll event handling to detect and limit two-finger scroll
gestures to the workflow canvas component
- Improved user experience by preventing accidental navigation or
zooming outside the intended canvas area
---------
Co-authored-by: Twill <agent@twill.ai>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## What
In this PR, we are fixing two issues:
- while deleting an object, the views are not properly refreshed leading
to FE bug. This is due to the fact that we were refetching **views
associated to the object** which has been deleted. As views are properly
deleted in the BE, the FE gets no views from this call and cannot
optimistically react. In this case, I'm triggering a complete view
refetch
- messaging error handling had a hole
## Improvements
- Add logs to all gql operations and rest calls to help debug CPU issues
on the backend. These are temporary and should be removed
- Remove nested relations from workflowVersions load (used to add manual
triggers in the side bar). On some workspaces this call result in a
response of 4MB which is heavy on CPU
- Investigated Redis Usage ==> made a few improvements, we are should
still migrate to the new cache service once available
- investigated db calls in messaging / calendar fetch list + workflow
enqueue run cron jobs. Everything seems to be properly batched
Closes [#1583](https://github.com/twentyhq/core-team-issues/issues/1583)
- Removed `messageId` column from `File` table and its references in
code (unrelated to this PR)
- Updated AI chat to use React Context API with persistent provider in
`CommandMenuContainer`
- Converted all AI chat component states to regular Recoil atoms (no
instance context needed)
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
This PR is the first part of the creation of the Chart editor.
https://github.com/user-attachments/assets/8b0af8ea-be41-4506-84cb-e40f5521cbf0
Done:
- Bar chart settings (except filters)
- Line chart settings (except filters)
In progress:
- Pie chart settings
- Number chart settings
- Gauge chart settings
Left to do:
- Loosen the backend validation to allow the user to save a partial
configuration, validate the graph configuration in the frontend and
display a error friendly message in the graph if the config is not
completed yet
- Implement the filter edition
- Finish the other graph types settings
## Context
Add routes to migration V2
- Resolvers
- Service v2
- Builder
- Validator
- Action runner
Next PR: Add to twenty-cli to sync routes with serverless
## Context
We are experiencing bad performance on Twenty. One of the root cause
hypothesis is that computing `currentUser.currentWorkspace.views` is CPU
consuming. Without views, the GetCurrentUser response is ~1000 lines.
With its ~10000 lines.
As graphql is going through all fields recursively this can be quite
heavy on CPU. We had a similar issues on ObjectMetadataItems 2 years ago
and came with storing the response in redis.
Note: I thought there was also a cache in RAM but this is not the case,
so to invalidate the cache we can just empty redis.
## How
- Extract getting all views from GetCurrentUser and update frontend to
perform both queries
- Add views to cached graphql operations
- invalidate the cache manually on view or related core entities update
/ create / delete / destroy
## Tests
I have tested a lot on v1
Fixes https://github.com/twentyhq/twenty/issues/14766
To reproduce
1. create a view with a any field filter and save it. After, remove any
field filter clicking on the X on the chip. No "Update view" shows.
2. now add another regular filter to the view and save it. Now remove
any field filter clicking on the X on the chip and remove the regular
filter you just added. Now "Update view" shows, click it and refresh the
page. Any field filter still shows
There were two problems
1. UpdateViewButtonGroup is in charge of showing "Update View" button.
But is never mounted if shouldExpandViewBar is falsy.
shouldExpandViewBar was not considering
viewAnyFieldFilterDifferentFromCurrentAnyFieldFilter so even if any
field filter had changed, UpdateViewButtonGroup still wasn't going to
show "Update view" button because it wasn't mounted
2. At save time, we added anyFieldFilterValue to the mutation payload if
```
...(view.anyFieldFilterValue && {
anyFieldFilterValue: view.anyFieldFilterValue,
}),
```,
but in JS an empty string evaluates to falsy.
In this PR https://github.com/twentyhq/twenty/pull/14785 we deprecated
viewFilterOperand (from camelCase to capital snakeCase values), but we
had not migrated the existing workflow steps values. Hence they cannot
be executed!
Let's fix that by dealing with both operands, new and deprecated, to
mitigate the issue. Then we will add a command to migrate the values.
- add serverlessFunction schema in twenty-cli
- add trigger schema in twenty-cli
- update serverless function code save and get
- sync serverless function