Commit Graph

4016 Commits

Author SHA1 Message Date
Marie dd98146c99 [Fix] display current object label in morph relation picker after rename 2/2 (#17484)
Following https://github.com/twentyhq/twenty/pull/17209

The goal is to display the updated label. For
SingleRecordPickerMenuItemsWithSearch, we did it in two parts to avoid
the breaking change by calling `objectLabelSingular` after its addition
has been deployed to prod
<img width="410" height="295" alt="image"
src="https://github.com/user-attachments/assets/d6014391-b943-4fdb-a15f-e62717463d74"
/>
2026-01-27 16:26:14 +00:00
Charles Bochet bc7791871f Introduce webhook v2 (#17456)
Migrate webhook to v2 entity
2026-01-27 15:32:33 +00:00
nitin 27e5b9797b [Dashboards] Fix page layout navigation edit mode sync (#17478)
navigating away - 

before - 


https://github.com/user-attachments/assets/ffd4c592-cf6d-403c-8aa2-9f55692fac65


after - 


https://github.com/user-attachments/assets/fc298e20-21ac-4f2f-b52a-8ecfdfd4eb38

new dashbaord - 

before - 


https://github.com/user-attachments/assets/f9a2d7ab-6b5b-41c3-b993-33745ab61683


after -


https://github.com/user-attachments/assets/cad68c35-6dfa-4fa2-ab9b-890900be3444




empty page layout - 

before - 



https://github.com/user-attachments/assets/27a5b61b-cd0c-4786-a461-e28f1e348822



after - 


https://github.com/user-attachments/assets/213e5242-493b-45de-a622-cf1463bb6380
2026-01-27 15:25:41 +00:00
Baptiste Devessier 856dc8be56 Activate Record Page Layouts flag by default (#17467) 2026-01-27 13:30:38 +00:00
Weiko c36d112ab4 Add upgrade to org plan card to RLS (#17455)
## Context

<img width="564" height="270" alt="Screenshot 2026-01-26 at 18 19 39"
src="https://github.com/user-attachments/assets/9a6bdd69-2fa1-449b-9985-bfc7a401780e"
/>
2026-01-27 11:51:08 +00:00
Abdul Rahman 2a8f834377 Integrate NavigationMenuItem with feature flag support (#17268)
## Implement Navigation Menu Items Frontend

Implements the frontend for navigation menu items, the new system
replacing favorites.

### Changes
- Added GraphQL fragments and queries for navigation menu items
- Added hooks for managing navigation menu items (create, update,
delete, sorting, filtering)
- Updated components to use navigation menu items instead of favorites
- Added test coverage for utility functions

### Migration Note
The favorites and navigation menu item modules currently exist in
parallel. The favorites code will be removed once all data has been
migrated to navigation menu items.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Replaces Favorites with feature-flagged `NavigationMenuItem` across
frontend and backend, while keeping Favorites as fallback until
migration completes.
> 
> - UI: new `navigation-menu-item` components (folders, orphan items,
drag provider/droppable, icons, skeleton), dispatcher components to
switch from Favorites, and updated “Add to favorites” action to create
`NavigationMenuItem` when `IS_NAVIGATION_MENU_ITEM_ENABLED`
> - DnD: shared `validateAndExtractFolderId` and droppable id utils
moved to `ui/layout/draggable-list`; favorites DnD updated to use shared
utils
> - GraphQL (client): add fragments, queries, mutations, hooks
(create/update/delete/find), and generated types; added
`RecordIdentifier` and `targetRecordIdentifier` on `NavigationMenuItem`
> - Prefetch: new prefetch state/effect for navigation menu items; skip
favorites prefetch when flag enabled
> - Backend: add DTOs (`NavigationMenuItem`, `RecordIdentifier`),
resolver `targetRecordIdentifier` field, service logic to fetch record
identifiers with permission-aware access and image signing,
`getRecordImageIdentifier` util, entity relation to `view`, and
migration adding FK on `viewId`
> - Feature flags & seeding: add `IS_NAVIGATION_MENU_ITEM_ENABLED` to
enums, dev seeder enables it; standard app seeds workspace navigation
menu items instead of favorites when flag on
> - Tests: add unit tests for sorting/labels/folder id and related utils
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c99746f08b9f84fc8cec4fcc3a7d7afb8ea92db7. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
2026-01-27 10:42:27 +00:00
Mazen-Saeed 8931f4681a fix isCalendarFieldReadOnly function to check if calender field is re… (#17319)
This PR fixes the inconsistency between Calendar and Table views when
trying to edit createdAt date field.

Previously, calendar cards could be dragged even though the createdAt
field are UI read-only, resulting in the confusing and inconsistent
behavior compared to the Table view where the field is not editable.

The issue was caused by the drag logic checking only user permissions
and not the field’s UI read-only metadata. This change updates the
calendar drag logic to also check for isUIReadOnly, ensuring that
records are not draggable when the selected calendar date field is
read-only.

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2026-01-26 16:28:53 +00:00
Richman018 9c9c0a7595 fix: prevent record title reset on focus for notes and tasks (#17439)
Fixes issue where focusing the record title input would reset the title
to empty. This ensures the draft value is properly initialized from the
field value if it's undefined or empty when the input is focused.

Fixes #17437

---------

Co-authored-by: Daniel <daniel@example.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2026-01-26 15:35:06 +00:00
Arun 4a5ffcc9d2 [Fix] Bug with one to many update in table #16340 (#17416)
fixes #16340 

we are updating the cache partially to prevent the flow of the corrupted
fields into the cache

the fields get corrupted during the mutation process potentially
overriding the recoil cache as we are passing the `newRecordCache`
directly in `upsertRecordsInStore`, the newRecordCache is thin and hence
the recoil wipes out the fields that are undefined or empty



we prevent this by extracting the partial data ( only the data which is
being updated in the field ) and passing it to `upsertRecordsInStore`,
as it only touched the specific fields and updates the data, leaving the
other fields untouched.


https://github.com/user-attachments/assets/5256bef7-70c3-47b3-b2ce-dd02ee1a2de8

---------

Co-authored-by: Arun kumar <arunkumar@Aruns-MacBook-Air.local>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2026-01-26 13:27:56 +00:00
Raphaël Bosi 7e7a535af0 Add front component widget (#17440)
Modified the data model and introduced a seed to introduce front
components widgets.
2026-01-26 12:45:58 +00:00
Félix Malfait 161689be18 feat: fix junction toggle persistence and add type-safe documentation paths (#17421)
## Summary

- **Fix junction relation toggle not being saved**: The form schema
wasn't tracking the `settings` field, so changes to
`junctionTargetFieldId` weren't marked as dirty
- **Add type-safe documentation paths**: Generate TypeScript constants
from `base-structure.json` to prevent broken documentation links
- **Create many-to-many relations documentation**: Step-by-step guide
for building many-to-many relations using junction objects
- **Update `getDocumentationUrl`**: Now uses shared constants from
`twenty-shared` for base URL, default path, and supported languages

## Key Changes

### Junction Toggle Fix
- Added `settings` field to the form schema in
`SettingsDataModelFieldRelationForm.tsx`
- Fixed the toggle to properly merge settings when updating
`junctionTargetFieldId`

### Type-Safe Documentation Paths
- New constants in `twenty-shared/constants`:
- `DOCUMENTATION_PATHS` - All 161 documentation paths as typed constants
  - `DOCUMENTATION_SUPPORTED_LANGUAGES` - 14 supported languages
  - `DOCUMENTATION_BASE_URL` / `DOCUMENTATION_DEFAULT_PATH`
- Generator script: `yarn docs:generate-paths`
- CI integration: Added to `docs-i18n-pull.yaml` workflow

### Documentation
- New article:
`/user-guide/data-model/how-tos/create-many-to-many-relations`
- Updated `/user-guide/data-model/capabilities/relation-fields.mdx` with
Lab warning and link

## Test plan
- [ ] Verify junction toggle saves correctly when enabled/disabled
- [ ] Verify documentation link opens correct localized page
- [ ] Verify `yarn docs:generate-paths` regenerates paths correctly
2026-01-25 13:29:20 +01:00
Aman Raj 62845cac87 Fix the default value of search record if the filter is boolean type (#17297)
This fixes the #15896. For problem statement. Please refer to the shared
video mentioned in the issue.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Aligns filter defaults across UI by centralizing initial value
computation.
> 
> - Add boolean handling in `useGetInitialFilterValue` to return
`value/displayValue` of `'false'` when operand is `IS`
> - Update `WorkflowDropdownStepOutputItems` to use
`getInitialFilterValue` for initial `value` instead of an empty string,
based on field type and default operand
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3ce05fa31aec1c9e771cb1b3ff20ddea3fab304f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
2026-01-24 22:27:05 +00:00
Abdullah. fa0615d41e Migrate attachments to morph relations + fix morph join column filtering (#17381)
Closes [1744](https://github.com/twentyhq/core-team-issues/issues/1744).

This PR migrates attachments to morph relations behind a feature flag,
following the TimelineActivity pattern. it introduces the
`IS_ATTACHMENT_MIGRATED` flag, updates standard field metadata and
indexes to use morph relations, adds a workspace migration that renames
`attachment.*Id` columns to `target*Id` and converts the corresponding
field metadata to `MORPH_RELATION` with a shared `morphId`. On the
frontend, attachment read/write paths now switch to `target*Id` when the
flag is enabled.

It also fixes optimistic filtering for morph join columns. The metadata
API deduplicates morph fields, so attachments now expose a single target
field of type `MORPH_RELATION` plus a `morphRelations` array listing
each target object. Because only one `settings.joinColumnName` is
returned (e.g. `targetRocketId`), filters like `targetCompanyId` don’t
map to any field and the optimistic cache code throws.
`doesMorphRelationJoinColumnMatch` resolves this by computing all valid
join column names from `morphRelations` using
`computeMorphRelationFieldName` and comparing them to the filter key.
That makes filters like `targetCompanyId` resolvable even with a single
target field, so attachment uploads and list matching no longer crash.

<img width="477" height="474" alt="image"
src="https://github.com/user-attachments/assets/50e19418-3438-4d1e-9f1f-1bc1a03174a9"
/>

<br />
<br />

Today the metadata API returns one morph field called `target` and a
list of possible targets (`morphRelations`), but it does not tell us the
join column for each target. That’s why the Frontend had to compute join
column names.

If we want to fix this at the API level, there are two options:

- Add join column names to each target in `morphRelations` (e.g. company
→ `targetCompanyId`). This is additive and low‑risk.
- Return each target as its own field (`targetCompany`, `targetPerson`,
etc.) instead of a single target. This is a larger change because it
changes the shape of metadata and would require more UI updates.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Introduces morph relations for attachments behind
`IS_ATTACHMENT_MIGRATED`, aligning server schema/metadata and frontend
behavior.
> 
> - Adds `IS_ATTACHMENT_MIGRATED` flag (frontend/server) and
seeds/defaults; updates generated GraphQL enums
> - New workspace upgrade `1.17` command migrates data: renames
`attachment.*Id` → `target*Id` and converts related fields to
`MORPH_RELATION` with shared `morphId`
> - Updates standard field metadata and indexes to `target*` (attachment
+ related objects), dev seeds, snapshots, and workspace entity types
> - Frontend: switches attachment read/write filters via
`getActivityTargetObjectFieldIdName` using the feature flag; updates
hooks/components (`useAttachments`, `useUploadAttachmentFile`, editors);
expands `Attachment` type
> - Fixes optimistic cache filtering to recognize morph join columns in
`isRecordMatchingFilter` by computing valid join-column keys from
`morphRelations`
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f208fa23b1135af114f484f6d65041a816d6e718. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
2026-01-24 21:17:24 +00:00
Arun 84d140025a fix: refresh virtualized table when field metadata is updated ( #16388 ) (#17214)
Fixes #16388 

Now we have used the metadata fetching from network using
resetVirtualizationBecauseDataChanged(), as we navigate back to the
table after updating the field content. As the virtualised table uses
the "cache-first" strategy, it fails to give fresh data from the data
base

Please let me know i we need to add the loading state (and it's UI
requirements) while it fetches the cell content, as currently the cell
is empty until it's populated by fresh data




https://github.com/user-attachments/assets/901085ba-4337-4a5d-86c8-15ac84250e8f

---------

Co-authored-by: Arun kumar <arunkumar@Aruns-MacBook-Air.local>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2026-01-24 18:56:00 +00:00
nitin 8255c51910 [Dashboards] Improve bar chart performance (#17399) 2026-01-23 13:03:36 +00:00
martmull 0091ef5f6c Sync built files (#17379)
as title, upload built files to local storage

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-01-23 13:43:53 +01:00
Raphaël Bosi 30620c79fd Add footer to iFrame widget settings (#17397)
Add footer to iFrame widget settings
2026-01-23 11:17:46 +00:00
nitin c02227472e Fix dashboard animations (#17389)
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
2026-01-23 10:36:13 +00:00
Raphaël Bosi 78dd43dbb0 Release dashboards V1 (#17386)
Remove `IS_PAGE_LAYOUT_ENABLED` feature flag entirely
2026-01-23 10:30:05 +00:00
Raphaël Bosi c76ddfd6d1 [DASHBOARDS] Fix pie chart gap when there is only one slice (#17388)
## Before
<img width="1290" height="764" alt="CleanShot 2026-01-23 at 11 00 44@2x"
src="https://github.com/user-attachments/assets/f276f42b-7f2d-4c2a-98cf-cb693cda1242"
/>

## After
<img width="1292" height="762" alt="CleanShot 2026-01-23 at 11 00 28@2x"
src="https://github.com/user-attachments/assets/a2d3df70-7965-4d4d-851b-729199a52510"
/>
2026-01-23 10:11:26 +00:00
Félix Malfait 41dd9856e6 fix(twenty-front): fix tsconfig to properly typecheck all files with tsgo (#17380)
## Summary

This PR fixes the `tsconfig` setup in `twenty-front` so that `tsgo -p
tsconfig.json` properly type-checks all files.

### Root Cause

The previous setup used TypeScript project references with `files: []`
in the main `tsconfig.json`. When running `tsgo -p tsconfig.json`, this
checks nothing because `tsgo` requires the `-b` (build) flag for project
references, but the configs weren't set up for composite mode.

### Changes

**Simplified tsconfig architecture (4 files → 2):**
- `tsconfig.json` - All files (dev, tests, stories) for
typecheck/IDE/lint
- `tsconfig.build.json` - Production files only (excludes tests/stories)

**Removed redundant configs:**
- `tsconfig.dev.json`
- `tsconfig.spec.json` 
- `tsconfig.storybook.json`

**Updated references:**
- `jest.config.mjs` → uses `tsconfig.json`
- `eslint.config.mjs` → uses `tsconfig.json`
- `vite.config.ts` → uses `tsconfig.json` for dev

**Type fixes (pre-existing errors revealed by proper typechecking):**
- Made `applicationId` optional in `FieldMetadataItem` and
`ObjectMetadataItem`
- Added missing `navigationMenuItem` translation
- Added `objectLabelSingular` to Search GraphQL query
- Fixed `sortMorphItems.test.ts` mock data

## Test plan

- [ ] Run `npx nx typecheck twenty-front` - should pass
- [ ] Run `npx nx lint twenty-front` - should work
- [ ] Run `npx nx test twenty-front` - should work
- [ ] Run `npx nx build twenty-front` - should work
- [ ] Verify IDE type checking works correctly
2026-01-23 11:22:23 +01:00
nitin cb9fe604e4 [Dashboards] Fix rich text widget empty side menu bug (#17377)
before - 



https://github.com/user-attachments/assets/d0f9efc2-a94a-41b5-8e46-c4c92b96c1da




after - 


https://github.com/user-attachments/assets/48018856-5166-490d-93d5-03abefa0eeb3
2026-01-23 09:26:45 +00:00
Baptiste Devessier 183cf6c803 Shrink table rows (#17360)
We often use the `fr` unit in a CSS grid thinking that it will force the
max-width of the grid's items. However, [`1fr` is always equal to
`minmax(auto, 1fr)`](https://stackoverflow.com/a/52861514). The
consequences are:

- By default, the width of the element will be `1fr`.
- However, if the size of the element becomes wider than the resolved
dimension of `1fr`, the item will expand to match its `auto` width. This
can be easily encountered when an item has a really long text. The
`auto` size will be the dimension of the long text displayed on a single
line.

If you want your element not to overflow, you can use `minmax(0, 1fr)`
instead of `1fr`.

This is a known behavior in the community. CSS frameworks like Tailwind
CSS even made their utility classes setting grid columns use `minmax(0,
xfr)` by default.

<img width="1380" height="84" alt="CleanShot 2026-01-22 at 16 11 00@2x"
src="https://github.com/user-attachments/assets/ace3c862-90ef-4a47-9ad5-9ae8b6e0fe40"
/>

See:
https://www.bigbinary.com/blog/understanding-the-automatic-minimum-size-of-flex-items.

Another fix is to use `min-width: 0;` on items themselves, instead of
using `minmax(0, 1fr)` at the grid level. This would make it possible to
create a reusable `Td` component.

## Data model - fields

### Before
<img width="1120" height="1808" alt="CleanShot 2026-01-22 at 15 46
48@2x"
src="https://github.com/user-attachments/assets/893115f4-219d-4a75-b628-56315298d791"
/>

### After
<img width="1120" height="1808" alt="CleanShot 2026-01-22 at 15 47
00@2x"
src="https://github.com/user-attachments/assets/093ae231-ccae-4df5-8227-a662a3fae97e"
/>

## Data model - relations

### Before
<img width="1172" height="754" alt="CleanShot 2026-01-22 at 16 00 33@2x"
src="https://github.com/user-attachments/assets/3367b849-a4f4-471b-b73a-a80a95368640"
/>

### After
<img width="1146" height="770" alt="CleanShot 2026-01-22 at 16 00 44@2x"
src="https://github.com/user-attachments/assets/ff08556e-fc16-4984-bd4b-bb15f4dabf36"
/>

## Object level object field permission

### Before
<img width="1150" height="1902" alt="CleanShot 2026-01-22 at 15 50
16@2x"
src="https://github.com/user-attachments/assets/adce1ba1-042b-4892-9a0e-af254109d03c"
/>

### After
<img width="1150" height="1902" alt="CleanShot 2026-01-22 at 15 50
01@2x"
src="https://github.com/user-attachments/assets/55476711-2573-43ae-b1d6-b8695f4305ab"
/>
2026-01-22 19:42:09 +00:00
nitin fd43eeda41 [Dashboards] Update default bar chart config (#17375)
https://github.com/user-attachments/assets/f7f11048-f27b-4c81-8626-71351a278f2b
2026-01-22 17:46:22 +00:00
Raphaël Bosi 23798ddc6b Remove debounced chart resize (#17376)
Remove debounced chart resize
2026-01-22 17:40:22 +00:00
Baptiste Devessier 3ef789dab4 Fix blank page layout after navigation (#17340)
Until now, it wasn’t possible to navigate between page layouts.
Dashboards don’t contain links to other dashboards. With record page
layouts, however, a page layout can now contain a link to another page
layout. If the user opens a record in the side panel and then clicks a
link to another record, the current page layout is replaced with the
page layout for the clicked record.

In this scenario, the `PageLayoutRenderer` component is not remounted.
As a result, the `isInitialized` state was not reset to `false`, and the
corresponding initialization `useEffect` was not triggered again.

All page layout states are component states bound to
`PageLayoutComponentInstanceContext`. For example, after navigating to
another record, `pageLayoutPersistedComponentState` was actually
`undefined` because the context's `instanceId` had changed.

Replacing the `isInitialized` state with a component state fixes the bug
and is consistent with the existing pattern of using component states to
store everything related to page layouts.

## Before


https://github.com/user-attachments/assets/24217145-51e5-49ef-8180-de62a6acfe10

## After


https://github.com/user-attachments/assets/e0ffe107-8fae-4f3a-9016-72c85bc735f4
2026-01-22 16:52:56 +00:00
Thomas Trompette f11442953c Remove if-else workflow node flag (#17370)
As title
2026-01-22 16:33:52 +00:00
Marie 8868ef4ec3 Remove useUpdateOneRecordV2 (#17355)
Previously we introduced useUpdateOneRecordV2 to handle morph relations.
In this PR we are removing it to only use useUpdateOneRecord which has
been adapted not to requires objectMetadataNameSingular in its props.
2026-01-22 16:27:57 +00:00
nitin 873749981b [Dashboards] fix dragging widget z index (#17371) 2026-01-22 16:23:20 +00:00
Raphaël Bosi f5045d830e [DASHBOARDS] Improve bar chart performances (#17358)
This PR implements various performances improvements for bar charts.

## Before


https://github.com/user-attachments/assets/26e1d10a-582c-46c6-abc4-a094e0bf7417


## After


https://github.com/user-attachments/assets/fb3ebb8d-929d-4a67-8391-e5d5b22c9e11
2026-01-22 16:22:38 +00:00
Baptiste Devessier fb2c9e12bc Fix a bunch of bugs on Record Page Layouts (#17342)
## Display task target and note target relations


https://github.com/user-attachments/assets/b291cc38-33b9-45b8-b685-e5890824176e

## Drop summary card in right drawer

### Before

<img width="1362" height="2160" alt="CleanShot 2026-01-22 at 14 27
09@2x"
src="https://github.com/user-attachments/assets/79f45d8f-976b-4999-8f62-5cb4b87b3ef6"
/>

### After

<img width="1362" height="2160" alt="CleanShot 2026-01-22 at 14 26
43@2x"
src="https://github.com/user-attachments/assets/fb67ddbb-0585-48bd-8ef6-c0139bba4062"
/>

## Add a tooltip to the see all action


https://github.com/user-attachments/assets/ab05cfab-8a6c-4caf-bfe9-698c8b7904e6

## Rename the Fields tab to Home

### Before

<img width="544" height="354" alt="CleanShot 2026-01-22 at 14 28 26@2x"
src="https://github.com/user-attachments/assets/62f7faa2-3a18-40af-8cdf-3fb9f7c7708c"
/>

### After

<img width="544" height="354" alt="CleanShot 2026-01-22 at 14 28 06@2x"
src="https://github.com/user-attachments/assets/76c21ec9-3723-4933-b34f-88b6403cbad5"
/>
2026-01-22 16:17:10 +00:00
Baptiste Devessier 267691a403 Render missing context provider for layouts (#17349)
## Before

<img width="1656" height="892" alt="image"
src="https://github.com/user-attachments/assets/05c6ced5-ff13-4987-a978-bae5111da9d4"
/>


## After



https://github.com/user-attachments/assets/a3b34590-8006-4363-8fa4-b52fb099bbff
2026-01-22 16:17:04 +00:00
Thomas Trompette c008e874e5 Allow variables with dots and keys (#17361)
Fixes
https://github.com/twentyhq/private-issues/issues/410#issuecomment-3781085655

Currently, JSON keys with spaces like { "toto toto": 123 } are rejected
with "JSON keys cannot contain spaces" error. This is problematic for
HTTP requests and webhook triggers where users cannot control the
response structure.

We use Handlebars to eval variables, segment-literal bracket notation to
escape keys with special characters:
Normal: {{step.normalKey}}
With spaces: `{{step.[key with space]}}`

So we simply need to wrap segments with spaces with brackets.

This PR: 
- Create shared path utilities to wrap the variable segments when needed
- Use it in all variable generation places
- Remove the restrictions

This body is now supported:
<img width="609" height="457" alt="Capture d’écran 2026-01-22 à 16 10
13"
src="https://github.com/user-attachments/assets/e7653c0a-df1e-49af-9c9a-4b7d59a99726"
/>
2026-01-22 16:06:53 +00:00
Ayesha Waseem 669da1a87d fix: display current object name in morph relation picker after rename (#17209)
**Summary**

Issue #16963: When an object is renamed, the morph relation picker still
shows the old name.

**Root cause**

The picker used searchRecord.objectNameSingular from the GraphQL search
response, which can be stale after a rename.
The search record stores the object name at query time, not the current
metadata.

**Solution**

- Updated SingleRecordPickerMenuItem:
- Added useObjectMetadataItems to access current object metadata.
- Look up the current object metadata by morphItem.objectMetadataId.
- Use labelSingular (or nameSingular as fallback) instead of
searchRecord.objectNameSingular for display.
- Updated MultipleRecordPickerMenuItemContent:
- Use objectMetadataItem.labelSingular (already available as a prop)
instead of searchRecord.objectNameSingular.

---------

Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu>
2026-01-22 15:40:57 +00:00
nitin 1e9cd2f5c6 [Dashboards] Dynamic paddings (#17298)
closes https://github.com/twentyhq/core-team-issues/issues/2055


https://github.com/user-attachments/assets/7a19066f-e891-4dfa-92ee-b85690765e60
2026-01-22 15:38:02 +00:00
martmull a68e05682b Serverless built updates (#17351)
follow up of https://github.com/twentyhq/twenty/pull/17317
2026-01-22 15:04:29 +00:00
nitin 92ea329128 [Dashboards] Add delete widget option in widget settings footer (#17344) 2026-01-22 14:30:30 +00:00
Abdul Rahman 4ccdbf1150 fix: remove empty else-if branches and add numbering (#17315) 2026-01-22 14:30:11 +00:00
Charles Bochet 235fc44228 Fix tests (#17356)
A small fix
2026-01-22 15:35:39 +01:00
Raphaël Bosi d6f088f720 [DASHBOARDS] Hide pinned workflow actions when dashboard is in edit mode (#17341)
## Before


https://github.com/user-attachments/assets/85fe3929-eebf-4412-bdb6-e069e623838d


## After



https://github.com/user-attachments/assets/5fd6d3ea-d667-4c70-86c1-ace57d3aee2f
2026-01-22 13:22:44 +00:00
Raphaël Bosi 2cda1f5f08 [DASHBOARD] Allow hovering small pie slices that are smaller than the gap (#17330)
Fixes https://github.com/twentyhq/core-team-issues/issues/2059

## Video QA

### Example with normal padding


https://github.com/user-attachments/assets/aa0c610a-cdac-472e-afe7-b995ef3f32cd

### Example with bigger padding to better see the behavior


https://github.com/user-attachments/assets/ff15b1f1-d18a-4fae-858a-2388f2cd9fa2
2026-01-22 12:18:31 +00:00
Weiko 9ee2cd0fe3 Enable RLS in lab (#17328) 2026-01-22 12:27:11 +01:00
Weiko 670ff1583e Fix RLS entitlement check + fix role page with RLS on object without object-permission not being displayed (#17326) 2026-01-22 11:48:24 +01:00
Thomas Trompette 981956a636 Use new SSE setup for workflow runs (#17309)
- Add a specific subscriber for workflow run
- Workflow runs use apollo cache. Adding updateRecordFromCache on record
updates
2026-01-21 17:26:43 +00:00
bitloi a4f5197e45 fix: implement polymorphic relation detach when selecting 'No {Field}' (#17264)
Fixes #17253

When clicking 'No {Field}' in a polymorphic relation picker, the
relation was not being cleared. This commit implements the missing
detach logic:

- RecordDetailMorphRelationSectionDropdownManyToOne: Call onSubmit with
newValue: null when no item is selected
- MorphRelationManyToOneFieldInput: Call persistMorphManyToOne with
valueToPersist: null for detach case
- useMorphPersistManyToOne: Implement actual detach logic that sets all
morph relation ID fields to null via updateOneRecord

Also adds unit tests for the useMorphPersistManyToOne hook.
2026-01-21 15:45:14 +00:00
nitin 26a70c23e9 [Dashboards] fix line chart duplicate widget bleed by hashing series IDs (#17303)
closes last issue in the bug bucket -
https://github.com/twentyhq/core-team-issues/issues/2109

<img width="695" height="41" alt="CleanShot 2026-01-21 at 19 40 38"
src="https://github.com/user-attachments/assets/c6872e11-1e36-4b4e-ab06-9943c22cfab2"
/>


 ### Root cause - 
line chart series have stable IDs, so Apollo normalizes them across
duplicated widgets and X‑axis edits leak
 
 ### Fix - 
prefix series IDs with a deterministic hash of objectMetadataId +
configuration
 
 before - 
 


https://github.com/user-attachments/assets/538eded8-7036-415a-b707-a10007c3aa11


 


after - 


https://github.com/user-attachments/assets/cf9ca89a-e7c9-4106-b6cd-fbc072d9a233
2026-01-21 15:39:29 +00:00
Lucas Bordeau 2835935f11 Handled SSE create event (#17293)
This PR handles SSE create event.

It also fixes a regression on board, which was making it flash with
skeletons on any update / create.

This PR was a good test case to experience how each main components of
the app : table, board, calendar, reacts to a create event.

It is not straightforward for now, because each component handles
records with its own state management to turn those records into a
coherent display of rows or cards.

The requests for each component are also different : fetch more, group
by, multiple requests in parallel, so the cleanest way to handle
optimistic effect requires to create one small optimistic engine per
component tuned for its internal data logic.

For now we've decided to implement what's doable in a reasonable amount
of time and that includes not handling table with groups for now.

Creating a clean optimistic logic for each component will be done later.

# QA

## Importing 100 records via the API (could be a script, a workflow, an
AI calling tools, a CSV import, etc.)


https://github.com/user-attachments/assets/d38a3770-8b0a-4f83-8275-5e7d1be0a5c6

## Creating from different components and seeing the SSE event being
processed by other components


https://github.com/user-attachments/assets/106b2f49-19cb-4190-92b7-0653c8373366
2026-01-21 10:49:20 +00:00
Baptiste Devessier b56f45e871 Remove extra spacing for empty widgets (#17289)
See the _Account Owner_ field

## Before

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 10
11@2x"
src="https://github.com/user-attachments/assets/5b726009-e68c-492e-bb36-de42062db6bd"
/>

## After

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 09
44@2x"
src="https://github.com/user-attachments/assets/f421efa3-d0f7-4ec0-93c0-a6b1f5d0e36c"
/>

<img width="1148" height="2120" alt="CleanShot 2026-01-20 at 16 09
57@2x"
src="https://github.com/user-attachments/assets/636ed2f0-34d8-4bd5-a47c-aef2121d1cbc"
/>
2026-01-21 09:21:26 +00:00
Copilot 9b7953e7a3 Fix widget injection order: place relation widgets after FIELDS, Notes after relations (#17274)
> [!NOTE]
> This code is temporary. It will be dropped once Record Page Layouts
can be fully configured.

## Before



https://github.com/user-attachments/assets/3f6aed00-2fd5-47d4-bd74-002a68030627



## After

<img width="3456" height="2160" alt="CleanShot 2026-01-20 at 15 17
15@2x"
src="https://github.com/user-attachments/assets/2f9baf0a-e003-4cb6-a023-6e104b097df3"
/>

The `usePageLayoutWithRelationWidgets` hook was appending relation
widgets to the end of the widget list. This resulted in incorrect
ordering where relation widgets appeared after Notes and other widgets
instead of immediately following the FIELDS widget.

## Changes

- **Widget injection logic**: Modified `injectRelationWidgetsIntoLayout`
to find the first FIELDS widget and insert relation widgets immediately
after it, rather than appending to end
- **Notes positioning**: Extract and reposition NOTES widget to appear
after all relation widgets, maintaining correct semantic order: `FIELDS
→ Relations → Notes → Other widgets`
- **Fallback behavior**: When no FIELDS widget exists, append relation
widgets to end as before
- **Test coverage**: Added 6 test cases covering injection order, Notes
positioning, and edge cases (missing widgets, empty relations,
non-record pages)

## Example

Before:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, NOTES, GRAPH, Relation1, Relation2]
```

After:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, Relation1, Relation2, NOTES, GRAPH]
```

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `googlechromelabs.github.io`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (dns block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-headless-shell-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/twentyhq/twenty/settings/copilot/coding_agent)
(admins only)
>
> </details>

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

Start implementation

<analysis>
[Chronological Review: The conversation began with the user requesting
an update to the `usePageLayoutWithRelationWidgets` function to inject
relation widgets after the first FIELDS widget and to position a Note
widget after all relation widgets. The user then asked for a plan
without comments, followed by a request to start the implementation of
the plan.]

[Intent Mapping: 
1. "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
2. "We want the Note widget to be after all the relation widgets we
inject."
3. "Prepare a plan. Don't write any comment."
4. "Start implementation."]

[Technical Inventory: 
- Function: `usePageLayoutWithRelationWidgets`
- Context: This function is part of a page layout management system,
likely dealing with dynamic widget placement based on certain
conditions.]

[Code Archaeology: 
- File:
`/Users/devessier/Web/twenty/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts`
- Function Purpose: To manage the layout of widgets on a page,
specifically handling relation widgets and their placement relative to
other widgets.]

[Progress Assessment: 
- Completed: User has defined the requirements for widget placement.
- Pending: Implementation of the specified changes to the
`usePageLayoutWithRelationWidgets` function.]

[Context Validation: All necessary context for continuing the
implementation of the widget placement logic is captured, including the
specific order of widget injection.]

[Recent Commands Analysis: 
- Last Agent Commands: The user requested to "Start implementation."
- Tool Results Summary: No specific tool results were generated as the
conversation focused on planning and implementation requests.
- Pre-Summary State: The agent was actively preparing to implement the
changes to the `usePageLayoutWithRelationWidgets` function when the
token budget was exceeded.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout in the application.]

</analysis>

<summary>
1. Conversation Overview:
- Primary Objectives: 
- "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
- "We want the Note widget to be after all the relation widgets we
inject."
  - "Prepare a plan. Don't write any comment."
  - "Start implementation."
- Session Context: The user is focused on modifying the widget layout in
a page layout management system, specifically the order of relation and
Note widgets.
- User Intent Evolution: The user transitioned from defining
requirements to requesting a plan and then moving directly to
implementation.

2. Technical Foundation:
- Function: `usePageLayoutWithRelationWidgets`
- Purpose: To manage the layout of widgets on a page, particularly the
placement of relation widgets.

3. Codebase Status:
- File Name: `usePageLayoutWithRelationWidgets.ts`
- Purpose: To handle the layout of widgets dynamically based on
specified rules.
- Current State: Awaiting implementation of the user-defined widget
placement logic.
- Key Code Segments: The function signature is defined, but the
implementation details are yet to be added.

4. Problem Resolution:
- Issues Encountered: No specific technical problems were reported; the
focus was on planning and implementation.
- Solutions Implemented: None yet, as the implementation phase has just
begun.
- Debugging Context: No ongoing troubleshooting efforts were mentioned.
- Lessons Learned: The importance of clear widget placement requirements
was emphasized.

5. Progress Tracking:
- Completed Tasks: User has articulated the requirements for widget
placement.
- Partially Complete Work: Implementation of the specified changes is
pending.
- Validated Outcomes: No features have been confirmed working yet as
implementation has not started.

6. Active Work State:
- Current Focus: The user is preparing to implement the changes to the
`usePageLayoutWithRelationWidgets` function.
- Recent Context: The user has defined the order of widget placement and
is ready to start coding.
- Working Code: The function is currently defined but lacks the
implementation logic.
- Immediate Context: The user is focused on implementing the logic for
injecting relation widgets and positioning the Note widget.

7. Recent Operations:
- Last Agent Commands: "Start implementation."
- Tool Results Summary: No specific results were generated; the focus
was on user requests.
- Pre-Summary State: The agent was preparing to implement the changes to
the `usePageLayoutWithRelationWidgets` function.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout.

8. Continuation Plan:
- Pending Task 1: Implement the logic to inject relation widgets after
the first FIELDS widget.
- Pending Task 2: Ensure the Note widget is positioned after all
relation widgets...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

Created from [VS
Code](https://code.visualstudio.com/docs/copilot/copilot-coding-agent).

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
2026-01-21 08:38:08 +00:00
Félix Malfait 3ed67b825e feat: implement generic many-to-many junction relation support (#16820)
## Overview

This PR implements **generic many-to-many relation support** through
junction tables (also known as associative entities or join tables).
This replaces the need for hardcoded taskTarget/noteTarget logic and
provides a flexible foundation for modeling complex entity
relationships.

## Architecture

### Data Model

Many-to-many relationships are implemented using a **junction object
pattern**:

```
┌─────────┐       ┌──────────────────┐       ┌─────────┐
│  Pet    │──────>│   PetRocket      │<──────│ Rocket  │
│         │ 1:N   │  (junction)      │  N:1  │         │
│ rockets ├───────┤ pet    : Pet     ├───────┤         │
└─────────┘       │ rocket : Rocket  │       └─────────┘
                  └──────────────────┘
```

The junction object (PetRocket) has:
- A `MANY_TO_ONE` relation to **Pet** (the source)
- A `MANY_TO_ONE` relation to **Rocket** (the target)

The source object (Pet) has a `ONE_TO_MANY` relation pointing to the
junction, with **field settings** that specify which target field to
follow.

### Field Settings Schema

Junction configuration is stored in `FieldMetadataRelationSettings`:

```typescript
{
  relationType: "ONE_TO_MANY",
  // Points to the target field on the junction object
  junctionTargetFieldId?: string;      // For regular relations
  junctionTargetMorphId?: string;      // For polymorphic relations
}
```

**Two configuration modes:**
1. **`junctionTargetFieldId`** - References a specific `RELATION` field
on the junction
2. **`junctionTargetMorphId`** - References a `morphId` group for
polymorphic targets (e.g., link to Person OR Company)

### GraphQL Query Generation

When a junction relation is detected, the GraphQL fields are generated
to fetch the nested target:

```graphql
query GetPetWithRockets {
  pet(id: "...") {
    rockets {           # ONE_TO_MANY to junction
      id
      rocket {          # Target field on junction
        id
        name
        __typename
      }
    }
  }
}
```

For polymorphic junction targets:
```graphql
caretakerPerson { id, name }
caretakerCompany { id, name }
```

## Frontend Architecture

### Display Flow

1. **Detection**: `hasJunctionConfig()` checks if field has junction
settings
2. **Config Resolution**: `getJunctionConfig()` resolves junction object
metadata and target fields
3. **Record Extraction**: `extractTargetRecordsFromJunction()` extracts
target records from junction records
4. **Rendering**: Target records displayed as chips (not junction
records)

### Edit Flow

1. **Picker Opening**: Initializes the multi-record picker with:
   - Searchable object types (derived from junction target fields)
   - Pre-selected items (extracted from existing junction records)
   
2. **Selection Handling**: Manages create/delete of junction records:
   - **Select**: Creates new junction record with source + target IDs
   - **Deselect**: Finds and deletes the junction record
- **Optimistic Updates**: Manually updates Recoil store before API call

### Key Trade-offs

| Decision | Trade-off |
|----------|-----------|
| Junction records managed manually | More control over optimistic
updates, but requires manual cache management |
| Settings stored per-field | Flexible (same junction can power
different views), but requires UI to configure |
| Polymorphic via morphId groups | Supports N target types, but adds
query complexity |
| Feature flag gated | Safe rollout, but requires flag management |

## Backend Changes

- **Validation**: Junction target field must exist and be a valid
`MANY_TO_ONE` relation
- **Settings**: Extended `FieldMetadataRelationSettings` type with
junction fields
- **Dev Seeder**: Added sample junction objects (PetRocket,
EmploymentHistory, PetCareAgreement) for testing

## How to Test

1. Enable the `IS_JUNCTION_RELATIONS_ENABLED` feature flag
2. Create objects with junction pattern (Pet → PetRocket → Rocket)
3. Configure the junction target in field settings (advanced mode)
4. Verify:
- Display shows target objects (Rockets), not junction records
(PetRockets)
   - Picker allows selecting/deselecting targets
   - Changes persist correctly



https://github.com/user-attachments/assets/d04f057a-228c-4de8-af48-76bb2d72cac1

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-01-20 21:58:13 +01:00