Commit Graph

31 Commits

Author SHA1 Message Date
Charles Bochet c53a13417e Remove all styled(Component) patterns in favor of parent wrappers and props (#18430)
## Summary

Eliminates all ~350 `styled(Component)` usages across `twenty-front` and
`twenty-ui` (212 files changed). Each was replaced following these
rules:

- **Margin/layout CSS** (margin, padding, flex, align-self, width) →
wrapped in a `styled.div`/`styled.span` parent container
- **Third-party components** (Link, TextareaAutosize,
ReactPhoneNumberInput, Handle, etc.) → parent container with child CSS
selectors (`> a`, `> textarea`, `> input`, etc.)
- **Intrinsic behavior via existing props** (TableRow
`gridTemplateColumns`, TableCell `color`/`align`) → replaced
`styled(TableRow)` / `styled(TableCell)` with direct prop usage
- **Other visual overrides on twenty-ui components** (Card, Section,
TabList, Button, MenuItem, ScrollWrapper, etc.) → parent wrappers with
`> div` / `> *` child selectors
- **Extending styled.div/span** → merged all CSS into a single
`styled.div`/`styled.span`

Also adds `overflow: hidden` to parent containers wrapping
`ScrollWrapper` so scroll activates correctly with the new wrapper
structure.

### Migration patterns

| Before | After |
|--------|-------|
| `styled(Avatar)` with `margin-right` | `<StyledAvatarContainer><Avatar
/></StyledAvatarContainer>` |
| `styled(Link)` with `text-decoration: none` |
`<StyledLinkContainer><Link /></StyledLinkContainer>` with `> a { ... }`
|
| `styled(TableRow)` with `grid-template-columns` | `<TableRow
gridTemplateColumns="..." />` |
| `styled(TableCell)` with `color` / `align` | `<TableCell color={...}
align="right" />` |
| `styled(Card)` with `margin-top` | `<StyledCardContainer><Card
/></StyledCardContainer>` |
| `styled(TabList)` with `background` |
`<StyledTabListContainer><TabList /></StyledTabListContainer>` with `>
div { ... }` |
| `styled(StyledBase)` extending a `styled.div` | Single merged
`styled.div` with all styles inlined |
2026-03-05 18:16:25 +01:00
nitin 5853891b02 refactor!: rename Command Menu page/navigation layer to Side Panel (#18393) 2026-03-05 15:46:31 +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
Raphaël Bosi 7da8450075 [FRONT COMPONENTS] Headless components (#18096)
## Description

- Add `isHeadless` field to `FrontComponent` entity so front components
can run without rendering UI in the command menu
- Introduce headless front component mounting logic:
`HeadlessFrontComponentMountRoot` at the application root,
`useMountHeadlessFrontComponent`, and `useUnmountHeadlessFrontComponent`
hooks to mount/unmount headless components
- Expand the SDK with new action components (`Action`, `ActionLink`,
`ActionOpenSidePanelPage`) and host communication functions
(`openSidePanelPage`, `unmountFrontComponent`)
- Move `CommandMenuPages` type to twenty-shared so the SDK can reference
it for side panel navigation

## Video QA



https://github.com/user-attachments/assets/4f9e3bb1-fcd1-42be-b3f4-a97e80c2add2
2026-02-20 14:14:42 +00:00
Charles Bochet 1a13258302 Keep migrating to jotai (#18064)
And we continue!
2026-02-19 12:10:02 +01:00
PandaMan befbcef824 fix: prevent tab synchronization between different records (#17559)
Fixes issue where tabs were synchronized when opening two records of the
same type in show page and side panel.

The root cause was that tab instance IDs were only based on
`pageLayoutId`, causing all records using the same page layout to share
the same tab state.

This change includes the record ID in the tab instance ID, making tabs
unique per record while maintaining backward compatibility for cases
where no record ID is available.

Fixes #17522

---------

Co-authored-by: Eruis <github@eruis.example>
2026-02-13 13:04:07 +00: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
Baptiste Devessier 617f634b6d Use backend types in Record Page Layouts' frontend (#17794) 2026-02-09 14:36:02 +00:00
Copilot 28763428f6 Differentiate edit mode behavior for record pages vs dashboards (#17550)
Temporary disable tabs edition for record page layouts


https://github.com/user-attachments/assets/c1f5d7fd-f125-4fb0-bf9c-96fadec14cd2

---------

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: Baptiste Devessier <baptiste@devessier.fr>
2026-02-02 13:28:51 +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
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
Baptiste Devessier 51c0a8dd86 [Page layouts] Sync tabs with URL hash (#16341)
There are three identified scenarios.

## First scenario

The user uses a desktop. They click on a company, which is opened in the
side panel. They click on a tab, like "Tasks". The user chooses to open
the record in fullscreen. The "Tasks" tab is opened by default in the
fullscreen record page.

If the user refreshes the page, the default tab is shown: "Timeline".
**This was the behavior on the show pages, and I kept it.** If we wanted
to show the "Tasks" tab here, we would have to put the id of the "Tasks"
tab in the URL hash when we open the record in fullscreen and an active
tab id is already set.


https://github.com/user-attachments/assets/205776ac-9ad7-4e79-af9a-6b44360a5d77

## Second scenario

The user uses a desktop or a mobile device and interacts with a company
record. They click on a tab. If they refresh, the selected tab will be
displayed by default.

### Desktop


https://github.com/user-attachments/assets/d4e1908b-a591-42f6-bb82-5aec592e2778

### Mobile


https://github.com/user-attachments/assets/7bab73cc-a2ff-4ce8-9bc6-325efef2d500

## Third scenario

The user uses a desktop. They click on a dashboard, which is opened in
fullscreen mode by default. They select tabs. If they refresh, the last
selected tab is opened by default.

When the user turns edit mode on, **the url hash is removed**. The last
selected tab remains selected. If the user selects another tab, the url
hash isn't set, but the newly selected tab is displayed correctly. If
the user saves their changes, the selected tab remains selected.
However, if they refresh the page, the default tab replaces the
previously selected tab.

Not setting the url hash when editing a page layout simplifies the code.
I'm okay with this tradeoff as the feature is primarily meant to let
users share specific tabs of their records.

**This behavior will also be used for record page layout once edit mode
is supported.**


https://github.com/user-attachments/assets/869657a1-6895-4ade-816c-972c77aaab9e

Closes https://github.com/twentyhq/core-team-issues/issues/1784
2025-12-08 10:47:46 +01:00
Baptiste Devessier 2466d81ace [Record Page Layouts] Pin record header (#16339)
https://github.com/user-attachments/assets/df988bf2-d2d6-49a4-b062-bc7ec49e1fad

Closes https://github.com/twentyhq/core-team-issues/issues/1806
2025-12-05 16:10:08 +01:00
Raphaël Bosi 2b80d9e015 [DASHBOARDS] Widget duplication (#15979)
## Widget duplication

- Created a shared component for the option menu shared by the record
page, the dashboards and the workflows
- Fix arrow selection in the option menu in workflows, which wasn't
working
- Scroll to the new widget after creation and open the new widget
settings


https://github.com/user-attachments/assets/47b8dade-44bd-4ba2-a81c-01b09e8718d3
2025-11-24 14:38:18 +01:00
Baptiste Devessier 3e187b4ce3 [Page Layouts] Focus specific tabs on mobile and side panel (#15984)
## On mobile


https://github.com/user-attachments/assets/4a8017b2-56a9-4759-bc18-8d99fed9f80a

## In side panel


https://github.com/user-attachments/assets/f36f0827-699a-4f76-8e84-a8ceaeb71396

## Dashboards

Untouched, but keep working.


https://github.com/user-attachments/assets/fe583f80-ea8d-453e-95ea-6b99175d1899

## Other Record Page Layouts

Untouched, but keep working.


https://github.com/user-attachments/assets/c64f731f-5d28-4585-8cb5-c3940fb1ab6f
2025-11-21 13:39:17 +01:00
Raphaël Bosi 5b52ac992e [DASHBOARDS] Tab duplication (#15906)
Closes https://github.com/twentyhq/core-team-issues/issues/1878

- Allow tab duplication
- Add autofocus on the tiltle input upon the creation and duplication of
tabs and the creation of widgets
- Fix a bug where cancelling the edition while on a new tab, by
resetting the active tab id if it's not in the persisted tabs


https://github.com/user-attachments/assets/a4dc2f0b-1a56-406a-bde7-cca17f9cdbc1
2025-11-19 10:57:37 +01:00
Baptiste Devessier e0d1f74648 Page layout conditional display (#15802)
- 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
2025-11-18 10:35:38 +01:00
Baptiste Devessier bfe1f47065 Create old fields design widget (#15645)
In this PR:

- Pass `layoutMode` and `tabId` via PageLayoutContentContext provider
- Getting `pageLayoutType` from the current page layout
- Getting isInPinnedTab through `useIsInPinnedTab` hook

## Before

<img width="3456" height="2160" alt="CleanShot 2025-11-06 at 14 22
44@2x"
src="https://github.com/user-attachments/assets/763bb413-5739-45ef-85ed-82a72415886f"
/>

## After

<img width="3456" height="2162" alt="CleanShot 2025-11-06 at 14 20
38@2x"
src="https://github.com/user-attachments/assets/eee6cccd-9d36-426e-a22f-400e8f7f9413"
/>

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-11-06 18:16:42 +01:00
nitin 369b1b19af Tabs sidepanel settings on dashboards (#15454)
question -- should we have confirmation modal on delete -- if yes --
should it appear on save -- or on delete?
figma -
https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=79469-817497&t=5gFTRiI5f8nPyzc6-0
Note - Figma has a new side panel header, which is unavailable for now,
so I used the existing one

In a follow-up, we could even have icons for tabs -- would be cool --
and an icon picker for the icons?

followups which could be addressed in separate PRs - 
- ~~tabs being edited state (select state?) -- this would have blue
border around tab~~ done
- add icons on tabs -- need to update the entity to add icon -- and set
a default (dashboard) -- at least
- icon picker in the sidepanel header

closes -
https://discord.com/channels/1130383047699738754/1430204556884836532

video QA





https://github.com/user-attachments/assets/a4ad4b93-911d-46ce-9b68-347fd48fe933
2025-10-30 18:45:31 +05:30
Baptiste Devessier b9bdb12b3c Hide page layout tab bar when there are less than two tabs to display (#15385)
- DIsplay the tab bar only if more than 1 tabs can be displayed
- Remove the selfDisplayMode feature: the first tab is now considered to
be the pinned tab on record pages


https://github.com/user-attachments/assets/3f61931d-fde4-4065-b646-5b3a7bdebcc0
2025-10-28 17:20:32 +05:30
nitin 06a195af41 [Dashboards] - Tabs reordering (#14798)
closes https://github.com/twentyhq/core-team-issues/issues/1544

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-23 17:28:00 +00:00
Baptiste Devessier c6addc2bb4 Implement basic edition for record page layouts (#15237)
1. Add actions for record page layouts to side panel
2. Support drag-and-drop of widgets


https://github.com/user-attachments/assets/c4cef7c5-08a8-4999-b8a5-e1831cc66709
2025-10-22 10:48:09 +02:00
Baptiste Devessier e58668003d Support side panel in record page layout (#15216)
## Demo


https://github.com/user-attachments/assets/078b67d3-52d7-4ddf-a65a-fb002f82cfdd

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

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-21 13:01:43 +02:00
Copilot b4bf0be089 Fix duplicate height properties in styled components (#15200)
## Overview

This PR fixes instances where `height` properties were incorrectly
defined multiple times within the same styled component definitions
across the codebase.

## Problem

Several styled components had duplicate `height` CSS properties where
the exact same property was declared twice in the same selector, causing
confusion and reducing code maintainability. The second declaration
would override the first, making the first one dead code.

## Changes

Fixed 4 files across the codebase:

### Duplicate `height: 100%` Removed
**StyledShowPageRightContainer** in twenty-front:
- `PageLayoutRendererContent.tsx` 
- `ShowPageSubContainer.tsx`
- `PageLayoutRecordPageRenderer.tsx`

Each had `height: 100%` declared twice in the same component definition
(strict duplicates).

### Duplicate `height` with Different Values Removed
**StyledCommandKey** in twenty-ui:
- `MenuItemHotKeys.tsx` - Had both `height: ${({ theme }) =>
theme.spacing(5)}` and `height: 18px` declared. Removed the first
declaration as the second was overriding it.

## Testing

-  ESLint checks pass
-  TypeScript compilation successful
-  CodeQL security analysis - no issues
-  Comprehensive codebase scan confirms no remaining strict height
duplicates

## Impact

- **Visual Changes**: None - purely cleanup refactoring
- **Performance**: No runtime impact
- **Breaking Changes**: None
- **Code Quality**: Improved CSS maintainability by removing dead code

---

**Total:** 4 files modified, 5 lines removed



Created from VS Code via the <a
href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github">GitHub
Pull Request</a> extension.

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



<details>

<summary>Original prompt</summary>

> Hi! It seems that a few styled components in the #codebase are defined
with duplicate `height: 100%`, like in
#file:PageLayoutRendererContent.tsx:27-36.
> 
> Your job is to find all the places where we defined `height: 100%`
incorrectly several times and to keep only one `height` property.


</details>

Created from VS Code via the [GitHub Pull
Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github)
extension.

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

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

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: Baptiste Devessier <baptiste@devessier.fr>
2025-10-20 16:33:55 +02:00
Baptiste Devessier f6d133f285 Scaffold all company cards as widgets (#15149)
This PR doesn't support mobile and side-panel modes. The fields tab will
be displayed in these cases.

## Demo


https://github.com/user-attachments/assets/0b2e699c-2b8e-4212-8c75-00d7d2e25237

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-18 22:26:27 +02:00
nitin 4b87908752 [Dashboards]- Addition of tabs (#14756) 2025-09-30 11:23:52 +02:00
nitin 09b9071182 [Dashboards] - Add scroll wrapper (#14561)
## Summary
  - Fixed nested scrolls and sticky tabs in dashboard view
  - Drag select now auto-scrolls via ScrollWrapper context

## Changes
- Wrapped content in ScrollWrapper which provides context ID
automatically
  - Removed overflow from grid container to fix nested scrolls
2025-09-17 15:39:22 +02:00
Raphaël Bosi 3b868c3d2a Page layout refactoring (#14535)
In this PR:
- Refactored the page layout renderer
- Converted all the states to component states
- Removed the page layout edition in settings

TODOs in next PRs:
- Fix bug with the drag selector not taking the scroll into account to
display the dragged area
- Readd the tab edition in edit mode
2025-09-16 15:42:51 +00:00