Commit Graph

67 Commits

Author SHA1 Message Date
Charles Bochet 647c32ff3e Deprecate runtime theme objects in favor of CSS variables (#18402)
## Summary

- **Eliminate `ICON_SIZES` / `ICON_STROKES` constants**: all icon
dimensions are now resolved at runtime via
`resolveThemeVariableAsNumber(themeCssVariables.icon.size.X)`, ensuring
values always come from computed CSS variables
- **No more consumer imports from `twenty-ui/theme`**: moved
`ColorSchemeContext`, `ColorSchemeProvider`, `ThemeColor`,
`MAIN_COLOR_NAMES`, `getNextThemeColor`, `AnimationDuration` to
`twenty-ui/theme-constants`
- **Remove `ThemeContext` / `ThemeContextProvider` / `ThemeProvider` /
`ThemeType`**: replaced across ~300 files with `themeCssVariables` (for
CSS contexts) or `resolveThemeVariable` / `resolveThemeVariableAsNumber`
(for JS runtime values)
- **Simplify provider chain**: only `ColorSchemeProvider` remains — it
toggles `light`/`dark` class on `document.documentElement` and provides
`colorScheme` via React context
- **Fix pre-existing test failures**: `useIcons.test.ts`
(non-configurable ES module spy) and
`turnRecordFilterGroupIntoGqlOperationFilter.test.ts`
(`Omit<RecordFilter, 'id'>` type mismatch)

### Theme access pattern (before → after)

| Context | Before | After |
|---------|--------|-------|
| CSS (Linaria) | `${({ theme }) => theme.font.color.primary}` |
`${themeCssVariables.font.color.primary}` |
| JS runtime (icon size, animation) | `theme.icon.size.md` /
`ICON_SIZES.md` |
`resolveThemeVariableAsNumber(themeCssVariables.icon.size.md)` |
| Color scheme check | `theme.name === 'dark'` |
`useContext(ColorSchemeContext).colorScheme === 'dark'` |
2026-03-05 14:39:01 +01:00
Charles Bochet c41a8e2b23 [DevXP] Simplify twenty-ui theme system: replace auto-generated files with static CSS variables (#18389)
## Summary

Now that Twenty has fully migrated from Emotion to Linaria, the theme
system has been simplified to remove unnecessary complexity that existed
only to support the old runtime injection pattern.

### What changed

- **Deleted** `generateThemeConstants.ts` script and the entire
`generated/` directory — no more auto-generation
- **Added** `theme-light.css` and `theme-dark.css`: static CSS files
with 991 custom properties each, scoped under `.light` and `.dark`
selectors respectively
- **Moved** `themeCssVariables.ts` out of `generated/` and hand-maintain
it as a static `as const` object of `var(--t-*)` references (Linaria can
statically evaluate these at build time)
- **Extracted** numeric constants (`MOBILE_VIEWPORT`, `ICON_SIZES`,
`ICON_STROKES`) into a new `constants.ts` — CSS variables can't be used
in media queries or as numeric icon size props
- **Simplified** `ThemeContextProvider`: removed
`ThemeCssVariableInjectorEffect` entirely; now uses a single
`useLayoutEffect` to toggle `.light`/`.dark` class on `<html>`
- **Added** `class="light"` to `index.html` as default to prevent FOUC
before React hydration

### Why

The previous setup maintained a dual system: JS theme objects
(`THEME_LIGHT`/`THEME_DARK`) used at runtime, plus a generation script
that produced CSS variable entry arrays, which were then injected into
the DOM by `ThemeCssVariableInjectorEffect`. With Linaria, theme values
only need to be CSS custom properties — the JS objects were redundant.
This PR removes ~250 lines of infrastructure while keeping the same
theming capabilities.
2026-03-04 23:30:25 +01:00
Charles Bochet 3bfdc2c83f chore(twenty-front): migrate command-menu, workflow, page-layout and UI modules from Emotion to Linaria (PR 4-6/10) (#18342)
## Summary

Continues the Emotion → Linaria migration (PR 4-6 from the [migration
plan](docs/emotion-to-linaria-migration-plan.md)). Migrates **311
files** across four module groups:

| Module | Files |
|---|---|
| command-menu | 53 |
| workflow | 84 |
| page-layout | 84 |
| UI (partial - first ~80 files) | ~80 |
| twenty-ui (TEXT_INPUT_STYLE) | 1 |
| misc (hooks, keyboard-shortcut-menu, file-upload) | ~9 |

### Migration patterns applied

- `import styled from '@emotion/styled'` → `import { styled } from
'@linaria/react'`
- `import { useTheme } from '@emotion/react'` → `import { useContext }
from 'react'` + `import { ThemeContext } from 'twenty-ui/theme'`
- `${({ theme }) => theme.X.Y.Z}` → `${themeCssVariables.X.Y.Z}` (static
CSS variables)
- `theme.spacing(N)` → `themeCssVariables.spacing[N]`
- `styled(motion.div)` → `motion.create(StyledBase)` (11 components)
- `styled(Component)<TypeParams>` → wrapper div approach for non-HTML
elements
- Multi-declaration interpolations split into one CSS property per
interpolation
- Interpolation return types fixed (`&&` → ternary `? : ''`)
- `TEXT_INPUT_STYLE` converted from function to static string constant
(backward compatible)
- Emotion `<Global>` replaced with `useEffect` style injection
- Complex runtime-dependent styles use CSS custom properties via
`style={}` prop

### After this PR

- **Remaining files**: ~400 (object-record: ~160, settings: ~200, UI:
~44)
- **No breaking changes**: CSS variables resolve identically to the
previous Emotion theme values
2026-03-03 16:42:03 +01:00
Charles Bochet bc4ae35bc4 Rework atom naming (#18240)
## Summary

- **Enhanced the `matching-state-variable` ESLint rule** to enforce
consistent naming for `ComponentState`, `FamilyState`, and
`ComponentFamilyState` hooks — previously it only covered `useAtomState`
and `useAtomStateValue`
- **The rule now checks 12 hooks** across three categories: value hooks
(`useAtomComponentStateValue`, `useAtomFamilyStateValue`, etc.), state
hooks (`useAtomComponentState`, `useAtomComponentFamilyState`), and
setter hooks (`useSetAtomState`, `useSetAtomComponentState`,
`useSetAtomFamilyState`, `useSetAtomComponentFamilyState`)
- **Fixed all 225 resulting lint violations** across 151 files, renaming
variables to match their state atom names (e.g. `currentViewId` →
`contextStoreCurrentViewId`, `selectedRecord` → `recordStore`). Cases
where the same state is accessed with different family keys/instance IDs
are suppressed with `eslint-disable-next-line`.

## Naming convention

| Hook | State argument | Valid | Invalid |
|------|---------------|-------|---------|
| `useAtomStateValue` | `fooState` | `const foo = ...` | `const bar =
...` |
| `useAtomComponentStateValue` | `fooComponentState` | `const foo = ...`
| `const bar = ...` |
| `useAtomFamilyStateValue` | `fooFamilyState` | `const foo = ...` |
`const bar = ...` |
| `useAtomComponentFamilyStateValue` | `fooComponentFamilyState` |
`const foo = ...` | `const bar = ...` |
| `useAtomState` | `fooState` | `const [foo, setFoo] = ...` | `const
[bar, setBar] = ...` |
| `useAtomComponentState` | `fooComponentState` | `const [foo, setFoo] =
...` | `const [bar, setBar] = ...` |
| `useAtomComponentFamilyState` | `fooComponentFamilyState` | `const
[foo, setFoo] = ...` | `const [bar, setBar] = ...` |
| `useSetAtomState` | `fooState` | `const setFoo = ...` | `const setBar
= ...` |
| `useSetAtomComponentState` | `fooComponentState` | `const setFoo =
...` | `const setBar = ...` |
| `useSetAtomFamilyState` | `fooFamilyState` | `const setFoo = ...` |
`const setBar = ...` |
| `useSetAtomComponentFamilyState` | `fooComponentFamilyState` | `const
setFoo = ...` | `const setBar = ...` |
2026-02-25 22:28:25 +01:00
Charles Bochet d81f006979 Rename Jotai state hooks and utilities to consistent Atom-based naming (#18209)
## Summary

Rename all Jotai-based state management hooks and utilities to a
consistent naming convention that:
1. Removes legacy Recoil naming (`useRecoilXxxV2`, `createXxxV2`)
2. Always includes `Atom` in the name to distinguish from jotai native
hooks
3. Follows a consistent ordering: `Atom` → `Component` → `Family` →
`Type` (`State`/`Selector`)
4. Includes the type qualifier (`State` or `Selector`) in all
value-reading hooks to avoid naming conflicts with jotai's native
`useAtomValue`

## Naming Convention

**Hooks**:
`use[Set]Atom[Component][Family][State|Selector][Value|State|CallbackState]`
**Utils**: `createAtom[Component][Family][State|Selector]`

## Changes

### Hooks renamed (definition files + all usages across ~1,500 files):

| Old Name (Recoil) | Intermediate (Atom) | Final Name |
|---|---|---|
| `useRecoilValueV2` | `useAtomValue` | **`useAtomStateValue`** |
| `useRecoilStateV2` | `useAtomState` | `useAtomState` |
| `useSetRecoilStateV2` | `useSetAtomState` | `useSetAtomState` |
| `useFamilyRecoilValueV2` | `useFamilyAtomValue` |
**`useAtomFamilyStateValue`** |
| `useSetFamilyRecoilStateV2` | `useSetFamilyAtomState` |
**`useSetAtomFamilyState`** |
| `useFamilySelectorValueV2` | `useFamilySelectorValue` |
**`useAtomFamilySelectorValue`** |
| `useFamilySelectorStateV2` | `useFamilySelectorState` |
**`useAtomFamilySelectorState`** |
| `useRecoilComponentValueV2` | `useAtomComponentValue` |
**`useAtomComponentStateValue`** |
| `useRecoilComponentStateV2` | `useAtomComponentState` |
`useAtomComponentState` |
| `useSetRecoilComponentStateV2` | `useSetAtomComponentState` |
`useSetAtomComponentState` |
| `useRecoilComponentFamilyValueV2` | `useAtomComponentFamilyValue` |
**`useAtomComponentFamilyStateValue`** |
| `useRecoilComponentFamilyStateV2` | `useAtomComponentFamilyState` |
`useAtomComponentFamilyState` |
| `useSetRecoilComponentFamilyStateV2` |
`useSetAtomComponentFamilyState` | `useSetAtomComponentFamilyState` |
| `useRecoilComponentSelectorValueV2` | `useAtomComponentSelectorValue`
| `useAtomComponentSelectorValue` |
| `useRecoilComponentFamilySelectorValueV2` |
`useAtomComponentFamilySelectorValue` |
`useAtomComponentFamilySelectorValue` |
| `useRecoilComponentStateCallbackStateV2` |
`useAtomComponentStateCallbackState` |
`useAtomComponentStateCallbackState` |
| `useRecoilComponentSelectorCallbackStateV2` |
`useAtomComponentSelectorCallbackState` |
`useAtomComponentSelectorCallbackState` |
| `useRecoilComponentFamilyStateCallbackStateV2` |
`useAtomComponentFamilyStateCallbackState` |
`useAtomComponentFamilyStateCallbackState` |
| `useRecoilComponentFamilySelectorCallbackStateV2` |
`useAtomComponentFamilySelectorCallbackState` |
`useAtomComponentFamilySelectorCallbackState` |

### Utilities renamed:

| Old Name (Recoil) | Final Name |
|---|---|
| `createStateV2` | **`createAtomState`** |
| `createFamilyStateV2` | **`createAtomFamilyState`** |
| `createSelectorV2` | **`createAtomSelector`** |
| `createFamilySelectorV2` | **`createAtomFamilySelector`** |
| `createWritableSelectorV2` | **`createAtomWritableSelector`** |
| `createWritableFamilySelectorV2` |
**`createAtomWritableFamilySelector`** |
| `createComponentStateV2` | **`createAtomComponentState`** |
| `createComponentFamilyStateV2` | **`createAtomComponentFamilyState`**
|
| `createComponentSelectorV2` | **`createAtomComponentSelector`** |
| `createComponentFamilySelectorV2` |
**`createAtomComponentFamilySelector`** |

## Details

- All definition files renamed to match new convention
- All import paths and usages updated across the entire `twenty-front`
package
- Jotai's native `useAtomValue` is aliased as `useJotaiAtomValue` in the
wrapper hook to avoid collision
- Legacy Recoil files in `state/utils/` and `component-state/utils/`
left untouched (separate naming scope)
- Typecheck passes cleanly
2026-02-25 01:55:46 +01:00
Charles Bochet 0f2af6a6cb Jotai 13 (#18178)
## Recoil to Jotai Migration — PR 13: Workflow, Page Layout, AI,
Activities, Settings, SSE & Remaining Modules

### Summary

This is the 13th PR in the [14-PR migration
series](packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md)
to replace Recoil with Jotai as the state management library in
`twenty-front`. This PR migrates the remaining consumer code across all
modules — updating ~1,076 files with ~10k insertions/deletions.

### What changed

**New Jotai infrastructure (13 new files):**
- `createComponentSelectorV2` and `createComponentFamilySelectorV2` —
instance-scoped derived atoms, replacing Recoil's component selectors
- 6 new hooks: `useRecoilComponentSelectorValueV2`,
`useRecoilComponentFamilySelectorValueV2`,
`useRecoilComponentSelectorCallbackStateV2`,
`useRecoilComponentFamilySelectorCallbackStateV2`,
`useRecoilComponentStateCallbackStateV2`,
`useRecoilComponentFamilyStateCallbackStateV2`
- New types: `ComponentSelectorV2`, `ComponentFamilySelectorV2`,
`SelectorCallbacksV2`
- Extended `buildGetHelper` to support the new selector patterns

**State definition migrations:**
- `createState()` → `createStateV2()` (Jotai `atom()`)
- `createFamilyState()` → `createFamilyStateV2()` (Jotai `atom()` with
family cache)
- Recoil `selector`/`selectorFamily` → Jotai derived `atom()` via V2
utilities

**Hook migrations (~2,400 removed, ~1,545 added V2 equivalents):**
- `useRecoilValue` → `useRecoilValueV2`
- `useRecoilState` → `useRecoilStateV2`
- `useSetRecoilState` → `useSetRecoilStateV2`
- `useRecoilCallback` → `useCallback` + `jotaiStore.get/set`
- `useRecoilComponentValue` → `useRecoilComponentValueV2`
- `useSetRecoilComponentState` → `useSetRecoilComponentStateV2`
- `useRecoilComponentFamilyValue` → `useRecoilComponentFamilyValueV2` /
`useFamilySelectorValueV2`
- ~397 direct `import from 'recoil'` removed

**Deleted files (9 files):**
- `dateLocaleState.ts` — consolidated
- `currentAIChatThreadTitleStateV2.ts` — renamed to replace the original
- `isCommandMenuOpenedState.ts` — removed
- `filesFieldUploadState.ts` — replaced by V2
- `recordStoreFamilySelector.ts`, `recordStoreFieldValueSelector.ts`,
`recordStoreRecordsSelector.ts` — replaced by V2 equivalents
- `settingsAllRolesSelector.ts` — replaced by `useSettingsAllRoles` hook
- `settingsRoleIdsStateV2.ts` — consolidated

**Modules migrated:**
- Workflow (diagram, steps, variables, filters)
- Page Layout (widgets, fields, graph, tabs)
- AI (chat, agents, browsing context)
- Activities (rich text editor, targets, timeline)
- Action Menu (single/multiple record actions)
- Command Menu (navigation, history, pages, workflow)
- Context Store
- Record Board, Record Table, Record Calendar, Record Index
- Record Field, Record Filter, Record Sort, Record Group
- Record Drag, Record Picker, Record Store
- Views (view bar, view picker, filters, sorts)
- Settings (roles, permissions, SSO, security, data model, developers,
domains)
- SSE (event streams, subscriptions)
- Spreadsheet Import
- Auth, Favorites, Front Components, Information Banner
2026-02-24 17:37:01 +01:00
Charles Bochet 1a13258302 Keep migrating to jotai (#18064)
And we continue!
2026-02-19 12:10:02 +01:00
neo773 84afbb4d2c feat: add draft email workflow action (#17793)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-02-13 18:20:20 +01:00
Charles Bochet b456f79167 Reduce leak between gql schema (#17878)
## Reduce type leakage between GraphQL schemas

### Why

Twenty runs two separate GraphQL schemas: **core** and **metadata**.
NestJS's `@nestjs/graphql` uses a global `TypeMetadataStorage` that
accumulates all decorated types across all modules. When each schema is
built, every registered type leaks into both schemas regardless of which
module it belongs to.

This means the core schema's generated TypeScript
(`generated/graphql.ts`) contained ~2,700 lines of types that only
belong to the metadata schema (and vice versa). This creates confusion
about type ownership, inflates generated code, and makes it harder to
reason about which API surface each schema actually exposes.

### How

**1. Patch `@nestjs/graphql` to support schema-scoped type resolution**

- **(Already done)** Added a `resolverSchemaScope` option to
`GqlModuleOptions`, allowing each schema to declare a scope (e.g.
`'metadata'`)
- `ResolversExplorerService` now filters resolvers by a
`RESOLVER_SCHEMA_SCOPE` metadata key, so each schema only sees its own
resolvers
- `GraphQLSchemaFactory` now performs a **reachability walk**
(`computeReachableTypes`) starting from scoped resolver return types and
arguments, only including types that are transitively referenced —
handling unions, interfaces, and prototype chains
- Type definition storage and orphaned reference registry are cleared
between schema builds to prevent cross-contamination

**2. Register `ClientConfig` as orphaned type in metadata schema**

Since `ClientConfig` is needed in the metadata schema but not directly
returned by a resolver, it's explicitly declared via
`buildSchemaOptions.orphanedTypes`.

**3. Regenerate frontend types and fix imports**

- `generated/graphql.ts` shrank by ~2,700 lines (types moved to where
they belong)
- `generated-metadata/graphql.ts` gained types like `ClientConfig` that
were previously missing
- ~500 frontend files updated to import from the correct generated file
2026-02-12 10:58:52 +01:00
Lakshay Manchanda 2d42b0a726 - Added accomodation of mobile navigation bar in command menu (#17419)
Fix for #16743 

Added styles to accommodate for mobile navigation bar for better
visibility and better access of the command menu buttons.

## CommandMenuAskAIPage.tsx (Not shown in the issue)
### Before
<img width="1100" height="1792" alt="screenshot-2026-01-24_17-22-45"
src="https://github.com/user-attachments/assets/8a337ffe-9dfc-4e52-b744-622cc6989ae4"
/>

### After 
Since the component here already had some padding on it the mobile
navigation bar offset was lesser than it is in other places.
<img width="1092" height="1778" alt="screenshot-2026-01-24_17-28-33"
src="https://github.com/user-attachments/assets/00a217f1-d98e-4303-bdc1-df112d71a553"
/>


## CommandMenuWorkflowEditStep.tsx
### Before
As mentioned in the issue

### After
<img width="755" height="1061" alt="image"
src="https://github.com/user-attachments/assets/487d0b20-f544-4e85-99d5-eb5add4b2cb4"
/>

## CommandMenuWorkflowRunViewStep.tsx
### Before
As shown in the issue

### After
<img width="1102" height="1784" alt="screenshot-2026-01-24_17-19-31"
src="https://github.com/user-attachments/assets/a95773e3-5f61-46d0-93cb-e678e087a42f"
/>

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-02-09 23:48:45 +01:00
Thomas Trompette f7adf1698e Improve API + set functions in state (#17560)
Prefetch functions and set these in state

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-01-30 09:24:07 +00:00
Thomas Trompette 3ad7a9ac94 App logic function as step (#17525)
- Add logic function as step
- Rename previous one as Code

<img width="494" height="573" alt="Capture d’écran 2026-01-28 à 16 15
29"
src="https://github.com/user-attachments/assets/82f7e720-20da-4926-b5bc-94a33cd1f53a"
/>
<img width="494" height="264" alt="Capture d’écran 2026-01-28 à 16 15
39"
src="https://github.com/user-attachments/assets/a0444ae9-8c50-418d-8c5f-68c4da08fce7"
/>
<img width="494" height="264" alt="Capture d’écran 2026-01-28 à 16 15
58"
src="https://github.com/user-attachments/assets/3025db09-4e59-421e-8dc5-f3a7a7326554"
/>

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-01-29 11:59:24 +01:00
Thomas Trompette f11442953c Remove if-else workflow node flag (#17370)
As title
2026-01-22 16:33:52 +00:00
Abdul Rahman e51cf607c9 Fix if-else node drag-to-create (#16944)
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2026-01-06 13:28:29 +01:00
Abdul Rahman dff5e3cd7b feat: Add If/Else node (#16833)
Closes [#1265](https://github.com/twentyhq/core-team-issues/issues/1265)
2026-01-01 14:05:56 +00:00
Félix Malfait 1088f7bbab feat: add lingui/no-unlocalized-strings ESLint rule and fix translations (#16610)
## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.

## Changes

### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files

### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components

## Status
🚧 **Work in Progress** - ~124 files remaining to fix

This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.

## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
2025-12-17 22:08:33 +01:00
Abdul Rahman 04b0a65e73 feat: fix Command Menu Side Panel Layout (#15883)
[Figma
Design](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=81380-344641&t=FpjWNOK2gZuDQQfr-0)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds a side panel layout for the Command Menu, routes modals into a
local container, updates the top bar and context chips, and standardizes
small button sizes.
> 
> - **Command Menu**:
> - **Side Panel Layout**: Introduces `CommandMenuSidePanelLayout` with
animated width, hosts `CommandMenuRouter`, and provides a modal
container via `ModalContainerContext`.
> - **Top Bar**: Redesign (`CommandMenuTopBar`) with back icon, optional
AI sparkles action, compact height
(`COMMAND_MENU_SEARCH_BAR_HEIGHT=40`), and updated placeholder.
> - **Context Chips**: Adds `CommandMenuLastContextChip` and
`CommandMenuRecordInfo`; extends `CommandMenuContextChip` with `page`
prop; updates `CommandMenuContextChipGroups` to render last chip as
record info when applicable.
> - **Container Simplification**: `CommandMenuContainer` simplified to
just provide contexts and `AgentChatProvider`.
> - **Modal System**:
> - Adds `ModalContainerContext` and updates `Modal` to portal into
provided container; `Modal.Backdrop` supports `isInContainer`.
> - Updates usages (e.g., `UserOrMetadataLoader`, `ActionModal`) to
align with new modal behavior.
> - **Page Integration**:
> - Replaces `PageBody` with `CommandMenuSidePanelLayout` in
`RecordShowPage` and `RecordIndexContainerGater`.
> - Removes global `CommandMenuRouter` from `DefaultLayout` (keeps
keyboard shortcuts).
> - **UI/Styling**:
> - Standardizes several buttons to `size="small"` (e.g., command
actions, open record, options, reply, workflow footer).
> - Adjusts `ShowPageSubContainer` styling when rendered inside command
menu.
>   - Storybook tests updated for new placeholder text.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
81fcaa145618a2fa1c3e11e2dc88fe832c51374c. 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: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
2025-11-21 16:12:41 +01:00
DipakHalkude 8369168abe feat: display specific action types in workflow side panel tooltips (#15013)
## What I've Implemented

I've added tooltips that show the original action type when you hover
over workflow step icons in the side panel. Now even if you rename an
action to something custom, you can still see what type of action it
actually is by hovering over the icon.

## The Problem This Solves

Before this change, once you renamed a workflow action (like changing
"Create Record" to "Add New Customer"), there was no way to tell what
the original action type was. This made it really confusing when
collaborating with others or when coming back to your own workflows
after some time - you couldn't tell if an action was a "Create Record"
or "Update Record" or something else.

## What Changed

Updated action type labels: Instead of showing just "Action" for all
record operations, the system now shows specific types like "Create
Record", "Update Record", "Delete Record", etc.

Added hover tooltips: When you hover over the action icons in the
workflow side panel, a tooltip appears showing the original action type,
even if you've renamed the step title.

## Before vs After

Before: All actions showed "Action" in the side panel, making it
impossible to distinguish between different types after renaming.

After: Each action shows its specific type in tooltips, so you always
know what you're working with.

This should make workflow management much clearer, especially when
multiple people are collaborating on the same workflow!

Resolves #14878

## Demo Video

See it in action:
https://www.loom.com/share/c0e0ec24e4524d0685b841e9ceb011d0?t=65&sid=dc05e58a-8869-4969-aa67-9772242fe697

---------

Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
2025-10-22 10:20:49 +00:00
Thomas Trompette bf3c3fc5a5 Workflow command menu fixes (#15234)
- Move trash button to command menu footer
<img width="132" height="102" alt="Capture d’écran 2025-10-21 à 18 12
19"
src="https://github.com/user-attachments/assets/ad6a9374-a28f-4498-b8f3-ca576981693c"
/>

- Add footer to triggers + on missing steps
- Catch step body errors so the user can still delete the step when an
error happens
<img width="529" height="419" alt="Capture d’écran 2025-10-21 à 18 13
17"
src="https://github.com/user-attachments/assets/0ac07511-f4ad-40c4-98f1-afb53c0f7a89"
/>
2025-10-22 10:17:06 +02:00
Thomas Trompette 91b54e7e68 Remove iterator feature flag (#15165)
As title
2025-10-17 09:19:39 +00:00
Thomas Trompette 0d0cce7bb1 Iterators design updates (#15146)
A few fixes :
- add a counter of iteration for nodes that have been run
<img width="601" height="538" alt="Capture d’écran 2025-10-16 à 17 50
07"
src="https://github.com/user-attachments/assets/de9d56ed-3057-4a67-b1d7-b421f644d11d"
/>

- if a node has fail, we should end the workflow even if a step is still
running
- fix position for add node button in iterator loop :
Before


https://github.com/user-attachments/assets/eafeda64-977f-415d-bab4-b7d0c44406b0

After


https://github.com/user-attachments/assets/6ff221a3-b287-45f6-977f-b127f9998692
2025-10-16 16:32:47 +00:00
Thomas Trompette 650a037f67 Prevent losing form data when workflow is not defined (#15055)
When updating an active version, we :
- create a draft
- update the draft

Issue is that after the creation, workflow is shortly null. So the
component is re-rendered and the form data are lost.
Removing the check on the null allow to keep the form data.
2025-10-13 17:25:08 +02:00
Baptiste Devessier 9b88cee1ce Visualize iterator sub step's output by iteration index (#14747)
## Demo


https://github.com/user-attachments/assets/27afff85-4f33-4e91-b28a-3dc868bb9ad7

## With several sub steps

> [!NOTE]
> Please note that as I created the component state, the iteration index
will be different for every step of a workflow run. We might have one
component state instance per iterator node. We can implement it in
another PR if we think it's the way to go.


https://github.com/user-attachments/assets/6b8cd06d-9051-474d-b438-dd6155cc3810
2025-09-29 15:40:42 +02:00
Thomas Trompette 358ab54690 Fix workflow menu icons (#14767)
As title
2025-09-29 12:00:05 +00:00
Thomas Trompette 2c34e1fb8a Allow bulk records for manual trigger (#14725)
https://github.com/user-attachments/assets/d6c565eb-9a29-4830-9396-5f979c8caa7b

- Added a new component for manual trigger (mostly duplicated from
previous one). Will remove the old one once all data are migrated
- Updated schema output so the current item of the iterator can be typed

Todo left:
- migrate old triggers
- add an util to search iterator output. Today current item fields will
be displayed as not found
- set new manual triggers for workflow runs
2025-09-26 14:17:40 +02:00
Raphaël Bosi 1fdfac4f0d Update menu items across the app (#14724)
Closes https://github.com/twentyhq/core-team-issues/issues/1568


https://github.com/user-attachments/assets/a0fe6538-8405-4b52-a6ce-7afa846ee5f3
2025-09-25 18:14:32 +02:00
Thomas Trompette 3fbc257c06 Make filters a regular step (#14586)
- Remove special design logic for filters and edge with filters
- Remove backend logic to delete filters along with parent steps
- Add filters to the node type picker



https://github.com/user-attachments/assets/5b6ceda4-d86f-48a3-8c93-37a885398a8d
2025-09-18 13:07:23 +00:00
Thomas Trompette 8b345c9fd2 Change workflow node type (#14570)
- Update step endpoint now supports the case where node type is updated
- Added a new page for edition that re-uses the existing select action
component

Additional work:
- moved submitForm function to workflow runner service



https://github.com/user-attachments/assets/1fcb0adc-fe17-41f9-b2ea-4f84318db860
2025-09-17 15:36:42 +00:00
Baptiste Devessier 60de57f469 Implement creating node before initial step + deleting loop edge (#14525)
https://github.com/user-attachments/assets/d4ef0fa1-89e0-4848-9a4d-93dee81254f7

## Remaining to handle in future PRs

- It would be easier for the frontend if the loop edge was stored in the
backend instead of being an edge case the frontend must handle
- Optimistic rendering when deleting loop edge
2025-09-16 13:50:56 +02:00
Thomas Trompette 3bfb5bbd86 Add duplicate step action (#14392)
https://github.com/user-attachments/assets/ac89440c-6f84-4d2a-b88a-da7ddfb181eb
2025-09-10 15:25:40 +00:00
Thomas Trompette 7094f0ee08 Remove is branch enabled feature flag (#14357)
As title
2025-09-09 10:07:20 +02:00
Baptiste Devessier d94e984a80 Render WorkflowActionMenuItems as a proper component (#14223) 2025-09-02 09:53:25 +02:00
Thomas Trompette bcd5cf7f25 Add first frontend version for iterators (#14213)
Add a base for testing iterator step in frontend. Loop next step ids
need to be added to an input since the loop handle does not exist on the
step yet.

Here is an example of run:
<img width="1271" height="653" alt="Capture d’écran 2025-09-01 à 16 21
39"
src="https://github.com/user-attachments/assets/06ecf981-be17-4d31-84f6-11c2cc98cb66"
/>

First create record in the loop name is `{{currentItem}} - first`, the
second is `{{currentItem}} - second`
<img width="276" height="326" alt="Capture d’écran 2025-09-01 à 16 22
25"
src="https://github.com/user-attachments/assets/0ae03b65-aeda-41a6-8693-c17711ec2102"
/>
2025-09-01 16:46:33 +00:00
P Harshith Rao 521ef5ea4c fix: workflow node classification (#13945)
closes #13391 
As the solution said to classify workflow nodes based on colors I have
updated them with the following colors and new categories to better
classify and identify different node types as told:

  - Trigger Colors: Data (palette blue), Other (palette purple)
- Action Colors: Data (text/tertiary), AI (palette pink), Flow (tag
green), Human Input (palette orange)
 
<img width="451" height="690" alt="image"
src="https://github.com/user-attachments/assets/8c000f22-8f25-4b1d-ac6f-f2639b1eff94"
/>

<img width="451" height="690" alt="image"
src="https://github.com/user-attachments/assets/15a11a98-3fec-4374-a5f7-d7caf2ae5334"
/>

<img width="480" height="690" alt="image"
src="https://github.com/user-attachments/assets/6382f538-be6d-4595-b967-e7911f7ad61f"
/>

<img width="899" height="761" alt="image"
src="https://github.com/user-attachments/assets/98c97cd4-54cd-4bee-91b5-dd7cd3b03a5e"
/>

---------

Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
2025-08-20 11:31:03 +02:00
Thomas Trompette 50c4c50169 Remove workflow context + clean action config (#13919)
- Remove create related version and runs from workflow action config
- Remove WorkflowStepContextProvider. Use states instead
2025-08-14 10:17:07 +00:00
Thomas Trompette 88735f6788 Stop propagating full workflow in components (#13900)
Workflow, workflow version and workflow runs should not be passed
through props or context. These are set in recoil component states and
that's where all hooks should look for these.

This PR stop propagating workflow with version through all components.
2025-08-13 16:02:13 +00:00
Thomas Trompette 954da6da9d Delete filter on branch deletion (#13834)
When the target is not in the direct next step ids of the source (step
or trigger), check if there is a filter between both. If yes, delete the
filter and remove it from source next step ids.

Also refactored the existing.
2025-08-12 12:30:08 +02:00
Félix Malfait 464a480043 Continue ESLINT9 Migration (#13795)
Might already fix #13793
2025-08-10 23:25:58 +02:00
Thomas Trompette 3e1c9295d9 Do not throw when no selected not found (#13727)
<img width="1299" height="713" alt="Capture d’écran 2025-08-07 à 15 25
23"
src="https://github.com/user-attachments/assets/b9c3c3d3-9521-442a-8493-54a06418ade0"
/>

Remove error from console. When deleting a step, we get the command menu
opened for a short time. We should simply return null until the command
menu gets closed

Fix
https://twenty-v7.sentry.io/issues/6795046867/?project=4507072563183616&referrer=github-open-pr-bot
2025-08-07 16:17:52 +02:00
Baptiste Devessier b8cd4491c2 Replace useOnSelectionChange with onClick listeners for steps opening (#13672)
This PR cleans a few parts of the workflow features and fixes a few
improper behaviors.

Here is a non-exaustive list of improved things:

- Clicking on an action will always reset the command menu's navigation
stack. Previously, the behavior wasn't unified between workflows,
workflow versions and workflow runs.
- Opening a step in the side panel is now down in the `onClick` event
handler put on individual Reactflow nodes. This makes interoperability
between filters (which are buttons on edges) and traditional actions.
- Simplified the code that automatically opens pending forms in the side
panel. This feature should now work more predictably.
- Splitted the `WorkflowDiagramEmptyTrigger` component into
`WorkflowDiagramEmptyTriggerEditable` and
`WorkflowDiagramEmptyTriggerReadonly`. This makes handling events easier
as the behavior isn't the same in both cases.
- Dropped all `useOnSelectionChange` hooks that were used to open steps
in the side panel upon user selection.
- Created a specific `WorkflowRunDiagramStepNode` instead of using
`WorkflowDiagramStepNodeReadonly` in `WorkflowRunDiagramCanvas`
- Deleted `useHandleWorkflowRunDiagramCanvasInit` as it was used to open
the initially selected step in the command menu, which is now handled in
an effect that works for all cases, including workflow run's state
refreshing.

Closes https://github.com/twentyhq/core-team-issues/issues/1227
Closes https://github.com/twentyhq/twenty/issues/11923
2025-08-06 19:00:48 +02:00
Lucas Bordeau de802b1447 Removed all v2 in naming of state management logic (#13675)
This PR removes any V2 naming in state management logic and some minor
utils and hooks.

It has a lot of changes but nearly all of them were made by the rename
functionality of vscode which is deterministic, so it shouldn't
introduce any regression.

QA has been made on this PR on the main features of the app without any
noticeable issue.

Also renamed some other v2 naming related items : 
- TextInputV2 => TextInput
- TextInput => SettingsTextInput
- ObjectFilterDropdownFilterSelectMenuItemV2 =>
ObjectFilterDropdownFilterSelectMenuItem
- useInitDraftValueV2 => useInitDraftValue
- useOpenRecordTableCellV2 => useOpenRecordTableCell
2025-08-06 12:09:15 +02:00
martmull 9a05673093 Implement branch front end (#13489)
First PR to support workflow branches on frontend
2025-08-04 14:11:46 +00:00
martmull 47386e92a3 22 branches 3 (#13181)
This PR does not produce any functional changes for our users. It
prepares the branches for workflows by:

- decommissioning `output` and `context` fields or `workflowRun` records
and use newly created `state` field from front-end and back-end
- use `stepStatus` computed by `back-end` in `front-end`
- add utils and types in `twenty-shared/workflow` (not completed, a
follow-up is scheduled
https://github.com/twentyhq/core-team-issues/issues/1211)
- add concurrency to `workflowQueue` message queue to avoid weird branch
execution when using forms in workflow branches
- add a WithLock decorator for better dev experience of
`CacheLockService.withLock` usage

Here is an example of such a workflow running (front branch display is
not yet done that's why it looks ugly) ->
https://discord.com/channels/1130383047699738754/1258024460238192691/1392897615171158098
2025-07-16 11:16:04 +02:00
Baptiste Devessier e8a2d71844 Scaffold filters creation and deletion (#12990)
When the feature flag is activated, we can now create filters and delete
them. This PR mainly updates how we generate workflow diagrams.


https://github.com/user-attachments/assets/1a4aef46-7c3c-45fa-953f-0bd1908b9be7
2025-07-02 17:01:44 +02:00
Abdul Rahman 65df511179 feat: Add AI Agent workflow action node (#12650)
https://github.com/user-attachments/assets/8593e488-cb00-4fd2-b903-5ba5766e0254

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
Co-authored-by: martmull <martmull@hotmail.fr>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
Co-authored-by: Joseph Chiang <josephj6802@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Guillim <guillim@users.noreply.github.com>
Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com>
Co-authored-by: Naifer <161821705+omarNaifer12@users.noreply.github.com>
Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: Ajay A Adsule <103304466+AjayAdsule@users.noreply.github.com>
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Marty <91310557+real-marty@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
2025-06-22 21:42:04 +02:00
Baptiste Devessier 1e0ee9421d Hide tab system for pending form nodes (#12769)
| | Before | After |
|--------|--------|--------|
| **Pending form** | ![CleanShot 2025-06-20 at 15 49
03@2x](https://github.com/user-attachments/assets/6cfac5d2-3db2-4dfa-844e-68bf2432c7ba)
| ![CleanShot 2025-06-20 at 15 41
01@2x](https://github.com/user-attachments/assets/720cbcb1-8468-418a-a2c0-09b00da76592)
|
| **Completed form** | ![CleanShot 2025-06-20 at 15 41
18@2x](https://github.com/user-attachments/assets/12b18de0-efe4-45d2-af2e-fc48d6911225)
| ![CleanShot 2025-06-20 at 15 49
13@2x](https://github.com/user-attachments/assets/0e35708e-a933-4127-8d61-d5d7f6c85802)
|
2025-06-20 18:35:25 +02:00
nitin 6f156a69b0 feat: new tab list (#12384)
closes #9904

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-06-05 20:44:21 +02:00
nitin 819b3c6c0d followup #12033 (#12150)
follow up #12033

in #12033, SettingsDataModelFieldRelationForm I changed the the use of
objectMetadataItems to activeObjectMetadataItems, which filtered out
system objects. The naming was one factor for this confusion
Renaming it everywhere to specify that they don't include system objects

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-05-21 11:45:33 +02:00
Baptiste Devessier 1543c900ae Migrate workflow states to component states (#11773)
- Migrated all workflow Recoil states to component states to isolate
each workflow visualizer instance. The use case of having two workflow
visualizers displayed at the same time appeared recently and will grow
in the near future.
- We chose to use the `recordId` as the value for the `instanceId` of
the component states. Currently, there are a few cases where two
workflows or two workflow runs are rendered at the same time. As a
consequence, relying on the `recordId` is enough for the moment.
- However, there is one case where it's necessary to have a component
state scoped to a workflow visualizer instance: the
`workflowVisualizerStatusState`. This component is tightly coupled to
the `<Reactflow />` component instance rendered in the workflow
visualizer, and it must be set to its default value when the component
first renders. I achieved that by using another component instance
context whose instanceId is an identifier returned by the `useId()` hook
in the Workflow Run Card component.
2025-05-05 10:58:11 +02:00
Baptiste Devessier cc211550ae Workflow runs in side panel (#11669)
Vidéo explicative : https://share.cleanshot.com/VsvWknlW

Closes https://github.com/twentyhq/core-team-issues/issues/810
Closes https://github.com/twentyhq/core-team-issues/issues/806

Known issues to fix later:

- https://github.com/twentyhq/core-team-issues/issues/879
2025-04-24 11:33:17 +02:00