## 🐛 Issues Fixed
### 1. Role Editing Not Working for New Agents
When creating a new agent and then creating a role for it, the role
permissions appeared as non-editable. This was caused by:
- In create mode, `agentId = ''` (empty string from `useParams`)
- Empty string is **falsy** but `isDefined('')` returns **true**
- This caused incorrect behavior in role exclusivity checks and API
calls
### 2. Missing Role Data Loading
The agent form wasn't loading role data needed for permission editing
because it was missing the `SettingsRolesQueryEffect` component.
### 3. Navigation Conflicts
The `useSaveDraftRoleToDB` hook contained navigation logic that caused
conflicts when used from different contexts (role detail page vs agent
form).
### 4. Linting Errors
Unused `useIcons` import in `SettingsAgentRoleTab.tsx`.
---
## 🔧 Changes Made
### Agent Role Tab (`SettingsAgentRoleTab.tsx`)
- ✅ Use `isNonEmptyString(agentId)` to validate agentId (follows
codebase patterns)
- ✅ Improved role exclusivity logic to handle both create and edit
modes:
- **Edit mode**: Role must be assigned exclusively to this agent
- **Create mode**: Role must not be assigned to anyone yet
- ✅ Only call `assignRoleToAgent` when a valid agentId exists
- ✅ Pass `undefined` instead of empty string for `fromAgentId` prop
### Role Hook (`useSaveDraftRoleToDB.ts`)
- ✅ Removed navigation logic from the hook (better separation of
concerns)
- ✅ Removed `useNavigateSettings` and `SettingsPath` dependencies
- ✅ Hook now only handles data persistence, not navigation
### Role Component (`SettingsRole.tsx`)
- ✅ Added navigation after successful role creation in create mode
- ✅ Navigation now handled by the component using the hook
### Agent Form (`SettingsAgentForm.tsx`)
- ✅ Added `SettingsRolesQueryEffect` to ensure role data is loaded
- ✅ Agent form handles its own navigation after save
---
## ✅ Testing Scenarios
All these scenarios now work correctly:
1. ✅ Create new agent → Create role → Edit permissions → Save agent
2. ✅ Edit existing agent → Create role → Edit permissions → Save
3. ✅ Edit existing agent → Try to edit shared role (shows warning
message)
4. ✅ Navigate to object-level permissions from agent form with proper
breadcrumbs
---
## 📝 Technical Details
### Root Cause Analysis
The main issue was that empty string was being treated differently in
different checks:
- `agentId &&` → evaluates to `false` (empty string is falsy)
- `isDefined(agentId)` → returns `true` (empty string is defined)
This inconsistency caused the role to appear as non-editable and
attempted invalid API calls.
### Solution
Used `isNonEmptyString()` from `@sniptt/guards` which properly checks
both that the value is defined AND not empty, following codebase
conventions.
---
## 🎯 Impact
- Fixes critical bug preventing role permission configuration for new
agents
- Improves code quality with better separation of concerns
- Makes navigation logic more predictable and maintainable
- Follows codebase patterns and guidelines
Previously, single-item fields `maxItemCount: 1` didn’t show updated
values after pressing `Enter` the UI only refreshed on click outside.
This happened because the items list was hidden while
`shouldAutoEditSingleItem` remained true.
This PR updates the render condition to also check `!isInputDisplayed`,
ensuring the items list re-renders immediately after editing.
**Result:** Instant UI updates on Enter with no impact on multi-item
fields.
Fixes#15792
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
I know you did this change in order to make the frontend faster and less
resource eating. However it broke this optimistic rendering part
@lucasbordeau.
I suggest this quick fix, but I am open to a sharper one as well
Fixes https://github.com/twentyhq/twenty/issues/15838
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
## Overview
This PR refactors the `CalendarEventDetails` component to dynamically
display both standard and custom fields added via the metadata API,
instead of using a hardcoded field list.
## Changes
- Replaced hardcoded `fieldsToDisplay` array with dynamic field fetching
using `useFieldListFieldMetadataItems` hook
- Split fields into `standardFields` (maintaining original order) and
`customFields`
- Introduced `renderField` helper function to eliminate code duplication
- Custom fields now automatically appear at the bottom of the detail
panel after standard fields
- Maintained exact field order and all existing functionality including
participant response status display
## Technical Details
- Uses existing `useFieldListFieldMetadataItems` pattern already
established in the codebase
- Standard field order explicitly defined: startsAt, endsAt,
conferenceLink, location, description
- Participant response status (Yes/Maybe/No) correctly positioned
between first 2 and last 3 standard fields
- All fields respect permissions and visibility settings from metadata
## Testing
- ✅ Verified all standard fields display in correct order
- ✅ Added custom field `mycustomfieldtest` via metadata API - displays
correctly at bottom
- ✅ Participant responses (Yes/Maybe/No) render correctly with avatars
- ✅ Event title, creation date, and event chip display properly
- ✅ Canceled event styling (strikethrough) works
- ✅ No console errors or regressions detected
- ✅ Read-only behavior maintained (calendar events sync from external
sources)
## Screenshots
<img width="1401" height="746" alt="CleanShot 2025-11-17 at 11 23 57"
src="https://github.com/user-attachments/assets/3d967ec5-6d31-4fc3-b971-c73ea521c87d"
/>
## Related
This enables users to extend calendar events with custom metadata fields
that will automatically display in the UI without code changes.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Closes https://github.com/twentyhq/core-team-issues/issues/1600.
Two remarks
- This `limit` variable does not reduce postgre's work at it still needs
to scan the whole table. It did not seem possible to me to optimize this
as we cannot foresee which dimensions will be used by the user, and an
optimization could only result from an index on the dimension(s) (e.g.:
group companies by addressCity limit 50 can be optimized if we have an
index on companies.addressCity + we had a default orderBy on
adressCity). But this will still optimize the FE which at the moment
receives all groups and truncates the result.
- I have not done the work on the FE as the addition of limit is a
breaking change, and will break until the workspaces' schema is rebuilt,
so we need to flush the cache. I think this could be acceptable as the
feature is in the lab but I preferred not doing it yet as it would have
no impact since in the BE I added a default limit to 50 groups, and I
expect more FE work will be done to allow the user to choose their own
limit
- Allow widgets to be hidden based on device's type conditions: desktop
or mobile
- Create two widgets for rich text fields on tasks and notes. One widget
is displayed below fields on mobile (and in the right drawer); the other
is displayed on a separate tab on desktop
- In read mode, hide tabs if they contain no visible widgets. If there
is no tab left to display, display at least the first one with no
widgets.
- In edit mode, display all tabs and all widgets.
## Demo
https://github.com/user-attachments/assets/65ef1261-3902-4432-a420-48983b763b2c
Closes https://github.com/twentyhq/core-team-issues/issues/1811
## Summary
This PR replaces the Active/Inactive accordion sections with a modern
filter dropdown button and enables full access to system objects in
advanced mode.
## Changes
### UI Improvements
- ✅ Replaced accordion sections with a filter button dropdown (matching
the design pattern from the Group filter)
- ✅ Added 'Deactivated' toggle filter (hidden by default, uses
IconArchive)
- ✅ Added 'System objects' toggle filter (only visible in advanced mode,
uses IconSettings)
- ✅ Fixed search input width to properly fill available space
- ✅ Proper button sizing and alignment
### System Objects Support
- ✅ Made system objects visible when 'System objects' filter is toggled
on
- ✅ System objects are now fully clickable and accessible
- ✅ Updated object detail page to support system objects
- ✅ Updated field creation/edit pages to support system objects
- ✅ System objects can now have custom fields added
### Architecture
- ✅ Implemented scalable filter architecture using a single filtered
list
- ✅ Easy to add more filters in the future (e.g., show remote objects)
- ✅ All filters work independently and can be combined
## Testing
- [x] Tested deactivated objects toggle
- [x] Tested system objects toggle (only shows in advanced mode)
- [x] Tested clicking on system objects
- [x] Tested adding custom fields to system objects
- [x] No linter errors
## Screenshots
See attached screenshots in the conversation for the new filter UI.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds a filter dropdown to the Settings Objects table (incl.
deactivated/system toggles), enables system objects across
settings/field flows, seeds core views (workspace members, messages,
threads, calendar events), and adds actions for Workspace Members.
>
> - **Settings UI**
> - **Objects Table**: Replaces Active/Inactive sections with a single
filterable list and dropdown (`Deactivated`, `System objects` in
advanced mode); updates props to `objectMetadataItems` and removes
accordion sections.
> - **Search/UX**: Search input fills available space; inactive rows
show activation/delete menu; active rows remain navigable.
> - **Pages Updated**: `SettingsObjects`,
`SettingsApplicationDetailContentTab` switch to new table API; object
detail and new-field flows use `findObjectMetadataItemByNamePlural`
(works with system objects).
> - **Action Menu**
> - **Workspace Members**: Adds `WORKSPACE_MEMBERS_ACTIONS_CONFIG` with
"Manage members in settings" action; wired into `getActionConfig` for
`WorkspaceMember`.
> - **Server (Core Views Seed)**
> - Adds default views: `workspaceMembersAllView`, `messagesAllView`,
`messageThreadsAllView`, `calendarEventsAllView`; included in
`prefillCoreViews`.
> - Marks workflow entities as system (`WorkflowRun`,
`WorkflowVersion`).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
6a2856df85104665bd986cc63f2d54f19bf668d0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Resolves [Dependabot Alert
301](https://github.com/twentyhq/twenty/security/dependabot/301).
Locked the version of "ai" at 5.0.52 in order to stop it from bumping to
5.0.93 directly when the ^ is introduced since it breaks the code across
multiple files.
Fixes#14723
### Summary
Fixes a UI while creating a custom CRON
### Changes Made
- updated `WorkflowEditTriggerCronForm.tsx` to use the predefined
`FormSelectFieldInput.tsx` instead of using `Select.tsx`
- added `hint` prop to `FormSelectFieldInput.tsx`
- updated styling for showing `Upcoming execution time`
- added conditional render for `Schedule` and `Upcoming Execution Time`
- `Schedule` section is not rendered in Custom Cron
### Steps to Reproduce (Before Fix)
1. Navigate to - /object/workflow/xxxx
2. Try adding a new Trigger
3. Set Trigger Interval to Cron (Custom)
### Before

### After

---------
Co-authored-by: Charles Bochet <charles@twenty.com>
# Introduction
Remove the v2 feature flag for view-field field-metadata and
object-metadata metadata entities
## Some details
- Disabled nestjs-query for object metadata creation and explicitly
calling it
- removed all v1 integration tests files
## Remarks
Not remove v2 referencing in both filenaming right now will handle that
globally later
## Breaking change
Due to object metadata resolver createOne standardization had to rename
the input from `CreateObjectInput` to `CreateOneObjectInput`
Resolves [Dependabot Alert
293](https://github.com/twentyhq/twenty/security/dependabot/293).
Updates the playwright version used to `1.56.1`. The alert could have
also been ignored since the playwright download only happens in CI and
local environments, not the production environment. However, it's an
easy fix instead of just ignoring the alert.
Resolves [Dependabot Alert
95](https://github.com/twentyhq/twenty/security/dependabot/95) - babel
vulnerable to arbitrary code execution when compiling specifically
crafted malicious code.
These were the few options we had for a direct drop-in replacement.
- [x-var](https://www.npmjs.com/package/x-var?activeTab=readme)
- [cross-let](https://www.npmjs.com/package/cross-let)
- [cross-var-no-babel](https://www.npmjs.com/package/cross-var-no-babel)
x-var has the most weekly downloads among the three and it is also the
most actively maintained fork of the original cross-var package that
introduced the vulnerability. There is no syntax difference per the
documentation, but I do not have a windows machine to test.
`cross-var-no-babel` offers the most minimal changes, but is also
abandoned without a public-facing repo.
- Users with a single workspace are allowed to update their email across
`core.user` and `workspace_xyz.workspaceMember`.
- The latter happens asynchronously (built it like this for non-blocking
with multiple workspaces), but since we restrict the email update
functionality to a single user, we can also update the email in
workspaceMember synchronously - I left asynchronous there to receive
feedback on whether we should move to synchronous or not.
- Merged main and resolved conflicts to ensure we use the
`SettingsPermissionGuard` and the updated `workspace.service.ts` code.
One edge-case that I was trying to communicate on Discord:
Say that an admin is a member of multiple workspaces. Therefore, they
can allow roles with PROFILE_INFORMATION permission to update their
email.
<p align="center">
<img width="553" height="115" alt="image"
src="https://github.com/user-attachments/assets/80382b1f-a9e3-4dac-b606-c2defeb2c330"
/>
</p>
However, since the admin is part of multiple workspaces, he/she cannot
even update own email - the field stays disabled, leading to some
confusion.
<p align="center">
<img width="545" height="255" alt="image"
src="https://github.com/user-attachments/assets/5e6d27db-c9a8-4d5e-9ab6-65c77beae5b4"
/>
</p>
However, the workspace can have another member with admin role or some
other role that has PROFILE_INFORMATION permission flag. That user will
be and should be allowed to update email, so we cannot hide `email` from
dropdown options.
<p align="center">
<img width="585" height="283" alt="image"
src="https://github.com/user-attachments/assets/a670d3ac-cf48-4865-a425-b909093d8420"
/>
</p>
The behavior is fine imo, just a little confusing for members with more
than one workspace.
I have also tested the flow by signing up to YC workspace with my org
google account (twenty.com), then changing email to my personal address.
- After changing, I need to login using Google with my personal account
to access YC workspace again.
- If I login using Google with org google account (twenty.com), a new
user account is created.
This behavior is consistent with Notion and Linear.
Finally, as for the verification of email, the user is asked to verify
email while they're logged in, but just in case they logout without
verifying, the next login would force them to verify their email in the
email/password flow.
However, for Social/SSO, they must verify before they logout or else
they'd have to contact support for assistance. I have not looked into
how to show verification screen while logging in via Social/SSO yet, but
if that's something critical for completeness here, I shall revisit it.
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
This PR fixes the filter on not empty phones.
We can end up with phone numbers that only have a country code, but it
makes no sense to consider this special edge case "not empty".
If we have no phone number, then it's empty, so this PR applies this
particular use case for NOT EMPTY filter on phone fields.
Fixes https://github.com/twentyhq/twenty/issues/15638
This PR fixes a crash of the app when a Snackbar tries to render an
object.
<img width="3002" height="754" alt="image"
src="https://github.com/user-attachments/assets/988f97eb-5c8a-44f8-ac8e-e2953b872bac"
/>
What happened : the backend sent a MessageDescriptor in
`extensions.userFriendlyMessage` while it should have sent a translated
string.
But it is a problem that the Snackbar can end up rendering objects and
crashing the whole app because it is a the highest level in the tree.
So this PR hardens many points to avoid future bugs :
- Sanitize what is rendered by the Snackbar to avoid any crash
- Translate any MessageDescriptor object that could end up in the
frontend
- Fix the places in the backend where a MessageDescriptor wasn't
translated and sent directly to the frontend in
`use-graphql-error-handler.hook.ts`
Fixes https://github.com/twentyhq/twenty/issues/15685
Fixes https://github.com/twentyhq/core-team-issues/issues/1869
Fixes#12090
### Summary
Fixes a UI layout shift issue in table rows in Tasks page, where bottom
borders appeared inconsistently across rows.
The problem occurred because `shouldDisplayBorderBottom` conditionally
applied the bottom border, which caused slight height differences
between rows and visual "jumping" when rendering focused styling.
### Changes Made
- Ensured consistent `border-bottom` rendering
- Removed conditional rendering prop to always display bottom border
### Steps to Reproduce (Before Fix)
1. Navigate to - /objects/tasks?viewId=xxxx
2. Interact with row elements
3. Notice a slight layout shift when first row is active/focused
### Before
https://github.com/user-attachments/assets/ab95db5d-0922-4460-a086-a03f58375824
### After
https://github.com/user-attachments/assets/a6b4d50c-b6f0-456b-90d4-fc3f0cde42bc
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This PR improves Kanban performance to attain the stock library speed.
There are still some performance issues at load time and drop, but they
are harder to address.
Before, everything is re-rendering at every card move, full re-render at
drop :
https://github.com/user-attachments/assets/7a1cf419-c8d8-4d56-a1a3-4269dce7b5a9
After, only the columns and card outer containers get re-rendered for
d&d, only two columns re-render at drop :
https://github.com/user-attachments/assets/d2dea914-5cc3-4693-9c2d-c7c789ae3452
We can still improve performance and re-render but that would represent
a global effort on D&D on general, table has the same problem currently,
but since we implemented virtualization it is bearable for now.
Issue -
- tooltip gets too long when groupby has too much data points
- we need max height on tooltips
- we need to be able to scroll inside the tooltip
- not possible if the tooltip is following the cursor (which library
enforces)
- its not easy to customize Nivo's own tooltip to make it work how we
want it
what we want -
- let the tooltip anchor to the bar in such a way -- that the top of the
bar aligns with the vertical middle of the tooltip
- add max height to the tooltip
what I did -
- use floating portal from floating-ui/react
- get the hover datum (ie hovered bars) dimensions on mouse enter to
render the tooltip
- anchor the tooltip at the calculated position
- floating-ui handles the basic things like flipping/offset/shift
- clear the states as required
---------
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Fixes#15508
Addresses the issue where the "Export View" functionality was not
exporting all records from a view. Users reported that only a subset of
records were exported, even when the view contained more. This led to
incomplete data exports.
The core of the problem was found within the `useLazyFetchAllRecords`
hook. The `fetchMoreRecordsLazy` function, which is responsible for
fetching subsequent pages of data during the export process, was
inadvertently using a default page limit (`DEFAULT_SEARCH_REQUEST_LIMIT`
of 60 records) instead of the intended `pageSize` (200 records). This
discrepancy caused the export process to prematurely stop fetching
records.
The PR modifies
`packages/twenty-front/src/modules/object-record/hooks/useLazyFetchAllRecords.ts`
to explicitly pass the `limit` parameter (which correctly reflects the
`pageSize`) to the `fetchMoreRecordsLazy` function. This ensures that
all subsequent data fetches during an export operation adhere to the
configured page size, allowing for the complete retrieval of all
records.