From 3c3d837cb7caad6ebc67598a48aab66ec44ac510 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?=
<71827178+bosiraphael@users.noreply.github.com>
Date: Thu, 20 Nov 2025 10:57:33 +0100
Subject: [PATCH] Introduce primary actions (#15826)
- These actions are displayed in the accent color
- Added new context store state
`contextStoreIsPageInEditModeComponentState`
- Reverted the order in which the actions are displayed (the first
action should appear to the right of the top bar action menu)
https://github.com/user-attachments/assets/3a0769cf-61ed-4619-8c4d-e3a01765b63c
---
.../display/components/ActionButton.tsx | 6 +-
.../display/components/ActionDisplay.tsx | 1 +
.../constants/DashboardActionsConfig.tsx | 126 ++--
.../constants/DefaultRecordActionsConfig.tsx | 597 +++++++++---------
.../RecordPageLayoutActionsConfig.tsx | 8 +-
.../constants/WorkflowActionsConfig.tsx | 229 +++----
.../constants/WorkflowRunsActionsConfig.tsx | 67 +-
.../WorkflowVersionsActionsConfig.tsx | 52 +-
.../action-menu/actions/types/ActionConfig.ts | 1 +
.../actions/types/ActionViewType.ts | 1 +
.../PageHeaderActionMenuButtons.tsx | 2 +-
.../action-menu/hooks/useRegisteredActions.ts | 29 +-
.../useExecuteTasksOnAnyLocationChange.ts | 18 +
.../useCopyContextStoreAndActionMenuStates.ts | 16 +
.../hooks/useOpenRecordInCommandMenu.ts | 14 +
...textStoreIsPageInEditModeComponentState.ts | 9 +
.../hooks/useSetIsPageLayoutInEditMode.ts | 26 +-
17 files changed, 633 insertions(+), 569 deletions(-)
create mode 100644 packages/twenty-front/src/modules/context-store/states/contextStoreIsPageInEditModeComponentState.ts
diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionButton.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionButton.tsx
index 97904db1a29..3a3fd0296f7 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionButton.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionButton.tsx
@@ -24,6 +24,8 @@ export const ActionButton = ({
? getActionLabel(action.shortLabel)
: undefined;
+ const buttonAccent = action.isPrimaryCTA ? 'blue' : 'default';
+
return (
<>
{action.shortLabel ? (
@@ -31,7 +33,7 @@ export const ActionButton = ({
Icon={action.Icon}
size="small"
variant="secondary"
- accent="default"
+ accent={buttonAccent}
to={to}
onClick={onClick}
title={shortLabel}
@@ -43,7 +45,7 @@ export const ActionButton = ({
Icon={action.Icon}
size="small"
variant="secondary"
- accent="default"
+ accent={buttonAccent}
to={to}
onClick={onClick}
ariaLabel={label}
diff --git a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDisplay.tsx b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDisplay.tsx
index 1d851151b3c..0676f813329 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDisplay.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/display/components/ActionDisplay.tsx
@@ -15,6 +15,7 @@ export type ActionDisplayProps = {
shortLabel?: MessageDescriptor | string;
description?: MessageDescriptor | string;
Icon: IconComponent;
+ isPrimaryCTA?: boolean;
accent?: MenuItemAccent;
hotKeys?: string[];
};
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DashboardActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DashboardActionsConfig.tsx
index 590520b063c..229a345f2a3 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DashboardActionsConfig.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DashboardActionsConfig.tsx
@@ -1,5 +1,4 @@
import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKeys';
-import { AddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/components/AddToFavoritesSingleRecordAction';
import { CancelDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/CancelDashboardSingleRecordAction';
import { EditDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/EditDashboardSingleRecordAction';
import { SaveDashboardSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/dashboard-actions/components/SaveDashboardSingleRecordAction';
@@ -11,37 +10,16 @@ import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
import { PageLayoutSingleRecordActionKeys } from '@/page-layout/actions/PageLayoutSingleRecordActionKeys';
import { msg } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
-import {
- IconDeviceFloppy,
- IconHeart,
- IconPencil,
- IconX,
-} from 'twenty-ui/display';
+import { IconDeviceFloppy, IconPencil, IconX } from 'twenty-ui/display';
export const DASHBOARD_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
config: {
- [PageLayoutSingleRecordActionKeys.ADD_TO_FAVORITES_READ_MODE]: {
- key: PageLayoutSingleRecordActionKeys.ADD_TO_FAVORITES_READ_MODE,
- label: msg`Add to favorites`,
- shortLabel: msg`Add to favorites`,
- isPinned: true,
- position: 0,
- Icon: IconHeart,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ selectedRecord }) =>
- isDefined(selectedRecord) &&
- !selectedRecord?.isRemote &&
- !isDefined(selectedRecord?.deletedAt),
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- },
[PageLayoutSingleRecordActionKeys.EDIT_LAYOUT]: {
key: PageLayoutSingleRecordActionKeys.EDIT_LAYOUT,
label: msg`Edit Dashboard`,
shortLabel: msg`Edit`,
isPinned: true,
- position: 1,
+ position: 3,
Icon: IconPencil,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -53,29 +31,13 @@ export const DASHBOARD_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
availableOn: [ActionViewType.SHOW_PAGE],
component: ,
},
- [PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: {
- key: PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION,
- label: msg`Cancel Edition`,
- shortLabel: msg`Cancel`,
- isPinned: true,
- position: 0,
- Icon: IconX,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ selectedRecord }) =>
- isDefined(selectedRecord) &&
- !selectedRecord?.isRemote &&
- !isDefined(selectedRecord?.deletedAt) &&
- isDefined(selectedRecord?.pageLayoutId),
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- },
[PageLayoutSingleRecordActionKeys.SAVE_LAYOUT]: {
key: PageLayoutSingleRecordActionKeys.SAVE_LAYOUT,
label: msg`Save Dashboard`,
shortLabel: msg`Save`,
isPinned: true,
- position: 1,
+ isPrimaryCTA: true,
+ position: 4,
Icon: IconDeviceFloppy,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -84,9 +46,26 @@ export const DASHBOARD_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
!selectedRecord?.isRemote &&
!isDefined(selectedRecord?.deletedAt) &&
isDefined(selectedRecord?.pageLayoutId),
- availableOn: [ActionViewType.SHOW_PAGE],
+ availableOn: [ActionViewType.PAGE_EDIT_MODE],
component: ,
},
+ [PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: {
+ key: PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION,
+ label: msg`Cancel Edition`,
+ shortLabel: msg`Cancel`,
+ isPinned: true,
+ position: 5,
+ Icon: IconX,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ shouldBeRegistered: ({ selectedRecord }) =>
+ isDefined(selectedRecord) &&
+ !selectedRecord?.isRemote &&
+ !isDefined(selectedRecord?.deletedAt) &&
+ isDefined(selectedRecord?.pageLayoutId),
+ availableOn: [ActionViewType.PAGE_EDIT_MODE],
+ component: ,
+ },
},
actionKeys: [
NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
@@ -107,61 +86,62 @@ export const DASHBOARD_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
NoSelectionRecordActionKeys.GO_TO_NOTES,
],
propertiesToOverwrite: {
- [NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
+ [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
position: 0,
+ label: msg`Navigate to next dashboard`,
+ },
+ [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
+ position: 1,
+ label: msg`Navigate to previous dashboard`,
+ },
+ [NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
+ position: 2,
label: msg`Create new dashboard`,
},
- [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
- position: 3,
- isPinned: false,
- },
- [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
- position: 4,
- },
[SingleRecordActionKeys.DELETE]: {
- position: 2,
+ position: 6,
label: msg`Delete dashboard`,
},
+ [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
+ position: 7,
+ isPinned: true,
+ },
+ [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
+ position: 8,
+ isPinned: true,
+ },
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
- position: 5,
+ position: 9,
label: msg`Export dashboard`,
},
[SingleRecordActionKeys.DESTROY]: {
- position: 6,
+ position: 10,
label: msg`Permanently destroy dashboard`,
},
[SingleRecordActionKeys.RESTORE]: {
- position: 7,
+ position: 11,
label: msg`Restore dashboard`,
},
- [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
- position: 8,
- label: msg`Navigate to previous dashboard`,
- },
- [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
- position: 9,
- label: msg`Navigate to next dashboard`,
- },
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
- position: 10,
- },
- [NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
- position: 11,
- },
- [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 12,
},
- [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
+ [NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
position: 13,
},
- [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
+ [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 14,
},
- [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
+ [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
position: 15,
},
- [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
position: 16,
},
+ [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
+ position: 17,
+ },
+ [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ position: 18,
+ },
},
});
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx
index 026b85cd052..7aafd48006b 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx
@@ -65,13 +65,37 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
| MultipleRecordsActionKeys,
ActionConfig
> = {
+ [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
+ label: msg`Navigate to next record`,
+ position: 0,
+ isPinned: true,
+ Icon: IconChevronDown,
+ shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
+ availableOn: [ActionViewType.SHOW_PAGE],
+ component: ,
+ },
+ [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
+ label: msg`Navigate to previous record`,
+ position: 1,
+ isPinned: true,
+ Icon: IconChevronUp,
+ shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
+ availableOn: [ActionViewType.SHOW_PAGE],
+ component: ,
+ },
[NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
type: ActionType.Standard,
scope: ActionScope.Object,
key: NoSelectionRecordActionKeys.CREATE_NEW_RECORD,
label: msg`Create new record`,
shortLabel: msg`New record`,
- position: 0,
+ position: 2,
isPinned: true,
Icon: IconPlus,
shouldBeRegistered: ({ objectPermissions, hasAnySoftDeleteFilterOnView }) =>
@@ -81,180 +105,13 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
component: ,
},
- [SingleRecordActionKeys.EXPORT_NOTE_TO_PDF]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.EXPORT_NOTE_TO_PDF,
- label: msg`Export to PDF`,
- shortLabel: msg`Export`,
- position: 1,
- isPinned: false,
- Icon: IconFileExport,
- shouldBeRegistered: ({ selectedRecord, isNoteOrTask }) =>
- isDefined(isNoteOrTask) &&
- isNoteOrTask &&
- isNonEmptyString(selectedRecord?.bodyV2?.blocknote),
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- },
- [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.ADD_TO_FAVORITES,
- label: msg`Add to favorites`,
- shortLabel: msg`Add to favorites`,
- position: 2,
- isPinned: true,
- Icon: IconHeart,
- shouldBeRegistered: ({
- selectedRecord,
- isFavorite,
- hasAnySoftDeleteFilterOnView,
- }) =>
- !selectedRecord?.isRemote &&
- !isFavorite &&
- !isDefined(selectedRecord?.deletedAt) &&
- !hasAnySoftDeleteFilterOnView,
- availableOn: [
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ActionViewType.SHOW_PAGE,
- ],
- component: ,
- },
- [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
- label: msg`Remove from favorites`,
- shortLabel: msg`Remove from favorites`,
- isPinned: true,
- position: 3,
- Icon: IconHeartOff,
- shouldBeRegistered: ({
- selectedRecord,
- isFavorite,
- hasAnySoftDeleteFilterOnView,
- }) =>
- isDefined(selectedRecord) &&
- !selectedRecord?.isRemote &&
- isDefined(isFavorite) &&
- isFavorite &&
- !isDefined(selectedRecord?.deletedAt) &&
- !hasAnySoftDeleteFilterOnView,
- availableOn: [
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ActionViewType.SHOW_PAGE,
- ],
- component: ,
- },
- [SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
- label: msg`Export`,
- shortLabel: msg`Export`,
- position: 4,
- Icon: IconFileExport,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ selectedRecord }) =>
- isDefined(selectedRecord) && !selectedRecord.isRemote,
- availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
- component: ,
- requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
- },
- [SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
- label: msg`Export`,
- shortLabel: msg`Export`,
- position: 4,
- Icon: IconFileExport,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ selectedRecord }) =>
- isDefined(selectedRecord) && !selectedRecord.isRemote,
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
- },
- [MultipleRecordsActionKeys.MERGE]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: MultipleRecordsActionKeys.MERGE,
- label: msg`Merge records`,
- shortLabel: msg`Merge`,
- position: 5,
- Icon: IconArrowMerge,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({
- objectMetadataItem,
- numberOfSelectedRecords,
- objectPermissions,
- }) =>
- isDefined(objectMetadataItem?.duplicateCriteria) &&
- isDefined(numberOfSelectedRecords) &&
- Boolean(objectPermissions.canUpdateObjectRecords) &&
- Boolean(objectPermissions.canDestroyObjectRecords) &&
- numberOfSelectedRecords <= MUTATION_MAX_MERGE_RECORDS,
- availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
- component: ,
- },
- [MultipleRecordsActionKeys.EXPORT]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: MultipleRecordsActionKeys.EXPORT,
- label: msg`Export records`,
- shortLabel: msg`Export`,
- position: 6,
- Icon: IconFileExport,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: () => true,
- availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
- component: ,
- requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
- },
- [NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
- type: ActionType.Standard,
- scope: ActionScope.Object,
- key: NoSelectionRecordActionKeys.IMPORT_RECORDS,
- label: msg`Import records`,
- shortLabel: msg`Import`,
- position: 7,
- Icon: IconFileImport,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
- !hasAnySoftDeleteFilterOnView,
- availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
- component: ,
- requiredPermissionFlag: PermissionFlagType.IMPORT_CSV,
- },
- [NoSelectionRecordActionKeys.EXPORT_VIEW]: {
- type: ActionType.Standard,
- scope: ActionScope.Object,
- key: NoSelectionRecordActionKeys.EXPORT_VIEW,
- label: msg`Export view`,
- shortLabel: msg`Export`,
- position: 8,
- Icon: IconFileExport,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: () => true,
- availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
- component: ,
- requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
- },
[SingleRecordActionKeys.DELETE]: {
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
key: SingleRecordActionKeys.DELETE,
label: msg`Delete`,
shortLabel: msg`Delete`,
- position: 9,
+ position: 3,
Icon: IconTrash,
accent: 'default',
isPinned: true,
@@ -281,7 +138,7 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
key: MultipleRecordsActionKeys.DELETE,
label: msg`Delete records`,
shortLabel: msg`Delete`,
- position: 10,
+ position: 4,
Icon: IconTrash,
accent: 'default',
isPinned: true,
@@ -300,129 +157,13 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
component: ,
},
- [NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
- type: ActionType.Standard,
- scope: ActionScope.Object,
- key: NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
- label: msg`See deleted records`,
- shortLabel: msg`Deleted records`,
- position: 11,
- Icon: IconRotate2,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
- !hasAnySoftDeleteFilterOnView,
- availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
- component: ,
- },
- [NoSelectionRecordActionKeys.CREATE_NEW_VIEW]: {
- type: ActionType.Standard,
- scope: ActionScope.Object,
- key: NoSelectionRecordActionKeys.CREATE_NEW_VIEW,
- label: msg`Create View`,
- shortLabel: msg`Create View`,
- position: 12,
- Icon: IconLayout,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
- !hasAnySoftDeleteFilterOnView,
- availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
- component: ,
- },
- [NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
- type: ActionType.Standard,
- scope: ActionScope.Object,
- key: NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
- label: msg`Hide deleted records`,
- shortLabel: msg`Hide deleted`,
- position: 13,
- Icon: IconEyeOff,
- accent: 'default',
- isPinned: false,
- shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
- isDefined(hasAnySoftDeleteFilterOnView) && hasAnySoftDeleteFilterOnView,
- availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
- component: ,
- },
- [SingleRecordActionKeys.DESTROY]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.DESTROY,
- label: msg`Permanently destroy record`,
- shortLabel: msg`Destroy`,
- position: 14,
- Icon: IconTrashX,
- accent: 'danger',
- isPinned: true,
- shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) =>
- (objectPermissions.canDestroyObjectRecords &&
- !isRemote &&
- isDefined(selectedRecord?.deletedAt)) ??
- false,
- availableOn: [
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ActionViewType.SHOW_PAGE,
- ],
- component: ,
- },
- [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD,
- label: msg`Navigate to previous record`,
- position: 15,
- isPinned: true,
- Icon: IconChevronUp,
- shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- },
- [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD,
- label: msg`Navigate to next record`,
- position: 16,
- isPinned: true,
- Icon: IconChevronDown,
- shouldBeRegistered: ({ isInRightDrawer }) => !isInRightDrawer,
- availableOn: [ActionViewType.SHOW_PAGE],
- component: ,
- },
- [MultipleRecordsActionKeys.DESTROY]: {
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- key: MultipleRecordsActionKeys.DESTROY,
- label: msg`Permanently destroy records`,
- shortLabel: msg`Destroy`,
- position: 17,
- Icon: IconTrashX,
- accent: 'danger',
- isPinned: true,
- shouldBeRegistered: ({
- objectPermissions,
- isRemote,
- hasAnySoftDeleteFilterOnView,
- numberOfSelectedRecords,
- }) =>
- (objectPermissions.canDestroyObjectRecords &&
- !isRemote &&
- isDefined(hasAnySoftDeleteFilterOnView) &&
- hasAnySoftDeleteFilterOnView &&
- isDefined(numberOfSelectedRecords) &&
- numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
- false,
- availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
- component: ,
- },
[SingleRecordActionKeys.RESTORE]: {
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
key: SingleRecordActionKeys.RESTORE,
label: msg`Restore record`,
shortLabel: msg`Restore`,
- position: 18,
+ position: 5,
Icon: IconRefresh,
accent: 'default',
isPinned: true,
@@ -452,7 +193,7 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
key: MultipleRecordsActionKeys.RESTORE,
label: msg`Restore records`,
shortLabel: msg`Restore`,
- position: 19,
+ position: 6,
Icon: IconRefresh,
accent: 'default',
isPinned: true,
@@ -472,13 +213,273 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
component: ,
},
+ [SingleRecordActionKeys.DESTROY]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.DESTROY,
+ label: msg`Permanently destroy record`,
+ shortLabel: msg`Destroy`,
+ position: 7,
+ Icon: IconTrashX,
+ accent: 'danger',
+ isPinned: true,
+ shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) =>
+ (objectPermissions.canDestroyObjectRecords &&
+ !isRemote &&
+ isDefined(selectedRecord?.deletedAt)) ??
+ false,
+ availableOn: [
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ActionViewType.SHOW_PAGE,
+ ],
+ component: ,
+ },
+ [MultipleRecordsActionKeys.DESTROY]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: MultipleRecordsActionKeys.DESTROY,
+ label: msg`Permanently destroy records`,
+ shortLabel: msg`Destroy`,
+ position: 8,
+ Icon: IconTrashX,
+ accent: 'danger',
+ isPinned: true,
+ shouldBeRegistered: ({
+ objectPermissions,
+ isRemote,
+ hasAnySoftDeleteFilterOnView,
+ numberOfSelectedRecords,
+ }) =>
+ (objectPermissions.canDestroyObjectRecords &&
+ !isRemote &&
+ isDefined(hasAnySoftDeleteFilterOnView) &&
+ hasAnySoftDeleteFilterOnView &&
+ isDefined(numberOfSelectedRecords) &&
+ numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
+ false,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ component: ,
+ },
+
+ [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.ADD_TO_FAVORITES,
+ label: msg`Add to favorites`,
+ shortLabel: msg`Add to favorites`,
+ position: 9,
+ isPinned: true,
+ Icon: IconHeart,
+ shouldBeRegistered: ({
+ selectedRecord,
+ isFavorite,
+ hasAnySoftDeleteFilterOnView,
+ }) =>
+ !selectedRecord?.isRemote &&
+ !isFavorite &&
+ !isDefined(selectedRecord?.deletedAt) &&
+ !hasAnySoftDeleteFilterOnView,
+ availableOn: [
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ActionViewType.SHOW_PAGE,
+ ],
+ component: ,
+ },
+ [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.REMOVE_FROM_FAVORITES,
+ label: msg`Remove from favorites`,
+ shortLabel: msg`Remove from favorites`,
+ isPinned: true,
+ position: 10,
+ Icon: IconHeartOff,
+ shouldBeRegistered: ({
+ selectedRecord,
+ isFavorite,
+ hasAnySoftDeleteFilterOnView,
+ }) =>
+ isDefined(selectedRecord) &&
+ !selectedRecord?.isRemote &&
+ isDefined(isFavorite) &&
+ isFavorite &&
+ !isDefined(selectedRecord?.deletedAt) &&
+ !hasAnySoftDeleteFilterOnView,
+ availableOn: [
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ActionViewType.SHOW_PAGE,
+ ],
+ component: ,
+ },
+ [SingleRecordActionKeys.EXPORT_NOTE_TO_PDF]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.EXPORT_NOTE_TO_PDF,
+ label: msg`Export to PDF`,
+ shortLabel: msg`Export`,
+ position: 11,
+ isPinned: false,
+ Icon: IconFileExport,
+ shouldBeRegistered: ({ selectedRecord, isNoteOrTask }) =>
+ isDefined(isNoteOrTask) &&
+ isNoteOrTask &&
+ isNonEmptyString(selectedRecord?.bodyV2?.blocknote),
+ availableOn: [ActionViewType.SHOW_PAGE],
+ component: ,
+ },
+ [SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX,
+ label: msg`Export`,
+ shortLabel: msg`Export`,
+ position: 12,
+ Icon: IconFileExport,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ selectedRecord }) =>
+ isDefined(selectedRecord) && !selectedRecord.isRemote,
+ availableOn: [ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION],
+ component: ,
+ requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
+ },
+ [SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW,
+ label: msg`Export`,
+ shortLabel: msg`Export`,
+ position: 13,
+ Icon: IconFileExport,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ selectedRecord }) =>
+ isDefined(selectedRecord) && !selectedRecord.isRemote,
+ availableOn: [ActionViewType.SHOW_PAGE],
+ component: ,
+ requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
+ },
+ [MultipleRecordsActionKeys.MERGE]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: MultipleRecordsActionKeys.MERGE,
+ label: msg`Merge records`,
+ shortLabel: msg`Merge`,
+ position: 14,
+ Icon: IconArrowMerge,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({
+ objectMetadataItem,
+ numberOfSelectedRecords,
+ objectPermissions,
+ }) =>
+ isDefined(objectMetadataItem?.duplicateCriteria) &&
+ isDefined(numberOfSelectedRecords) &&
+ Boolean(objectPermissions.canUpdateObjectRecords) &&
+ Boolean(objectPermissions.canDestroyObjectRecords) &&
+ numberOfSelectedRecords <= MUTATION_MAX_MERGE_RECORDS,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ component: ,
+ },
+ [MultipleRecordsActionKeys.EXPORT]: {
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ key: MultipleRecordsActionKeys.EXPORT,
+ label: msg`Export records`,
+ shortLabel: msg`Export`,
+ position: 15,
+ Icon: IconFileExport,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: () => true,
+ availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
+ component: ,
+ requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
+ },
+ [NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
+ type: ActionType.Standard,
+ scope: ActionScope.Object,
+ key: NoSelectionRecordActionKeys.IMPORT_RECORDS,
+ label: msg`Import records`,
+ shortLabel: msg`Import`,
+ position: 16,
+ Icon: IconFileImport,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
+ !hasAnySoftDeleteFilterOnView,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ component: ,
+ requiredPermissionFlag: PermissionFlagType.IMPORT_CSV,
+ },
+ [NoSelectionRecordActionKeys.EXPORT_VIEW]: {
+ type: ActionType.Standard,
+ scope: ActionScope.Object,
+ key: NoSelectionRecordActionKeys.EXPORT_VIEW,
+ label: msg`Export view`,
+ shortLabel: msg`Export`,
+ position: 17,
+ Icon: IconFileExport,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: () => true,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ component: ,
+ requiredPermissionFlag: PermissionFlagType.EXPORT_CSV,
+ },
+ [NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
+ type: ActionType.Standard,
+ scope: ActionScope.Object,
+ key: NoSelectionRecordActionKeys.SEE_DELETED_RECORDS,
+ label: msg`See deleted records`,
+ shortLabel: msg`Deleted records`,
+ position: 18,
+ Icon: IconRotate2,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
+ !hasAnySoftDeleteFilterOnView,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ component: ,
+ },
+ [NoSelectionRecordActionKeys.CREATE_NEW_VIEW]: {
+ type: ActionType.Standard,
+ scope: ActionScope.Object,
+ key: NoSelectionRecordActionKeys.CREATE_NEW_VIEW,
+ label: msg`Create View`,
+ shortLabel: msg`Create View`,
+ position: 19,
+ Icon: IconLayout,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
+ !hasAnySoftDeleteFilterOnView,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ component: ,
+ },
+ [NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
+ type: ActionType.Standard,
+ scope: ActionScope.Object,
+ key: NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS,
+ label: msg`Hide deleted records`,
+ shortLabel: msg`Hide deleted`,
+ position: 20,
+ Icon: IconEyeOff,
+ accent: 'default',
+ isPinned: false,
+ shouldBeRegistered: ({ hasAnySoftDeleteFilterOnView }) =>
+ isDefined(hasAnySoftDeleteFilterOnView) && hasAnySoftDeleteFilterOnView,
+ availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
+ component: ,
+ },
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
type: ActionType.Navigation,
scope: ActionScope.Global,
key: NoSelectionRecordActionKeys.GO_TO_WORKFLOWS,
label: msg`Go to Workflows`,
shortLabel: msg`See Workflows`,
- position: 20,
+ position: 21,
Icon: IconSettingsAutomation,
accent: 'default',
isPinned: false,
@@ -495,6 +496,7 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
ActionViewType.INDEX_PAGE_BULK_SELECTION,
ActionViewType.SHOW_PAGE,
+ ActionViewType.PAGE_EDIT_MODE,
],
component: (
,
},
[PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: {
@@ -55,7 +55,7 @@ export const RECORD_PAGE_LAYOUT_ACTIONS_CONFIG =
label: msg`Cancel Edition`,
shortLabel: msg`Cancel`,
isPinned: true,
- position: 2,
+ position: 0,
Icon: IconX,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -65,7 +65,7 @@ export const RECORD_PAGE_LAYOUT_ACTIONS_CONFIG =
!isDefined(selectedRecord?.deletedAt),
// TODO: Once backend is ready, uncomment the line below
// isDefined(selectedRecord?.pageLayoutId),
- availableOn: [ActionViewType.SHOW_PAGE],
+ availableOn: [ActionViewType.PAGE_EDIT_MODE],
component: ,
},
},
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx
index c9e9cd401dc..0ac014ac62a 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowActionsConfig.tsx
@@ -28,6 +28,7 @@ import { msg } from '@lingui/core/macro';
import { AppPath } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
+ IconCopy,
IconHistoryToggle,
IconNoteOff,
IconPlayerPause,
@@ -36,7 +37,6 @@ import {
IconPower,
IconReorder,
IconVersions,
- IconCopy,
} from 'twenty-ui/display';
const areWorkflowTriggerAndStepsDefined = (
@@ -61,7 +61,8 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
label: msg`Activate Workflow`,
shortLabel: msg`Activate`,
isPinned: true,
- position: 1,
+ isPrimaryCTA: true,
+ position: 3,
Icon: IconPower,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -83,7 +84,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
label: msg`Deactivate Workflow`,
shortLabel: msg`Deactivate`,
isPinned: true,
- position: 2,
+ position: 4,
Icon: IconPlayerPause,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -102,7 +103,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
label: msg`Discard Draft`,
shortLabel: msg`Discard Draft`,
isPinned: true,
- position: 3,
+ position: 5,
Icon: IconNoteOff,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -117,86 +118,12 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
],
component: ,
},
- [WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW]: {
- key: WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW,
- label: msg`Duplicate Workflow`,
- shortLabel: msg`Duplicate`,
- isPinned: false,
- position: 10,
- Icon: IconCopy,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
- isDefined(workflowWithCurrentVersion) &&
- isDefined(workflowWithCurrentVersion.currentVersion) &&
- !isDefined(selectedRecord?.deletedAt),
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
- [WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION]: {
- key: WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION,
- label: msg`See active version`,
- shortLabel: msg`See active version`,
- isPinned: false,
- position: 4,
- Icon: IconVersions,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ workflowWithCurrentVersion, selectedRecord }) =>
- (workflowWithCurrentVersion?.statuses?.includes('ACTIVE') || false) &&
- (workflowWithCurrentVersion?.statuses?.includes('DRAFT') || false) &&
- !isDefined(selectedRecord?.deletedAt),
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
- [WorkflowSingleRecordActionKeys.SEE_RUNS]: {
- key: WorkflowSingleRecordActionKeys.SEE_RUNS,
- label: msg`See runs`,
- shortLabel: msg`See runs`,
- isPinned: true,
- position: 5,
- Icon: IconHistoryToggle,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
- isDefined(workflowWithCurrentVersion) &&
- !isDefined(selectedRecord?.deletedAt),
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
- [WorkflowSingleRecordActionKeys.SEE_VERSIONS]: {
- key: WorkflowSingleRecordActionKeys.SEE_VERSIONS,
- label: msg`See versions history`,
- shortLabel: msg`See versions`,
- isPinned: false,
- position: 6,
- Icon: IconVersions,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
- isDefined(workflowWithCurrentVersion) &&
- !isDefined(selectedRecord?.deletedAt),
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
[WorkflowSingleRecordActionKeys.TEST]: {
key: WorkflowSingleRecordActionKeys.TEST,
label: msg`Test Workflow`,
shortLabel: msg`Test`,
isPinned: true,
- position: 7,
+ position: 6,
Icon: IconPlayerPlay,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -217,12 +144,69 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
],
component: ,
},
+
+ [WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION]: {
+ key: WorkflowSingleRecordActionKeys.SEE_ACTIVE_VERSION,
+ label: msg`See active version`,
+ shortLabel: msg`See active version`,
+ isPinned: false,
+ position: 7,
+ Icon: IconVersions,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ shouldBeRegistered: ({ workflowWithCurrentVersion, selectedRecord }) =>
+ (workflowWithCurrentVersion?.statuses?.includes('ACTIVE') || false) &&
+ (workflowWithCurrentVersion?.statuses?.includes('DRAFT') || false) &&
+ !isDefined(selectedRecord?.deletedAt),
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
+ [WorkflowSingleRecordActionKeys.SEE_RUNS]: {
+ key: WorkflowSingleRecordActionKeys.SEE_RUNS,
+ label: msg`See runs`,
+ shortLabel: msg`See runs`,
+ isPinned: true,
+ position: 8,
+ Icon: IconHistoryToggle,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
+ isDefined(workflowWithCurrentVersion) &&
+ !isDefined(selectedRecord?.deletedAt),
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
+ [WorkflowSingleRecordActionKeys.SEE_VERSIONS]: {
+ key: WorkflowSingleRecordActionKeys.SEE_VERSIONS,
+ label: msg`See versions history`,
+ shortLabel: msg`See versions`,
+ isPinned: false,
+ position: 9,
+ Icon: IconVersions,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
+ isDefined(workflowWithCurrentVersion) &&
+ !isDefined(selectedRecord?.deletedAt),
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
+
[WorkflowSingleRecordActionKeys.ADD_NODE]: {
key: WorkflowSingleRecordActionKeys.ADD_NODE,
label: msg`Add a node`,
shortLabel: msg`Add a node`,
isPinned: false,
- position: 8,
+ position: 10,
Icon: IconPlus,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -237,7 +221,7 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
label: msg`Tidy up workflow`,
shortLabel: msg`Tidy up`,
isPinned: false,
- position: 9,
+ position: 11,
Icon: IconReorder,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -247,6 +231,25 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
availableOn: [ActionViewType.SHOW_PAGE],
component: ,
},
+ [WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW]: {
+ key: WorkflowSingleRecordActionKeys.DUPLICATE_WORKFLOW,
+ label: msg`Duplicate Workflow`,
+ shortLabel: msg`Duplicate`,
+ isPinned: false,
+ position: 12,
+ Icon: IconCopy,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ shouldBeRegistered: ({ selectedRecord, workflowWithCurrentVersion }) =>
+ isDefined(workflowWithCurrentVersion) &&
+ isDefined(workflowWithCurrentVersion.currentVersion) &&
+ !isDefined(selectedRecord?.deletedAt),
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
[NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS]: {
type: ActionType.Navigation,
scope: ActionScope.Global,
@@ -294,16 +297,18 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
NoSelectionRecordActionKeys.GO_TO_NOTES,
],
propertiesToOverwrite: {
- [NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
+ [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
position: 0,
+ label: msg`Navigate to next workflow`,
+ },
+ [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
+ position: 1,
+ label: msg`Navigate to previous workflow`,
+ },
+ [NoSelectionRecordActionKeys.CREATE_NEW_RECORD]: {
+ position: 2,
label: msg`Create new workflow`,
},
- [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
- position: 10,
- },
- [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
- position: 11,
- },
[SingleRecordActionKeys.DELETE]: {
position: 12,
label: msg`Delete workflow`,
@@ -312,72 +317,70 @@ export const WORKFLOW_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
position: 13,
label: msg`Delete workflows`,
},
- [SingleRecordActionKeys.DESTROY]: {
+ [SingleRecordActionKeys.ADD_TO_FAVORITES]: {
position: 14,
+ },
+ [SingleRecordActionKeys.REMOVE_FROM_FAVORITES]: {
+ position: 15,
+ },
+ [SingleRecordActionKeys.DESTROY]: {
+ position: 16,
label: msg`Permanently destroy workflow`,
},
[SingleRecordActionKeys.EXPORT_FROM_RECORD_INDEX]: {
- position: 15,
+ position: 17,
label: msg`Export workflow`,
shouldBeRegistered: ({ selectedRecord }) =>
!isDefined(selectedRecord?.deletedAt),
},
[SingleRecordActionKeys.EXPORT_FROM_RECORD_SHOW]: {
- position: 16,
+ position: 18,
label: msg`Export workflow`,
},
[MultipleRecordsActionKeys.EXPORT]: {
- position: 17,
+ position: 19,
label: msg`Export workflows`,
},
[NoSelectionRecordActionKeys.EXPORT_VIEW]: {
- position: 17,
+ position: 20,
label: msg`Export view`,
},
[MultipleRecordsActionKeys.DESTROY]: {
- position: 18,
+ position: 21,
label: msg`Permanently destroy workflows`,
},
- [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: {
- position: 19,
- label: msg`Navigate to previous workflow`,
- },
- [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: {
- position: 20,
- label: msg`Navigate to next workflow`,
- },
[NoSelectionRecordActionKeys.SEE_DELETED_RECORDS]: {
- position: 21,
+ position: 22,
label: msg`See deleted workflows`,
},
[NoSelectionRecordActionKeys.HIDE_DELETED_RECORDS]: {
- position: 22,
+ position: 23,
label: msg`Hide deleted workflows`,
},
[NoSelectionRecordActionKeys.IMPORT_RECORDS]: {
- position: 23,
+ position: 24,
label: msg`Import workflows`,
},
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
- position: 24,
- },
- [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 25,
},
- [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
+ [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 26,
},
- [NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
+ [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
position: 27,
},
- [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
+ [NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
position: 28,
},
- [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
+ [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
position: 29,
},
- [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
position: 30,
},
+ [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ position: 31,
+ },
},
});
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx
index 2323879eda9..9266a35e0dd 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowRunsActionsConfig.tsx
@@ -18,11 +18,44 @@ import {
export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
config: {
+ [WorkflowRunSingleRecordActionKeys.SEE_VERSION]: {
+ key: WorkflowRunSingleRecordActionKeys.SEE_VERSION,
+ label: msg`See version`,
+ shortLabel: msg`See version`,
+ position: 0,
+ isPinned: true,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ Icon: IconVersions,
+ shouldBeRegistered: () => true,
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
+
+ [WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW]: {
+ key: WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW,
+ label: msg`See workflow`,
+ shortLabel: msg`See workflow`,
+ position: 1,
+ isPinned: true,
+ type: ActionType.Standard,
+ scope: ActionScope.RecordSelection,
+ Icon: IconSettingsAutomation,
+ shouldBeRegistered: () => true,
+ availableOn: [
+ ActionViewType.SHOW_PAGE,
+ ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
+ ],
+ component: ,
+ },
[WorkflowRunSingleRecordActionKeys.STOP]: {
key: WorkflowRunSingleRecordActionKeys.STOP,
label: msg`Stop`,
shortLabel: msg`Stop`,
- position: 0,
+ position: 2,
isPinned: true,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
@@ -42,38 +75,6 @@ export const WORKFLOW_RUNS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig({
],
component: ,
},
- [WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW]: {
- key: WorkflowRunSingleRecordActionKeys.SEE_WORKFLOW,
- label: msg`See workflow`,
- shortLabel: msg`See workflow`,
- position: 1,
- isPinned: true,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- Icon: IconSettingsAutomation,
- shouldBeRegistered: () => true,
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
- [WorkflowRunSingleRecordActionKeys.SEE_VERSION]: {
- key: WorkflowRunSingleRecordActionKeys.SEE_VERSION,
- label: msg`See version`,
- shortLabel: msg`See version`,
- position: 2,
- isPinned: true,
- type: ActionType.Standard,
- scope: ActionScope.RecordSelection,
- Icon: IconVersions,
- shouldBeRegistered: () => true,
- availableOn: [
- ActionViewType.SHOW_PAGE,
- ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
- ],
- component: ,
- },
},
actionKeys: [
SingleRecordActionKeys.ADD_TO_FAVORITES,
diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.tsx
index c2e57129414..5f7e1e17d28 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.tsx
+++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/WorkflowVersionsActionsConfig.tsx
@@ -26,22 +26,22 @@ import {
export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
{
config: {
- [WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT]: {
- key: WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT,
- label: msg`Use as draft`,
- shortLabel: msg`Use as draft`,
+ [WorkflowVersionSingleRecordActionKeys.SEE_RUNS]: {
+ key: WorkflowVersionSingleRecordActionKeys.SEE_RUNS,
+ label: msg`See runs`,
+ shortLabel: msg`See runs`,
position: 1,
isPinned: true,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
- Icon: IconPencil,
- shouldBeRegistered: ({ selectedRecord }) =>
- isDefined(selectedRecord) && selectedRecord.status !== 'DRAFT',
+ Icon: IconHistoryToggle,
+ shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
+ isDefined(workflowWithCurrentVersion),
availableOn: [
ActionViewType.SHOW_PAGE,
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
- component: ,
+ component: ,
},
[WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW]: {
key: WorkflowVersionSingleRecordActionKeys.SEE_WORKFLOW,
@@ -60,22 +60,22 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
],
component: ,
},
- [WorkflowVersionSingleRecordActionKeys.SEE_RUNS]: {
- key: WorkflowVersionSingleRecordActionKeys.SEE_RUNS,
- label: msg`See runs`,
- shortLabel: msg`See runs`,
+ [WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT]: {
+ key: WorkflowVersionSingleRecordActionKeys.USE_AS_DRAFT,
+ label: msg`Use as draft`,
+ shortLabel: msg`Use as draft`,
position: 3,
isPinned: true,
type: ActionType.Standard,
scope: ActionScope.RecordSelection,
- Icon: IconHistoryToggle,
- shouldBeRegistered: ({ workflowWithCurrentVersion }) =>
- isDefined(workflowWithCurrentVersion),
+ Icon: IconPencil,
+ shouldBeRegistered: ({ selectedRecord }) =>
+ isDefined(selectedRecord) && selectedRecord.status !== 'DRAFT',
availableOn: [
ActionViewType.SHOW_PAGE,
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
],
- component: ,
+ component: ,
},
[WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS]: {
key: WorkflowVersionSingleRecordActionKeys.SEE_VERSIONS,
@@ -99,7 +99,7 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
key: NoSelectionWorkflowRecordActionKeys.GO_TO_RUNS,
label: msg`Go to runs`,
shortLabel: msg`See runs`,
- position: 15,
+ position: 14,
Icon: IconHistoryToggle,
accent: 'default',
isPinned: true,
@@ -172,30 +172,30 @@ export const WORKFLOW_VERSIONS_ACTIONS_CONFIG = inheritActionsFromDefaultConfig(
label: msg`Navigate to next version`,
},
[NoSelectionRecordActionKeys.GO_TO_WORKFLOWS]: {
- position: 14,
+ position: 15,
isPinned: true,
},
[NoSelectionRecordActionKeys.GO_TO_PEOPLE]: {
- position: 15,
- },
- [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 16,
},
- [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
+ [NoSelectionRecordActionKeys.GO_TO_COMPANIES]: {
position: 17,
},
- [NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
+ [NoSelectionRecordActionKeys.GO_TO_OPPORTUNITIES]: {
position: 18,
},
- [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
+ [NoSelectionRecordActionKeys.GO_TO_DASHBOARDS]: {
position: 19,
},
- [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
+ [NoSelectionRecordActionKeys.GO_TO_SETTINGS]: {
position: 20,
},
- [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ [NoSelectionRecordActionKeys.GO_TO_TASKS]: {
position: 21,
},
+ [NoSelectionRecordActionKeys.GO_TO_NOTES]: {
+ position: 22,
+ },
},
},
);
diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/ActionConfig.ts b/packages/twenty-front/src/modules/action-menu/actions/types/ActionConfig.ts
index c6db06d5709..a4ae40fc102 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/types/ActionConfig.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/types/ActionConfig.ts
@@ -17,6 +17,7 @@ export type ActionConfig = {
position: number;
Icon: IconComponent;
isPinned?: boolean;
+ isPrimaryCTA?: boolean;
accent?: MenuItemAccent;
availableOn?: ActionViewType[];
shouldBeRegistered: (params: ShouldBeRegisteredFunctionParams) => boolean;
diff --git a/packages/twenty-front/src/modules/action-menu/actions/types/ActionViewType.ts b/packages/twenty-front/src/modules/action-menu/actions/types/ActionViewType.ts
index db450f084fb..19fd1d2d222 100644
--- a/packages/twenty-front/src/modules/action-menu/actions/types/ActionViewType.ts
+++ b/packages/twenty-front/src/modules/action-menu/actions/types/ActionViewType.ts
@@ -4,4 +4,5 @@ export enum ActionViewType {
INDEX_PAGE_SINGLE_RECORD_SELECTION = 'INDEX_PAGE_SINGLE_RECORD_SELECTION',
INDEX_PAGE_NO_SELECTION = 'INDEX_PAGE_NO_SELECTION',
SHOW_PAGE = 'SHOW_PAGE',
+ PAGE_EDIT_MODE = 'PAGE_EDIT_MODE',
}
diff --git a/packages/twenty-front/src/modules/action-menu/components/PageHeaderActionMenuButtons.tsx b/packages/twenty-front/src/modules/action-menu/components/PageHeaderActionMenuButtons.tsx
index 7f34f05e0e0..7e7d4830fcb 100644
--- a/packages/twenty-front/src/modules/action-menu/components/PageHeaderActionMenuButtons.tsx
+++ b/packages/twenty-front/src/modules/action-menu/components/PageHeaderActionMenuButtons.tsx
@@ -15,7 +15,7 @@ export const PageHeaderActionMenuButtons = () => {
const { actions } = useContext(ActionMenuContext);
const theme = useTheme();
- const pinnedActions = actions.filter((entry) => entry.isPinned);
+ const pinnedActions = actions.filter((entry) => entry.isPinned).toReversed();
const actionsWithPositionForAnimation = pinnedActions.map(
(action, index) => ({
diff --git a/packages/twenty-front/src/modules/action-menu/hooks/useRegisteredActions.ts b/packages/twenty-front/src/modules/action-menu/hooks/useRegisteredActions.ts
index da16ffc8689..1a0e214bb1e 100644
--- a/packages/twenty-front/src/modules/action-menu/hooks/useRegisteredActions.ts
+++ b/packages/twenty-front/src/modules/action-menu/hooks/useRegisteredActions.ts
@@ -5,6 +5,7 @@ import { type ShouldBeRegisteredFunctionParams } from '@/action-menu/actions/typ
import { getActionConfig } from '@/action-menu/actions/utils/getActionConfig';
import { getActionViewType } from '@/action-menu/actions/utils/getActionViewType';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
+import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
@@ -29,6 +30,10 @@ export const useRegisteredActions = (
contextStoreCurrentViewTypeComponentState,
);
+ const isFullTabWidgetInEditMode = useRecoilComponentValue(
+ contextStoreIsPageInEditModeComponentState,
+ );
+
const isRecordPageLayoutEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_ENABLED,
);
@@ -59,13 +64,23 @@ export const useRegisteredActions = (
const permissionMap = usePermissionFlagMap();
- const actionsToRegister = isDefined(viewType)
- ? Object.values(actionsConfig).filter(
- (action) =>
- action.availableOn?.includes(viewType) ||
- action.availableOn?.includes(ActionViewType.GLOBAL),
- )
- : [];
+ const actionsToRegister = Object.values(actionsConfig).filter((action) => {
+ if (isFullTabWidgetInEditMode) {
+ return (
+ isDefined(action.availableOn) &&
+ action.availableOn.includes(ActionViewType.PAGE_EDIT_MODE)
+ );
+ }
+
+ if (isDefined(viewType)) {
+ return (
+ action.availableOn?.includes(viewType) ||
+ action.availableOn?.includes(ActionViewType.GLOBAL)
+ );
+ }
+
+ return action.availableOn?.includes(ActionViewType.GLOBAL);
+ });
const actions = actionsToRegister
.filter((action) => {
diff --git a/packages/twenty-front/src/modules/app/hooks/useExecuteTasksOnAnyLocationChange.ts b/packages/twenty-front/src/modules/app/hooks/useExecuteTasksOnAnyLocationChange.ts
index a4f1e54d3bb..a028fbf8d45 100644
--- a/packages/twenty-front/src/modules/app/hooks/useExecuteTasksOnAnyLocationChange.ts
+++ b/packages/twenty-front/src/modules/app/hooks/useExecuteTasksOnAnyLocationChange.ts
@@ -1,8 +1,25 @@
+import { useRecoilCallback } from 'recoil';
+
+import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
+import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
export const useExecuteTasksOnAnyLocationChange = () => {
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
+ const resetIsPageInEditMode = useRecoilCallback(
+ ({ set }) =>
+ () => {
+ set(
+ contextStoreIsPageInEditModeComponentState.atomFamily({
+ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
+ }),
+ false,
+ );
+ },
+ [],
+ );
+
/**
* Be careful to put idempotent tasks here.
*
@@ -10,6 +27,7 @@ export const useExecuteTasksOnAnyLocationChange = () => {
*/
const executeTasksOnAnyLocationChange = () => {
closeAnyOpenDropdown();
+ resetIsPageInEditMode();
};
return { executeTasksOnAnyLocationChange };
diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCopyContextStoreAndActionMenuStates.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCopyContextStoreAndActionMenuStates.ts
index 3f34a8a20d2..d96badef654 100644
--- a/packages/twenty-front/src/modules/command-menu/hooks/useCopyContextStoreAndActionMenuStates.ts
+++ b/packages/twenty-front/src/modules/command-menu/hooks/useCopyContextStoreAndActionMenuStates.ts
@@ -4,6 +4,7 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
+import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useRecoilCallback } from 'recoil';
@@ -137,6 +138,21 @@ export const useCopyContextStoreStates = () => {
}),
contextStoreCurrentViewType,
);
+
+ const contextStoreIsFullTabWidgetInEditMode = snapshot
+ .getLoadable(
+ contextStoreIsPageInEditModeComponentState.atomFamily({
+ instanceId: instanceIdToCopyFrom,
+ }),
+ )
+ .getValue();
+
+ set(
+ contextStoreIsPageInEditModeComponentState.atomFamily({
+ instanceId: instanceIdToCopyTo,
+ }),
+ contextStoreIsFullTabWidgetInEditMode,
+ );
},
[],
);
diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts
index 8d0a66392b8..c881ba9e2e6 100644
--- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts
+++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts
@@ -8,6 +8,7 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainCo
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
+import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
@@ -141,6 +142,19 @@ export const useOpenRecordInCommandMenu = () => {
.getValue(),
);
+ set(
+ contextStoreIsPageInEditModeComponentState.atomFamily({
+ instanceId: pageComponentInstanceId,
+ }),
+ snapshot
+ .getLoadable(
+ contextStoreIsPageInEditModeComponentState.atomFamily({
+ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID,
+ }),
+ )
+ .getValue(),
+ );
+
const currentMorphItems = snapshot
.getLoadable(commandMenuNavigationMorphItemsByPageState)
.getValue();
diff --git a/packages/twenty-front/src/modules/context-store/states/contextStoreIsPageInEditModeComponentState.ts b/packages/twenty-front/src/modules/context-store/states/contextStoreIsPageInEditModeComponentState.ts
new file mode 100644
index 00000000000..2a9b86c817d
--- /dev/null
+++ b/packages/twenty-front/src/modules/context-store/states/contextStoreIsPageInEditModeComponentState.ts
@@ -0,0 +1,9 @@
+import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const contextStoreIsPageInEditModeComponentState =
+ createComponentState({
+ key: 'contextStoreIsPageInEditModeComponentState',
+ defaultValue: false,
+ componentInstanceContext: ContextStoreComponentInstanceContext,
+ });
diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useSetIsPageLayoutInEditMode.ts b/packages/twenty-front/src/modules/page-layout/hooks/useSetIsPageLayoutInEditMode.ts
index 3c6eb022e04..9b9beeb0131 100644
--- a/packages/twenty-front/src/modules/page-layout/hooks/useSetIsPageLayoutInEditMode.ts
+++ b/packages/twenty-front/src/modules/page-layout/hooks/useSetIsPageLayoutInEditMode.ts
@@ -1,6 +1,5 @@
-import { SingleRecordActionKeys } from '@/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey';
-import { forceRegisteredActionsByKeyState } from '@/action-menu/actions/states/forceRegisteredActionsMapComponentState';
-import { PageLayoutSingleRecordActionKeys } from '@/page-layout/actions/PageLayoutSingleRecordActionKeys';
+import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
+import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
@@ -18,23 +17,20 @@ export const useSetIsPageLayoutInEditMode = (pageLayoutIdFromProps: string) => {
pageLayoutId,
);
+ const contextStoreIsFullTabWidgetInEditModeState =
+ useRecoilComponentCallbackState(
+ contextStoreIsPageInEditModeComponentState,
+ MAIN_CONTEXT_STORE_INSTANCE_ID,
+ );
+
const setIsPageLayoutInEditMode = useRecoilCallback(
({ set }) =>
(value: boolean) => {
set(isPageLayoutInEditModeState, value);
- set(forceRegisteredActionsByKeyState, (prev) => ({
- ...prev,
- [PageLayoutSingleRecordActionKeys.EDIT_LAYOUT]: !value,
- [PageLayoutSingleRecordActionKeys.SAVE_LAYOUT]: value,
- [PageLayoutSingleRecordActionKeys.CANCEL_LAYOUT_EDITION]: value,
- [PageLayoutSingleRecordActionKeys.ADD_TO_FAVORITES_READ_MODE]: !value,
- [SingleRecordActionKeys.ADD_TO_FAVORITES]: value,
- [SingleRecordActionKeys.DELETE]: !value,
- [SingleRecordActionKeys.NAVIGATE_TO_PREVIOUS_RECORD]: !value,
- [SingleRecordActionKeys.NAVIGATE_TO_NEXT_RECORD]: !value,
- }));
+
+ set(contextStoreIsFullTabWidgetInEditModeState, value);
},
- [isPageLayoutInEditModeState],
+ [isPageLayoutInEditModeState, contextStoreIsFullTabWidgetInEditModeState],
);
return { setIsPageLayoutInEditMode };