- Automatically create and sync command menu items for all workflows
with a manual trigger
- Refactor `useCommandMenuItemsFromBackend`
- Prefill a _Quick Lead_ workflow command menu item during workspace
setup and dev seeding
- Add a ready prop to `HeadlessEngineCommandWrapperEffect` to prevent
premature execution when async data hasn't loaded yet
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
## Summary
- Renames `ObjectMetadataItem` to `EnrichedObjectMetadataItem` across
the entire frontend (~440 files) to clarify that this type includes
derived fields (`readableFields`, `updatableFields`, nested `fields[]`,
`indexMetadatas[]`) computed at read time from the metadata store
- Creates `splitObjectMetadataGqlResponse` that goes directly from a
GraphQL `ObjectMetadataItemsQuery` response to flat store items
(combining the old
`mapPaginatedObjectMetadataItemsToObjectMetadataItems` +
`splitObjectMetadataItemWithRelated` two-step flow into one call)
- Removes `ObjectMetadataItemWithRelated` type and all "WithRelated"
naming
- Renames `generatedMockObjectMetadataItems` to
`generateTestEnrichedObjectMetadataItemsMock` to make it clear this is
test-only enriched data
- Deletes `useLoadMockedObjectMetadataItems` hook (consolidated into
`useLoadMockedMinimalMetadata`)
- Ensures nothing destined for the metadata store computes
`readableFields`/`updatableFields` (preventing the localStorage bloat
from #18809)
## Type hierarchy (before → after)
**Before:**
```
ObjectMetadataItemsQuery → mapPaginated → ObjectMetadataItemWithRelated → enrich → ObjectMetadataItem
→ split → FlatObjectMetadataItem (store)
```
**After:**
```
ObjectMetadataItemsQuery → splitObjectMetadataGqlResponse → FlatObjectMetadataItem (store)
→ mapPaginated + enrich (tests only) → EnrichedObjectMetadataItem
```
## Test plan
- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx test twenty-front` passes (767 suites, 4505 tests)
- [x] `npx nx lint twenty-front` passes
- [ ] CI checks pass
Made with [Cursor](https://cursor.com)
- Fix duplication of tabs in dashboards that didn't open the duplicated
tab in the side panel: replaced the logic that used Math.round with an
algorithm that switches the positions of the elements. We will keep
relying on integers, but it will work well in all cases.
- Make dnd work with record page layouts, where there is a pinned tab
- Make tab movements work with pinned tab, too
- Allow user to set a tab as the pinned tab
- Make backend changes to be able to save tabs with `layoutMode`
https://github.com/user-attachments/assets/ce1130fa-71df-49ba-ba2f-6f971e15dd49
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Introduces a progress indicator for command menu items (both engine
commands and front components). When a command is running, the menu item
now displays a percentage alongside the loader spinner instead of just a
spinner.
- Added `commandMenuItemProgressFamilyState` to track per-item progress
and `CommandListItemLoader` to render it
- Exposed `updateProgress` in the twenty-sdk public API so front
components can report execution progress back to the host
- Wired progress reporting into `ExportMultipleRecordsCommand` as the
first consumer, showing CSV export progress
- Progress state is cleaned up on unmount for both engine commands and
headless front components
- Refactor
## Description
- Introduces a new engine command execution model that replaces the
previous approach of mapping `EngineComponentKey` to React components.
Instead, engine commands are now mounted headlessly via
`HeadlessEngineCommandMountRoot`, with their execution context populated
synchronously before mounting.
- Creates new headless command components
- Moves error handling from the SDK layer to the host app by wrapping
all mounted commands with a new `CommandMenuItemErrorBoundary`
The new flow works as follows:
- When a command menu item with an `engineComponentKey` is clicked,
`useCommandMenuItemFrontComponentCommands` calls
`useMountEngineCommand`, which synchronously reads the current context
store (object metadata, selected records, filters, view ID, etc.) and
writes a `MountedEngineCommandContext` into
`mountedEngineCommandsState`.
- The command is then mounted into `mountedEngineCommandsState`, which
triggers `HeadlessEngineCommandMountRoot` to render the corresponding
headless component from `ENGINE_COMPONENT_KEY_HEADLESS_COMPONENT_MAP`,
wrapped in `CommandMenuItemErrorBoundary`,
`ContextStoreComponentInstanceContext.Provider`, and
`EngineCommandComponentInstanceContext.Provider`.
- Each command component reads its execution context and delegates to
one of the 4 execution patterns: `HeadlessEngineCommandWrapperEffect`
(simple actions), `HeadlessConfirmationModalEngineCommandEffect`
(destructive actions needing confirmation),
`HeadlessNavigateEngineCommand` (GO_TO_* commands), or
`HeadlessOpenSidePanelPageEngineCommand` (SEARCH_RECORDS, ASK_AI,
VIEW_PREVIOUS_AI_CHATS).
- After execution, the command self-unmounts via
`useUnmountEngineCommand`, which removes the entry from
`mountedEngineCommandsState` and stops rendering the component.