Issue : #15794
## Context
As shown in the linked issue, slash menu dropdown for rich text editor
at bottom of notes editor screen extended beyond the page container,
causing main page to scroll.
## Changes Made
Added floating UI's `flip()` middleware to the component in
`CustomSlashMenu.tsx` so that the menu automatically flips upward when
detecting not enough space for dropdown. This prevents overflow beyond
the page container.
Additionally, added offset to menu when flipping up to prevent
overlapping of the current line / caret.
## Results
https://github.com/user-attachments/assets/47a79468-779c-4fd7-8e22-2b496456ea92
---------
Co-authored-by: Hyeonjae Park <hyeonjaep@Hyeonjaes-MacBook-Pro.local>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Fixes
- Fixes https://github.com/twentyhq/twenty/issues/15640 : we have some
viewGroups that were in the past wrongly migrated; eg deleteing an
enum's option did not lead to deleting the associated viewGroup. We have
not cleaned that (we could do a command), but we can identify them and
choose not to display them - otherwise currently they are shown as a
duplicate of "No value" column
- Fixes buggy edge case introduced by
https://github.com/twentyhq/twenty/pull/16382 : after updating a view
from Table/Calendar to Kanban, then visiting another view, when coming
back to the newly updated kanban view the view groups were empty. This
is due to to the view groups being optimistically created with
on-the-fly computed ids when the view is updated, while we call
refreshCoreViewsByObjectMetadataId() after. The view groups we get from
the refresh obviously have different ids. It is not possible to indicate
the ids of the view group through the update of the view as they are a
side effect of the view update.
Fixes multiple issues with CRON schedule input validation and execution
time display.
### Issues Fixed
1. **UTC label placement** - Added "UTC" suffix to specific times (e.g.,
"at 09:30 UTC") but not to interval descriptions (e.g., "every hour")
2. **Upcoming execution time calculation** - Fixed incorrect execution
times for malformed CRON expressions by implementing auto-correction
### Changes
- Created `normalizeCronExpression` utility to standardize cron
expressions before parsing
- Updated `formatTime` to support optional UTC suffix
- Enhanced `getHoursDescription` to append UTC to specific times
- Added comprehensive test coverage (102 tests passing)
### Before
- `"1 /3 * * *"` showed daily executions at same time (incorrect)
- `"9 * * *"` showed same time repeated 3 times (incorrect)
- No UTC labels on schedule descriptions (confusing)
### After
- All malformed expressions auto-corrected and show correct execution
times
- UTC labels clearly indicate timezone for specific times
- User-friendly error messages for truly invalid patterns
Closes#15870
Fixes: #15887
The currency fields were expecting values in micros (multiplied by
10^6), but
this requirement was not indicated anywhere in the workflow UI. Users
had to
manually add a code node to multiply amounts by 10^6 before using the
new
currency value.
This change integrates the micros conversion logic directly into
FormCurrencyFieldInput by using convertCurrencyAmountToCurrencyMicros
and
convertCurrencyMicrosToCurrencyAmount utilities. This allows users to
input
human-readable decimal amounts (e.g., 3.21 for $3.21) in the form, which
are
automatically converted to micros internally for storage and processing.
This eliminates the need for users to add separate code nodes for
conversion
and improves the user experience by making the expected input format
clear.
Updated the 3 hooks - activate, deactivate and discard draft
(deleteVersion) - so those updates the cache.
Removed the update listeners.
Also wrapped a few actions to unsure workflowId is not undefined when
received by useWorkflowWithCurrentVersion hook. Otherwise, useFindRecord
with by performed with skip true, disconnecting tmp from the cache and
making it miss a statuses update.
https://github.com/user-attachments/assets/5fc355e8-cf18-4881-855b-744eb253b79e
In this previous work aiming at eliminating the creation of viewGroups
being separated from the views, I removed the creation of viewGroups
from the FE at view creation, but forgot to do it at view update when
the user chooses "change Layout" option (from Table/Kanban/Calendar to
another).
In this PR
- I removed creation, deletion and destroy of viewGroups from
usePersistViewGroupRecords. I left update because it useful for
visibility and position
- since usePersistViewGroups record was also handling optimistic, I
moved optimistic logic to a new hook useViewsSideEffectsOnViewGroups. I
realized that so far we optimistic is only useful for creation. For the
update though we do need to compute the view groups that will be created
to be able to call loadRecordIndexState, so I created a function for
that, but I did not seek to perform real optimistic, with writing and
deleting groups from the cache as it was a bit heavier and not in use
for now and I want to merge this asap as it fixes the creation of
viewGroups.
There are three identified scenarios.
## First scenario
The user uses a desktop. They click on a company, which is opened in the
side panel. They click on a tab, like "Tasks". The user chooses to open
the record in fullscreen. The "Tasks" tab is opened by default in the
fullscreen record page.
If the user refreshes the page, the default tab is shown: "Timeline".
**This was the behavior on the show pages, and I kept it.** If we wanted
to show the "Tasks" tab here, we would have to put the id of the "Tasks"
tab in the URL hash when we open the record in fullscreen and an active
tab id is already set.
https://github.com/user-attachments/assets/205776ac-9ad7-4e79-af9a-6b44360a5d77
## Second scenario
The user uses a desktop or a mobile device and interacts with a company
record. They click on a tab. If they refresh, the selected tab will be
displayed by default.
### Desktop
https://github.com/user-attachments/assets/d4e1908b-a591-42f6-bb82-5aec592e2778
### Mobile
https://github.com/user-attachments/assets/7bab73cc-a2ff-4ce8-9bc6-325efef2d500
## Third scenario
The user uses a desktop. They click on a dashboard, which is opened in
fullscreen mode by default. They select tabs. If they refresh, the last
selected tab is opened by default.
When the user turns edit mode on, **the url hash is removed**. The last
selected tab remains selected. If the user selects another tab, the url
hash isn't set, but the newly selected tab is displayed correctly. If
the user saves their changes, the selected tab remains selected.
However, if they refresh the page, the default tab replaces the
previously selected tab.
Not setting the url hash when editing a page layout simplifies the code.
I'm okay with this tradeoff as the feature is primarily meant to let
users share specific tabs of their records.
**This behavior will also be used for record page layout once edit mode
is supported.**
https://github.com/user-attachments/assets/869657a1-6895-4ade-816c-972c77aaab9e
Closes https://github.com/twentyhq/core-team-issues/issues/1784
First PR to implement application tokens
- add new application role in twenty-server
- move duplicated constants and types to twenty-shared
- will add role configuration utils into twenty-sdk in another PR
Should be merged once https://github.com/twentyhq/twenty/pull/16206 has
been released + command run to prod
In this PR
- Remove usage of viewGroup.fieldMetadataId, both in BE and FE states.
- But we still need to properly populate it until we fully remove
viewGroup.fieldMetadataId from db and ORM entity (upcoming 3rd PR out of
3). fieldMetadataId was removed from CoreViewGroup type and
CreateViewGroupInput and is determined BE-side based on the associated
view's mainGroupByFieldMetadataId. **I expect this means a downtime on
viewGroup creation, until both FE and BE are deployed and cache is
flushed.** This seems acceptable to me as it only regards viewGroup
creation.
- this information is replaced by view.mainGroupByFieldMetadataID
- Handle view group creation, update and deletion in the BE as a
side-effect of a view creation, update or deletion. Optimistic effects
are still used
- Add validation at view creation or update regarding
mainGroupByFieldMetadata
Left to do in 3rd PR
- Remove viewGroup.fieldMetadataId from db and ORM entity
- Restore feature allowing to update an existing grouped view's group by
field (already OK on BE side but need to rebuild FE optimistic)
# Introduction
Migrating `pageLayoutTab` to the v2 engine
- Types and constants
- Builder and validate
- Runner
Introduced a new `StrictSyncableEntity` that enforces that
`universalIdentifier` and `applicationId` are defined
As these entities are brand new we could enforce this rule already
This still requires a migration command to associate the existing
entities to custom workspace application instance and define
universalIdentifier
Handled retro-comp of the migration through a migration as upgrade
command fallback
---------
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
Timeline activities are fetched using the `useTimelineActivities` hook,
which relies on `useFindManyRecords`. Previously, it also fetched linked
object titles via `useLinkedObjectsTitle`.
When the user scrolled to the bottom, we triggered `fetchMore` from
`useFindManyRecords` to load additional data. This operation correctly
handled in-place loading and did not set the main `loading` flag to
`true`.
However, after `useFindManyRecords` returned updated data, new variables
were passed to `useLinkedObjectsTitle`, which didn’t leverage the
`fetchMore` pattern. As a result, the `loading` state of
`useLinkedObjectsTitle` was set to `true`, even when only partial data
changed.
Our logic for displaying the skeleton loader was:
```ts
const loading = loadingTimelineActivities || loadingLinkedObjectsTitle;
```
When `loadingLinkedObjectsTitle` turned `true`, this triggered the
entire list to unmount and remount, causing repeated unnecessary
reloads.
To resolve this, I no longer delay rendering the list while loading
linked objects title.
## Demo
The skeleton is only shown at the beginning of the navigation.
https://github.com/user-attachments/assets/cb79f91d-5151-4dc1-8e6b-a322e56a48c4
Closes https://github.com/twentyhq/twenty/issues/16210
Regression introduced by
https://github.com/twentyhq/twenty/pull/16244/files
Why:
- `ChipFieldDisplay` component is using a `recoilComponentState` and
`ContextStoreComponentContext` is not provided on Settings page
This fix
- does not use the componentState in the `ChipFieldDisplay` but in the
RecordIndex area where the `ContextStoreComponentContext` is provided
- this also improve the performance as recoilState access is not free
This PR fixes an issue where links added inside a note were not
appearing in the preview shown under Company -> Notes. The link would
only appear after opening the note, which led to inconsistent behavior.
Issue:
Blocknote represents text and links using different node types:
1. { "type": "text", "text": "Welcome to this demo!" }
2. {
"type": "link",
"content": { "type": "text", "text": "Example" },
"href": "https://example.com"
}
The existing preview function only handled plain text nodes and
completely ignored link nodes.
As a result:
- Links did not appear in the preview
- Links appeared only inside the full note view
Steps to Reproduce:
- Add a link inside a note
- Go to Company / People and view the note preview -> the link is
missing
- Open the note -> the link appears correctly
How I fixed it:
- A new recursive text extraction function (extractText) was added to
correctly read.
- Text nodes
- Link nodes (including inner content and the href)
- Nested child content
- Link previews now appear in the following format:
DisplayText (https://example.com)
Edit : closes https://github.com/twentyhq/twenty/issues/16043
---------
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Fixes Issue: #16188
**Bug:**
Object Selection Dropdown was not working correctly when searching by
object's name.
This was happening only when `Search Records` action is selected.
Other actions(Create, Delete, Update, Upsert) were working
correclty(object search was working).
**Reason:**
`Search Records` action uses `WorkflowObjectDropdownContent` component
which only filters based on `nameSingular` field in metadata (and doesnt
search based on lable text).
All other record action uses the `Select` component which filters by the
label property (set to `labelProperty`).
**Fix:**
Updated the filtering logic of `WorkflowObjectDropdownContent` component
to match the search text with label field also.
I could't find any unit tests for the `WorkflowObjectDropdownContent`
component (or any other component in workflow module). Let me know if I
missed it and if tests need to be added for this.
**Screenshot of working object search in Search Record**
<img width="2056" height="1220" alt="image"
src="https://github.com/user-attachments/assets/678d9806-899a-470c-9e82-b9f975d65950"
/>
# Introduction
Closes https://github.com/twentyhq/core-team-issues/issues/1980
In this PR we migrate the agent from v1 to v2.
## New FlatRoleTargetByAgentIdMaps
Derivated the `flatRoleTargetMaps` to be building a
`flatRoleTargetByAgentIdMaps` to ease retrieving a roleId to associate
to an agent
## Coverage
Added strong coverage on both failing and successful CRU agents
operations
---------
Co-authored-by: Weiko <corentin@twenty.com>
## What’s done
- Clicking a favorite folder on mobile now opens a clean drill-down
layer with only its contents
- Added a back button (icon + folder name) — visually 100% identical to
`NavigationDrawerBackButton`
- Sidebar no longer collapses when opening a folder
- Tree line is removed on mobile (consistent with current Twenty design)
## Intentional deviations from the mockup
1. **Back button does not replace `MultiWorkspaceDropdownButton`**
Kept `NavigationDrawerHeader` untouched — it’s responsible for the
hamburger menu and workspace name.
Added the back button as a separate block below.
→ This feels cleaner architecturally and avoids breaking other places
that use `NavigationDrawerHeader`.
2. **Vertical spacing between items is slightly tighter than in the
mockup**
Used `NavigationDrawerSubItem` — the standard component for nested items
in Twenty.
Current production spacing matches this exactly, so I preferred
consistency over pixel-perfect mockup matching.
Happy to adjust either point if the team prefers to follow the mockup
1:1.
Closes#15950
---------
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
CommandMenu did not push its own focus item to the focus stack when
focused. (See NavigationDrawerInput)
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
This PR fixes a bug that created a deletedAt column in sample CSV file
generated for import, that when used afterwards for import, would create
soft deleted records, which gives the impression that the CSV import
does not work.
In this PR (1/3)
- introduce view.mainGroupByFieldMetadataId as the new reference
determining which fieldMetadataId is used in a grouped view, in order to
deprecate viewGroup.fieldMetadataId which creates inconsistencies.
view.mainGroupByFieldMetadataId is now filled at every view creation,
though not in use yet.
- Introduce a command to backfill view.mainGroupByFieldMetadataId for
existing views + delete all viewGroup.fieldMetadataId with a
fieldMetadataId that is not view.mainGroupByFieldMetadataId. (It should
concern 37 active workspaces)
- Temporarily disable the option to change a grouped view's
fieldMetadataId as for now it creates inconsistencies. This feature can
be reintroduced when we have done the full migration.
In a next PR
- (2/3) use view.mainGroupByFieldMetadataId instead of
viewGroup.fieldMetadataId. In FE we may keep viewGroup.fieldMetadataId
as a state (TBD). View groups will now be created / deleted as a side
effect of view's mainGroupByFieldMetadataId update.
- (3/3) remove viewGroup.fieldMetadataId
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Introduce a read-only view of permissions similar to agent roles tab.
The role selector in the following image feels as if it would lead to a
new page, which is not what we want.
<img width="1281" height="813" alt="Permissions (If easier V1)"
src="https://github.com/user-attachments/assets/7b272f5a-40ef-43ad-83d8-f9e588b1cd6e"
/>
Therefore, I added a Select component for now and would love some
clarity on what's ideal.
<img width="554" height="651" alt="image"
src="https://github.com/user-attachments/assets/91575208-66b1-4ed1-86cd-f3aa528ad0dc"
/>
Also, the "Add Rule" button changes to enabled when we switch the role
from `Admin` to `Member` and clicking it redirects to
`/settings/roles/:role-id/add-object-permission`. Do we want to disable
the button completely?
One final thing: SettingsAgentRoleTab already re-uses
SettingsRolePermissions. I created MemberPermissionsTab to do the same.
I don't think we need an abstracted shared component here since both
tabs rely on the same shared child anyway. However, if we need a
refactor, I can use some direction on how the code should look.
Creating this PR as draft since I think there might be a couple
suggested changes on this.
This PR improves the general UX and DX of boards, by modifying the query
effect to only use paged group by queries.
In this PR we implement two more things in the backend for group by
queries :
- Fixed ORDER BY in the PARTITION BY sub-query (this wasn't working
because it was applied in the main query, so it sorted randomly picked
records, which was a correct sort on an incorrect dataset returned by
the sub-query)
- Added offset paging in PARTITION BY
Miscellaneous, various bug fixes and improvements along the way :
- Throttled loading of cards to avoid React freeze
- Handling of drag & drop
- Handling of create / delete / update
- Reworked skeleton (the library slows down a lot with hundreds of
skeleton for a spinning effect that is hardly noticed)
- Fixed refetch of aggregate queries (I included the new group by
aggregates query we use in the existing refetch mechanism)
- Re-trigger queries on filters and sorts changes
- Unselect all record ids when deleting / restoring / detroying
- Fetch only groups that still have records to lighten the group by
query.
# What remains to be done
This is still a naïve fetch more implementation that will work for a few
fetch more rounds, but if you scroll and load say 200 cards per column
on a board, React will re-render all 200 cards of each column each time.
We would probably need to virtualize the board with paged queries as we
did for the table, this could be done after this PR but seems less
urgent.
What's nice is that this new query pattern is well designed for
virtualization also, drawing from our experience with table
virtualization, and adapted to a multi-column request pattern, like a
2:2 matrix of records, for our boards.
So the remaining work would be to design a UI solution for virtualizing
this matrix of records, which could be quite different from our table
virtualization mechanism.